Initial commit
This commit is contained in:
373
skills/python-uv-scripts/anti-patterns/common-mistakes.md
Normal file
373
skills/python-uv-scripts/anti-patterns/common-mistakes.md
Normal file
@@ -0,0 +1,373 @@
|
||||
# Anti-Patterns: Common Mistakes with uv Scripts
|
||||
|
||||
This document lists common mistakes when creating uv scripts and how to avoid them.
|
||||
|
||||
## Critical Mistakes
|
||||
|
||||
### ❌ Using [tool.uv.metadata]
|
||||
|
||||
**WRONG**:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = []
|
||||
# [tool.uv.metadata] # ❌ THIS DOES NOT WORK
|
||||
# purpose = "testing"
|
||||
# team = "devops"
|
||||
# ///
|
||||
```
|
||||
|
||||
**Error**:
|
||||
|
||||
```text
|
||||
error: TOML parse error at line 4, column 7
|
||||
|
|
||||
4 | [tool.uv.metadata]
|
||||
| ^^
|
||||
unknown field `metadata`
|
||||
```
|
||||
|
||||
**Why**: `[tool.uv.metadata]` is not part of PEP 723 and is not supported by uv.
|
||||
|
||||
**CORRECT**: Use docstrings for metadata:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = []
|
||||
# ///
|
||||
"""
|
||||
Purpose: Testing automation
|
||||
Team: DevOps
|
||||
Author: team@example.com
|
||||
"""
|
||||
```
|
||||
|
||||
**NOTE**: `[tool.uv]` (without `.metadata`) IS valid for fields like `exclude-newer`. See reference/pep-723-spec.md.
|
||||
|
||||
### ❌ Adding Custom TOML Fields
|
||||
|
||||
**WRONG**:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# author = "me" # ❌ INVALID
|
||||
# version = "1.0.0" # ❌ INVALID
|
||||
# description = "test" # ❌ INVALID
|
||||
# dependencies = []
|
||||
# ///
|
||||
```
|
||||
|
||||
**CORRECT**: Only use `requires-python` and `dependencies` at the top level:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = []
|
||||
# ///
|
||||
"""
|
||||
Author: me
|
||||
Version: 1.0.0
|
||||
Description: test
|
||||
"""
|
||||
```
|
||||
|
||||
### ❌ Hardcoded Secrets
|
||||
|
||||
**WRONG**:
|
||||
|
||||
```python
|
||||
API_KEY = "sk-1234567890abcdef" # ❌ NEVER DO THIS
|
||||
PASSWORD = "super_secret_password" # ❌ NEVER DO THIS
|
||||
```
|
||||
|
||||
**CORRECT**: Use environment variables:
|
||||
|
||||
```python
|
||||
import os
|
||||
import sys
|
||||
|
||||
API_KEY = os.getenv("API_KEY")
|
||||
if not API_KEY:
|
||||
print("Error: API_KEY environment variable not set", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
**BETTER**: Use Infisical (following repo pattern):
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "infisical-python>=2.3.3",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
from infisical import InfisicalClient
|
||||
|
||||
client = InfisicalClient()
|
||||
api_key = client.get_secret("API_KEY", path="/production")
|
||||
```
|
||||
|
||||
### ❌ Missing Error Handling
|
||||
|
||||
**WRONG**:
|
||||
|
||||
```python
|
||||
import httpx
|
||||
|
||||
response = httpx.get("https://api.example.com") # ❌ No error handling
|
||||
data = response.json()
|
||||
```
|
||||
|
||||
**CORRECT**:
|
||||
|
||||
```python
|
||||
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 error: {e.response.status_code}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except httpx.RequestError as e:
|
||||
print(f"Request failed: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
## When NOT to Use Single-File Scripts
|
||||
|
||||
### ❌ Complex Applications
|
||||
|
||||
Don't use single-file scripts when:
|
||||
|
||||
- **Script exceeds 500 lines** → Use a proper uv project
|
||||
- **Multiple modules needed** → Use a proper uv project
|
||||
- **Shared code across scripts** → Use a proper uv project with shared library
|
||||
- **Web applications** → Use a proper uv project (Flask/FastAPI/Django)
|
||||
- **Long-running services** → Use a proper uv project
|
||||
- **Complex configuration** → Use a proper uv project with config files
|
||||
|
||||
**Example - Too Complex**:
|
||||
|
||||
```python
|
||||
# This should be a uv project, not a script:
|
||||
# - 15+ dependencies
|
||||
# - Database models
|
||||
# - API routes
|
||||
# - Background workers
|
||||
# - Multiple configuration files
|
||||
# - 1000+ lines of code
|
||||
```
|
||||
|
||||
### ❌ Heavy Dependencies
|
||||
|
||||
**WRONG**: Using heavy ML/data libraries in scripts:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# dependencies = [
|
||||
# "tensorflow>=2.15.0", # ❌ Too heavy for script
|
||||
# "torch>=2.1.0", # ❌ Too heavy for script
|
||||
# "transformers>=4.35.0", # ❌ Too heavy for script
|
||||
# ]
|
||||
# ///
|
||||
```
|
||||
|
||||
**Why**: These create very large environments. Use a proper project instead.
|
||||
|
||||
**OK**: Lightweight data processing:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# dependencies = [
|
||||
# "polars>=0.20.0", # ✓ Reasonable for scripts
|
||||
# "httpx>=0.27.0", # ✓ Reasonable for scripts
|
||||
# ]
|
||||
# ///
|
||||
```
|
||||
|
||||
## Common Syntax Mistakes
|
||||
|
||||
### ❌ Missing # on TOML Lines
|
||||
|
||||
**WRONG**:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
requires-python = ">=3.11" # ❌ Missing # at start
|
||||
dependencies = [] # ❌ Missing # at start
|
||||
# ///
|
||||
```
|
||||
|
||||
**CORRECT**:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# requires-python = ">=3.11" # ✓ Each line starts with #
|
||||
# dependencies = [] # ✓ Each line starts with #
|
||||
# ///
|
||||
```
|
||||
|
||||
### ❌ Wrong Marker Format
|
||||
|
||||
**WRONG**:
|
||||
|
||||
```python
|
||||
# /// scripts # ❌ Wrong: "scripts" not "script"
|
||||
# requires-python = ">=3.11"
|
||||
# ///
|
||||
|
||||
# // script # ❌ Wrong: "//" not "///"
|
||||
# requires-python = ">=3.11"
|
||||
# //
|
||||
|
||||
# /// script # ❌ Wrong: No closing marker
|
||||
# requires-python = ">=3.11"
|
||||
```
|
||||
|
||||
**CORRECT**:
|
||||
|
||||
```python
|
||||
# /// script # ✓ Exactly "/// script"
|
||||
# requires-python = ">=3.11"
|
||||
# /// # ✓ Exactly "///"
|
||||
```
|
||||
|
||||
## Dependency Management Mistakes
|
||||
|
||||
### ❌ No Version Constraints
|
||||
|
||||
**WRONG**:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# dependencies = [
|
||||
# "httpx", # ❌ No version specified
|
||||
# "requests", # ❌ Could break unexpectedly
|
||||
# ]
|
||||
# ///
|
||||
```
|
||||
|
||||
**CORRECT**:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# dependencies = [
|
||||
# "httpx>=0.27.0", # ✓ Minimum version specified
|
||||
# "requests>=2.31.0", # ✓ Prevents breaking changes
|
||||
# ]
|
||||
# ///
|
||||
```
|
||||
|
||||
### ❌ Overly Strict Pinning
|
||||
|
||||
**WRONG** (for utility scripts):
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# dependencies = [
|
||||
# "httpx==0.27.0", # ❌ Too strict, prevents updates
|
||||
# "rich==13.7.0", # ❌ Blocks security fixes
|
||||
# ]
|
||||
# ///
|
||||
```
|
||||
|
||||
**CORRECT**:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# dependencies = [
|
||||
# "httpx>=0.27.0", # ✓ Allows updates
|
||||
# "rich>=13.0.0", # ✓ Allows security fixes
|
||||
# ]
|
||||
# ///
|
||||
```
|
||||
|
||||
**NOTE**: Exact pinning (`==`) is appropriate for deployment scripts where reproducibility is critical.
|
||||
|
||||
## Additional Pitfalls
|
||||
|
||||
### ❌ Missing Shebang
|
||||
|
||||
**WRONG**:
|
||||
|
||||
```python
|
||||
# No shebang - script won't be directly executable
|
||||
# /// script
|
||||
# dependencies = ["requests"]
|
||||
# ///
|
||||
```
|
||||
|
||||
**CORRECT**:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# dependencies = ["requests"]
|
||||
# ///
|
||||
```
|
||||
|
||||
### ❌ Broad Exception Handling
|
||||
|
||||
**WRONG**:
|
||||
|
||||
```python
|
||||
try:
|
||||
do_something()
|
||||
except Exception: # Too broad!
|
||||
pass
|
||||
```
|
||||
|
||||
**CORRECT**:
|
||||
|
||||
```python
|
||||
try:
|
||||
do_something()
|
||||
except (FileNotFoundError, PermissionError) as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
### ❌ Platform-Specific Code Without Guards
|
||||
|
||||
**WRONG**:
|
||||
|
||||
```python
|
||||
import pwd # Unix-only, crashes on Windows
|
||||
user = pwd.getpwuid(os.getuid())
|
||||
```
|
||||
|
||||
**CORRECT**:
|
||||
|
||||
```python
|
||||
import sys
|
||||
if sys.platform != "win32":
|
||||
import pwd
|
||||
user = pwd.getpwuid(os.getuid())
|
||||
else:
|
||||
user = os.environ.get("USERNAME")
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
**Never use**:
|
||||
|
||||
- `[tool.uv.metadata]` (invalid field)
|
||||
- Custom fields in PEP 723 metadata (only `requires-python` and `dependencies`)
|
||||
- Hardcoded secrets
|
||||
- Scripts for complex applications (>500 lines, web apps, services)
|
||||
|
||||
**Always use**:
|
||||
|
||||
- Valid PEP 723 format only
|
||||
- `[tool.uv]` (without `.metadata`) for valid fields like `exclude-newer`
|
||||
- Environment variables or Infisical for secrets
|
||||
- Error handling for external calls
|
||||
- Version constraints on dependencies
|
||||
- Proper projects when scripts become too complex
|
||||
363
skills/python-uv-scripts/anti-patterns/when-not-to-use.md
Normal file
363
skills/python-uv-scripts/anti-patterns/when-not-to-use.md
Normal file
@@ -0,0 +1,363 @@
|
||||
# When NOT to Use Single-File Scripts
|
||||
|
||||
This document helps determine when to use a single-file uv script vs. when to use a proper uv project.
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```text
|
||||
Is this a Python program?
|
||||
├─ No → Use appropriate language/tool
|
||||
└─ Yes
|
||||
├─ Is it a one-time task or simple automation?
|
||||
│ ├─ Yes → Consider single-file script
|
||||
│ └─ No → Use proper uv project
|
||||
└─ Does it meet ANY of these criteria?
|
||||
├─ >500 lines of code → Use proper uv project
|
||||
├─ Multiple Python files needed → Use proper uv project
|
||||
├─ Web application or API → Use proper uv project
|
||||
├─ Long-running service → Use proper uv project
|
||||
├─ Complex configuration → Use proper uv project
|
||||
├─ Shared library code → Use proper uv project
|
||||
├─ Heavy ML/data dependencies → Use proper uv project
|
||||
└─ None of the above → Single-file script is appropriate
|
||||
```
|
||||
|
||||
## Use Proper uv Project When
|
||||
|
||||
### 1. Code Complexity
|
||||
|
||||
**Use project if**:
|
||||
|
||||
- Script exceeds 500 lines
|
||||
- Logic is spread across multiple functions/classes
|
||||
- Code would benefit from splitting into modules
|
||||
- Complex data models or class hierarchies
|
||||
|
||||
**Example - Too Complex for Script**:
|
||||
|
||||
```python
|
||||
# This needs a proper project structure:
|
||||
# - 800 lines of code
|
||||
# - Multiple classes (User, Database, API, Config)
|
||||
# - Would be clearer as separate modules
|
||||
# - Needs tests
|
||||
```
|
||||
|
||||
### 2. Multiple Files Needed
|
||||
|
||||
**Use project if**:
|
||||
|
||||
- Shared utilities across multiple scripts
|
||||
- Common data models used by different tools
|
||||
- Reusable library code
|
||||
- Multiple entry points
|
||||
|
||||
**Example**:
|
||||
|
||||
```text
|
||||
# This needs a project:
|
||||
my-tool/
|
||||
├── src/
|
||||
│ ├── __init__.py
|
||||
│ ├── database.py # Shared by multiple tools
|
||||
│ ├── models.py # Common data structures
|
||||
│ └── utils.py # Utility functions
|
||||
├── scripts/
|
||||
│ ├── import_data.py # Uses shared code
|
||||
│ └── export_data.py # Uses shared code
|
||||
└── pyproject.toml
|
||||
```
|
||||
|
||||
### 3. Web Applications
|
||||
|
||||
**Use project for**:
|
||||
|
||||
- Flask/FastAPI/Django applications
|
||||
- REST APIs
|
||||
- Web services
|
||||
- Applications with routes/controllers
|
||||
|
||||
**Example - Needs Project**:
|
||||
|
||||
```python
|
||||
# Don't use single-file script for web apps:
|
||||
from flask import Flask, request, jsonify
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# 50+ routes
|
||||
# Database models
|
||||
# Authentication
|
||||
# Background tasks
|
||||
# Configuration management
|
||||
|
||||
# This should be a proper project structure
|
||||
```
|
||||
|
||||
### 4. Long-Running Services
|
||||
|
||||
**Use project for**:
|
||||
|
||||
- Daemons
|
||||
- Background workers
|
||||
- Queue consumers
|
||||
- Services that run continuously
|
||||
|
||||
**Example**:
|
||||
|
||||
```python
|
||||
# Don't use script for services:
|
||||
# - Runs 24/7
|
||||
# - Monitors message queue
|
||||
# - Complex retry logic
|
||||
# - Logging configuration
|
||||
# - Health checks
|
||||
# - Graceful shutdown
|
||||
|
||||
# Needs proper project structure with:
|
||||
# - Proper logging setup
|
||||
# - Configuration management
|
||||
# - Process management (systemd/supervisor)
|
||||
```
|
||||
|
||||
### 5. Complex Configuration
|
||||
|
||||
**Use project if**:
|
||||
|
||||
- Multiple environment configs (dev/staging/prod)
|
||||
- YAML/JSON configuration files
|
||||
- Feature flags
|
||||
- Database connection pools
|
||||
|
||||
**Example**:
|
||||
|
||||
```text
|
||||
# This needs a project:
|
||||
config/
|
||||
├── dev.yaml
|
||||
├── staging.yaml
|
||||
└── production.yaml
|
||||
|
||||
# Single-file scripts should use simple env vars instead
|
||||
```
|
||||
|
||||
### 6. Heavy Dependencies
|
||||
|
||||
**Use project for**:
|
||||
|
||||
- Machine learning frameworks (TensorFlow, PyTorch)
|
||||
- Large data processing (PySpark)
|
||||
- Complex scientific computing
|
||||
- GUI frameworks
|
||||
|
||||
**Example - Too Heavy**:
|
||||
|
||||
```python
|
||||
# /// script
|
||||
# dependencies = [
|
||||
# "tensorflow>=2.15.0", # ❌ ~500MB download
|
||||
# "torch>=2.1.0", # ❌ ~800MB download
|
||||
# "transformers>=4.35.0", # ❌ Complex dependency tree
|
||||
# ]
|
||||
# ///
|
||||
|
||||
# Use proper project with managed virtual environment
|
||||
```
|
||||
|
||||
### 7. Testing Requirements
|
||||
|
||||
**Use project if**:
|
||||
|
||||
- Comprehensive test suite needed
|
||||
- Multiple test files
|
||||
- Fixtures and mocking
|
||||
- CI/CD integration
|
||||
|
||||
**Example**:
|
||||
|
||||
```text
|
||||
# This needs a project:
|
||||
tests/
|
||||
├── unit/
|
||||
│ ├── test_models.py
|
||||
│ ├── test_utils.py
|
||||
│ └── test_api.py
|
||||
├── integration/
|
||||
│ └── test_database.py
|
||||
└── conftest.py
|
||||
|
||||
# Can't reasonably organize this with single-file script
|
||||
```
|
||||
|
||||
### 8. Team Collaboration
|
||||
|
||||
**Use project if**:
|
||||
|
||||
- Multiple developers working on code
|
||||
- Code review processes
|
||||
- Versioning and releases
|
||||
- Documentation requirements
|
||||
|
||||
## Single-File Scripts ARE Appropriate For
|
||||
|
||||
### ✅ Good Use Cases
|
||||
|
||||
**One-time tasks**:
|
||||
|
||||
```python
|
||||
# Convert CSV format
|
||||
# Migrate data between systems
|
||||
# Clean up old files
|
||||
# Generate reports
|
||||
```
|
||||
|
||||
**Simple automation**:
|
||||
|
||||
```python
|
||||
# Check server health
|
||||
# Send notifications
|
||||
# Backup files
|
||||
# Parse logs
|
||||
```
|
||||
|
||||
**CLI utilities**:
|
||||
|
||||
```python
|
||||
# Format files
|
||||
# Validate data
|
||||
# Query APIs
|
||||
# Process input
|
||||
```
|
||||
|
||||
**Prototyping**:
|
||||
|
||||
```python
|
||||
# Test API endpoints
|
||||
# Experiment with libraries
|
||||
# Quick data analysis
|
||||
# Proof of concept
|
||||
```
|
||||
|
||||
### ✅ Characteristics of Good Single-File Scripts
|
||||
|
||||
- **<500 lines of code**
|
||||
- **Self-contained logic**
|
||||
- **Simple, clear purpose**
|
||||
- **Minimal dependencies (1-5 packages)**
|
||||
- **Standalone execution**
|
||||
- **Quick to understand**
|
||||
|
||||
## Examples of Good Single-File Scripts
|
||||
|
||||
### Example 1: Health Check
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "psutil>=5.9.0",
|
||||
# "rich>=13.0.0",
|
||||
# ]
|
||||
# ///
|
||||
"""Check system health and display metrics."""
|
||||
|
||||
import psutil
|
||||
from rich import print
|
||||
|
||||
def main():
|
||||
cpu = psutil.cpu_percent()
|
||||
mem = psutil.virtual_memory().percent
|
||||
disk = psutil.disk_usage('/').percent
|
||||
|
||||
print(f"CPU: {cpu}% | Memory: {mem}% | Disk: {disk}%")
|
||||
|
||||
if cpu > 80 or mem > 80 or disk > 80:
|
||||
print("[red]⚠ High resource usage![/red]")
|
||||
exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
### Example 2: API Query
|
||||
|
||||
```python
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "httpx>=0.27.0",
|
||||
# ]
|
||||
# ///
|
||||
"""Query API and display results."""
|
||||
|
||||
import httpx
|
||||
import sys
|
||||
|
||||
def main():
|
||||
response = httpx.get("https://api.github.com/users/octocat")
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
print(f"Name: {data['name']}")
|
||||
print(f"Public repos: {data['public_repos']}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
## Migration Path
|
||||
|
||||
**When a script outgrows single-file format**:
|
||||
|
||||
1. Create proper uv project:
|
||||
|
||||
```bash
|
||||
uv init my-tool
|
||||
cd my-tool
|
||||
```
|
||||
|
||||
2. Move script logic to `src/`:
|
||||
|
||||
```bash
|
||||
mv script.py src/my_tool/main.py
|
||||
```
|
||||
|
||||
3. Add dependencies to `pyproject.toml`:
|
||||
|
||||
```toml
|
||||
[project]
|
||||
dependencies = [
|
||||
"httpx>=0.27.0",
|
||||
"rich>=13.0.0",
|
||||
]
|
||||
```
|
||||
|
||||
4. Create entry point in `pyproject.toml`:
|
||||
|
||||
```toml
|
||||
[project.scripts]
|
||||
my-tool = "my_tool.main:main"
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
**Use single-file scripts for**:
|
||||
|
||||
- Simple automation (<500 lines)
|
||||
- One-off tasks
|
||||
- CLI utilities
|
||||
- Prototypes
|
||||
- Standalone tools
|
||||
|
||||
**Use proper uv projects for**:
|
||||
|
||||
- Complex applications (>500 lines)
|
||||
- Multiple files/modules
|
||||
- Web applications
|
||||
- Long-running services
|
||||
- Heavy dependencies
|
||||
- Team collaboration
|
||||
- Comprehensive testing
|
||||
|
||||
**When in doubt**: Start with a script. If it grows too complex, migrate to a project.
|
||||
Reference in New Issue
Block a user