# 1591 Pep723 E04def94

> Official Python specification for embedding dependency metadata in single-file scripts

- Skill: `tools-only/1591-pep723-e04def94` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/1591-pep723-e04def94`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/1591-pep723-e04def94/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/tools-only/1591-pep723-e04def94

---


# PEP 723 - Inline Script Metadata

## What is PEP 723?

PEP 723 is the official Python specification that defines a standard format for embedding metadata in single-file Python scripts. It allows scripts to declare their dependencies and Python version requirements without requiring separate configuration files like `pyproject.toml` or `requirements.txt`.

## Official Specification

The model must WebFetch this url before discussing the topic with the user [pep-0723](https://peps.python.org/pep-0723/)

## Key Concept

PEP 723 metadata is embedded **inside Python comments** using a special syntax, making the metadata human-readable and machine-parseable while keeping the script as a single portable file.
If implementing anything to interact with this metadata, such as a linting enhancer you must WebFetch [inline-script-metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata) to get the schema and syntax and implementation.

## The Problem It Solves

### The Challenge

When sharing Python scripts as standalone files (via email, gists, URLs, or chat), there's a fundamental problem:

- **Scripts often need external dependencies** (requests, rich, pandas, etc.)
- **No standard way** to declare these dependencies within the script itself
- **Tools can't automatically know** what packages to install to run the script
- **Users must read documentation** or comments to figure out requirements

### The Solution

PEP 723 provides a **standardized comment-based format** that:

- ✅ Embeds dependency declarations directly in the script
- ✅ Remains a valid Python file (metadata is in comments)
- ✅ Is machine-readable by package managers (uv, PDM, Hatch)
- ✅ Keeps everything in a single portable file

## Syntax

### Format

PEP 723 metadata is written as **TOML inside specially-formatted Python comments**:

```python
# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "requests<3",
#   "rich",
# ]
# ///
```

### Rules

1. **Opening marker**: `# /// script` (exactly, with spaces)
2. **Content**: Valid TOML, with each line prefixed by `#` and a space
3. **Closing marker**: `# ///` (exactly, with spaces)
4. **Location**: Typically near the top of the file, after shebang and module docstring
5. **Indentation**: Use consistent comment formatting

### Supported Fields

```toml
# /// script
# requires-python = ">=3.11"           # Minimum Python version
# dependencies = [                      # External packages
#   "requests>=2.31.0,<3",
#   "rich>=13.0",
#   "typer[all]>=0.12.0",
# ]
# ///
```

## When to Use PEP 723

### ✅ Use PEP 723 When

1. **Script has external dependencies**

   - Uses packages from PyPI (requests, pandas, rich, etc.)
   - Needs specific package versions
   - Example: A CLI tool that fetches data from APIs

2. **Sharing standalone scripts**

   - Sending scripts via email, gists, or chat
   - Publishing example scripts in documentation
   - Creating portable automation tools

3. **Scripts need reproducibility**
   - Version-pinned dependencies for consistent behavior
   - Specific Python version requirements
   - Example: Deployment scripts that must work identically across environments

### ❌ Don't Use PEP 723 When

1. **Script uses only stdlib**

   - No external dependencies = nothing to declare
   - Use simple shebang: `#!/usr/bin/env python3`
   - Example: A script that uses only `argparse`, `pathlib`, `json`

2. **Full project with pyproject.toml**

   - Projects have proper package structure
   - Use `pyproject.toml` for dependency management
   - PEP 723 is for **single-file scripts**, not projects

3. **Script is part of a package**
   - Package dependencies are declared in `pyproject.toml`
   - Script uses package-level dependencies
   - No need to duplicate declarations

## Shebang Requirements

### Scripts with PEP 723 Metadata

**Must use** the uv-based shebang for automatic dependency installation:

```python
#!/usr/bin/env -S uv --quiet run --active --script
# /// script
# requires-python = ">=3.11"
# dependencies = ["requests", "rich"]
# ///

import requests
from rich import print
```

**Why this shebang?**

- `uv --quiet run --active --script`: Tells uv to:
  - Read PEP 723 metadata from the script
  - Install declared dependencies automatically
  - Execute the script with correct environment

### Stdlib-Only Scripts

**Use** the standard Python shebang (no PEP 723 needed):

```python
#!/usr/bin/env python3

import argparse
import pathlib
import json

# No dependencies to declare
```

**Why no PEP 723?**

- Stdlib is always available (bundled with Python)
- Nothing to declare = no metadata needed
- Simpler is better when appropriate

## Complete Example

### Script with External Dependencies

```python
#!/usr/bin/env -S uv --quiet run --active --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "requests>=2.31.0,<3",
#   "rich>=13.0",
# ]
# ///

"""Fetch GitHub user info and display with rich formatting."""

import sys
from typing import Any

import requests
from rich.console import Console
from rich.panel import Panel

console = Console()


def fetch_user(username: str) -> dict[str, Any] | None:
    """Fetch GitHub user data."""
    response = requests.get(f"https://api.github.com/users/{username}")
    if response.status_code == 200:
        return response.json()
    return None


def main() -> None:
    """Main entry point."""
    if len(sys.argv) != 2:
        console.print("[red]Usage: script.py <github-username>[/red]")
        sys.exit(1)

    username = sys.argv[1]
    user = fetch_user(username)

    if user:
        console.print(
            Panel(
                f"[bold]{user['name']}[/bold]\n"
                f"Followers: {user['followers']}\n"
                f"Public Repos: {user['public_repos']}",
                title=f"GitHub: {username}",
            )
        )
    else:
        console.print(f"[red]User '{username}' not found[/red]")


if __name__ == "__main__":
    main()
```

**To run**:

```bash
chmod +x script.py
./script.py octocat
```

The script will:

1. Read PEP 723 metadata
2. Install `requests` and `rich` if not present
3. Execute with dependencies available

### Stdlib-Only Script

```python
#!/usr/bin/env python3

"""Simple JSON formatter using only stdlib."""

import argparse
import json
import sys
from pathlib import Path


def format_json(input_path: Path, indent: int = 2) -> None:
    """Format JSON file with specified indentation."""
    data = json.loads(input_path.read_text())
    formatted = json.dumps(data, indent=indent, sort_keys=True)
    print(formatted)


def main() -> None:
    """Main entry point."""
    parser = argparse.ArgumentParser(description="Format JSON files")
    parser.add_argument("file", type=Path, help="JSON file to format")
    parser.add_argument("--indent", type=int, default=2, help="Indentation spaces")

    args = parser.parse_args()
    format_json(args.file, args.indent)


if __name__ == "__main__":
    main()
```

**No PEP 723 needed** - all imports are from Python's standard library.

## Tool Support

### Package Managers

The following tools support PEP 723 inline script metadata:

- **uv**: [https://docs.astral.sh/uv/](https://docs.astral.sh/uv/)
- **PDM**: [https://pdm-project.org/](https://pdm-project.org/)
- **Hatch**: [https://hatch.pypa.io/](https://hatch.pypa.io/)

### Running Scripts with uv

```bash
# Make script executable
chmod +x script.py

# Run directly (uv reads PEP 723 metadata)
./script.py

# Or explicitly with uv
uv run script.py
```

### Alternative: PDM

```bash
pdm run script.py
```

## Common Patterns

### Version Constraints

```python
# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "requests>=2.31.0,<3",      # Major version constraint
#   "rich~=13.7",                # Compatible release
#   "typer[all]",                # With extras
# ]
# ///
```

### Development vs Production

**For scripts**, there's typically no separation - all dependencies are runtime dependencies. If you need development tools (testing, linting), those belong in a full project with `pyproject.toml`.

### Git-Based Dependencies

```python
# /// script
# dependencies = [
#   "mylib @ git+https://github.com/user/mylib.git@v1.0.0",
# ]
# ///
```

## Best Practices

### 1. Pin Major Versions

```python
# Good - prevents breaking changes
"requests>=2.31.0,<3"

# Avoid - might break on major updates
"requests"
```

### 2. Document the Script

```python
#!/usr/bin/env -S uv --quiet run --active --script
# /// script
# requires-python = ">=3.11"
# dependencies = ["requests", "rich"]
# ///

"""
Fetch and display GitHub user statistics.

Usage:
    ./github_stats.py <username>

Example:
    ./github_stats.py octocat
"""
```

### 3. Keep Scripts Focused

PEP 723 is for **single-file scripts**. If your script is growing large or needs multiple modules, consider creating a proper Python package with `pyproject.toml`.

### 4. Test Portability

```bash
# Test on clean environment
uv run --isolated script.py args
```

## Comparison: PEP 723 vs pyproject.toml

| Aspect           | PEP 723 (Script)          | pyproject.toml (Project)   |
| ---------------- | ------------------------- | -------------------------- |
| **Use case**     | Single-file scripts       | Multi-module packages      |
| **Dependencies** | Inline comments           | Separate TOML file         |
| **Portability**  | Single file to share      | Requires project structure |
| **Complexity**   | Simple, focused           | Full project metadata      |
| **When to use**  | Scripts with dependencies | Libraries, applications    |

## Validation

### Using /shebangpython Command

The `/shebangpython` command validates PEP 723 compliance:

```bash
/shebangpython script.py
```

**Checks**:

- ✅ Correct shebang for dependency type
- ✅ PEP 723 syntax if external dependencies detected
- ✅ Metadata fields are valid
- ✅ Execute permission set

See: [shebangpython skill](../../shebangpython/SKILL.md) or activate with `/shebangpython`

## Troubleshooting

### Script Won't Execute

**Problem**: `./script.py` fails with "dependencies not found"

**Solution**: Check shebang is correct for PEP 723:

```python
#!/usr/bin/env -S uv --quiet run --active --script
```

### Syntax Errors in Metadata

**Problem**: TOML parsing fails

**Solution**: Validate TOML syntax:

```python
# /// script
# requires-python = ">=3.11"    # ✅ Correct
# dependencies = [               # ✅ Correct - list syntax
#   "requests",
# ]
# ///
```

### Performance Concerns

**Problem**: Script slow to start (installing dependencies)

**Solution**: uv caches dependencies. First run may be slow, subsequent runs are fast. For production, consider packaging as a proper project.

## Migration

### From requirements.txt

**Before** (two files):

```text
# requirements.txt
requests>=2.31.0
rich>=13.0
```

```python
#!/usr/bin/env python3
# script.py (separate file)

import requests
from rich import print
```

**After** (single file):

```python
#!/usr/bin/env -S uv --quiet run --active --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "requests>=2.31.0",
#   "rich>=13.0",
# ]
# ///

import requests
from rich import print
```

### From Setup.py Scripts

**Before** (package structure):

```text
myproject/
├── setup.py
├── requirements.txt
└── scripts/
    └── tool.py
```

**After** (standalone script):

```python
#!/usr/bin/env -S uv --quiet run --active --script
# /// script
# requires-python = ">=3.11"
# dependencies = ["requests", "rich"]
# ///

# tool.py - now fully self-contained
```

## Summary

### Key Takeaways

1. **PEP 723 = Dependency Metadata for Single-File Scripts**

   - Standard format for declaring dependencies in comments
   - TOML content inside `# ///` delimiters

2. **When to Use**

   - Scripts **with external dependencies**
   - Need portability (single file to share)
   - Want automatic dependency installation

3. **When NOT to Use**

   - Stdlib-only scripts (nothing to declare)
   - Full projects (use `pyproject.toml`)
   - Package modules (use package dependencies)

4. **Shebang Requirements**

   - With PEP 723: `#!/usr/bin/env -S uv --quiet run --active --script`
   - Stdlib only: `#!/usr/bin/env python3`

5. **Tool Support**
   - uv, PDM, Hatch all support PEP 723
   - Automatic dependency installation on script execution

### Quick Reference

```python
# Template for PEP 723 script
#!/usr/bin/env -S uv --quiet run --active --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "package-name>=version",
# ]
# ///

"""Script description."""

import package_name

# Your code here
```

## See Also

- **Official PEP**: [https://peps.python.org/pep-0723/](https://peps.python.org/pep-0723/)
- **uv Documentation**: [https://docs.astral.sh/uv/](https://docs.astral.sh/uv/)
- **Skill Reference**: [Python Development SKILL.md](../SKILL.md)
- **Shebang Validation**: [shebangpython skill](../../shebangpython/SKILL.md) or `/shebangpython`

