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.txtThe script now starts with the metadata block:
# /// script
# requires-python = ">=3.13"
# dependencies = [
# "httpx>=0.27",
# "rich",
# ]
# ///
import httpx
from rich import printThe # /// 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.txtuv 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.pyuv 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.pyDelete 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.txtOptional: 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:
#!/usr/bin/env -S uv run --script
# /// script
# ...
# ///chmod +x report.py
./report.pyThe -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
- What is PEP 723? explains the standard the inline block follows
- How to write self-contained Python scripts using PEP 723 covers writing a new script from scratch
- How to migrate from requirements.txt to pyproject.toml with uv covers the project-layout alternative
- Inline script metadata guide (uv docs)