How to use UV when you are used to PIP
Table of Contents
This post teach you how to use uv and my recommendations when migrating from
pip.
How UV works #
UV manages the python environment and version by itself. You dont need to
install any python version, it will install the version declared at
.python-version if needed or use your $PATH python.
UV will enter or create a .venv whenever you run uv run as needed and will
manage it for you.
UV will create a new one-time-use venv whenever you run uvx.
That way our project dependency stay clean and we avoid dependency conflicts.
Setup development with UV #
If you already have python in your OS or prefers to manage python versions with
another tool (like mise or asdf), UV will always try to use what you have in
your $PATH before trying to install a new version in your OS.
Installing UV Standalone #
The standalone uv installation is easier to upgrade if you’re not tech savvy,
if you prefer to install via another package manager check
the docs.
Windows #
On PowerShell run
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.9.29/install.ps1 | iex"
If you see the error The term 'uv' is not recognized as the name of a cmdlet
reboot your OS.
Then check if it worked with
uv self version
*nix (Mac/Linux) #
With curl installed run
curl -LsSf https://astral.sh/uv/0.9.29/install.sh | sh;
then check if it worked with
uv self version
Upgrading UV Standalone #
If you run uv self update and get a newer version make sure to
also bump uv versions on everywhere that mentions it in your project
(Dockerfile, ci, Docs)
Windows #
uv self update
*nix #
It’s recommended to add UV_NO_MODIFY_PATH to avoid changing the $PATH and
breaking something
UV_NO_MODIFY_PATH=1 uv self update
Shell Autocompletion #
Fish/Elvish/Others #
Check the Shell Autocompletion on uv docs.
PowerShell #
UV #
if (!(Test-Path -Path $PROFILE)) {
New-Item -ItemType File -Path $PROFILE -Force
}
Add-Content -Path $PROFILE -Value '(& uv generate-shell-completion powershell) | Out-String | Invoke-Expression'
UVX #
if (!(Test-Path -Path $PROFILE)) {
New-Item -ItemType File -Path $PROFILE -Force
}
Add-Content -Path $PROFILE -Value '(& uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression'
Zsh #
echo 'eval "$(uv generate-shell-completion zsh)"' >> ~/.zshrc
echo 'eval "$(uvx --generate-shell-completion zsh)"' >> ~/.zshrc
then source the config
source ~/.zshrc
Bash #
echo 'eval "$(uv generate-shell-completion bash)"' >> ~/.bashrc
echo 'eval "$(uvx --generate-shell-completion bash)"' >> ~/.bashrc
then source the config
source ~/.bashrc
Using UV #
When you run uv run or uv tree it automatically updates the lock and syncs.
If you do not have a venv yet the uv sync or uv run will create it for you.
If you do not have python yet uv will download it for you.
To update the project environment (venv) run
uv sync
To update the project lock file run
uv lock
To list the dependency tree
uv tree
To add a package with specific version
uv add "package~=1.2.3"
To add a dev only package with specific version
uv add --dev "package~=1.2.3"
To remove a package
uv remove package
Any package installed in the pyproject.toml that used to run with python
should use uv
uv run <command>
for example to run pytest
uv run pytest
Any packages that are not in the pyproject.toml should use uvx
uvx <command>
uvx ruff format
for tools like ruff, it’s good to pin the latest version
uvx ruff@latest format
Inside Docker uv is also used
docker compose exec web uv run manage.py migrate
Using extra environment variables in docker #
To test something pytest is needed which is an environment only dependency so
the environment variable UV_NO_DEV=0 is needed as an e flag when entering
the container to tell uv to use the dev dependencies (pytest). If you want to
pass more variables like which DB to use for test in an example where you have
different DB engines you can just add more env variables to docker through the
e flag.
docker compose exec -e TEST_DB_ENGINE=sqlite -e UV_NO_DEV=0 web uv run pytest
Pip Migration #
If you used the old requirements.txt and want to use uv it is recommended to
switch to the modern pyproject.toml as per the PEP 621. By default when you
run uv add <package> it will add it on a pyproject.toml as dependencies. For
developer only dependencies do uv add --dev <package>.
Since simple requirements.txt do not have dependency groups to differentiate
between dev and prod dependencies I recommend going through your
requirements.txt and assigning correctly which is what.
I always like to pin dependencies versions with ~= that is the PEP 440
identifier for
compatible releases
and pin dev dependencies with >= to use the latest.
This is an example of a project with your-dependencies and pytest installed
in their correct places.
[project]
name = "project-name"
version = "0.0.1"
description = "cool project"
readme = "README.md"
requires-python = "~=3.14.2"
dependencies = [
"your-dependencies~=0.0.0",
]
[dependency-groups]
dev = [
"pytest>=9.0.2",
]
Builtin ruff format support #
uv format is a builtin alias to ruff format that is slightly faster because
it doesn’t create a venv
uv format
same as
uvx ruff format
You can pass ruff format arguments to ruff by doing
uv format -- --line-length 80
same as
uvx ruff format --line-length 80
UV also have builtin support for the --check and --diff flags of
ruff format.
ruff format is a drop-in replacement for Black, uv format --check is the
equivalent of black --check.
ruff check is a drop-in replacement for Flake8, isort, pydocstyle, pyupgrade,
autoflake, and more.
Both commands are from ruff but behave completely different because
ruff format --check is running a code formatter without applying the format
while ruff check is running the linters as you configured in ruff.
uv format --check
same as
uv format -- --check
same as
uvx ruff format --check
The other uv format flags do not behave the same as ruff format flags and
are used to either configure uv itself or configure how ruff format needs to
behave in relation to uv.