Contents

All‑in‑One Python Package Manager: UV!

Meet uv – A Blazingly Fast, All‑in‑One Python Package Manager

In my last post I dove into Poetry , one of the best‑loved modern packaging tools. However, Poetry is just one piece of an toolkit: we still reach for pip to install packages, virtualenv to isolate them, pyenv to juggle Python versions, and maybe Pipenv or pip‑tools for lock‑files. Each solves its own niche, yet hopping between them adds friction. uv removes that friction. This single, project manager—written in Rust and typically 10-1000x faster-replaces the whole stack: installing Python itself, creating virtual environments, resolving and locking dependencies, and even publishing to PyPI, all behind one concise CLI.


Why Python Packaging Needed a Fresh Start

Pain PointTraditional LandscapeHow uv Fixes It
Tool sprawlpip / virtualenv / pyenv / Poetry / pipxCovers the full workflow
PerformancePure‑Python dependency resolution and single‑threaded downloadsRust core, SAT‑based solver (PubGrub)
Reproducibilityrequirements.txt is order‑sensitive & lacks metadataDeterministic uv.lock capturing hashes & markers

Since its public launch, uv already powers >10 % of all downloads on PyPI—evidence that developers crave a faster, simpler workflow.


The Lock‑File Landscape at a Glance

Before we dive into uv, it helps to remember what “locking” looks like in the existing ecosystem:

  1. Pipenv – Pipfile + Pipfile.lock
  2. Poetry – pyproject.toml + poetry.lock
  3. pip – requirements.txt created via pip freeze

Each of these pins versions, but they store different metadata and often disagree on the resolver algorithm. uv adopts pyproject.toml syntax and adds its own uv.lock that captures exact versions and SHA‑256 hashes for deterministic installs.


From init to publish

1. Spin‑up

1
uv init myapp        # ➊ scaffold project & git repo

uv chooses a Python interpreter (downloading if necessary), creates .venv, writes .python-version, and commits a minimal pyproject.toml.

2. Add Dependencies

1
uv add ruff pytest   # ➋ add linting & tests
  • Environment bootstrap – if .venv doesn’t exist, it’s created.
  • Dependency resolution – the PubGrub SAT solver computes a compatible graph (an NP‑hard problem) in milliseconds.
  • Lockfile updateuv.lock is regenerated atomically so that users can reproduce the exact set.
  • Installation

If you Need to remove something, uv remove pytest cleans both the environment and the lockfile.

3. Iterate Quickly

1
2
3
uv run hello.py      # ➌ execute code inside .venv
uv run uvicorn main:app --reload # run uvicorn
uvx ruff check       # ➍ run a CLI tool in a throw‑away env

uv includes a dedicated interface for interacting with tools. Tools can be invoked without installation using uv tool run, in which case their dependencies are installed in a temporary virtual environment isolated from the current project.

Because it is very common to run tools without installing them, a uvx alias is provided for uv tool run — the two commands are exactly equivalent. For brevity, the documentation will mostly refer to uvx instead of uv tool run.

4. Manage Python Versions Mid‑Stream

1
2
3
uv python install 3.12.0   # ➎ fetch new interpreter
# switch by editing .python-version, then:
uv sync                    # rebuild .venv deterministically

No sudo, no global state—interpreters live under ~/.local/share/uv/python.

5. Publish

1
uv publish          # ➏ upload to PyPI

Zero extra config; metadata comes from pyproject.toml.


Workspaces: Multiple Apps, One Cohesive Repo

uv can treat a directory tree as a workspace. Sub‑projects share the same interpreter and .venv, which is perfect for monorepos housing a library, CLI, and docs site side‑by‑side.

1
2
uv init api            # creates api/ inside current repo
uv init web --no-workspace  # standalone env if you prefer isolation

Switching between shared and isolated workflows is a flag away.


Dependency Groups for Dev & Prod

Sometimes you want linting and testing tools only in development. uv follows Poetry’s group syntax:

1
2
uv add --dev pytest ruff      # added under [tool.poetry.group.dev]
uv sync --no-group dev        # skip dev deps

Testing with Pytest (and a VS Code Tip)

When you start a project, it’s worth deciding up front where your test files will live. I like to keep them in a dedicated tests/ directory that sits alongside the src/ directory rather than inside it. That keeps production code and test code clearly separated and makes the project tree easy to scan:

1
2
3
4
5
6
7
project_root/
├── src/
│   ├── main.py
│   └── utils.py
└── tests/
    ├── test_main.py
    └── test_utils.py

With this layout, tools such as pytest find your tests automatically, and IDEs can apply different run configurations or linters to src/ and tests/ without extra setup.

Enable pytest discovery in VS Code by adding:

1
2
3
4
5
// .vscode/settings.json
{
  "python.testing.pytestEnabled": true,
  "python.testing.cwd": "${workspaceFolder}/tests"
}

Install and run tests:

1
2
uv add --dev pytest
uv run pytest -q

Migrating an Existing venv + pip Project

  1. Freeze current deps:
    1
    
    pip freeze > requirements.txt
    
  2. Initialize uv in place:
    1
    
    uv init .
    
  3. Import:
    1
    2
    
    uv pip install -r requirements.txt
    uv lock
    
  4. Delete the old .venv and enjoy the speed‑up.

Cheat Sheet

pip / virtualenvuv
python -m venv .venvuv venv
pip install pkguv add pkg
pip install -r requirements.txtuv pip install -r requirements.txt
pip uninstall pkguv remove pkg
pip freezeuv pip freeze
pip listuv pip list

I hope you enjoyed my post!