Skip to content

How to convert a script with requirements.txt to PEP 723 inline metadata

A standalone Python script paired with a requirements.txt is the old way of saying “this script needs these libraries.” PEP 723 replaces the sidecar file with a TOML block embedded in the script itself, and uv reads that block to install dependencies into a temporary virtual environment on every run.

Translate the requirements.txt into inline metadata

Point uv add --script at the existing requirements file. uv reads it and prepends a PEP 723 block to the script:

uv add --script report.py -r requirements.txt

The script now starts with the metadata block:

report.py
# /// script
# requires-python = ">=3.13"
# dependencies = [
#     "httpx>=0.27",
#     "rich",
# ]
# ///
import httpx
from rich import print

The # /// script and # /// lines are the PEP 723 markers; everything between them is TOML, comment-prefixed so the file stays valid Python.

Fix the Python version

uv add --script writes whatever Python version uv is currently using into requires-python, which is almost never what the script actually needs. Edit the line to the real lower bound:

# requires-python = ">=3.10"

Or set it explicitly when first adding dependencies:

uv add --script report.py --python ">=3.10" -r requirements.txt

uv downloads a matching interpreter automatically when the script runs, so a reader on Python 3.9 doesn’t need to install Python 3.10 separately.

Run the script with uv

Replace the venv-and-pip dance with one command, on any platform:

uv run report.py

uv reads the inline block, materializes a temporary environment with the listed dependencies, and runs the script. Subsequent runs reuse the cached environment.

If the script lives next to a pyproject.toml, pass --script to force inline-metadata mode and ignore the surrounding project, as covered in how to fix the “no project table found” error:

uv run --script report.py

Delete the requirements.txt

The metadata now lives inside the script, so the sidecar is dead weight. Anyone with uv installed can clone the script alone and run it.

rm requirements.txt

Optional: make the script directly executable

On macOS and Linux, a uv-aware shebang lets the file run without typing uv run first. Windows resolves shebangs through the Python Launcher, which doesn’t recognize uv run --script, so Windows users keep invoking the script with uv run report.py.

Add the shebang as the first line and mark the file executable:

report.py
#!/usr/bin/env -S uv run --script
# /// script
# ...
# ///
chmod +x report.py
./report.py

The -S flag tells env to split the rest of the shebang on whitespace, which is what makes uv run --script work as a single shebang program.

When to keep the project layout instead

Stay on a pyproject.toml and lock file when the script imports first-party modules from a package on disk, or when multiple scripts share a dependency set worth pinning together. Inline metadata uses version constraints, not pinned hashes; if reproducibility needs the stronger guarantee a lock file provides, migrate to pyproject.toml instead.

Related

Last updated on

Please submit corrections and feedback...