Python Development with UV
SkillFiles & storageUse this before writing Python code, creating Python scripts, modifying Python files, or when user asks to "write python", "create a python script", "implement in python". TRIGGER when starting any Python development task.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Python Development with UV skill
What this skill tells your AI
The instructions your AI receives, as published by archibate/dotfiles-opencode in skills/writing-python/SKILL.md and read by ahel’s review.
Modern Python development using uv for package management, PEP 723 for single-file scripts, and best-in-class tooling.
Quick Start
Single-File Scripts (Default)
By default, create self-contained scripts using PEP 723 format:
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = [
# "typer",
# "rich",
# ]
# ///
"""
Script description and usage examples.
Usage:
uv run python3 script.py --help
uv run python3 script.py --option value
"""
import sys
# ... rest of script
Run with: uv run python3 script.py
Multi-File Projects
For larger projects requiring multiple files:
# Pin Python version
uv python pin 3.12
# Create virtual environment
uv venv --python 3.12
# Activate environment
source .venv/bin/activate
# Add dependencies
uv add package-name
# Run script
uv run python script.py
Development Tools
Testing, Linting, Type Checking
All tooling configuration is centralized in /tests/pyproject.toml.
Run from /tests directory:
cd tests
# Run tests
uv run pytest
uv run pytest -v # verbose
# Type checking
uv run pyright
uv run pyright --stats
# Linting & formatting
uv run ruff check ../.opencode/skill
uv run ruff check --fix ../.opencode/skill
uv run ruff format ../.opencode/skill
Tool choices:
- Ruff - Linter/formatter (replaces Black, isort, Flake8)
- Pyright - Type checker (replaces MyPy)
- Pytest - Test runner
For detailed usage, see:
references/ruff.md- Linting and formattingreferences/pyright.md- Static type checkingreferences/pytest.md- Testing framework
Script Development Workflow
Start Small - Build Incrementally
- Basic structure - Create script with
--helpflag - Test immediately - Run with
uv run python3 script.py --help - Add
--dry-run- Show what would happen without executing - Test again - Verify dry-run output
- Add
--verbose- Detailed output for debugging - Test again - Verify verbose mode
- Continue incrementally - Add features one at a time, testing each
Shebang Format
#!/usr/bin/env uv run python3
PEP 723 Dependencies
# /// script
# dependencies = [
# "typer", # Modern CLI framework
# "rich", # Beautiful terminal output
# "httpx", # Modern HTTP client
# ]
# ///
Minimize dependencies - Try using stdlib first.
UV Commands Reference
Package Management
uv add <package> # Add package to pyproject.toml
uv remove <package> # Remove package
uv sync # Install/sync dependencies
uv lock # Create/update lock file
Python Version Management
uv python install <version> # Install Python version
uv python list # List installed versions
uv python pin <version> # Set project Python version
Running Scripts
uv run python script.py # Run with project environment
uvx <tool> # Run tool in isolated environment
uv tool install <package> # Install global tool
Preferred Libraries
Core Utilities
- uv - Package manager (never use pip/python3 directly)
- typer - Modern CLI framework (built on click)
- rich - Beautiful terminal output
- python-dotenv - Environment variables (or Pydantic-Settings)
Development
- pytest - Testing framework
- ruff - Fast linting and formatting
- pyright - Static type checking
When Needed
- httpx - Modern HTTP client (replaces requests)
- Pydantic-Settings - Type-safe configuration with validation
- Polars - Fast DataFrame library (pandas alternative)
- DuckDB - Embedded analytical database
- Loguru - Simple, powerful logging
Test-Driven Development
TDD Cycle
- Red - Write failing test for new functionality
- Green - Write minimal code to pass test
- Refactor - Improve code while keeping tests green
When to Use TDD
General approach: Code directly as you see fit.
Use TDD when: Facing issues or building complex components.
Development Sequence (When Using TDD)
- Stubs - Define basic structure and interfaces
- Pseudocode - Plan detailed logic within stubs
- Data Layer - Implement data persistence and management
- Business Logic - Implement core application rules
- CLI/Frontend - Implement user interaction
Test Structure
See references/pytest.md for comprehensive testing guide.
Directory Structure
.opencode/skill/<skill>/
├── SKILL.md
└── scripts/
├── <script>.py
└── tests/
└── test_<script>.py
Helper Function Pattern
from pathlib import Path
import subprocess
SCRIPT_PATH = Path(__file__).parent.parent / "script.py"
def run_script(*args, env=None):
"""Execute script with uv run."""
cmd = ["uv", "run", str(SCRIPT_PATH)] + list(args)
result = subprocess.run(cmd, capture_output=True, text=True, env=env)
return result.stdout, result.stderr, result.returncode
Test Class Organization
class TestVersion:
"""Test --version flag."""
def test_version_flag(self):
"""--version should output version and exit with 0."""
stdout, stderr, code = run_script("--version")
assert code == 0
assert "version" in stdout.lower()
Exit Code Standards
| Code | Meaning |
|---|---|
| 0 | Success (version, help, dry-run) |
| 1 | Runtime/API error |
| 2 | Validation error |
| 130 | Keyboard interrupt |
Type Checking
See references/pyright.md for comprehensive type checking guide.
Basic Type Hints
def greet(name: str) -> str:
return f"Hello, {name}"
def find_user(id: int) -> str | None:
return None
def process(items: list[str]) -> dict[str, int]:
return {item: len(item) for item in items}
Docstrings
Use structured docstrings with Args, Returns, and Raises sections:
def calculate_total(items: list[dict], tax_rate: float = 0.0) -> float:
"""Calculate the total cost of items including tax.
Args:
items: List of item dictionaries with 'price' keys
tax_rate: Tax rate as decimal (e.g., 0.08 for 8%)
Returns:
Total cost including tax
Raises:
ValueError: If items is empty or tax_rate is negative
"""
if not items:
raise ValueError("Items list cannot be empty")
if tax_rate < 0:
raise ValueError("Tax rate cannot be negative")
subtotal = sum(item["price"] for item in items)
return subtotal * (1 + tax_rate)
Best Practices
- Use uv exclusively - Never run
python3orpipdirectly - Start with PEP 723 - Single-file scripts by default
- Minimize dependencies - Try stdlib first
- Test incrementally - Build and test feature by feature
- Use type hints - Catch errors early with pyright
- Format with ruff - Consistent code style
- Follow exit codes - 0 for success, 1 for runtime errors, 2 for validation
Common Pitfalls
Mutable Default Arguments
Never use mutable objects (lists, dicts) as default argument values:
# BAD - The list persists across calls!
def add_item(item, items=[]):
items.append(item)
return items
add_item("a") # ['a']
add_item("b") # ['a', 'b'] - Unexpected!
# GOOD - Use None and create inside function
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
Bare Except Clauses
Never use bare except: - always catch specific exceptions:
# BAD - Catches everything including KeyboardInterrupt
try:
do_something()
except:
pass
# GOOD - Catch specific exceptions
try:
do_something()
except (ValueError, TypeError) as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
Comparing with None
Use is / is not for None comparisons:
# BAD
if value == None:
...
# GOOD
if value is None:
...
Security
Environment Variables
Store secrets in .env files, never in code:
# Load from .env file
from dotenv import load_dotenv
import os
load_dotenv()
api_key = os.getenv("API_KEY")
if not api_key:
print("Error: API_KEY not set", file=sys.stderr)
sys.exit(1)
Required Practices
- Never commit secrets - Add
.envto.gitignore - Never log secrets - Don't print API keys, passwords, or tokens
- Never hardcode - Use environment variables for all credentials
- Validate early - Check for required env vars at startup
.gitignore Entry
# Environment variables
.env
.env.local
.env.*.local
Bundled Resources
references/ruff.md- Linting and formatting guidereferences/pyright.md- Type checking guidereferences/pytest.md- Testing framework guide
Signals
- GitHub stars
- 106
- Forks
- 21
- Last commit
- Apr 2026
Advanced
- Catalog kind
- skill
- Gateway key
writing-python-archibate- Source
- github.com/archibate/dotfiles-opencode