uv
pixi
prek
nox
(install via uv tool install)
uv tool install
These topics give you an understanding of what's going on. We'll introduce high-level abstractions next!
How to install stuff — three options:
/...
~/.local/...
.venv/
System or user installs sound nice, but:
Solution: a virtual environment — a folder (.venv) that isolates dependencies.
.venv
.venv ├── .gitignore ├── CACHEDIR.TAG ├── bin │ ├── activate # shell activation script │ ├── python # symlinks to your Python │ ├── python3 │ └── python3.14 ├── lib │ └── python3.14 │ └── site-packages # installed libraries └── pyvenv.cfg # marks this as a venv
Direct usage:
.venv/bin/python ...
Activation:
. .venv/bin/activate python ... deactivate
Activation sets PATH so the venv's bin/ comes first.
PATH
bin/
python3 -m venv .venv
virtualenv .venv
uv venv
We'll use uv.
A venv is expendable — you should be able to delete and recreate it.
Instead of manually installing, list packages in a file:
Libraries also use "Project" patterns for dev needs (tests, docs).
Creating, activating, installing, and locking are all separate steps with different tools.
Apps you install and run — never need to import alongside unrelated packages.
This is what pipx and uv tool do!
pipx
uv tool
uv tool install cowsay uv tool list uv tool upgrade cowsay uv tool uninstall cowsay
Combine install + run — uvx:
uvx
uvx cowsay
Makes a venv, downloads the app, runs it. Recreates if over a week old.
All of PyPI at your fingertips — no need to remember to update!
# /// script # dependencies = ["numpy"] # /// import numpy as np if __name__ == "__main__": print(np.array([1, 2, 3]))
uv run simple.py # dependencies downloaded into a temp venv
Very similar to libraries — key difference: always commit your lockfile.
You can make an unpublishable library:
[project] classifiers = [ "Private :: Do Not Upload", ]
Shared with others — must coexist in an environment with whatever else the user needs.
Basic pyproject.toml:
pyproject.toml
[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "example" version = "0.1.0"
The [build-system] tells tools how to build the package.
[build-system]
[build-system] requires = ["hatchling"] # 1: build-time only [project] dependencies = [] # 2: always installed [project.optional-dependencies] extra = [] # 3: user-requested extras [dependency-groups] dev = [] # 4: dev-only, not in metadata
b-s.r
p.d
p.o-d
d-g
If dependency-groups has a dev group, uv run installs it by default:
dependency-groups
dev
uv run
[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "example" version = "0.1.0" requires-python = ">=3.12" dependencies = ["numpy"] [dependency-groups] dev = ["pytest"]
uv run pytest
uv.lock
Edit dependencies? Just uv run again — lockfile and venv update automatically.