Add optional uv backend (opt-in via --use-uv / config)#454
Conversation
When a `uv` binary is found in PATH, fades now uses it as a faster backend to create the virtualenv (`uv venv`) and install dependencies (`uv pip install`), instead of stdlib `venv` + `pip`. Detection is via `helpers.get_uv_exe()` (shutil.which); fades does not depend on the `uv` PyPI package, since it runs against the system Python. The new `UvManager` mirrors `PipManager`'s interface (install/get_version/ freeze) so it plugs into the existing dispatch in `envbuilder.create_venv`. A `--no-uv` flag forces the classic pip backend even when uv is available. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Benchmark: uv vs pipWall time for
|
| Scenario | Backend | Wall time |
|---|---|---|
| Fresh venv (create + install) | pip (--no-uv) |
3.43s |
| Fresh venv (create + install) | uv | 0.39s |
| Existing/cached venv (no install) | either | ~0.16s |
≈ 8.7× faster on the create+install path.
jupyterlab (~60 transitive deps)
| Scenario | Backend | Wall time |
|---|---|---|
| Fresh, cold package cache (incl. downloads) | pip (--no-uv) |
16.63s |
| Fresh, cold package cache (incl. downloads) | uv | 12.70s |
| Fresh, warm package cache (already downloaded) | pip (--no-uv) |
13.97s |
| Fresh, warm package cache (already downloaded) | uv | 1.62s |
| Existing/cached venv (no install) | either | 0.92s |
- Cold cache (first-ever install): ≈ 1.3×. On a first install both backends spend most of the time downloading the same ~60 wheels over the network, which dominates and masks the backend difference.
- Warm cache (steady state, packages already fetched once): 13.97s → 1.62s ≈ 8.6× faster — this isolates uv's actual resolve+install speed and matches the django ratio.
Takeaways
- uv is ~8–9× faster on the install itself; the small cold-cache jupyterlab number is just because raw download bandwidth (identical for both backends) dominates the very first run.
- The cached-venv path is unchanged regardless of backend — no install happens, fades just reuses the venv.
Single run per row, warm/cold as noted; numbers will vary with network and machine.
- freeze: drop `uv pip freeze --quiet`, which empties the whole output on some uv versions (producing a 0-byte freeze file); filter uv's info line instead, mirroring get_version. - freeze: pick the backend from the venv's `pip_installed` metadata, not the current run's flags. A uv-built venv has no pip, and the cache key doesn't include the backend, so it can be reused under --no-uv / when uv is gone; PipManager.freeze would then crash on a missing pip. - uv `--python`: probe for `python` / `python.exe` so the uv backend works on Windows (uv treats --python as a literal path, no PATHEXT resolution). - main: fall back to the pip backend when `--venv-options` contains flags `uv venv` doesn't accept (e.g. --symlinks), keeping documented usage working. - CI: make the --no-uv check also assert fades' own exit status (a crash could silently pass the negated grep), and add a --freeze regression step. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Install uv in the native-windows job and add the uv auto-detect, --no-uv, and --freeze checks there. This confirms the uv path works on Windows, where the venv interpreter is Scripts\python.exe (regression coverage for the uv --python resolution). The grep-based steps run under bash (Git Bash), since the Windows runner defaults to PowerShell. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pip-installed uv wasn't reliably on PATH for fades' shutil.which on the Windows runner, so the auto-detect step couldn't confirm the uv backend. Use the official astral-sh/setup-uv action to guarantee uv on PATH, and capture + print fades output so failures in the uv path are diagnosable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Windows interpreter path was interpolated raw into the bash steps, where unquoted backslashes were stripped (command not found). Single-quote it and convert backslashes to forward slashes (accepted by Git Bash) so the uv path is actually exercised on Windows. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
uv seeds nothing by default, so uv-built venvs lacked pip and setuptools that the classic 'python -m venv' backend provides, breaking packages that expect them present. Pass --seed so the venv ships pip (+ setuptools, per the Python version, matching stdlib venv) at negligible cost. Note: this does not restore pkg_resources for packages like azure-cli, since uv seeds a recent setuptools (>=81) that removed it -- the same is true for the pip backend on Python 3.12+. Documented the 'setuptools<81' workaround. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
uv venvs now seed pip/setuptools (
|
Per discussion on #451, uv is no longer auto-detected/used just because it's on PATH (it produces different venvs than pip, so behaviour shouldn't change based on an external binary's presence). uv is now explicit: - `--use-uv` enables it; `--uv-path PATH` points at a specific uv (implies --use-uv); without a path uv is looked up in PATH and validated. If uv is requested but not found, fades errors out. - `use_uv=true` in any fades config file enables it permanently (machine/user/ repo `.fades.ini`); a CLI flag still wins. - `--pip-options`/`--venv-options` together with --use-uv is an error; new `--uv-pip-options` passes options to `uv pip install`. - the PyPI availability pre-check is skipped under uv (uv resolves directly). - `--freeze` uses plain pip freeze (uv venvs are seeded with pip), dropping the uv-specific freeze code. Detection/resolution lives in main._resolve_uv_backend (unit-tested); README and man page document the opt-in model and how to enable it always via config. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Switched uv from default to opt-inFollowing the discussion in #451 (thanks @facundobatista), uv is no longer auto-detected/used when present. It's now explicit:
The All green on Linux/macOS/Windows. |
Wrap get_confdir() in Path() so the one-liner works whether get_confdir returns a str (older fades) or a Path, and add -W ignore to silence the pkg_resources deprecation warning emitted by older installed versions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Adds
uvas an opt-in backend for venv creation and dependency installs. By default fades keeps usingvenv/pip; uv is used only when explicitly requested. Everything else (dependency parsing, venv indexing/cache, GC,--where/--rm, REPL/autoimport, config files) is unchanged.Closes #451.
Enabling uv
--use-uv— use uv for this run.--uv-path PATH— point at a specific uv executable (implies--use-uv); otherwise uv is looked up inPATHand validated. If uv is requested but not found, fades errors out.use_uv=truein any fades config file (/etc/fades/fades.ini, the per-userfades.ini, or a repo-local./.fades.ini) enables it permanently; a CLI flag still wins.Design notes
PipManager. fades does not depend on theuvPyPI package (it runs against the system Python); it uses a uv binary on PATH.UvManagermirrorsPipManager'sinstall/get_version, dispatched fromenvbuilder.create_venv. Backend resolution + option validation live inmain._resolve_uv_backend(unit-tested).uv venv --seedso the venv ships pip (and setuptools, per Python version) like the classic backend.--pip-options/--venv-optionsare errors (use--uv-pip-options);--freezeuses plain pip freeze on the seeded venv.Tests / CI
_resolve_uv_backend(enabled/disabled,--uv-path, not-found, option conflicts),UvManager, envbuilder dispatch,get_uv_exe.--use-uv= uv, conflict + bogus-path errors, and--freezeon a uv venv. Windows confirms the uv path end-to-end.🤖 Generated with Claude Code