Managing Python dependencies with uv
uv manages the dependencies and the virtual environment of a Python project. Everything rests on three files, always in the same order:
pyproject.toml ──uv lock──▶ uv.lock ──uv sync──▶ .venv/what you want exact versions what is installed(ranges >=, <)| File | Role | In git |
|---|---|---|
pyproject.toml | Wanted dependencies, with version ranges | Yes |
uv.lock | Exact resolved versions, for every platform | Yes |
.venv/ | Environment installed on the machine | No |
On the development machine
Section titled “On the development machine”| Step | Command |
|---|---|
| Add a dependency | uv add httpx |
| Add a dev dependency | uv add --dev pytest |
| Remove a dependency | uv remove httpx |
After a git pull | uv sync |
pyproject.toml edited by hand | uv lock, then uv sync |
| Upgrade one package | uv lock --upgrade-package httpx |
| Upgrade everything | uv lock --upgrade |
| Run a command | uv run pytest |
| Before committing | uv lock --check |
uv add and uv remove update all three layers at once. uv run syncs the .venv before running the command.
If the project declares its dev tools as optional dependencies ([project.optional-dependencies]) rather than as a dev group, use uv add --optional dev pytest and uv sync --extra dev.
In continuous integration
Section titled “In continuous integration”uv sync --lockeduv run pytestOn the server
Section titled “On the server”uv cache prune --ciuv sync --locked --no-devuv cache prune --ci empties the cache of downloaded packages. Without it, the cache keeps every installed version and ends up filling the disk.
--locked or --frozen
Section titled “--locked or --frozen”--lockedinstalls into the.venvthe packages at the exact versions listed inuv.lock, and fails if it no longer matchespyproject.toml. Use it in CI and in production: apyproject.tomlcommitted without its lock is caught.--frozenalso installs the versions fromuv.lock, but without checking that it still matchespyproject.toml. Avoid it, except in special cases.
