It’s 2025 and there are at least that many ways to manage Python dependencies. This short blog post will cover the two approaches I recommend for most use cases.

Quick and Dirty: venv and pip

If you’ve got Python (≳ 3.4) available in your path (try which python && python --version), then you can jump right in with this approach for quick and dirty usage, one-off scripts, etc. Here’s all you need to do:

mkdir myproj && cd myproj
python -m venv .venv
source .venv/bin/activate
pip install foo bar baz
# or if you have a requirements file...
pip install -r requirements.txt
# then to deactivate
deactivate

Now, if you use zsh and oh-my-zsh, then you can install the python plugin and have a few shortcuts available to you like mkv and vrun (IIRC), but that’s up to you. If you want to use some other shell, go for it; the above approach will still work. No matter what, you should be doing this at minimum any time you’re touching Python. It’s 2025, stop touching the system Python! This is sufficient to cover probably 80% of “normal” Python usage.

Short and Sweet: uv

I’ve done a few posts evangelizing some of the niceties of uv. Here’s how I like to use it. First, if you’re on a new machine, install it:

curl -LsSf https://astral.sh/uv/install.sh | sh

You may need to restart your shell if uv isn’t in your $PATH after installation, but in my experience, it “just works”. Now, it’s pretty similar to the above approach, with some minor tweaks:

mkdir myproj && cd myproj
uv init .
# add project dependencies
uv add foo bar baz
# or if you have a requirements file
uv add -r requirements.txt
# dump to requirements file if needed
uv pip freeze > requirements.txt

Now, one of the cool parts of uv is that it has a pip interface that’s fully compatible with the old pip interface you probably already know and love, but under the hood it uses the blazingly fast uv machinery. So if you don’t want to go all in on the uv way, or if you have a legacy project that uses regular old venv and requirements.txt, you can just use uv pip where you’d normally use pip, and you’re off to the races. The only thing to remember is that you’ll want to prefix all your commands with uv run; that’s pretty much it. If you’re worried about path issues, uv will traverse your directory tree and find the “right” virtual environment for your project (see the documentation for more info).

It’s worth noting that uv has a ton of other benefits and features:

  • Out-of-the-box pyproject.toml makes “modern” approaches to managing Python projects trivial
  • Support for inline metadata scripts with the --script flag (see my post about the shebang)
  • uv tool or the alias uvx for “global” Python tools
  • It’s freakishly fast. Really, it’s kind of unbelievable
  • Did I mention it’s fully compatible with the pip interface you already know? The speedup alone makes it worth it, not to mention everything else

Ok, enough fanboying, go build something.