6.9 KiB
6.9 KiB
PEP 723: Inline Script Metadata Specification
Complete reference for PEP 723 inline script metadata format used by uv.
Overview
PEP 723 defines a standardized way to embed dependency and configuration metadata directly within Python script files. This eliminates the need for separate requirements.txt files and enables self-contained, reproducible scripts.
Basic Format
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "package-name>=1.0.0",
# ]
# ///
Key Requirements:
- Must appear as comments (
#) - Must use
# /// scriptas opening marker - Must use
# ///as closing marker - Must be valid TOML syntax
- Recommended placement: After shebang, before module docstring
Complete Specification
Required Fields
requires-python
Specifies minimum Python version:
# /// script
# requires-python = ">=3.11"
# ///
Formats:
requires-python = ">=3.11" # Minimum version
requires-python = ">=3.11,<3.13" # Version range
requires-python = "==3.12" # Exact version
dependencies
Lists required packages:
# /// script
# dependencies = [
# "httpx>=0.27.0",
# "rich>=13.0.0",
# "typer~=0.9.0",
# ]
# ///
Version Specifiers:
"package" # Any version
"package>=1.0" # Minimum version
"package>=1.0,<2.0" # Version range
"package~=1.2.3" # Compatible release (1.2.x)
"package==1.2.3" # Exact version
Optional Fields
[tool.uv] Section
uv-specific configuration:
# /// script
# requires-python = ">=3.11"
# dependencies = []
#
# [tool.uv]
# exclude-newer = "2024-10-01T00:00:00Z"
# index-url = "https://pypi.org/simple"
# ///
Available options:
exclude-newer: Only use packages published before this dateindex-url: Alternative PyPI indexextra-index-url: Additional package indexesfind-links: Additional package sourcesno-index: Ignore PyPI entirely
[tool.uv.sources]
Custom package sources:
# /// script
# dependencies = ["my-package"]
#
# [tool.uv.sources]
# my-package = { git = "https://github.com/user/repo", tag = "v1.0" }
# ///
Valid [tool.uv] Fields
Additional uv-specific configuration (optional):
# /// script
# requires-python = ">=3.11"
# dependencies = []
#
# [tool.uv]
# exclude-newer = "2025-01-01T00:00:00Z" # Reproducibility constraint
# ///
Note: For custom metadata like purpose, team, author, use Python docstrings instead:
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
"""
Purpose: cluster-monitoring
Team: infrastructure
Author: devops@example.com
Created: 2024-10-20
"""
Complete Examples
Minimal Script
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
"""Simple script with no dependencies"""
print("Hello, world!")
Production Script
#!/usr/bin/env -S uv run --script --quiet
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx>=0.27.0",
# "rich>=13.0.0",
# "typer>=0.9.0",
# ]
#
# [tool.uv]
# exclude-newer = "2025-01-01T00:00:00Z"
# ///
"""
API client for Proxmox cluster monitoring
Purpose: api-client
Team: infrastructure
Author: devops@spaceships.work
Usage:
check_cluster.py [--node NODE] [--json]
"""
import typer
import httpx
from rich import print
With Git Dependencies
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "my-internal-lib",
# ]
#
# [tool.uv.sources]
# my-internal-lib = { git = "https://github.com/org/lib.git", tag = "v1.2.3" }
# ///
With Local Dependencies
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "my-local-package",
# ]
#
# [tool.uv.sources]
# my-local-package = { path = "../my-package", editable = true }
# ///
Placement Guidelines
Correct Placement
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
"""Module docstring comes after metadata"""
import sys
def main():
pass
if __name__ == "__main__":
main()
Multiple Metadata Blocks (Invalid)
# ❌ INVALID - Only one metadata block allowed
# /// script
# requires-python = ">=3.11"
# ///
# /// script
# dependencies = ["httpx"]
# ///
Validation
Valid Metadata
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx>=0.27.0", # Comments allowed in arrays
# "rich",
# ]
# ///
Invalid Metadata
# ❌ Missing closing marker
# /// script
# requires-python = ">=3.11"
# ❌ Invalid TOML syntax
# /// script
# dependencies = httpx # Missing quotes
# ///
# ❌ Not in comments
/// script
requires-python = ">=3.11"
///
Creating Metadata
Manual Creation
Add metadata block manually:
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "httpx>=0.27.0",
# ]
# ///
import httpx
Using uv init
Generate script with metadata:
uv init --script my_script.py --python 3.11
Using uv add
Add dependencies to existing script:
uv add --script my_script.py httpx rich typer
Validation Tools
Check Metadata Validity
# Parse metadata
uv run --script my_script.py --dry-run
# Validate with custom tool
python tools/validate_script.py my_script.py
Extract Metadata
import re
import tomllib
def extract_metadata(script_path: str) -> dict:
"""Extract PEP 723 metadata from script"""
with open(script_path) as f:
content = f.read()
# Find metadata block
pattern = r'# /// script\n((?:# .*\n)+)# ///'
match = re.search(pattern, content)
if not match:
return {}
# Parse TOML (remove leading # from each line)
toml_lines = match.group(1).split('\n')
toml_content = '\n'.join(line[2:] for line in toml_lines if line.startswith('# '))
return tomllib.loads(toml_content)
Compatibility
PEP 723 Support
- ✅ uv (native support)
- ✅ pip (via
pip-run) - ✅ pipx (v1.4.0+)
- ⚠️ Other tools (check documentation)
Fallback for Non-Supporting Tools
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.11"
# dependencies = ["httpx"]
# ///
"""
Fallback installation for non-PEP-723 tools:
pip install httpx
python script.py
"""
Best Practices
- Always include requires-python - Prevents compatibility issues
- Pin major versions -
>=X.Y.Zfor stability - Add metadata section - Document purpose and ownership
- Keep dependencies minimal - Only what's necessary
- Document fallbacks - Help non-uv users
- Validate syntax - Use validation tools
- Version consistently - Match project conventions