The Zen-style Intro into `uv`
What most people miss when starting with uv.
Chapter 1: The Zen of Not Activating
Is this a Python environment? No. This is just a folder.
Can we run a Python script here? Not yet. We need a project.
How do we make a project? uv init
What did that do? It made a pyproject.toml. It also gave you something to run.
Where is the environment? Nowhere. It isn’t born until you ask for it.
Should we create one with uv venv? You can.
But do we have to? No.
Then how do we run code?uv run main.py
Wait—.venv appeared. Did I do that? You asked to run. uv did the boring part.
The Habit of the Old Way
In the old days, how did we “use the env”? We activated it:
source .venv/bin/activateAnd what did that change? Your prompt. Your $PATH. Your entire terminal mood.
Does uv want me to do that? Not unless you enjoy rituals.
If I don’t activate it, how does uv find packages?uv run reads your pyproject.toml, finds (or creates) the project environment, and runs inside it for this command only.
So “active” is not a state… It’s a habit.
The Zen of Ephemerality
My script needs httpx. Is it installed? Not yet.
Should I do uv pip install httpx? Only if you want a fast way to feel old.
What is the new move? uv add httpx
What did that actually change? Your project now admits the truth in pyproject.toml. And the lockfile learns it, too.
Now: uv run main.py? Yes.
New terminal window: do I activate anything? No. Just run again.
Is this magic? No. It’s a light switch.
Explain. You don’t “activate a room.” You turn on the light when you enter.
uv run is the switch.
The Deeper Insight
If I never activate, can my IDE still work? Yes. Point it at .venv.
What if I delete .venv? Try it.
It’s gone. Now what happens when I run again? It comes back.
Do I download the world again? No. uv keeps a global cache.
Rebuilding the env is mostly “re-pointing,” not “re-installing.”
So the environment is… disposable? Yes.
The environment is a shadow.
The project file is the thing.
Is that the Zen?
Stop caring about the environment.
Start caring about the project.
Chapter 2: The Ghost in the Machine
Do you have Python installed? Probably.
Which version? “Some version.”
Does uv care? Less than you think.
Try this: uv run --python 3.13 main.py
What happens? uv looks for Python 3.13. If it’s missing, it fetches it.
Did it install Python into my system? Not the way you fear. It installs into uv’s own managed toolchain.
So my system isn’t polluted? Correct.
What is this Python then? A project-usable Python. A ghost Python.
The Myth of the Global Python
In the old days, how did we get a new Python? brew install python or an installer (sudo apt install python - ubuntu Linux, scoop install python - Windows)
or pyenv
or a small prayer.
Was it always smooth? No.
Does uv need your shell paths to be perfect? No. It goes and gets what you asked for.
Can one project use 3.9 while another uses 3.13? Yes. And neither has to “own” your machine.
How make it forever use 3.12? uv python pin 3.12 . It creates a .python-version file — with the content “3.12” — in your project folder. (Transfer your pyproject.toml, uv.lock, and .python-version file to your colleagues to recreate your project exactly in their machine.)
The Lockfile Insight
Do you see uv.lock? Yes. It looks like machine poetry.
Is it for you to read? No. It is for your future self to trust.
What does it lock? Exactly what was resolved. For the platforms and Python versions you care about. So the same project becomes the same environment again.
If I send the folder to a friend on Linux and I’m on a Mac? uv can recreate the environment on their machine from the lockfile—without guesswork.
Do they need a giant toolchain? Just uv.
# Installing `uv`
# Linux or MacOS:
curl -LsSf https://astral.sh/uv/install.sh | sh
# or (if you don't have curl and only wget):
wget -qO- https://astral.sh/uv/install.sh | sh
# Windows PowerShell (use irm for downloading and iex for executing the script):
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"Chapter 3: The uv pip Trap
I know pip. I love pip. Can I use uv pip install requests? Yes. It will be lightning fast.
Is that good? Fast is not the same as correct.
What’s the problem? After uv pip install, ask two questions:
Did it update pyproject.toml? No.
Did it update uv.lock? No.
Then what did you just build? A secret environment. A private truth. A project that cannot be reproduced.
So the project becomes a liar. Yes.
(uv pip install installs to the currently active virtual environment!)
The Two Faces of uv
Then why does uv pip exist? Because the world still contains requirements.txt. And uv is kind to old places.
So uv pip is… A bridge. Not a home.
And uv add? That’s the home.
What happens when I type uv add requests? uv writes it to pyproject.toml. uv locks it in uv.lock. uv syncs the environment to match.
Three truths at once? Yes. Files, lock, environment: aligned.
The “Sync” Revelation
My teammate added a package. I pulled. Now my .venv is stale. What do I do? uv sync
What does it do? It makes your environment match the lockfile. Exactly.
Does it remove things that aren’t in the lockfile? Yes. It deletes the lies.
Is that safe? It is the definition of safe.
The Global Cache
If sync adds/removes things often, isn’t that expensive? Not when installs are mostly links.
So if I have 10 projects using pandas, do I store it 10 times? No. Mostly one cached copy, many cheap links.
So creating envs becomes… Pointer work. Not mountain moving.
So: should I ever use uv pip? Only when you must live in the past. Otherwise: don’t.
Chapter 4: The Script with Pockets
I have one file: process_data.py. It needs pandas. Do I need a whole project? Not anymore.
Try this:
uv add --script process_data.py pandasWhere did it put the dependency? Not in a pyproject.toml. In the script itself.
How? At the top you’ll see a metadata comment block. The script carries its own manifest.
# /// script
# dependencies = [
# "requests<3"
# ]
# ///PEP 723: The Portable Script
Is it “just comments”? To humans: yes. To uv: no.
What happens when I run:
uv run --script process_data.pyWhat does uv do? Reads the embedded metadata. Creates an isolated environment for the script. Runs it.
If I send this single file to a colleague? They can run it with uv and nothing else.
So no more README that says “pip install 12 things”… Yes. The script is now honest and fully reproducible.
Tool Isolation (uvx)
What about tools like ruff or black—I want them available but not as project deps. Then don’t add them.
Use:
uvx ruff check .What did uvx do? Ran the tool in an isolated, ephemeral environment. Then put it away.
Is that like pipx? Yes. Same idea. Smoother because it shares the same cache.
So my project stays clean. And your machine stays calm.
Chapter 5: The Zero-Cost Mirage
uv feels too fast. Is it cheating? No. It’s just not doing needless work.
What is the needless work? The Great Copy.
What was the Great Copy? Download → unpack → duplicate files into every .venv forever.
What does uv do instead? Download once into a global cache. Then link into environments.
So my .venv is… Real enough to run. Cheap enough to delete.
The Final Lesson
I used to waste the first 20 minutes of every project on venv rituals. And now?
Now I type uv init, then uv run. You have stopped being a Plumber of Environments.
What are you now? A Developer of Code.
What should I do to celebrate? Go delete a .venv.
Just because you can.
Summary Table for the Master uv-er
|
If you want to… |
Use this |
Instead of… |
|---|---|---|
|
Start a project |
uv init |
mkdir + python -m venv |
|
Run code (no activation) |
uv run <file>.py |
source .venv/bin/activate |
|
Add a dependency |
uv add <pkg> |
pip install + pip freeze |
|
Match the lockfile |
uv sync |
“works on my machine” |
|
Run a one-off tool |
uvx <tool> … |
global installs or tool clutter |
|
Make a script self-contained |
uv add --script <file>.py <pkg> |
“here’s a requirements.txt… somewhere” |