Most Python developers deal with fragmentation in the tools to manage environments and dependencies. There are many tools at your disposal – pip, virtualenv, Poetry, and conda. All of these tools have their constructs and requirements. However, when combined, you can quickly see how they can complicate any necessary workflows. This is where UV will help as the one true Python package manager you need.
For those unaware, UV is a modern, high-performance Python package manager written in Rust. Never mind the other tools, UV’s goal is to make use of the functionality of all these tools into one conditional experience that opens with one terminal command. UV is being developed by Astral and is intended to be benchmarked against pip, virtualenv, pip-tools, and aspects of pyenv with a goal of being a significantly faster tool in an all-in-one dependency and environment management!
As a black box, UV is a modern, high-performance Python package manager and installer written in Rust. It is a drop-in replacement for traditional Python package managers like pip. It provides similar or improved speed, improved reliability, and consistency with dependency resolution. UV was designed to provide solutions to some of the most visible pain points in the Python ecosystem. Long install times, dependency resolution pitfalls, and the enterprise-level complications with environment management! All of these pain points are exemplary use cases for UV, and it has a unique architecture and thoughtful implementation to achieve its fast speed and efficient package workflows. It can be 10-100 times faster than existing package management choices!
UV’s ambition is to streamline common Python development workflows by offering integrated capabilities for:
Unlike traditional tools that operate independently, UV provides a cohesive, “batteries-included” approach to Python development. It aims to reduce the number of tools and commands developers need to manage.
Key features that make UV stand out include:
Whether working on small personal projects or managing large-scale Python applications, UV provides a robust and efficient solution as a Python package manager.
The first question developers often ask before switching to a new tool is “How does it compare to the one I am already using?”. In the Python dependency and project management arena, pip, Poetry, Conda, and virtualenv are already the most common tools. However, UV has its own benefits among the list of Python package managers available today.
The following table highlights UV’s position among established Python management tools:
| Feature | UV | pip + virtualenv | Poetry | Conda |
|---|---|---|---|---|
| Implementation | Rust | Python | Python | Python + C++ |
| Speed | 10-100x faster than pip | Baseline | Faster than pip | Slower than pip |
| Memory Usage | Very efficient | Higher | Moderate | High |
| Environment Management | Built-in | Separate tools needed | Built-in | Built-in |
| Dependency Resolution | Fast, modern resolver | Basic | Modern resolver | Comprehensive |
| Non-Python Packages | No | No | No | Yes |
| Lock Files | Yes (uv.lock) | No (basic requirements.txt) | Yes | Yes |
| Project Structure | Yes | No | Yes | No |
| Package Publishing | Yes | Yes (with twine) | Yes | Yes |
| Compatibility | Works with existing pip ecosystem | Standard Python tool | More opinionated approach | Own ecosystem |
| Error Handling | Clear error messages | Basic | Good | Good |
| Resource Footprint | Minimal | Moderate | Moderate | Heavy |
| Scientific Computing Focus | No | No | No | Yes |
| Cross-platform Consistency | Yes | Limited | Good | Excellent |
With this, we shall now explore the strengths, weaknesses, and the comparison of these tools with UV individually.
Pip and virtualenv have always been separate tools for Python environment and package management. Of these, pip is specifically for packages, and virtualenv is specifically for isolated environments. Here is a quick look at their strengths and weaknesses combined:
| Category | Strengths | Weaknesses |
|---|---|---|
| pip + virtualenv |
– Established ecosystem with years of adoption – Large amount of documentation and community support – Simple and effective for basic projects |
– Requires separate steps for environment setup and package install – Slow dependency resolution for large or complex projects – No built-in lockfile for reproducibility |
Here are some ways in which UV is clearly the more preferable choice among the two:
When using pip + virtualenv to set up an environment and install packages, this involves:
virtualenv env
source env/bin/activate
pip install -r requirements.txt
When using UV, you would run:
uv env create
uv install
UV typically finishes installs much faster than pip + virtualenv, and guarantees the exact same package versions are installed on different machines using a generated lockfile.
A versatile and powerful environment and package manager, Conda is used quite often within the scientific community and in data science. It is intended to support all packages (not just Python packages), including system-level dependencies and system libraries that are important for executing more complex scientific computing workflows.
Here is a look at the strengths and weaknesses of Conda for Python development.
| Category | Strengths | Weaknesses |
|---|---|---|
| Conda |
– Supports non-Python packages like CUDA, BLAS, compilers – Strong environment isolation across projects – Consistent behavior across Windows, macOS, and Linux – Simplifies switching between Python versions |
– Slower package installation due to binary size and resolver complexity – Consumes more disk space and memory – May lag behind PyPI on latest package versions |
Here are the aspects in which UV trumps Conda:
Setting up an environment using Conda typically looks like this:
conda create -n myenv python=3.9 numpy scipy
conda activate myenv
Whereas with UV, the process is:
uv env create -p python=3.9
uv install numpy scipy
Conda is a very powerful tool for scientific and data science projects because it is able to manage system-level packages and provide a variety of platforms in the same environment. However, there is some downside to using conda in terms of speed of installation and memory usage, which may be noteworthy in some cases.
In comparison, UV is especially useful in instances where installation speed, low overhead, and staying within the Python ecosystem are one’s primary concerns. Also, when the project does not have many non-Python dependencies, at which point it is still useful and advantageous to use conda.
Poetry is a modern all-in-one Python package manager that performs dependency management, project scaffolding, and package publishing all in an organized, opinionated manner.
Also read: How to Build a RAG Evaluator Python Package with Poetry?
| Category | Strengths | Weaknesses |
|---|---|---|
| Poetry |
– Strong dependency resolver handles complex version conflicts – Built-in project scaffolding promotes clean structure – Integrated publishing to PyPI simplifies deployments – Generates poetry.lock for reproducible builds
|
– Opinionated structure may reduce flexibility – Slower dependency resolution on large projects – Compatibility issues with mixed pip-based workflows |
Here are some clear strengths of Poetry:
Some of the limitations of Poetry include:
Here are some reasons why UV trumps Poetry as a Python package manager:
Creating a project and adding dependencies with Poetry:
[poetry new myproject
cd myproject
poetry add requests flask
poetry install]
Poetry manages both the project structure and the environment. With UV, you can install dependencies in an already built project:
uv install requests flask
UV is about quick installs and environment management without `dictating` a structural layout, making its lack of structure quite easy to adopt step-wise.
So you have decided to give UV a chance as your next Python package manager. Smart choice, and here is how you can go about it.
You can install UV on macOS and Linux via the terminal with curl:
curl -LsSf https://astral.sh/uv/install.sh | sudo sh
On Windows, run it from PowerShell (you must run with administrator privileges):
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
You can also install it using Homebrew:
brew install uv
A pip installation is supported, but not recommended. Once installed, check that it’s working by checking the version:
uv version
To start a new project using UV, you need to navigate to the project directory that you already have or create your new one:
mkdir myproject
cd myproject
uv init
For example, if it were called uv init explore-uv, it would create a new project named explore-uv. This first command will create a project directory (explore-uv) and automatically create:
UV combines creating an environment and adding dependencies to a project in one command: uv add
uv add scikit-learn xgboost
When you first execute uv add, UV creates a new virtual environment in the current working directory and installs the dependencies you specified. The second time you run uv add, it will use your existing virtual environment and install or update the new packages you requested.
UV manages dependencies with a timely modern dependency resolver, analysing the entire dependency graph and finding compatible versions of packages to avoid version conflicts. At the end of each add command, UV updates your pyproject.toml and uv.lock files with the versions you installed, maintaining an accurate record.
To remove a dependency and its child dependencies, run uv remove:
uv remove scikit-learn
Once you install your dependencies, you can run Python scripts using uv run instead of using python script.py:
uv run hello.py
This command makes certain that the script is run in the project’s virtual environment created by UV.
Here are some ways in which UV streamlines working with Python versions.
UV can detect existing or installed versions of Python on your machine:
uv python list --only-installed
This command will show a listing of every version of Python that UV finds, including ones that were installed via Conda or Homebrew.
You can switch Python versions for your UV project at any point, provided the new version satisfies the requires-python specification in your pyproject.toml file (e.g., requires-python = “>=3.9”).
To set a Python version:
uv python use 3.11
This embeds the Python version in .python-version and maintains consistency. If it can’t find the requested version, UV will download and install it in ~/.local/share/uv/python. Then, UV will create a new venv in the project directory and replace the old one. After updating the Python version, you may want to reinstall your dependencies:
uv pip install -e .
If you get Permission Denied-related errors, you may need to use sudo (macOS/Linux) or run your command prompt as an administrator (Windows). A better option is to change ownership of the UV home directory:
sudo chown -R $USER ~/.local/share/uv # macOS or Linux
uv python --version
The UV tool also provides interfaces to manage Python packages that expose themselves as command-line tools (black, flake8, pytest…).
Black is a popular code formatter for Python that automatically reformats your code to follow a consistent style, improving readability and maintaining uniform code style across your project.
uv tool run tells UV to run a tool, and black is the tool name (Python code formatter). hello.py is the target file to format. This command runs Black on your hello.py file to auto-format it according to Black’s style rules.
Using uv tool run:
uv tool run black hello.py
Using the shorter uvx command:
uvx black hello.py
When these commands are run, UV creates a temporary virtual environment in its cache, installs the tool, and runs it from there. This allows you to use command-line tools without installing them in the project’s virtual environment, leading to faster execution and cleaner project dependencies. These cached environments are automatically cleaned up when UV’s cache is cleared and are perfect for occasional use of development tools.
Lock files (uv.lock) are an important part of dependency management in UV. Each time you run an UV add command, UV creates and/or updates a uv.lock file. The uv.lock file:
UV automatically tracks the lock file, and you should check it into version control to ensure dependency versions are consistent across your development team.
Lock files and requirements.txt both deal with dependencies, but they serve different purposes:
| Feature | uv.lock | requirements.txt |
|---|---|---|
| Reproducibility | High | Low to moderate |
| Generated by | UV resolver automatically | Manual or pip freeze |
| Editable? | No (auto-generated) | Yes |
The former, Lock files, are an important component of development, as they help establish reproducible builds. requirements.txt files are a bit less complex than lock files and typically only contain direct dependencies, as they are more widely recognized across Python tools and could serve as a method of sharing/deploying code with the end-user that does not use UV. You can maintain both by using the UV lock file for development, and, when it comes time to deploy, generating a requirements.txt like so:
uv export -o requirements.txt
UV provides sophisticated methods for managing dependencies:
The add command can be used to update, change constraints, or specify exact versions of existing dependencies:
Installing the latest version:
uv add requests
Installing a specific version:
uv add requests=2.1.2
Changing constraint bounds:
uv add 'requests<3.0.0'
Making a dependency platform-specific:
uv add 'requests; sys_platform="linux"'
Optional dependencies are packages not required for core functionality but needed for specific features (e.g., Pandas’ excel or plot extras).
First, install the core package:
uv add pandas
Then, add its optional dependencies:
uv add pandas --optional plot excel
These will be listed in your pyproject.toml under [project.optional-dependencies].
Dependency groups allow you to organise dependencies (e.g., development, test, and documentation dependencies) to keep production dependencies separate.
To install a new dependency into a particular group, you would use the –group flag:
uv add --group group_name package_name
Users can use –group, –only-group, and –no-group flags to further control which group(s) are installed.
The migration from pip and virtualenv to UV is nearly seamless. This is because UV is built to comply with existing Python packaging standards.
If you have an existing project:
pip freeze > requirements.txt
Next, you would initiate a new UV project in the same directory:
uv init.
Now you can install your dependencies from the requirements file:
uv pip install -r requirements.txt
Here is a quick reference for replacing common pip/virtualenv commands:
| pip/virtualenv command | UV equivalent |
|---|---|
| python -m venv .venv | uv venv |
| pip install package | uv add package |
| pip install -r requirements.txt | uv pip install -r requirements.txt |
| pip uninstall package | uv remove package |
| pip freeze | uv pip freeze |
| pip list | uv pip list |
After you have migrated, you can safely delete your old virtualenv directory. If you find you need to fall back to traditional pip commands, you can always make use of the pip compatibility layer built into UV.
UV stands out from the lot of Python package managers, offering a modern, fast, and effective alternative for managing packages compared to previously established tools. The main advantages of UV include:
Whether you start a brand new project or upgrade an existing one, UV is a solid solution that can improve your Python development workflow. And since it is compatible with existing tools and processes, it is a simple decision for developers who want to take their development toolchain into the 21st century without disrupting their workflows.
As developers, we live in a continuously evolving environment. Tools like UV are examples of how modern languages like Rust can improve developer experience. All while retaining the ease and accessibility that Python developers depend upon.
So now that you know of the clear advantages UV offers as a Python package manager, give it a try for your next project. Make sure to check out the official GitHub repo for current updates or contributions. Also, share your experiences with the development community to help expand the adoption and future enhancements of UV.