Skip to content

Proposal: support uv as an optional backend for venv creation & installs #451

Closed

Description

@gilgamezh

Context

Relates to #423 (PEP 723 support).

uv has become the de-facto fast Python package/venv manager and overlaps heavily with what fades does. This issue does two things:

  1. Documents a feature-by-feature comparison of every fades command/feature against its uv equivalent.
  2. Proposes a concrete first step: use uv as an optional backend (create the venv + install deps with uv instead of pip/venv when uv is available), keeping all the rest of fades' behaviour unchanged.

The comparison is meant to inform a future decision about fades' direction. For now the only proposed action is the optional-backend work below.

fades → uv feature comparison

Docs read: uv latest (scripts, run, CLI reference) and fades master README/CLI.

CLI options

fades option What it does uv equivalent Same?
fades script.py Run script in managed venv uv run script.py ✅
#!/usr/bin/env fades Self-executing script #!/usr/bin/env -S uv run --script ✅
fades http://.../script.py Run script from pastebin/URL uv run https://.../script.py ✅ (fades also resolves pastebin/gist/linkode share pages)
import x # fades Mark dep via import comment ❌ none — uv uses PEP 723 or --with ❌ fades-only
docstring deps Deps in docstring after fades line ❌ none ❌ fades-only
-d, --dependency Add dep on CLI (repeatable) uv run --with PKG ✅
-r, --requirement Install from requirements file(s) uv run --with-requirements req.txt ✅
-p, --python Pick interpreter uv run --python 3.11 (also auto-downloads) ✅ (uv superset)
-i, --ipython Use IPython REPL uv run --with ipython ipython ⚠️ manual, not a flag
-a, --autoimport Auto-import deps in REPL ❌ none ❌ fades-only
(no script) Open REPL in the env uv run python ✅
--system-site-packages Venv sees system packages uv venv --system-site-packages (create-time only) ⚠️ partial
-x, --exec Run any installed executable in venv uv run <exe> / uvx <exe> ✅
-m, --module Run module uv run -m module ✅
--check-updates / -U Re-resolve to newest uv run --refresh / --upgrade ✅
--freeze FILE Dump pip freeze of env uv lock --script (.py.lock) / uv export ⚠️ different format
--no-precheck-availability Skip PyPI existence check n/a (uv resolves directly) —
--avoid-pip-upgrade Don't auto-upgrade pip n/a (no pip) —
--pip-options Pass-through pip args native flags only; no generic passthrough ⚠️ partial
--python-options Pass-through python args uv run python <opts> ✅
--virtualenv-options Pass-through virtualenv args limited uv venv flags ⚠️ partial
-v/--verbose, -q/--quiet Verbosity uv -v / uv -q ✅
-V, --version Version + system info uv version ✅

Repository / source support

fades feature uv equivalent Same?
pypi::pkg default PyPI ✅
vcs::git+https://... --with "pkg @ git+https://..." ✅
file:///local/project --with /local/path ✅
alternate index uv run --index URL ✅

Venv lifecycle / maintenance

fades feature uv equivalent Same?
--get-venv-dir / --where ❌ envs live opaquely in uv cache ❌ fades-only
--rm UUID uv cache clean / prune (no per-UUID removal) ⚠️ partial
--clean-unused-venvs=N uv cache prune (no day-based policy) ⚠️ partial
per-depset venv reuse automatic (keyed on resolved depset) ✅

Configuration

fades feature uv equivalent Same?
.ini at system/user/project, merged uv.toml / [tool.uv] + env vars, layered ✅ (different format)
config-injected global deps into every env ❌ none ❌ fades-only

Genuine fades-only features (uv can't replicate today)

  1. # fades import-comment & docstring dependency marking (fades' signature feature).
  2. --autoimport into the REPL.
  3. --where/--get-venv-dir and --rm UUID (per-venv management by UUID).
  4. --clean-unused-venvs=N day-based GC.
  5. Config-injected global dependencies.

How the comparison shifts once fades supports PEP 723 (#423)

Once #423 lands, fades will read # /// script ... /// blocks. This narrows the gap meaningfully:

  • Interoperability: fades and uv (and pipx/pip-run) could run each other's scripts. The # fades comment convention stays as the fades-native way; PEP 723 becomes the portable way.
  • Dependency declaration moves from "fades-only" toward "✅ same standard" — the biggest semantic difference between the tools largely disappears.
  • What remains distinctly fades: the # fades comment style, --autoimport, explicit per-UUID venv management (--where/--rm), day-based venv GC, and config-injected global deps.

So post-#423 the two tools converge on the input format but still differ on venv management philosophy (fades exposes/manages individual venvs by UUID; uv hides them in a cache).

Proposal: use uv as an optional backend

Add uv as an optional dependency. When uv is present on the system, fades uses it for the parts where it's strictly better; when it's absent, fades behaves exactly as today.

Concretely:

  • Venv creation: use uv venv instead of the venv/virtualenv path.
  • Dependency installation: use uv pip install (or uv add) instead of pip.
  • Everything else stays fades: dependency parsing (# fades comments, docstring, -d/-r), venv indexing by UUID, reuse logic, --where/--rm/--clean-unused-venvs, config files, REPL/--autoimport, etc.

This gives fades users uv's speed and built-in Python provisioning without changing fades' UX or losing any of its unique features, and it's a low-risk, incremental step.

Open questions to resolve before implementing:

  • Detection & opt-out: auto-detect uv on PATH, with a config flag / env var to force on/off?
  • Does uv's venv layout stay compatible with fades' UUID indexing and reuse-by-hash logic?
  • How to map --pip-options / --virtualenv-options onto uv invocations.
  • --freeze semantics when using uv.

Scope of this issue

This issue is for documenting the comparison and discussing the optional-backend approach. No implementation is proposed here yet beyond the outline above — the broader question of fades' long-term direction vs. uv is intentionally left for a future decision.

Activity

  1. facundobatista commented on Jun 20, 2026

    @facundobatista
    Member

    Great comparison and proposal, thanks!

    I think we can totally have uv as an optional backend. I just wouldn't switch to uv by default if the binary is present.

    uv has not the same behaviour than pip, which will make fades behaviour to change (the final virtual environments content) only by the presence of an external process.

    I propose to have a envvar/option pair (FADES_USE_UV and --use-uv) that enables the behaviour (and if uv is not in the path is an error... we can have a --uv-path to specify the location of an executable too).

    Taking that in consideration, some comments around the proposal and questions...

    Venv creation: use uv venv instead of the venv/virtualenv path.

    Yes, but only if uv usage is explicitly indicated.

    Dependency installation: use uv pip install (or uv add) instead of pip.

    Yes, but only if uv usage is explicitly indicated. Note that in this case we should also skip the initial "package availability" step, which was created to fail early, and uv covers that.

    Everything else stays fades: dependency parsing (# fades comments, docstring, -d/-r), venv indexing by UUID, reuse logic, --where/--rm/--clean-unused-venvs, config files, REPL/--autoimport, etc.

    Yes!

    Detection & opt-out: auto-detect uv on PATH, with a config flag / env var to force on/off?

    In my proposal it has to be explicitly indicated.

    Does uv's venv layout stay compatible with fades' UUID indexing and reuse-by-hash logic?

    We wouldn't be using the "venvs directory management" from uv, just create the venv where we say so (in fades current structure) and install packages there. We wouldn't even be using packages removal or updates.

    How to map --pip-options / --virtualenv-options onto uv invocations

    If uv is indicated to use, and --pip-options or --virtualenv-options are indicated, it's an error. We should also add a --uv-options to customize uv runs.

    --freeze semantics when using uv.

    Probably in this case we should produce uv's lock file, what do you think?

  2. gilgamezh commented on Jun 20, 2026

    @gilgamezh
    ContributorAuthor

    I think you are right, it's also a nicer and a bit more conservative way to incorporate a new backend. If in 1 year we see this is always the default we change it. Also based on the news who knows what will happen with a project that was promised to be OSS forever.. so good to be conservative.

    I'll make it optional

    In my proposal it has to be explicitly indicated.

    You mean never discover? or --uv-path should be optional?

  3. gilgamezh commented on Jun 20, 2026

    @gilgamezh
    ContributorAuthor

    You mean never discover? or --uv-path should be optional?

    Updated #454 I left --uv-path optional, seems the simplest

  4. facundobatista commented on Jun 20, 2026

    @facundobatista
    Member

    Yes, --uv-path is optional. If present, we use it, else we use "uv".

  5. gilgamezh commented on Jun 21, 2026

    @gilgamezh
    ContributorAuthor

    Cool, this is all implemented in #454 now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions