# 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](https://pydevtools.com/handbook/explanation/what-is-pep-723.md) replaces the sidecar file with a TOML block embedded in the script itself, and [uv](https://pydevtools.com/handbook/reference/uv.md) reads that block to install dependencies into a temporary [virtual environment](https://pydevtools.com/handbook/explanation/what-is-a-virtual-environment.md) 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:

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

The script now starts with the metadata block:

```python {filename="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:

```python
# requires-python = ">=3.10"
```

Or set it explicitly when first adding dependencies:

```bash
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:

```bash
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](https://pydevtools.com/handbook/how-to/how-to-fix-no-project-table-found-error-in-uv.md):

```bash
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.

```bash
rm requirements.txt
```
```powershell
Remove-Item 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](https://docs.python.org/3/using/windows.html#python-launcher-for-windows), 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:

```python {filename="report.py"}
#!/usr/bin/env -S uv run --script
# /// script
# ...
# ///
```

```bash
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](https://pydevtools.com/handbook/explanation/what-is-a-lock-file.md) 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](https://pydevtools.com/handbook/how-to/migrate-requirements.txt.md) instead.

## Related

- [uv: A Complete Guide](https://pydevtools.com/handbook/explanation/uv-complete-guide.md) covers what uv does, how fast it is, the core workflows, and recent releases.
- [What is PEP 723?](https://pydevtools.com/handbook/explanation/what-is-pep-723.md) explains the standard the inline block follows
- [How to write self-contained Python scripts using PEP 723](https://pydevtools.com/handbook/how-to/how-to-write-a-self-contained-script.md) covers writing a new script from scratch
- [How to migrate from requirements.txt to pyproject.toml with uv](https://pydevtools.com/handbook/how-to/migrate-requirements.txt.md) covers the project-layout alternative
- [Inline script metadata guide (uv docs)](https://docs.astral.sh/uv/guides/scripts/)
