Initial commit
This commit is contained in:
548
skills/python3-development/references/PEP723.md
Normal file
548
skills/python3-development/references/PEP723.md
Normal file
@@ -0,0 +1,548 @@
|
||||
---
|
||||
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 <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 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)
|
||||
Reference in New Issue
Block a user