Expert Python developer specializing in modern tooling (uv, ruff, pyright), type safety, and clean module design. This skill should be used PROACTIVELY when working on any Python code - implementing features, designing APIs, debugging issues, or reviewing code quality. Use unless a more specific subagent role applies.
Applies modern Python standards with strict type checking, uv, ruff, and pyright.
/plugin marketplace add rbergman/dark-matter-marketplace/plugin install dm-lang@dark-matter-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/pyproject-ruff.tomlreferences/pyproject-template.tomlreferences/pyrightconfig.jsonSenior-level Python expertise for production projects. Focuses on modern tooling, strict type checking, and Pythonic idioms.
pyproject.toml for project conventions and tooling configNon-Negotiable:
Foundational Principles:
mise manages language runtimes per-project. Ensures all contributors use the same Python version.
# Install mise (once)
curl https://mise.run | sh
# In project root
mise use python@3.12
# Creates .mise.toml - commit it
# Team members just run: mise install
uv is the modern Python package manager (10-100x faster than pip).
# Initialize project
uv init project-name
cd project-name
# Or in existing directory
uv init
# Add dependencies
uv add httpx pydantic
# Add dev dependencies
uv add --dev pytest pytest-cov pytest-asyncio ruff pyright
# Set up src layout (required for imports to work)
mkdir -p src/projectname tests
mv *.py src/projectname/ 2>/dev/null || true
touch src/projectname/__init__.py tests/__init__.py tests/conftest.py
# Copy configs from this skill's references/ directory:
# references/pyproject-template.toml -> use as pyproject.toml base
# references/pyrightconfig.json -> pyrightconfig.json
# For build system, invoke just-pro skill
# Verify
just check # Or: uv run ruff check . && uv run pyright
Critical: uv init creates a minimal pyproject.toml. For src layout to work, you MUST add:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/projectname"]
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
Without [build-system] and [tool.hatch.build.targets.wheel], tests cannot import your package.
git clone <repo> && cd <repo>
just setup # Runs mise trust/install + uv sync
just check # Verify everything works
Or manually:
mise trust && mise install # Get pinned Python version
uv sync # Install dependencies from lockfile
Why uv? Lockfile-based reproducibility, automatic venv management, 10-100x faster than pip.
Invoke the just-pro skill for build system setup. It covers:
Why just? Consistent toolchain frontend between agents and humans. Instead of remembering uv run ruff check --fix ., use just fix.
Auto-Fix First - Always try auto-fix before manual fixes:
just fix # Or: uv run ruff check --fix . && uv run ruff format .
Verification:
just check # Or: uv run ruff check . && uv run pyright && uv run pytest
Some libraries lack type stubs. In pyright strict mode, use Any:
from typing import Any
import structlog # No complete type stubs
# Annotate as Any to silence pyright
logger: Any = structlog.get_logger()
When to use Any:
Avoid # type: ignore - it silences all errors. Explicit Any is clearer.
Ruff's TCH rules flag imports used only for type hints. Move them to a TYPE_CHECKING block:
from __future__ import annotations # Required for forward refs
from typing import TYPE_CHECKING
from mypackage.service import run_service # Runtime import
if TYPE_CHECKING:
from mypackage.models import User # Type-only import
def process(user: User) -> None: # Works due to __future__ annotations
run_service(user)
Rules:
TYPE_CHECKINGfrom __future__ import annotations enables string-based forward refsWhen a field might be None, assert before accessing:
# BAD - pyright error: "x" could be None
result.error_message.lower()
# GOOD - narrow the type first
assert result.error_message is not None
result.error_message.lower()
# OR use conditional
if result.error_message:
result.error_message.lower()
from typing import TypeVar, Protocol
from collections.abc import Callable, Iterator, Sequence
# Basic annotations
def process(items: list[str], timeout: float = 30.0) -> dict[str, int]:
...
# Generic functions
T = TypeVar("T")
def first(items: Sequence[T]) -> T | None:
return items[0] if items else None
# Protocols for structural typing (duck typing with types)
class Readable(Protocol):
def read(self, n: int = -1) -> bytes: ...
def load_data(source: Readable) -> bytes:
return source.read()
# Custom exceptions with context
class ValidationError(Exception):
def __init__(self, field: str, message: str) -> None:
self.field = field
self.message = message
super().__init__(f"{field}: {message}")
# Explicit error handling
def parse_config(path: str) -> Config:
try:
with open(path) as f:
data = json.load(f)
except FileNotFoundError:
raise ConfigError(f"Config file not found: {path}") from None
except json.JSONDecodeError as e:
raise ConfigError(f"Invalid JSON in {path}: {e}") from e
return Config.from_dict(data)
# Use Result pattern for expected failures (optional)
from dataclasses import dataclass
@dataclass
class Ok[T]:
value: T
@dataclass
class Err[E]:
error: E
type Result[T, E] = Ok[T] | Err[E]
from dataclasses import dataclass, field
from pydantic import BaseModel, Field
# Simple data containers
@dataclass(frozen=True, slots=True)
class Point:
x: float
y: float
# Pydantic for validation and serialization
class UserCreate(BaseModel):
name: str = Field(min_length=1, max_length=100)
email: str
age: int = Field(ge=0, le=150)
model_config = {"strict": True}
import asyncio
from collections.abc import AsyncIterator
# Async context managers
async def fetch_with_timeout(url: str, timeout: float = 10.0) -> bytes:
async with asyncio.timeout(timeout):
async with httpx.AsyncClient() as client:
response = await client.get(url)
response.raise_for_status()
return response.content
# Async generators
async def paginate(client: Client, url: str) -> AsyncIterator[Item]:
while url:
response = await client.get(url)
for item in response.items:
yield item
url = response.next_url
# Gather with error handling
async def fetch_all(urls: list[str]) -> list[bytes | Exception]:
tasks = [fetch_with_timeout(url) for url in urls]
return await asyncio.gather(*tasks, return_exceptions=True)
from contextlib import contextmanager, asynccontextmanager
from collections.abc import Generator, AsyncGenerator
@contextmanager
def temporary_config(overrides: dict[str, str]) -> Generator[None, None, None]:
original = config.copy()
config.update(overrides)
try:
yield
finally:
config.clear()
config.update(original)
@asynccontextmanager
async def database_transaction(db: Database) -> AsyncGenerator[Transaction, None]:
tx = await db.begin()
try:
yield tx
await tx.commit()
except Exception:
await tx.rollback()
raise
import pytest
from unittest.mock import Mock, patch
# Parametrized tests
@pytest.mark.parametrize(
"input_val,expected",
[
("hello", "HELLO"),
("", ""),
("123", "123"),
],
ids=["normal", "empty", "digits"],
)
def test_uppercase(input_val: str, expected: str) -> None:
assert uppercase(input_val) == expected
# Fixtures
@pytest.fixture
def sample_user() -> User:
return User(name="Test", email="test@example.com")
@pytest.fixture
def mock_client() -> Mock:
client = Mock(spec=APIClient)
client.get.return_value = {"status": "ok"}
return client
# Async tests
@pytest.mark.asyncio
async def test_fetch_data(mock_client: Mock) -> None:
result = await fetch_data(mock_client, "test-id")
assert result.status == "ok"
# Exception testing
def test_invalid_input_raises() -> None:
with pytest.raises(ValueError, match="must be positive"):
process_value(-1)
import logging
from typing import Any
import structlog
# Note: structlog lacks complete type stubs. Use Any for logger type.
logger: Any = structlog.get_logger()
# Configure structlog for console output
structlog.configure(
processors=[
structlog.stdlib.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.dev.ConsoleRenderer(), # Or JSONRenderer() for prod
],
wrapper_class=structlog.make_filtering_bound_logger(logging.INFO),
context_class=dict,
logger_factory=structlog.PrintLoggerFactory(),
)
# Structured logging with context
logger.info("request_processed", method="GET", path="/api/users", duration_ms=42)
logger.error("operation_failed", error=str(e), user_id=user_id)
# Bind context for a scope
bound_logger: Any = logger.bind(request_id=request_id, user_id=user_id)
bound_logger.info("starting_operation")
project/
├── src/
│ └── projectname/
│ ├── __init__.py
│ ├── api/ # HTTP handlers
│ ├── domain/ # Business logic
│ └── infra/ # External integrations
├── tests/
│ ├── conftest.py # Shared fixtures
│ ├── test_api/
│ └── test_domain/
├── pyproject.toml
├── pyrightconfig.json
├── uv.lock
└── justfile
Rules: Use src/ layout for installable packages. One module = one purpose. Avoid utils, common, helpers modules.
# Validate toolchain versions meet requirements
doctor:
#!/usr/bin/env bash
set -euo pipefail
echo "Checking toolchain..."
# Validate Python version (requires 3.12+)
PY_VERSION=$(python --version | grep -oE '[0-9]+\.[0-9]+')
if [[ "$(printf '%s\n' "3.12" "$PY_VERSION" | sort -V | head -1)" != "3.12" ]]; then
echo "FAIL: Python $PY_VERSION < 3.12 required"
exit 1
fi
echo "OK: Python $PY_VERSION"
# Check uv is available
if ! command -v uv &> /dev/null; then
echo "FAIL: uv not found. Install: curl -LsSf https://astral.sh/uv/install.sh | sh"
exit 1
fi
echo "OK: uv $(uv --version | head -1)"
echo "All checks passed"
# Setup with first-run detection
setup:
#!/usr/bin/env bash
if [[ -f .setup-complete ]]; then
echo "Already set up. Run 'just setup-force' to reinstall."
exit 0
fi
mise trust && mise install
uv sync
touch .setup-complete
echo "Setup complete"
setup-force:
rm -f .setup-complete
@just setup
except: clauses (always specify exception type)def f(items=[]) - use None and create inside)from module import *)Any when specific types workprint() for logging (use logging/structlog)Before writing code:
pyproject.toml for project structure and dependenciesWhen writing code:
Before committing:
just check (standard for projects using just)uv run ruff check --fix . && uv run ruff format .uv run pyright && uv run pytestApplies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.