I've almost completely switched from "python" to "uv run"
A reluctant adopter
Honestly, I've never been a fan of venvs. I always preferred to just install to my system python. Some might cringe at this, but, hey, I'm just a guy trying to get stuff done. I don't like the whole “source .venv/bin/activate,” I’m not a fan of tools that auto-activate it, and I would never insist on folks using venv for any projects I publish.
Most of the time I work on one python project at a time and this works fine, but lately I've been working on multiple projects with different teams and different requirements, and I reluctantly got on board the venv train.
And now that I was using venvs I had an excuse to take a look at this `uv` tool I'd been hearing about. Its main page advertises significantly faster venv creation, and so I put off trying it for a while. I don't create venvs that often, and they don't really break my flow, why would I need to save a few seconds on such an activity?
Finally, in a fit of boredom, I decided to give it a try. Boy was I blown away!
A zealous convert
This thing is fast! I know I said I didn't need the speed up, but once I had it I didn't want to go back.
The more I used it, the more I discovered features and workflows that made my life simpler. At first I used it simply as a replacement for the standard venv creation and population, which I note below for reference.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
I went through a couple iterations. Iteration 1 was just a 1:1 command replacement
uv venv
source .venv/bin/activate
uv pip install -r requirements.txt
Even with this primitive replacement the last step was much faster.
Eventually I discovered that the ‘proper’ way to use uv was to put your requirements in a pyproject.toml, and use `uv sync` to create the venv, so my workflow went down to
uv sync
source .venv/bin/activate
But it gets even better, you don't need to activate the venv at all!
An enlightened evangelist
There a subcommand of uv called ‘run’ which handles the venv for you, and it works like this
uv run myscript.py
And that's it! It's assumed that you're running this from the directory where you have your pyproject.toml, or a subdirectory, and in that case uv will make a venv, populate it, run your command in it, and then drop you back to your shell with all your environment variables intact!
What’s more, you can skip the pyproject.toml and put your dependencies in your script itself using PEP 723 metadata, like this:
# /// script
# dependencies = [
# "numpy<2",
# "matplotlib",
# ]
# ///
import numpy as np
import matplotlib.pyplot as plt
plt.plot(t:=np.linspace(-np.pi, np.pi), np.sin(t))
plt.grid()
plt.show()
And then you can run it with “uv run <script-name>.py”, just like I showed above. I really love this ability to keep everything in one file. Some might say, what’s the big deal, without this you just need to maintain a pyproject.toml which will have the same dependency list. Well to those fine folks I say you do you, I like it this way :)
It’s not all sunshine and rainbows
It’s still early days for uv, and while it’s moving fast, you will notice some incompleteness here and there. The most obvious ones I’ve noticed are with VSCode. It struggles to handle the symlinks that uv venv uses on my mac, and it doesn’t recognize the inline script metadata and complains about not being able to find libraries. But besides this lack of polish, which is expected for a new and growing tool, I haven’t seen any real showstoppers.