# How to write self-contained Python scripts using PEP 723 inline metadata


{{< callout type="info" >}}
By using [PEP 723](https://pydevtools.com/handbook/explanation/what-is-pep-723.md) inline metadata,
you can create self-contained Python scripts that declare their own dependencies. This allows you to run your scripts in isolated environments without needing to manually set up virtual environments or install dependencies.
{{< /callout >}}


## Prerequisites

- [uv installed](https://docs.astral.sh/uv/getting-started/installation/) on your system
- Basic knowledge of Python scripting

## Step 1: Create a simple script

First, create a basic Python script that requires external dependencies:

```python
# github_stats.py
import requests

def get_repo_stats(repo):
    url = f"https://api.github.com/repos/{repo}"
    response = requests.get(url)
    data = response.json()
    return data

if __name__ == "__main__":
    repo = "astral-sh/uv"
    stats = get_repo_stats(repo)
    print(f"Repository: {stats['full_name']}")
    print(f"Stars: {stats['stargazers_count']}")
    print(f"Forks: {stats['forks_count']}")
    print(f"Open Issues: {stats['open_issues_count']}")
```

> [!TIP]
> To scaffold a new script with the PEP 723 block already filled in, run `uv init --script github_stats.py --python 3.12`. The Add dependencies step still applies.

## Step 2: Add dependencies using uv

Instead of manually editing the script to add metadata, use [uv](https://pydevtools.com/handbook/reference/uv.md)'s built-in command:

```bash
uv add --script github_stats.py --python="3.9" requests
```

This adds the required inline metadata to your script:

```python
# /// script
# requires-python = ">=3.9"
# dependencies = [
#     "requests",
# ]
# ///

import requests
# rest of script...
```

## Step 3: Run the script with uv

Now run your script using uv:

```bash
uv run github_stats.py
```

When you run this command, uv will:
1. Read the inline metadata
2. Create a temporary virtual environment
3. Install the specified dependencies
4. Execute your script in this environment

## Step 4: Make your script executable (Unix systems)

To run your script directly without typing `uv run`, add a shebang line and make it executable:

```bash
# Edit your script to add this as the first line
#!/usr/bin/env -S uv run --script

# Then make it executable
chmod +x github_stats.py
```

Now you can run the script directly:

```bash
./github_stats.py
```

The `-S` flag tells `env` to split the string that follows it into separate arguments. Without it, the kernel treats `uv run --script` as a single program name and fails with `No such file or directory`, because the Linux `execve` syscall only accepts one optional argument after the interpreter path. `-S` is standard on macOS (BSD env) and on Linux with GNU coreutils 8.30 (2018) or newer.

## Running with pdm

Inline metadata is a Python standard defined in [PEP 723](https://pydevtools.com/handbook/explanation/what-is-pep-723.md), and can also be used with other tools like [PDM](https://pydevtools.com/handbook/reference/pdm.md) (`pdm run github_stats.py`), [Hatch](https://pydevtools.com/handbook/reference/hatch.md) (`hatch run github_stats.py`), and [pip](https://pydevtools.com/handbook/reference/pipx.md) (`pipx run github_stats.py`).

## Learn more

- [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.
