Skip to content

    Chapter 7, Environment

    uv: the unified manager

    The uv installs Python versions, creates environments, resolves dependencies, runs scripts, and installs tools, all in a single very fast binary. It is what I use by default in new projects.

    This chapter uses terminal commands. The files for this chapter are in exemplos/.

    Why I use uv

    The uv is made by Astral (the creators of ruff) and written in Rust. What convinced me was the combination of three things: it replaces pyenv, venv, pip, pipx and a good part of Poetry; it downloads ready-made Pythons instead of compiling; and it has a pip-compatible interface for when you already have a setup.

    Install

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

    With Homebrew: brew install uv.

    It is also possible to install with pipx install uv or pip install uv. If you installed via the official installer, update with:

    Terminal
    uv self update
    

    Manage Python versions

    The uv downloads and keeps the versions you request. No compiler or previous Python is needed:

    Terminal
    uv python install 3.12 3.13 3.14
    uv python list
    uv python pin 3.13
    

    The uv python pin writes the file .python-version in the folder, the same file that pyenv uses.

    Create and manage a project

    Terminal
    uv init meu-app
    cd meu-app
    uv add requests
    uv add --dev pytest
    uv run python main.py
    uv run pytest
    

    The uv add changes the pyproject.toml and updates the file uv.lock. The uv run ensures that the environment exists and is up to date before executing, so you don't need to activate anything. The pyproject.toml looks like this:

    exemplos/uv/pyproject.toml
    [project]
    name = "meu-app"
    version = "0.1.0"
    description = "Exemplo de projeto gerenciado pelo uv"
    readme = "README.md"
    requires-python = ">=3.12"
    dependencies = [
        "requests>=2.34.2",
    ]
    
    [dependency-groups]
    dev = [
        "pytest>=9.1.1",
    ]
    

    Other commands that I use all the time:

    Terminal
    uv remove requests
    uv sync
    uv lock
    uv tree
    

    Scripts with embedded dependencies

    For a standalone script that needs a library, uv accepts metadata in the file itself (a standard called PEP 723). It creates a temporary environment just for that execution:

    exemplos/uv/script_inline.py
    # /// script
    # requires-python = ">=3.12"
    # dependencies = ["rich"]
    # ///
    from rich import print
    
    print("[bold]Olá[/bold] de um script com dependências embutidas")
    
    Terminal
    uv run exemplos/uv/script_inline.py
    

    To add a dependency to an existing script: uv add --script script.py requests.

    Command line tools

    Terminal
    uv tool install ruff
    uvx ruff check .
    uv tool list
    uv tool upgrade --all
    

    The uvx is a shortcut for uv tool run: runs the tool in a temporary environment, like pipx run.

    Compatibility with pip

    If you already have requirements.txt and don't want to migrate now, use the compatible interface:

    Terminal
    uv venv
    uv pip install -r requirements.txt
    

    uv add versus uv pip install

    The uv add registers the dependency in pyproject.toml and in the lockfile. The uv pip install only installs in the environment, in pip style, and does not register anything. Mixing the two in the same project is the most common cause of "it works on my machine".