-
Notifications
You must be signed in to change notification settings - Fork 0
Package Management with uv
How to use uv effectively as a package manager.
uv is a Python package manager that also manages python itself and virtual environments. So far, I like it. There's definitely a learning curve, and it enforces some things strictly which can cause frustrating errors, but ultimately I think it does a better job at this than other tools I've come across.
| Command | Action |
|---|---|
uv init |
Initialize a repo. Run inside repo. |
--app |
...as an application. |
--lib |
...as a library. |
uv python list --all-versions |
List available versions of Python. |
uv python install <version> |
Install specific version of Python. |
uv venv <env name> |
Create a new virtual environment. |
--python <version> |
...with this version of Python. |
--prompt <name> |
...with this env name in the shell. |
uv add <package> |
Add a package. |
uv remove <package> |
Remove a package. |
uv sync |
Synchronize pyproject.toml to a virtual environment. |
--upgrade-package <package> |
Update an already added package to latest version. |
--upgrade-package <package>==<version> |
Update an already added package to a specific version. |
--extra <tag> |
Synchronize environment with a set of optional libraries. |
--extra <tag 1> --extra <tag 2> |
Synchronize environment with multiple sets of optional libraries. |
--active |
Synchronize currently active environment with pyproject.toml settings. |
--active --extra <tag> |
Synchronize currently active environment with a set of optional libraries. |
uv tree |
See dependency tree. |
uv build |
Build as a package. |
Before you start using uv, make sure that you do not have pyenv installed. It can cause problems with the activation of virtual environments. See here for details. uv replaces pyenv in all functions so you shouldn't need pyenv.
Instructions here.
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
curl -LsSf https://astral.sh/uv/install.sh | sh
By default, uv will automatically download and install a needed version of Python (for example if you clone a repo and want to set up a virtual environment that reflects the repo requirements). I don't like behavior that automatically downloads applications to my system. I prefer it to error out and then tell me that it requires something which I then decide to download or not.
Create a config file located here:
%APPDATA%\uv\uv.toml
Make sure to show hidden files to see the .config folder.
~/.config/uv/uv.toml
Contents:
# Disable automatic interpreter downloads
python-downloads = "manual"
# (Optional) prefer system Pythons over uv-managed ones
python-preference = "system"
The different things you use uv for.
List versions of Python uv can install.
It shows you where the version of python is if you already have it installed on your machine which is nice.
uv python list --all-versions
Install a specific version of Python.
uv python install <python version>
uv python install 3.12.1
Find where a specific version of Python is located on your system.
uv python find <python version> --system
uv python find 3.12.1 --system
(i.e. after cloning a repo, set up the project for management).
In the repo top level...
uv init
For creating a general application:
uv init --app
For creating a library (something is intended to be packaged and shared with a broader audience):
uv init --lib
uv venv <env name> --python <python version>
uv venv .venv --python 3.12.1
uv venv <env name> --python <python version> --prompt <env name in shell prompt>
uv venv .venv --python 3.12.7 --prompt my-env
uv sync
If someone wants to use pip, the requirements.txt file should just have -e . in it (and maybe extra indexes???).
pip install -r -requirements.txt
If someone wants to use pip but there are optional dependencies (have to bypass requirements.txt):
pip install -e ".[<opt-tag>]"
- Adjust the python version specified in
pyproject.toml.requires-python = "==3.12.*"
-
uv python pin 3.12.5to update the.python-versionfile.- Why
uv syncwon't update this and you have to change it with an additional command, I don't know.- It's because all minor versions are supposed to work together, so you typically only enforce "3.12.x", or "3.5.x", etc. And pyproject.toml defines your global (repo/project) needs, while the ".python-version" defines your local wants. If you want to play with 3.12.3, or 3.12.8, it's totally up to you. But uv will enforce the global requirement while letting you set the local environment as long as it still follows the law of pyproject.toml.
- Why
-
uv sync.-
uv.lockshould now be updated.
-
Adding libraries with uv vs. pip.
uv add <lib>
uv add <lib>[<opt-tag>]
uv add git+https://github.com/YOURORG/REPO.git
uv add git+https://github.com/YOURORG/REPO.git@<release-tag>
uv add git+https://github.com/YOURORG/REPO.git --tag <release-tag>
uv add "<lib-name> @ git+https://github.com/YOURORG/REPO.git" --tag <release-tag>
with optional dependencies:
uv add "<lib-name>[<opt-tag>] @ git+https://github.com/YOURORG/REPO.git" --tag <version-tag>
uv add "<lib-name>[<opt-tag-1>,<opt-tag-2>] @ git+https://github.com/YOURORG/REPO.git" --tag <version-tag>
- Supposedly uv handles an explicit tag more appropriately, but I've seen it work fine with the
@<release-tag>notation. - uv can infer the name of the library or you can pass it explicitly.
- Passing it explicitly matters more when you have an optional install (I think it's required in this case).
pip install git+https://github.com/YOURORG/REPO.git@<release-tag>
pip install "<lib-name> @ git+https://github.com/YOURORG/REPO.git@<release-tag>"
pip install "<lib-name>[<opt-tag>] @ git+https://github.com/YOURORG/REPO.git@<release-tag>"
pip install "<lib-name>[<opt-tag-1>,<opt-tag-2>] @ git+https://github.com/YOURORG/REPO.git@<release-tag>"
Add dependencies for your project but under an optional umbrella. Allows the user to install the library with a specific set of dependencies.
I want to add these libraries to my project as an optional install for users (they will be added to the pyproject.toml file under an optional dependency block). E.g. I'm building a library, and I want the user of my library to be able to install it with different optional dependencies.
uv add <lib-1> <lib-2> --optional <tag>
Alternatively, edit the pyproject.toml directly and create the optional dependencies section.
I want to add this library with its optional dependency to my project.
uv add lib[tag]
For a custom library:
uv add "<lib-name>[<opt-tag>] @ git+https://github.com/YOURORG/REPO.git" --tag <version-tag>
uv add "<lib-name>[<opt-tag-1>,<opt-tag-2>] @ git+https://github.com/YOURORG/REPO.git" --tag <version-tag>
uv makes it very easy to quickly shift between sets of optional dependencies for a virtual environment.
Activate your virtual environment (uv defaults to looking for ".venv" if you explicitly don't have one active).
Sync environment to set of optional dependencies.
uv sync --extra <tag>
uv sync --extra <tag-1> --extra <tag-2>
...back to base dependencies.
uv sync
Installing PyTorch is a special case, both by itself and additionally because of uv.
PyTorch can be installed in two ways:
- With the CUDA binaries.
- The CUDA binaries let you use the GPU.
- This is almost always the version that you typically need.
- Without the CUDA binaries.
- Still useful, but if you can't use the GPU then why bother.
The version without CUDA exists on pypi and can be installed normally with uv add torch/pip install torch. But the CUDA capable version is tracked on PyTorch's website. To access it, you have to add an "extra index" that basically tells your installed to "go look here for packages first". In a requirements.txt file, this would have a line like this at the top: --extra-index-url https://download.pytorch.org/whl/cu130.
uv, however, has a specific method of handling extra indexes that can cause problems here. Let's say you attempted to add PyTorch with CUDA to your project via uv. In uv, uv add torch torchvision --extra-index-url https://download.pytorch.org/whl/cu130 puts the PyTorch index ahead of PyPI for resolution. uv’s default index strategy is first-index: for any package name that appears on the first index found, uv will try to install from that index and stop looking elsewhere (to prevent dependency-confusion, docs here). Sometimes, this can expose another library (i.e. the library is tracked at the location pointed to by the index) that has restrictions that aren't compatible with your setup and cause an error. For example, when I tried to add pytorch via uv add ..., it happened to expose the markupsafe library (which apparently is a normal dependency of pytorch) but the exposed version needed Python 3.13 and I was running 3.12 in my virtual environment, so uv errored out saying there was an incompatibility.
When using uv, track PyTorch specifically in the pyproject.toml like this:
dependencies = [
"transformers>=4.57.1",
"torch",
"torchvision"
]
[[tool.uv.index]]
name = "pytorch-cu130" # I think you can call this name whatever, just be consistent.
url = "https://download.pytorch.org/whl/cu130"
explicit = true # Only used for torch packages explicitly mapped below.
# Route just these packages to the PyTorch index.
[tool.uv.sources]
torch = { index = "pytorch-cu130" }
torchvision = { index = "pytorch-cu130" }
This sets up a specific link for a specific set of packages and let's the extra index only apply to the PyTorch packages. It's a bit wordy, but it makes a very explicit connection.
Be sure to still add --extra-index-url https://download.pytorch.org/whl/cu130 to your requirements.txt.
This is the equivalent of pip uninstall <package> except that this also uninstalls the unneeded dependencies instead of just the base package. TBD on whether it intelligently understands whether a library is needed just as a dependency or whether you also need it for something else (probably yes - should be what the lock file is for).
uv remove <package>
Create a virtual environment with the necessary python version and packages installed based on all the settings files.
uv sync