UV Package Manager: Better Python Dependency Management
Episode #43: Forget about Pipx, virtualenv, Poetry, pyenv (and many more) and embrace UV, the ultimate tool for your Python projects.
A warm welcome to the 191 new subscribers who joined the Cloud Native Engineer newsletter in the last three weeks.
It's unbelievable how things can change with a single Substack note.
If you are new here and you don't know what I am talking about, feel free to read my story at A Thank You to my 1K Subscribers (And How Being Seen Changed Everything).
Are you a Python developer?
Are you frustrated with your development tools?
Have you ever wished there was a better way to handle dependencies in Python?
I have the answer you have been looking for.
I've been a Python developer for the last 10 years and have never been so excited about programming in Python.
Years of frustration are finally gone.
In this article, I will introduce you to Uv, the latest package manager revolutionising Python tooling.
This article is not meant to be an alternative to the UV documentation.
The documentation by Astral (the company behind UV) is excellent, and repeating it here would be a waste of your time and mine.
I aim to introduce this tool, compare it to the Python ecosystem, and explain how I use it daily.
The outline of this article is as follows:
The story so far
UV adopts the industry's best practices
Why is UV better than other solutions?
How do I use UV in my development tools?
Bonus feature: Inline dependencies
I have published a cheatsheet for UV on Gumroad at UV Python Package Manager - The Essential Cheatsheet.
In a previous article, A Thank You to my 1K Subscribers (And How Being Seen Changed Everything), I have described my decision to remove my paywall from my newsletter and go completely free.
So, if after reading this article, you have enjoyed my content, please donate by buying a cheatsheet from Gumroad, donate via Buy me a Coffee or simply like and reshare this article so it can reach a broader audience.
Your donation will help keep this newsletter free for everyone.
Thank you for being so supportive.
The story so far
I have written in a previous article Effortless Python Development with Nix how I have already abandoned pipenv, pyenv and virtualenv in favour of a tool called Poetry.
If you have never heard of any of those, do not worry. You don't have to learn them or even be aware of what they used to do.
Poetry was already a huge step forward in the history of Python development tools.
But there is now a better alternative.
A while ago, I wrote another article Goodbye Dependency Headaches: Discover the Power of Pipx for Python Package Management where I introduced pipx, a great tool that allows you to install each Python tool in an isolated virtual environment to avoid dependency conflicts.
UV combines the best characteristics of Poetry and Pipx in a single tool that is both fast and easy to use.
Speed and usability are usually at the top of my mind when I choose a tool to use.
UV adopts the industry's best practices
From the official Astral website, you can read about UV:
An extremely fast Python package and project manager written in Rust.
🚀 A single tool to replace
pip
,pip-tools
,pipx
,poetry
,pyenv
,twine
,virtualenv
, and more.
⚡️ 10-100x faster thanpip
.
If you have ever written any Python code, you are already familiar with virtual environments.
If you haven't, you can learn about it from this great article Python Virtual Environments: A Primer – Real Python. If you read that article, bear in mind that with UV, you will have a much simpler way to create virtual environments than what is suggested there.
Each tool I have used in the past (aka pyenv, virtualenv, pipenv) has its own way of creating virtual environments. Some prefer creating them in a global fixed location; others prefer to make them close to your code.
The approach that UV follows is my favourite one.
UV creates a hidden directory called .venv
in the same root folder where you have your Python code.
The benefits of this approach are many.
Keeping code and virtualenv next to each other makes development a lot easier when you have to activate the virtualenv from your shell or if you want to eliminate the entire project and the associated venv.
You will always know where the venv for your project is stored.
For managing dependencies, Uv adopts a lock file approach similar to Poetry.
There is a file called pyproject.toml
where you add your dependency rules (greater than this version or lower than that version) and another file, uv.lock
, where the dependencies are pinned to fixed versions.
This approach allows the developer to define their high-level dependencies in a human-friendly file and separate it from the file used by the machine to create reproducible environments.
Forget about requirements.txt
; using a single file for humans and machines was never a great idea.
Uv is compatible with pip and requirements.txt, but you don't have to use those features. Those are there primarily for backward compatibility.
Finally, Uv adopts the same approach as Pipx for installing tools in isolated virtual environments.
Please have a read at Goodbye Dependency Headaches: Discover the Power of Pipx for Python Package Management for more information about Pipx and installing tools in Python.
Why is UV better than other solutions?
By now, you should have a good idea of how Uv adopts best practices from established tools and combines multiple tools into one (namely Pipx and Poetry, for me).
But what is the fuss about UV? Why is everyone using it?
UV is extremely fast compared to other solutions.
While speed is usually not at the top of your mind when you think about Python development tools, speed is very beneficial in many contexts.
Speed is a great benefit when you have to install dependencies many times a day for all your developers as part of a CI/CD pipeline. You might save lots of money each month in cloud costs by running faster pipelines and countless hours in development time.
Furthermore, UV uses a global caching mechanism that avoids storing the same dependency multiple times on the same machine. This can also have enormous implications for storage savings in a cloud scenario.
There are lots more UV features that make it a great solution.
I have barely scratched the surface of what you can do with UV, but I am already in love.
How do I use UV in my development tools?
If you have read my article Effortless Python Development with Nix, you might wonder by now how I am combining Nix with UV.
I discovered UV in the first place because I recently had an issue at work where Python installed by Nix would break when installing a specific dependency while compiling Python from the source code.
After looking around for a more straightforward setup, I discovered UV can not only be used to install dependencies but also to install Python itself.
Instead of compiling Python from source, as Nix does, Uv uses binaries from Astral for a quicker setup.
What if you combine UV with the Nix dependency manager for non-Python tools?
I have been using UV for anything about Python and Nix for everything else.
Furthermore, direnv integrates with UV to activate the virtual environment.
Instead of running the command source .venv/bin/activate
every time I want to run a Python project, I now use the following two lines in my .envrc
:
layout python-venv
PATH_add .venv/bin
The second command is particularly useful if Python is installed by Uv or other binaries are installed in your virtualenv. All those binaries will be available in your shell with no extra effort.
Bonus feature: Inline dependencies
There might be a case where you have a single Python file that you want to distribute as a tool to your colleagues.
I'm sure you would like to avoid publishing a package on Pypi or creating a docker image if it's just in the initial development.
UV provides an easier alternative to the previous two options, avoiding writing long instructions on creating virtualenvs and installing dependencies.
You can add inline dependencies to your Python script like this.
uv init --script example.py
uv add --script example.py 'requests<3' 'rich'
The content of your example.py
will look like this:
Finally, you can run this script using uv run example.py
.
More information on how to use scripts is available at Running scripts | uv.
I only discovered this feature with UV, but it is also available in Pipx.
This is a good find. Thanks for sharing.