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
|
||||
Reference in New Issue
Block a user