Initial commit
This commit is contained in:
209
skills/python-uv-scripts/patterns/api-clients.md
Normal file
209
skills/python-uv-scripts/patterns/api-clients.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# API Client Patterns
|
||||
|
||||
Patterns for building API clients with uv scripts.
|
||||
|
||||
## Basic GET Request
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "httpx>=0.27.0",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
import httpx
|
||||
|
||||
response = httpx.get("https://api.github.com/users/octocat")
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
print(f"Name: {data['name']}")
|
||||
```
|
||||
|
||||
## Authenticated Requests
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "httpx>=0.27.0",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
import httpx
|
||||
import os
|
||||
import sys
|
||||
|
||||
api_token = os.getenv("API_TOKEN")
|
||||
if not api_token:
|
||||
print("Error: API_TOKEN not set", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
headers = {"Authorization": f"Bearer {api_token}"}
|
||||
|
||||
response = httpx.get(
|
||||
"https://api.example.com/data",
|
||||
headers=headers,
|
||||
timeout=10.0
|
||||
)
|
||||
response.raise_for_status()
|
||||
print(response.json())
|
||||
```
|
||||
|
||||
## POST with JSON
|
||||
|
||||
```python
|
||||
import httpx
|
||||
|
||||
data = {
|
||||
"name": "example",
|
||||
"status": "active"
|
||||
}
|
||||
|
||||
response = httpx.post(
|
||||
"https://api.example.com/resources",
|
||||
json=data,
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=10.0
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
print(f"Created: {result['id']}")
|
||||
```
|
||||
|
||||
## Query Parameters
|
||||
|
||||
```python
|
||||
import httpx
|
||||
|
||||
params = {
|
||||
"q": "python",
|
||||
"sort": "stars",
|
||||
"order": "desc"
|
||||
}
|
||||
|
||||
response = httpx.get(
|
||||
"https://api.github.com/search/repositories",
|
||||
params=params
|
||||
)
|
||||
response.raise_for_status()
|
||||
repos = response.json()
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```python
|
||||
import httpx
|
||||
import sys
|
||||
|
||||
try:
|
||||
with httpx.Client(timeout=10.0) as client:
|
||||
response = client.get("https://api.example.com/data")
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
except httpx.HTTPStatusError as e:
|
||||
status = e.response.status_code
|
||||
if status == 401:
|
||||
print("Error: Unauthorized - check API key", file=sys.stderr)
|
||||
elif status == 404:
|
||||
print("Error: Resource not found", file=sys.stderr)
|
||||
elif status >= 500:
|
||||
print(f"Error: Server error ({status})", file=sys.stderr)
|
||||
else:
|
||||
print(f"Error: HTTP {status}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
except httpx.RequestError as e:
|
||||
print(f"Error: Request failed - {type(e).__name__}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
## Retry Logic
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "httpx>=0.27.0",
|
||||
# "tenacity>=8.2.0",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
import httpx
|
||||
from tenacity import retry, stop_after_attempt, wait_exponential
|
||||
|
||||
@retry(
|
||||
stop=stop_after_attempt(3),
|
||||
wait=wait_exponential(multiplier=1, min=2, max=10)
|
||||
)
|
||||
def fetch_data(url: str):
|
||||
"""Fetch data with automatic retry."""
|
||||
response = httpx.get(url, timeout=10.0)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
data = fetch_data("https://api.example.com/data")
|
||||
```
|
||||
|
||||
## Pagination
|
||||
|
||||
```python
|
||||
import httpx
|
||||
|
||||
def fetch_all_pages(base_url: str, headers: dict):
|
||||
"""Fetch all pages from paginated API."""
|
||||
all_results = []
|
||||
next_url = base_url
|
||||
|
||||
with httpx.Client(headers=headers, timeout=10.0) as client:
|
||||
while next_url:
|
||||
response = client.get(next_url)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
all_results.extend(data["results"])
|
||||
|
||||
# Get next page URL
|
||||
next_url = data.get("next")
|
||||
|
||||
return all_results
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
```python
|
||||
import httpx
|
||||
import time
|
||||
|
||||
def fetch_with_rate_limit(urls: list[str], requests_per_second: int = 2):
|
||||
"""Fetch URLs respecting rate limit."""
|
||||
delay = 1.0 / requests_per_second
|
||||
results = []
|
||||
|
||||
for url in urls:
|
||||
response = httpx.get(url)
|
||||
response.raise_for_status()
|
||||
results.append(response.json())
|
||||
|
||||
time.sleep(delay)
|
||||
|
||||
return results
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
For a complete API client template, see: `assets/templates/api-client.py`
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Always set timeouts (default: 10 seconds)
|
||||
- Use `with httpx.Client()` for multiple requests
|
||||
- Handle specific HTTP status codes (401, 404, 500)
|
||||
- Don't log sensitive data (tokens, responses)
|
||||
- Use environment variables for credentials
|
||||
- Implement retry logic for transient failures
|
||||
- Respect rate limits
|
||||
129
skills/python-uv-scripts/patterns/cli-applications.md
Normal file
129
skills/python-uv-scripts/patterns/cli-applications.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# CLI Application Patterns
|
||||
|
||||
Patterns for building command-line applications with uv scripts.
|
||||
|
||||
## Basic CLI with Typer
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "typer>=0.9.0",
|
||||
# "rich>=13.0.0",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
import typer
|
||||
from rich import print
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
@app.command()
|
||||
def main(
|
||||
name: str = typer.Argument(..., help="Your name"),
|
||||
greeting: str = typer.Option("Hello", "--greeting", "-g"),
|
||||
):
|
||||
"""Greet someone."""
|
||||
print(f"[green]{greeting}, {name}![/green]")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
```
|
||||
|
||||
## Multiple Subcommands
|
||||
|
||||
```python
|
||||
import typer
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
@app.command()
|
||||
def create(name: str):
|
||||
"""Create a new resource."""
|
||||
print(f"Creating: {name}")
|
||||
|
||||
@app.command()
|
||||
def delete(name: str):
|
||||
"""Delete a resource."""
|
||||
print(f"Deleting: {name}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
./script.py create foo
|
||||
./script.py delete bar
|
||||
```
|
||||
|
||||
## File Input/Output
|
||||
|
||||
```python
|
||||
import typer
|
||||
from pathlib import Path
|
||||
|
||||
def process_file(
|
||||
input_file: Path = typer.Argument(..., exists=True),
|
||||
output: Path = typer.Option(None, "--output", "-o"),
|
||||
):
|
||||
"""Process a file."""
|
||||
content = input_file.read_text()
|
||||
|
||||
# Process
|
||||
result = content.upper()
|
||||
|
||||
if output:
|
||||
output.write_text(result)
|
||||
print(f"Written to: {output}")
|
||||
else:
|
||||
print(result)
|
||||
```
|
||||
|
||||
## Progress Bars
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "rich>=13.0.0",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
from rich.progress import track
|
||||
import time
|
||||
|
||||
for item in track(range(100), description="Processing..."):
|
||||
time.sleep(0.01) # Simulate work
|
||||
```
|
||||
|
||||
## Formatted Tables
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "rich>=13.0.0",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
table = Table(title="Results")
|
||||
table.add_column("Name", style="cyan")
|
||||
table.add_column("Status", style="green")
|
||||
|
||||
table.add_row("Task 1", "✓ Complete")
|
||||
table.add_row("Task 2", "⏳ Pending")
|
||||
|
||||
console.print(table)
|
||||
```
|
||||
|
||||
For complete template, see: `assets/templates/cli-app.py`
|
||||
54
skills/python-uv-scripts/patterns/data-processing.md
Normal file
54
skills/python-uv-scripts/patterns/data-processing.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Data Processing Patterns
|
||||
|
||||
> **Status**: 🚧 Placeholder - Content in development
|
||||
|
||||
## Overview
|
||||
|
||||
Patterns for data analysis, ETL, and processing using Polars, pandas, and other data libraries in UV single-file
|
||||
scripts.
|
||||
|
||||
## Topics to Cover
|
||||
|
||||
- [ ] Polars patterns (recommended for performance)
|
||||
- [ ] Pandas alternatives
|
||||
- [ ] CSV/Excel processing
|
||||
- [ ] JSON data manipulation
|
||||
- [ ] Data validation and cleaning
|
||||
- [ ] Aggregation and transformation
|
||||
- [ ] Memory-efficient processing
|
||||
|
||||
## Quick Example
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = ["polars>=0.20.0"]
|
||||
# ///
|
||||
import polars as pl
|
||||
|
||||
def analyze_csv(file_path: str):
|
||||
df = pl.read_csv(file_path)
|
||||
|
||||
# Basic analysis
|
||||
summary = df.describe()
|
||||
print(summary)
|
||||
|
||||
# Filter and aggregate
|
||||
result = (
|
||||
df.filter(pl.col("value") > 100)
|
||||
.groupby("category")
|
||||
.agg(pl.col("value").mean())
|
||||
)
|
||||
print(result)
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
This file will be expanded to include:
|
||||
|
||||
- Complete Polars patterns
|
||||
- Performance optimization techniques
|
||||
- Large file processing strategies
|
||||
- Data validation patterns
|
||||
- Export formats and options
|
||||
198
skills/python-uv-scripts/patterns/error-handling.md
Normal file
198
skills/python-uv-scripts/patterns/error-handling.md
Normal file
@@ -0,0 +1,198 @@
|
||||
# Error Handling Patterns
|
||||
|
||||
Best practices for error handling in uv scripts.
|
||||
|
||||
## Exit Codes
|
||||
|
||||
Always use appropriate exit codes:
|
||||
|
||||
- `0` - Success
|
||||
- `1` - General error
|
||||
- `2` - Invalid usage
|
||||
|
||||
```python
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: script.py <input>", file=sys.stderr)
|
||||
sys.exit(2) # Invalid usage
|
||||
|
||||
try:
|
||||
result = process()
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1) # General error
|
||||
|
||||
# Success
|
||||
sys.exit(0)
|
||||
```
|
||||
|
||||
## Try-Except Pattern
|
||||
|
||||
```python
|
||||
import sys
|
||||
|
||||
def main():
|
||||
try:
|
||||
# Operations that might fail
|
||||
data = fetch_data()
|
||||
result = process(data)
|
||||
save(result)
|
||||
|
||||
except FileNotFoundError as e:
|
||||
print(f"File not found: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
except ValueError as e:
|
||||
print(f"Invalid data: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
## HTTP Error Handling
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "httpx>=0.27.0",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
import httpx
|
||||
import sys
|
||||
|
||||
try:
|
||||
response = httpx.get("https://api.example.com", timeout=10.0)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
except httpx.HTTPStatusError as e:
|
||||
print(f"HTTP {e.response.status_code} error", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
except httpx.RequestError as e:
|
||||
print(f"Request failed: {type(e).__name__}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
except httpx.TimeoutException:
|
||||
print("Request timed out", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
## File Operation Errors
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
def read_config(path: Path):
|
||||
"""Read configuration file with error handling."""
|
||||
try:
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(f"Config not found: {path}")
|
||||
|
||||
if not path.is_file():
|
||||
raise ValueError(f"Not a file: {path}")
|
||||
|
||||
return path.read_text()
|
||||
|
||||
except PermissionError:
|
||||
print(f"Permission denied: {path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error reading config: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
## Subprocess Errors
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["command", "arg"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
print(result.stdout)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Command failed with exit code {e.returncode}", file=sys.stderr)
|
||||
print(f"Error output: {e.stderr}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
except FileNotFoundError:
|
||||
print("Command not found", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
## Validation Errors
|
||||
|
||||
```python
|
||||
import sys
|
||||
|
||||
def validate_input(value: str) -> int:
|
||||
"""Validate and convert input."""
|
||||
if not value:
|
||||
print("Error: Empty value", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
try:
|
||||
number = int(value)
|
||||
except ValueError:
|
||||
print(f"Error: Not a number: {value}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
if number < 0:
|
||||
print("Error: Must be non-negative", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
return number
|
||||
```
|
||||
|
||||
## Graceful Cleanup
|
||||
|
||||
```python
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def main():
|
||||
temp_file = Path("/tmp/data.tmp")
|
||||
|
||||
try:
|
||||
# Create temporary file
|
||||
temp_file.write_text("data")
|
||||
|
||||
# Process
|
||||
result = process(temp_file)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
finally:
|
||||
# Always cleanup
|
||||
if temp_file.exists():
|
||||
temp_file.unlink()
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
- Always write errors to `stderr`
|
||||
- Use specific exception types
|
||||
- Provide helpful error messages
|
||||
- Use appropriate exit codes
|
||||
- Clean up resources in `finally` blocks
|
||||
- Don't expose secrets in error messages
|
||||
32
skills/python-uv-scripts/patterns/security-patterns.md
Normal file
32
skills/python-uv-scripts/patterns/security-patterns.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Security Patterns for UV Single-File Scripts
|
||||
|
||||
> **Status**: 🚧 Placeholder - Content in development
|
||||
|
||||
## Overview
|
||||
|
||||
Security patterns and best practices for handling secrets, authentication, and input validation in UV single-file
|
||||
Python scripts.
|
||||
|
||||
## Topics to Cover
|
||||
|
||||
- [ ] Infisical secrets management integration
|
||||
- [ ] Keyring for credential storage
|
||||
- [ ] Environment variable security
|
||||
- [ ] Input validation and sanitization
|
||||
- [ ] API token handling
|
||||
- [ ] SSH key management
|
||||
- [ ] Avoiding hardcoded credentials
|
||||
|
||||
## Quick Reference
|
||||
|
||||
For now, see the main security section in [../reference/security-patterns.md](../reference/security-patterns.md).
|
||||
|
||||
## TODO
|
||||
|
||||
This file will be expanded to include:
|
||||
|
||||
- Complete Infisical integration patterns
|
||||
- Best practices for secret rotation
|
||||
- Multi-environment secret management
|
||||
- Security checklist for scripts
|
||||
- Common security vulnerabilities and fixes
|
||||
61
skills/python-uv-scripts/patterns/system-automation.md
Normal file
61
skills/python-uv-scripts/patterns/system-automation.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# System Automation Patterns
|
||||
|
||||
> **Status**: 🚧 Placeholder - Content in development
|
||||
|
||||
## Overview
|
||||
|
||||
Patterns for system administration, monitoring, and automation using psutil, subprocess, and system libraries in UV
|
||||
single-file scripts.
|
||||
|
||||
## Topics to Cover
|
||||
|
||||
- [ ] psutil for system monitoring
|
||||
- [ ] subprocess for command execution
|
||||
- [ ] File system operations
|
||||
- [ ] Process management
|
||||
- [ ] SSH remote execution
|
||||
- [ ] Cron/scheduled task integration
|
||||
- [ ] Log file analysis
|
||||
|
||||
## Quick Example
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = ["psutil>=5.9.0", "rich>=13.0.0"]
|
||||
# ///
|
||||
import psutil
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
def show_disk_usage():
|
||||
table = Table(title="Disk Usage")
|
||||
table.add_column("Device", style="cyan")
|
||||
table.add_column("Mount", style="magenta")
|
||||
table.add_column("Used", style="yellow")
|
||||
table.add_column("Free", style="green")
|
||||
|
||||
for partition in psutil.disk_partitions():
|
||||
usage = psutil.disk_usage(partition.mountpoint)
|
||||
table.add_row(
|
||||
partition.device,
|
||||
partition.mountpoint,
|
||||
f"{usage.percent}%",
|
||||
f"{usage.free / (1024**3):.2f} GB"
|
||||
)
|
||||
|
||||
console.print(table)
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
This file will be expanded to include:
|
||||
|
||||
- Complete psutil monitoring patterns
|
||||
- Safe subprocess execution
|
||||
- SSH automation patterns
|
||||
- System health checks
|
||||
- Automated maintenance tasks
|
||||
Reference in New Issue
Block a user