skills/python/SKILL.md
Python development with modern tooling
npx skillsauth add jcsaaddupuy/badrobots pythonInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
IMPORTANT: Before writing Python code, read these reference files for detailed rules:
# Format files/directories
uvx black src/
uvx black tests/
uvx black script.py
# Check without modifying
uvx black --check src/
# Format entire project
uvx black .
# Check for issues
uvx ruff check src/
uvx ruff check .
# Auto-fix issues
uvx ruff check --fix src/
# With specific rules
uvx ruff check --select E,W,F src/
# Type check with mypy
uvx mypy src/
# Type check specific file
uvx mypy src/module.py
# Strict mode
uvx mypy --strict src/
# Alternative: pyright (faster)
uvx pyright src/
src/<package>/ or tests/)logger = logging.getLogger(__name__)logger.info("Message %s", variable) NOT f-stringslogger.exception("message")SecretStr from PydanticBaseSettings for environment variableshttpx.AsyncClient for async operationsmyproject/
├── pyproject.toml # Project config (uv managed)
├── uv.lock # Dependency lock
├── .python-version # Python version pin
├── README.md # Project documentation
├── src/
│ └── myproject/
│ ├── __init__.py # Package marker + version
│ ├── main.py # Entry point
│ ├── models.py # Data models
│ ├── services.py # Business logic
│ └── utils.py # Helper functions
├── tests/
│ ├── __init__.py
│ ├── conftest.py # Pytest fixtures
│ ├── test_models.py
│ ├── test_services.py
│ └── test_utils.py
# ✅ GOOD - Full type hints
from typing import Optional, List, Dict, Any
def process_data(
items: List[str],
config: Dict[str, Any],
timeout: Optional[int] = None
) -> Dict[str, int]:
"""Process items according to config.
Args:
items: List of items to process
config: Configuration dictionary
timeout: Optional timeout in seconds
Returns:
Dictionary mapping items to counts
Raises:
ValueError: If items is empty
"""
if not items:
raise ValueError("Items cannot be empty")
result: Dict[str, int] = {}
for item in items:
result[item] = len(item)
return result
# ❌ BAD - No type hints
def process_data(items, config, timeout=None):
result = {}
for item in items:
result[item] = len(item)
return result
def complex_function(
param1: str,
param2: int,
optional: bool = False
) -> tuple[str, int]:
"""One-line summary of function.
More detailed description explaining what the function does,
any important algorithms, or behavior notes.
Args:
param1: Description of first parameter
param2: Description of second parameter
optional: Whether to enable optional behavior
Returns:
A tuple containing:
- Processed string result
- Count of operations performed
Raises:
ValueError: If param2 is negative
TypeError: If param1 is not a string
Example:
>>> result, count = complex_function("test", 5)
>>> print(result)
'TEST'
"""
if param2 < 0:
raise ValueError("param2 must be non-negative")
return param1.upper(), param2 * 2
from dataclasses import dataclass
from typing import ClassVar, Optional
@dataclass
class DataModel:
"""A data model representing a thing.
Attributes:
name: The name of the thing
value: The value associated with it
active: Whether the thing is active
"""
name: str
value: int
active: bool = True
# Class variable
MAX_VALUE: ClassVar[int] = 1000
def __post_init__(self) -> None:
"""Validate data after initialization."""
if self.value > self.MAX_VALUE:
raise ValueError(f"Value cannot exceed {self.MAX_VALUE}")
def process(self) -> Optional[str]:
"""Process the data model.
Returns:
Processed result or None if not active
"""
if not self.active:
return None
return f"{self.name}: {self.value}"
from typing import Optional
import logging
logger = logging.getLogger(__name__)
def safe_operation(data: dict[str, Any]) -> Optional[str]:
"""Perform operation with proper error handling.
Args:
data: Input data dictionary
Returns:
Result string or None on error
"""
try:
result = data["key"]
return str(result)
except KeyError as e:
logger.error("Missing required key: %s", e)
return None
except Exception as e:
logger.exception("Unexpected error: %s", e)
raise
# tests/test_services.py
import pytest
from myproject.services import DataService
class TestDataService:
"""Test suite for DataService."""
def test_basic_operation(self):
"""Test basic service operation."""
service = DataService()
result = service.process("input")
assert result == "expected"
def test_error_handling(self):
"""Test that errors are handled correctly."""
service = DataService()
with pytest.raises(ValueError, match="Invalid input"):
service.process("")
@pytest.mark.parametrize("input,expected", [
("test", "TEST"),
("hello", "HELLO"),
("123", "123"),
])
def test_multiple_inputs(self, input: str, expected: str):
"""Test with multiple input values."""
service = DataService()
assert service.process(input) == expected
# tests/conftest.py
import pytest
from myproject.models import DataModel
@pytest.fixture
def sample_data() -> dict[str, Any]:
"""Provide sample data for tests."""
return {
"name": "test",
"value": 42,
"active": True
}
@pytest.fixture
def data_model(sample_data: dict[str, Any]) -> DataModel:
"""Provide a DataModel instance."""
return DataModel(**sample_data)
# tests/test_models.py
def test_with_fixture(data_model: DataModel):
"""Test using the fixture."""
assert data_model.name == "test"
assert data_model.value == 42
# Run tests with coverage
uv run pytest --cov=src --cov-report=html --cov-report=term
# View HTML report
open htmlcov/index.html
# Fail if coverage below threshold
uv run pytest --cov=src --cov-fail-under=80
from contextlib import contextmanager
from typing import Generator
@contextmanager
def managed_resource() -> Generator[Resource, None, None]:
"""Context manager for resource handling.
Yields:
The managed resource instance
"""
resource = Resource()
try:
resource.open()
yield resource
finally:
resource.close()
# Usage
with managed_resource() as res:
res.do_something()
import asyncio
from typing import List
async def fetch_data(url: str) -> dict[str, Any]:
"""Fetch data asynchronously.
Args:
url: URL to fetch from
Returns:
Fetched data as dictionary
"""
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.json()
async def fetch_multiple(urls: List[str]) -> List[dict[str, Any]]:
"""Fetch multiple URLs concurrently."""
tasks = [fetch_data(url) for url in urls]
return await asyncio.gather(*tasks)
from enum import Enum, auto
class Status(Enum):
"""Status enumeration."""
PENDING = auto()
PROCESSING = auto()
COMPLETED = auto()
FAILED = auto()
def process_item(status: Status) -> str:
"""Process based on status."""
match status:
case Status.PENDING:
return "waiting"
case Status.PROCESSING:
return "active"
case Status.COMPLETED:
return "done"
case Status.FAILED:
return "error"
from dataclasses import dataclass, field
from datetime import datetime
from typing import List
@dataclass
class User:
"""User data transfer object."""
id: int
username: str
email: str
created_at: datetime = field(default_factory=datetime.now)
tags: List[str] = field(default_factory=list)
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary."""
return {
"id": self.id,
"username": self.username,
"email": self.email,
"created_at": self.created_at.isoformat(),
"tags": self.tags,
}
import logging
from typing import Optional
# Module-level logger
logger = logging.getLogger(__name__)
def setup_logging(level: str = "INFO") -> None:
"""Configure logging for the application.
Args:
level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
"""
logging.basicConfig(
level=getattr(logging, level.upper()),
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(),
logging.FileHandler('app.log')
]
)
def process_with_logging(data: dict[str, Any]) -> Optional[str]:
"""Process data with proper logging."""
logger.debug("Processing data: %s", data)
try:
result = expensive_operation(data)
logger.info("Successfully processed: %s", result)
return result
except ValueError as e:
logger.warning("Validation error: %s", e)
return None
except Exception as e:
logger.exception("Unexpected error: %s", e)
raise
from pydantic_settings import BaseSettings
from pydantic import Field
class Settings(BaseSettings):
"""Application settings from environment."""
app_name: str = "myapp"
debug: bool = False
database_url: str = Field(..., env="DATABASE_URL")
api_key: str = Field(..., env="API_KEY")
max_connections: int = 10
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
# Usage
settings = Settings()
from typing import Protocol
class StorageProtocol(Protocol):
"""Protocol for storage backends."""
def save(self, key: str, value: str) -> None: ...
def load(self, key: str) -> str: ...
class MemoryStorage:
"""In-memory storage implementation."""
def __init__(self) -> None:
self._data: dict[str, str] = {}
def save(self, key: str, value: str) -> None:
self._data[key] = value
def load(self, key: str) -> str:
return self._data[key]
class DataService:
"""Service with dependency injection."""
def __init__(self, storage: StorageProtocol) -> None:
self._storage = storage
def process(self, key: str, value: str) -> None:
"""Process and store data."""
processed = value.upper()
self._storage.save(key, processed)
import typer
from typing import Optional
app = typer.Typer()
@app.command()
def process(
input_file: str = typer.Argument(..., help="Input file path"),
output_file: Optional[str] = typer.Option(None, "--output", "-o", help="Output file path"),
verbose: bool = typer.Option(False, "--verbose", "-v", help="Verbose output"),
) -> None:
"""Process input file and generate output.
Args:
input_file: Path to input file
output_file: Optional output file path
verbose: Enable verbose logging
"""
if verbose:
typer.echo(f"Processing {input_file}...")
# Processing logic here
result = f"Processed: {input_file}"
if output_file:
with open(output_file, 'w') as f:
f.write(result)
typer.echo(f"Output written to {output_file}")
else:
typer.echo(result)
if __name__ == "__main__":
app()
1. Understand requirement
↓
2. Read relevant reference files
├─ references/core-rules.md for structure
├─ references/logging.md if adding logs
├─ references/testing.md for tests
└─ references/secrets.md if handling sensitive data
↓
3. Check existing code structure
├─ Read pyproject.toml for dependencies
├─ Identify relevant modules in src/
└─ Find corresponding test files
↓
4. Plan implementation
├─ Determine where code belongs
├─ Identify needed dependencies
└─ Plan test scenarios
↓
5. Implement code
├─ Add type hints
├─ Write docstrings
├─ Handle errors properly
└─ Follow project style (references/core-rules.md)
↓
6. Write tests
├─ Follow references/testing.md
├─ Create test_<module>.py
├─ Test happy path
├─ Test edge cases
└─ Test error handling
↓
7. Verify quality
├─ Format: uvx black src/ tests/
├─ Lint: uvx ruff check src/ tests/
├─ Type check: uvx mypy src/
└─ Run tests: uv run pytest (see references/testing.md)
↓
8. Fix any issues and repeat step 7
# All-in-one check (run before committing)
uvx black . && \
uvx ruff check --fix . && \
uvx mypy src/ && \
uv run pytest
# Individual checks
uvx black --check . # Check formatting
uvx black . # Apply formatting
uvx ruff check . # Lint check
uvx ruff check --fix . # Lint fix
uvx mypy src/ # Type check
uvx mypy --strict src/ # Strict type check
uv run pytest # Run tests
uv run pytest -v # Verbose tests
uv run pytest --cov=src # With coverage
uv run pytest -k test_name # Run specific test
uv run pytest -x # Stop on first failure
None and create in functionlist[str], dict[str, int])| operator (Python 3.10+)development
DuckDB patterns for JSON/JSONL analysis, array unnesting, and common gotchas. Use when querying JSON files, nested data, or encountering "UNNEST not supported here" errors.
development
Mealie recipe manager API: recipes, shopping lists, meal plans. Requires MEALIE_BASE_URL and MEALIE_API_KEY.
business
TimeWarrior time tracking: start/stop intervals, query durations by tag or issue, compute totals for issue tracker time reporting
development
Bookmark manager for saving, searching, and annotating web content. Use when: (1) saving a webpage for later reference, (2) searching previously saved bookmarks, (3) adding highlights/annotations to saved content, (4) user asks to 'bookmark this' or 'save this article'. Requires READECK_BASE_URL and READECK_API_KEY environment variables.