--- title: "PEP 723 - Inline Script Metadata" description: "Official Python specification for embedding dependency metadata in single-file scripts" version: "1.0.0" last_updated: "2025-11-04" document_type: "reference" official_specification: "https://peps.python.org/pep-0723/" python_compatibility: "3.11+" related_docs: - "../SKILL.md" - "./python-development-orchestration.md" --- # 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 [/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 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 command reference](~/.claude/commands/shebangpython.md) ## 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 command](~/.claude/commands/shebangpython.md)