Python foundations

Adopt the uv workflow for Python projects

Move a Python project to uv with pinned interpreters, a lockfile, single-command runs, and inline script metadata — including the migration path from pip, venv, and Poetry.

Outcome

Run one Python project end to end on uv — pinned interpreter, locked dependencies, reproducible commands, and dependency-carrying single-file scripts — and leave pip-forgetting behind.

Prerequisites
  • A Python project with a requirements file, pyproject.toml, or Poetry lock
  • uv installed, or a willingness to install it with one command
  • No project rewrite required

uv has become the default workflow for new Python projects: one Rust binary manages interpreters, virtual environments, dependencies, lockfiles, and project commands. For AI coding it matters even more — a single uv run gives your agent a reproducible environment without shell history archaeology.

You are done with this guide when uv run executes your checks on a pinned interpreter with a committed lockfile, and a clean clone reaches the same result.

Pin the interpreter first

uv python install 3.12
uv python pin 3.12

The pin lands in .python-version, so every contributor, CI runner, and coding agent resolves the same interpreter. This replaces per-machine Python guesses — the same discipline the free-threading decision guide relies on.

Manage dependencies with a real lockfile

uv init
uv add httpx pytest
uv lock

uv.lock records the full resolved graph, including transitive dependencies and hashes. Commit it. uv add and uv remove keep pyproject.toml and the lockfile in sync, which removes the two-files-drift failure mode of hand-edited requirements.

The uv documentation covers projects, workspaces, and tool management; the feature overview is the fastest map of what replaces which old command.

Run everything through uv

uv run pytest -q
uv run python -m mypackage.cli

uv run syncs the environment from the lockfile before executing, so “works on my machine” stops being a state you can reach by accident. This is also the exact property that makes AI-generated code trustworthy enough to run: the agent cannot silently depend on a package that is not declared.

Scripts that carry their own dependencies

Single-file scripts can declare dependencies inline, so they run anywhere without a project around them:

# /// script
# dependencies = ["httpx"]
# ///
import httpx

print(httpx.get("https://flypython.com/llms.txt").status_code)

Run it with uv run script.py — uv creates an ephemeral environment from the header block. Useful for automation one-offs you still want reproducible.

Migrate without a rewrite

  • From pip + venv: delete the manual activation habit; uv pip install -r requirements.txt once, then move entries to uv add.
  • From Poetry: uv init in place, uv add your dependencies from pyproject.toml, delete poetry.lock, and commit uv.lock.
  • From tox/Make glue: keep the tasks, but make every line start with uv run.

Packaging and publishing follow the same standard — see the Python packaging user guide for what pyproject.toml must declare when you ship a package; uv build produces the artifacts from it.

Sources

The uv documentation covers projects, interpreters, lockfiles, and scripts; its feature overview provides the command-by-command replacement map; the Python packaging user guide defines the pyproject.toml publishing contract. All three are linked where they are used above.

Verification record

Documentation review

Editorial review against the uv documentation and the Python packaging user guide. Verified 2026-09-06.

About the author

Organizational byline for FlyPython guides, verification records, and corrections. Editorial standards and contact details →