546 lines
13 KiB
Markdown
546 lines
13 KiB
Markdown
---
|
|
name: python-uv-scripts
|
|
description: >
|
|
Python single-file script development using uv and PEP 723 inline metadata.
|
|
Prevents invalid patterns like [tool.uv.metadata].
|
|
Use when creating standalone Python utilities, converting scripts to uv format,
|
|
managing script dependencies, implementing script testing,
|
|
or establishing team standards for script development.
|
|
---
|
|
|
|
# Python Single-File Scripts with uv
|
|
|
|
Expert guidance for creating production-ready, self-contained Python scripts using uv's inline dependency management
|
|
(PEP 723).
|
|
|
|
## Quick Start
|
|
|
|
### Create Your First uv Script
|
|
|
|
```python
|
|
#!/usr/bin/env -S uv run --script
|
|
# /// script
|
|
# requires-python = ">=3.11"
|
|
# dependencies = [
|
|
# "httpx>=0.27.0",
|
|
# "rich>=13.0.0",
|
|
# ]
|
|
# ///
|
|
|
|
import httpx
|
|
from rich import print
|
|
|
|
response = httpx.get("https://api.github.com")
|
|
print(f"[green]Status: {response.status_code}[/green]")
|
|
```
|
|
|
|
Make it executable:
|
|
|
|
```bash
|
|
chmod +x script.py
|
|
./script.py # uv automatically installs dependencies
|
|
```
|
|
|
|
### Convert Existing Script
|
|
|
|
```bash
|
|
# Add inline metadata to existing script
|
|
./tools/convert_to_uv.py existing_script.py
|
|
|
|
# Validate PEP 723 metadata
|
|
./tools/validate_script.py script.py
|
|
```
|
|
|
|
## Core Concepts
|
|
|
|
### What is PEP 723?
|
|
|
|
**PEP 723** defines inline script metadata for Python files:
|
|
|
|
```python
|
|
# /// script
|
|
# requires-python = ">=3.11"
|
|
# dependencies = [
|
|
# "package>=1.0.0",
|
|
# ]
|
|
# ///
|
|
```
|
|
|
|
**Benefits:**
|
|
|
|
- ✅ Dependencies live with the code
|
|
- ✅ No separate `requirements.txt`
|
|
- ✅ Reproducible execution
|
|
- ✅ Version constraints included
|
|
- ✅ Self-documenting
|
|
|
|
### uv Script Execution Modes
|
|
|
|
**Mode 1: Inline Dependencies** (Recommended for utilities)
|
|
|
|
```python
|
|
#!/usr/bin/env -S uv run --script
|
|
# /// script
|
|
# dependencies = ["httpx"]
|
|
# ///
|
|
```
|
|
|
|
**Mode 2: Project Mode** (For larger scripts)
|
|
|
|
```bash
|
|
uv run script.py # Uses pyproject.toml
|
|
```
|
|
|
|
### Mode 3: No Dependencies
|
|
|
|
```python
|
|
#!/usr/bin/env -S uv run
|
|
# Standard library only
|
|
```
|
|
|
|
## Critical Anti-Patterns: What NOT to Do
|
|
|
|
### ❌ NEVER Use [tool.uv.metadata]
|
|
|
|
**WRONG** - This will cause errors:
|
|
|
|
```python
|
|
# /// script
|
|
# requires-python = ">=3.11"
|
|
# [tool.uv.metadata] # ❌ THIS DOES NOT WORK
|
|
# purpose = "testing"
|
|
# ///
|
|
```
|
|
|
|
**Error**:
|
|
|
|
```text
|
|
error: TOML parse error at line 3, column 7
|
|
unknown field `metadata`
|
|
```
|
|
|
|
**Why**: `[tool.uv.metadata]` is not part of PEP 723 and is not supported by uv.
|
|
|
|
**CORRECT** - Use Python docstrings for metadata:
|
|
|
|
```python
|
|
# /// script
|
|
# requires-python = ">=3.11"
|
|
# dependencies = []
|
|
# ///
|
|
"""
|
|
Purpose: Testing automation
|
|
Team: DevOps
|
|
Author: team@example.com
|
|
"""
|
|
```
|
|
|
|
**Valid `tool.uv` fields** (if needed):
|
|
|
|
```python
|
|
# /// script
|
|
# requires-python = ">=3.11"
|
|
# dependencies = []
|
|
# [tool.uv]
|
|
# exclude-newer = "2025-01-01T00:00:00Z" # For reproducibility
|
|
# ///
|
|
```
|
|
|
|
## Real-World Examples from This Repository
|
|
|
|
### Example 1: Cluster Health Checker
|
|
|
|
See [examples/03-production-ready/check_cluster_health_enhanced.py](examples/03-production-ready/check_cluster_health_enhanced.py)
|
|
|
|
**Current version** (basic):
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
import subprocess
|
|
# Manual dependency installation required
|
|
```
|
|
|
|
**Enhanced with uv** (production-ready):
|
|
|
|
```python
|
|
#!/usr/bin/env -S uv run --script --quiet
|
|
# /// script
|
|
# requires-python = ">=3.11"
|
|
# dependencies = [
|
|
# "rich>=13.0.0",
|
|
# "typer>=0.9.0",
|
|
# ]
|
|
# ///
|
|
"""
|
|
Purpose: Cluster health monitoring
|
|
Team: Infrastructure
|
|
"""
|
|
|
|
import typer
|
|
from rich.console import Console
|
|
from rich.table import Table
|
|
```
|
|
|
|
### Example 2: CEPH Health Monitor
|
|
|
|
See [examples/03-production-ready/ceph_health.py](examples/03-production-ready/ceph_health.py)
|
|
|
|
Pattern: JSON API interaction with structured output
|
|
|
|
## Best Practices from This Repository
|
|
|
|
### 1. Security Patterns
|
|
|
|
See [reference/security-patterns.md](reference/security-patterns.md) for complete security guide including:
|
|
|
|
- Secrets management (environment variables, keyring, Infisical)
|
|
- Input validation
|
|
- Dependency security
|
|
- File operations security
|
|
- Command execution security
|
|
|
|
### 2. Version Pinning Strategy
|
|
|
|
Following this repository's approach (from `pyproject.toml`):
|
|
|
|
```python
|
|
# /// script
|
|
# requires-python = ">=3.11"
|
|
# dependencies = [
|
|
# "httpx>=0.27.0", # Minimum version for compatibility
|
|
# "rich>=13.0.0", # Known good version
|
|
# "ansible>=11.1.0", # Match project requirements
|
|
# ]
|
|
# ///
|
|
```
|
|
|
|
**Pinning levels:**
|
|
|
|
- `>=X.Y.Z` - Minimum version (most flexible)
|
|
- `~=X.Y.Z` - Compatible release (patch updates only)
|
|
- `==X.Y.Z` - Exact version (most strict)
|
|
|
|
See [reference/dependency-management.md](reference/dependency-management.md).
|
|
|
|
### 3. Team Standards
|
|
|
|
**File naming:**
|
|
|
|
```bash
|
|
check_cluster_health.py # ✅ Descriptive, snake_case
|
|
validate_template.py # ✅ Action-oriented
|
|
cluster.py # ❌ Too generic
|
|
```
|
|
|
|
**Shebang pattern:**
|
|
|
|
```python
|
|
#!/usr/bin/env -S uv run --script --quiet
|
|
# --quiet suppresses uv's own output
|
|
```
|
|
|
|
**Documentation template:**
|
|
|
|
```python
|
|
#!/usr/bin/env -S uv run --script
|
|
# /// script
|
|
# requires-python = ">=3.11"
|
|
# dependencies = []
|
|
# ///
|
|
"""
|
|
Check Proxmox cluster health
|
|
|
|
Purpose: cluster-monitoring
|
|
Team: infrastructure
|
|
Author: devops@spaceships.work
|
|
|
|
Usage:
|
|
python check_cluster_health.py [--node NODE] [--json]
|
|
|
|
Examples:
|
|
python check_cluster_health.py --node foxtrot
|
|
python check_cluster_health.py --json
|
|
"""
|
|
```
|
|
|
|
### 4. Error Handling Patterns
|
|
|
|
Following Ansible best practices from this repository:
|
|
|
|
```python
|
|
import sys
|
|
import subprocess
|
|
|
|
def run_command(cmd: str) -> str:
|
|
"""Execute command with proper error handling"""
|
|
try:
|
|
result = subprocess.run(
|
|
cmd.split(),
|
|
capture_output=True,
|
|
text=True,
|
|
check=True
|
|
)
|
|
return result.stdout
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"Error: Command failed: {cmd}", file=sys.stderr)
|
|
print(f" {e.stderr}", file=sys.stderr)
|
|
sys.exit(1)
|
|
except FileNotFoundError:
|
|
print(f"Error: Command not found: {cmd.split()[0]}", file=sys.stderr)
|
|
sys.exit(1)
|
|
```
|
|
|
|
See [patterns/error-handling.md](patterns/error-handling.md).
|
|
|
|
### 5. Testing Patterns
|
|
|
|
**Inline testing** (for simple scripts):
|
|
|
|
```python
|
|
#!/usr/bin/env -S uv run --script
|
|
# /// script
|
|
# requires-python = ">=3.11"
|
|
# dependencies = []
|
|
# ///
|
|
|
|
def validate_ip(ip: str) -> bool:
|
|
"""Validate IP address format"""
|
|
import re
|
|
pattern = r'^(\d{1,3}\.){3}\d{1,3}$'
|
|
return bool(re.match(pattern, ip))
|
|
|
|
# Inline tests
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
# Run tests if --test flag provided
|
|
if len(sys.argv) > 1 and sys.argv[1] == "--test":
|
|
assert validate_ip("192.168.1.1") == True
|
|
assert validate_ip("256.1.1.1") == False
|
|
print("All tests passed!")
|
|
sys.exit(0)
|
|
|
|
# Normal execution
|
|
print(validate_ip("192.168.3.5"))
|
|
```
|
|
|
|
See [workflows/testing-strategies.md](workflows/testing-strategies.md).
|
|
|
|
## When NOT to Use Single-File Scripts
|
|
|
|
See [anti-patterns/when-not-to-use.md](anti-patterns/when-not-to-use.md) for details.
|
|
|
|
**Use a proper project instead when:**
|
|
|
|
- ❌ Script exceeds 500 lines
|
|
- ❌ Multiple modules/files needed
|
|
- ❌ Complex configuration management
|
|
- ❌ Requires packaging/distribution
|
|
- ❌ Shared library code across multiple scripts
|
|
- ❌ Web applications or long-running services
|
|
|
|
**Example - Too Complex for Single File:**
|
|
|
|
```python
|
|
# This should be a uv project, not a script:
|
|
# - 15+ dependencies
|
|
# - Database models
|
|
# - API routes
|
|
# - Background workers
|
|
# - Configuration management
|
|
# - Multiple environments
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
See pattern guides for complete examples:
|
|
|
|
- [CLI Applications](patterns/cli-applications.md) - Typer, Click, argparse patterns
|
|
- [API Clients](patterns/api-clients.md) - httpx, requests, authentication
|
|
- [Data Processing](patterns/data-processing.md) - Polars, pandas, analysis
|
|
- [System Automation](patterns/system-automation.md) - psutil, subprocess, system admin
|
|
|
|
## CI/CD Integration
|
|
|
|
### GitHub Actions
|
|
|
|
```yaml
|
|
name: Run Health Checks
|
|
|
|
on:
|
|
schedule:
|
|
- cron: '0 */6 * * *' # Every 6 hours
|
|
|
|
jobs:
|
|
health-check:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install uv
|
|
uses: astral-sh/setup-uv@v3
|
|
|
|
- name: Check cluster health
|
|
run: |
|
|
uv run --script tools/check_cluster_health.py --json
|
|
env:
|
|
PROXMOX_TOKEN: ${{ secrets.PROXMOX_TOKEN }}
|
|
```
|
|
|
|
### GitLab CI
|
|
|
|
```yaml
|
|
cluster-health:
|
|
image: ghcr.io/astral-sh/uv:python3.11-bookworm-slim
|
|
script:
|
|
- uv run --script tools/check_cluster_health.py
|
|
only:
|
|
- schedules
|
|
```
|
|
|
|
See [workflows/ci-cd-integration.md](workflows/ci-cd-integration.md).
|
|
|
|
## Tools Available
|
|
|
|
### Script Validation
|
|
|
|
```bash
|
|
# Validate PEP 723 metadata
|
|
./tools/validate_script.py script.py
|
|
|
|
# Output:
|
|
# ✓ Valid PEP 723 metadata
|
|
# ✓ Python version specified
|
|
# ✓ Dependencies properly formatted
|
|
```
|
|
|
|
### Script Conversion
|
|
|
|
```bash
|
|
# Convert requirements.txt-based script to uv
|
|
./tools/convert_to_uv.py old_script.py
|
|
|
|
# Creates:
|
|
# - old_script_uv.py with inline dependencies
|
|
# - Preserves original script
|
|
```
|
|
|
|
## Progressive Disclosure
|
|
|
|
For deeper knowledge:
|
|
|
|
### Reference Documentation
|
|
|
|
- [PEP 723 Specification](reference/pep-723-spec.md) - Complete inline metadata spec
|
|
- [Dependency Management](reference/dependency-management.md) - Version pinning strategies
|
|
- [Security Patterns](reference/security-patterns.md) - Secrets, validation, input sanitization
|
|
|
|
### Pattern Guides
|
|
|
|
- [CLI Applications](patterns/cli-applications.md) - Typer, Click, argparse patterns
|
|
- [API Clients](patterns/api-clients.md) - httpx, requests, authentication
|
|
- [Data Processing](patterns/data-processing.md) - Polars, pandas, analysis
|
|
- [System Automation](patterns/system-automation.md) - psutil, subprocess, system admin
|
|
- [Error Handling](patterns/error-handling.md) - Exception handling, logging
|
|
|
|
> **Note:** See [Common Patterns](#common-patterns) section above for quick access to these guides.
|
|
|
|
### Working Examples
|
|
|
|
- [NetBox API Client](examples/04-api-clients/netbox_client.py) - Production-ready API client with Infisical, validation, error handling, and Rich output
|
|
- [Cluster Health Checker](examples/03-production-ready/check_cluster_health_enhanced.py) - Production-ready monitoring script with Typer, Rich, and JSON output
|
|
|
|
### Anti-Patterns
|
|
|
|
- [When NOT to Use](anti-patterns/when-not-to-use.md) - Signs you need a proper project
|
|
- [Common Mistakes](anti-patterns/common-mistakes.md) - Pitfalls and how to avoid them
|
|
|
|
### Workflows
|
|
|
|
- [Team Adoption](workflows/team-adoption.md) - Rolling out uv scripts across teams
|
|
- [CI/CD Integration](workflows/ci-cd-integration.md) - GitHub Actions, GitLab CI
|
|
- [Testing Strategies](workflows/testing-strategies.md) - Inline tests, pytest integration
|
|
|
|
## Related Skills
|
|
|
|
- **Ansible Best Practices** - Many Ansible modules could be standalone uv scripts
|
|
- **Proxmox Infrastructure** - Validation tools use this pattern
|
|
- **NetBox + PowerDNS Integration** - API interaction scripts
|
|
|
|
## Quick Reference
|
|
|
|
### Shebang Options
|
|
|
|
```python
|
|
# Standard script execution
|
|
#!/usr/bin/env -S uv run --script
|
|
|
|
# Quiet mode (suppress uv output)
|
|
#!/usr/bin/env -S uv run --script --quiet
|
|
|
|
# With Python version
|
|
#!/usr/bin/env -S uv run --script --python 3.11
|
|
```
|
|
|
|
### Common Dependencies
|
|
|
|
```python
|
|
# CLI applications
|
|
"typer>=0.9.0" # Modern CLI framework
|
|
"click>=8.0.0" # Alternative CLI framework
|
|
"rich>=13.0.0" # Rich text and formatting
|
|
|
|
# API clients
|
|
"httpx>=0.27.0" # Modern async HTTP client
|
|
"requests>=2.31.0" # Traditional HTTP client
|
|
|
|
# Data processing
|
|
"polars>=0.20.0" # Fast dataframe library
|
|
"pandas>=2.0.0" # Traditional dataframe library
|
|
|
|
# Infrastructure
|
|
"ansible>=11.1.0" # Automation (from this repo)
|
|
"infisical-python>=2.3.3" # Secrets (from this repo)
|
|
|
|
# System automation
|
|
"psutil>=5.9.0" # System monitoring
|
|
```
|
|
|
|
### Metadata Template
|
|
|
|
```python
|
|
#!/usr/bin/env -S uv run --script --quiet
|
|
# /// script
|
|
# requires-python = ">=3.11"
|
|
# dependencies = [
|
|
# # Add dependencies here
|
|
# ]
|
|
# ///
|
|
"""
|
|
One-line description
|
|
|
|
Purpose: describe-purpose
|
|
Team: team-name
|
|
Author: email@example.com
|
|
|
|
Usage:
|
|
python script.py [OPTIONS]
|
|
|
|
Examples:
|
|
python script.py --help
|
|
"""
|
|
```
|
|
|
|
## Best Practices Summary
|
|
|
|
1. **Always specify Python version** - `requires-python = ">=3.11"`
|
|
2. **Pin dependencies appropriately** - Use `>=X.Y.Z` for utilities
|
|
3. **Add metadata in docstrings** - Put team info, purpose, and author in module docstring
|
|
4. **Include comprehensive docstrings** - Document purpose, usage, and examples
|
|
5. **Handle errors gracefully** - Use try/except with clear messages
|
|
6. **Validate inputs** - Check arguments before processing
|
|
7. **Use quiet mode** - `--quiet` flag for production scripts
|
|
8. **Keep it focused** - Single file, single purpose
|
|
9. **Test inline** - Add `--test` flag for simple validation
|
|
10. **Secure secrets** - Never hardcode, use env vars or keyring
|