uv — The Unified Python Toolchain

uv — The Unified Python Toolchain

uv is an extremely fast Python package and project manager written in Rust by Astral (the Ruff team). It is a single static binary that consolidates the jobs previously spread across pip, pip-tools, virtualenv/venv, pyenv, pipx, poetry, twine, and build — typically 10-100x faster than the pip/pip-tools baseline. This reference covers the five pillars named in the brief: project/workspace management, the universal lockfile, Python-version install/pinning, the tool/pipx replacement, and the pip-compatible interface — plus the build backend, PEP 723 scripts, and configuration/caching.

For everyday Python idioms, async, typing, and a uv quick-start cheat sheet, see references/python-patterns.md. For static type checkers (mypy/Pyright/ ty/Pyrefly) see references/python-static-type-checking.md; for pytest/ Hypothesis see references/python-testing.md. This file is the deep, tool-specific reference for uv itself. Defer to the official docs (https://docs.astral.sh/uv/) as the source of truth for exact flags/versions.

Command surface at a glance

uv <command> top-level commands (uv 0.11.x):

Group Commands
Project init, add, remove, sync, lock, run, tree, export, version, build, publish
Python mgmt python install/pin/list/find/uninstall/dir
Tools (pipx) tool install/run (alias uvx)/upgrade/list/uninstall/dir/update-shell
pip interface pip install/compile/sync/freeze/list/show/tree/uninstall/check
Env / misc venv, cache (clean/prune/dir), self update, auth, format, audit

Two front-doors that confuse newcomers: the project interface (uv add, uv sync, uv run — operates on pyproject.toml + uv.lock, the recommended path) and the pip interface (uv pip ... — a drop-in low-level imperative layer with no lockfile). Don’t mix them on the same environment expecting managed state; the project interface owns uv.lock, the pip interface does not.


1. Project & workspace management

Single project

uv init myproject          # app layout: pyproject.toml, main.py, .python-version, .gitignore
uv init --lib mylib        # library layout: src/mylib/__init__.py + [build-system]
uv init --package myapp    # packaged app (installable, has [build-system])
uv add requests 'httpx>=0.27'
uv add --dev pytest ruff   # dev dependency group (PEP 735 [dependency-groups])
uv add --group docs mkdocs # named dependency group
uv remove requests
uv run pytest              # run a command inside the project env (auto-syncs first)
uv run python script.py
uv tree                    # show the resolved dependency tree
uv version --bump minor    # read/update [project].version

uv run and uv sync auto-create the .venv, auto-install the pinned Python if missing, auto-lock, and auto-sync before running — so the venv is an implementation detail you rarely activate manually. Key files:

Dependency groups (PEP 735, the modern replacement for [project.optional-dependencies] “extras” used as dev deps): dev is the implicit default group. Control install scope on sync/run: --group <g>, --only-group <g>, --no-dev, --no-default-groups, --all-groups. Extras (consumer-facing optional features) are separate: --extra <e>, --all-extras.

Dependency sources[tool.uv.sources] redirects a dependency away from PyPI:

[tool.uv.sources]
mylib       = { workspace = true }                        # local workspace member
httpx       = { git = "https://github.com/encode/httpx", tag = "0.27.0" }
foo         = { path = "../foo", editable = true }         # local editable path
bar         = { url = "https://example.com/bar-1.0-py3-none-any.whl" }
torch       = { index = "pytorch" }                        # pin to a named [[tool.uv.index]]

[tool.uv.sources] is non-standard metadata that uv strips when building a distribution — it affects your dev resolution, not what downstream consumers get.

Workspaces (monorepo)

A workspace is multiple packages in one repo sharing one uv.lock and one .venv, each with its own pyproject.toml. Inspired by Cargo workspaces.

# root pyproject.toml
[project]
name = "albatross"
dependencies = ["bird-feeder", "tqdm>=4,<5"]

[tool.uv.workspace]
members = ["packages/*"]
exclude = ["packages/seeds"]

[tool.uv.sources]
bird-feeder = { workspace = true }   # resolve from the workspace, editable

2. The universal lockfile (uv.lock)

uv.lock is a universal (cross-platform) resolution: one lockfile valid for every OS, architecture, and Python version inside the project’s requires-python range. A package can appear multiple times with different versions/URLs gated by environment markers (sys_platform, python_full_version, etc.); the marker chooses which entry installs on a given machine.

uv lock                 # create/update uv.lock
uv lock --check         # CI gate: fail if lockfile is stale (was --locked/--frozen era)
uv lock --upgrade       # re-resolve everything to newest allowed
uv lock --upgrade-package requests   # bump just one package
uv sync                 # install exactly what the lock says into .venv
uv sync --frozen        # install from lock without re-resolving (fail if missing/stale)
uv sync --locked        # assert lock is up-to-date, then install (CI)
uv sync --no-install-project   # deps only, skip the project itself (Docker layer caching)

Resolution knobs (also valid in the pip interface):

Export / interopuv.lock is uv-native; export to standard formats for other tooling:

uv export --format requirements.txt -o requirements.txt
uv export --format pylock.toml      -o pylock.toml      # PEP 751 standard lock
uv export --format cyclonedx1.5     -o sbom.json        # SBOM

uv reads pylock.toml (PEP 751) for install but keeps uv.lock as its native format because PEP 751 doesn’t yet capture everything uv needs (e.g. full fork/marker model). Treat uv.lock as the source of truth and pylock.toml/ requirements.txt as generated artifacts.

uv.lock is deterministic and committed. The cross-platform guarantee is the headline benefit over a platform-specific pip-compile requirements.txt.


3. Python version install & pinning

uv downloads and manages standalone CPython/PyPy builds (python-build-standalone) — no pyenv needed, and no system Python required.

uv python install                # install the latest CPython
uv python install 3.12 3.13      # install several
uv python install [email protected]
uv python install --default      # also expose as `python`/`python3` on PATH (uv 0.8+)
uv python install --reinstall
uv python list                   # installed + downloadable
uv python list --only-installed
uv python pin 3.12               # write .python-version for this project
uv python pin --resolved 3.12.7  # pin an exact patch
uv python find 3.12              # print the path uv would use
uv python uninstall 3.11
uv python dir                    # where managed interpreters live

Selection & preference:


4. Tool / pipx replacement (uv tool, uvx)

uv runs and installs CLI tools from Python packages in isolated environments, replacing pipx.

uvx ruff check            # ephemeral: run ruff in a throwaway env (== uv tool run ruff)
uvx pycowsay 'hi'
uvx [email protected] check      # pin the tool version
uvx --from httpie http    # package name != command name
uvx --with mkdocs-material mkdocs build   # add extra deps to the ephemeral env

uv tool install ruff      # persistent: install into ~/.local + symlink the executables onto PATH
uv tool install 'httpie>0.1.0'
uv tool install mkdocs --with mkdocs-material        # bundle plugins
uv tool install --with-executables-from ansible-core ansible
uv tool install git+https://github.com/httpie/cli   # from VCS
uv tool list
uv tool upgrade --all
uv tool uninstall ruff
uv tool update-shell      # add the tool bin dir to PATH in your shell rc
uv tool dir --bin         # where executables are linked (XDG-based)

5. pip-compatible interface (uv pip)

A near-drop-in, much faster reimplementation of the pip / pip-tools workflow. Operates imperatively on an environment with no lockfile and no automatic project management — use it for legacy flows, scripts, containers, or when you explicitly want pip semantics.

uv venv                              # create .venv (add --python 3.12 to choose)
uv pip install ruff 'httpx>=0.27'
uv pip install -r requirements.txt
uv pip install -e .                  # editable install of the current project
uv pip install --system ruff         # install into the active/system interpreter (Docker)
uv pip compile requirements.in -o requirements.txt   # pip-tools replacement
uv pip compile --universal requirements.in -o requirements.txt  # cross-platform, with markers
uv pip compile --generate-hashes requirements.in -o requirements.txt
uv pip sync requirements.txt         # make the env EXACTLY match the file (removes extras)
uv pip freeze / list / show / tree / check / uninstall

Deliberate differences from pip (uv is stricter / more correct by default):


Build backend, publishing & PEP 723 scripts

Build backend. Since mid-2025 uv init --package/--lib default to uv’s own PEP 517 backend uv_build (package uv-build), zero-config for pure-Python projects; Hatchling remains a fine alternative for projects needing plugins or non-pure builds.

[build-system]
requires = ["uv_build>=0.10,<0.12"]
build-backend = "uv_build"
uv build              # produce sdist + wheel in dist/
uv publish            # upload to PyPI (replaces twine); use trusted publishing / token

PEP 723 inline-script metadata — single-file scripts declare their own deps and Python, run in an isolated ephemeral env:

# /// script
# requires-python = ">=3.12"
# dependencies = ["httpx", "rich>=13"]
# ///
import httpx
uv run script.py                 # uses the embedded /// script /// block
uv add --script script.py httpx  # edit the inline block programmatically
uv run --with rich --no-project example.py   # add deps ad hoc without a block

Configuration & cache


Practical patterns

Anti-patterns

Troubleshooting

References