The model must use this skill when : 1. working within any python project. 2. Python CLI applications with Typer and Rich are mentioned by the user. 2. tasked with Python script writing or editing. 3. building CI scripts or tools. 4. Creating portable Python scripts with stdlib only. 5. planning out a python package design. 6. running any python script or test. 7. writing tests (unit, integration, e2e, validation) for a python script, package, or application. Reviewing Python code against best practices or for code smells. 8. The python command fails to run or errors, or the python3 command shows errors. 9. pre-commit or linting errors occur in python files. 10. Writing or editing python code in a git repository.\n<hint>This skill provides : 1. the users preferred workflow patterns for test-driven development, feature addition, refactoring, debugging, and code review using modern Python 3.11+ patterns (including PEP 723 inline metadata, native generics, and type-safe async processing). 2. References to favor
The model must use this skill when : 1. working within any python project. 2. Python CLI applications with Typer and Rich are mentioned by the user. 2. tasked with Python script writing or editing. 3. building CI scripts or tools. 4. Creating portable Python scripts with stdlib only. 5. planning out a python package design. 6. running any python script or test. 7. writing tests (unit, integration, e2e, validation) for a python script, package, or application. Reviewing Python code against best practices or for code smells. 8. The python command fails to run or errors, or the python3 command shows errors. 9. pre-commit or linting errors occur in python files. 10. Writing or editing python code in a git repository.\n<hint>This skill provides : 1. the users preferred workflow patterns for test-driven development, feature addition, refactoring, debugging, and code review using modern Python 3.11+ patterns (including PEP 723 inline metadata, native generics, and type-safe async processing). 2. References to favored modules. 3. Working pyproject.toml configurations. 4. Linting and formatting configuration and troubleshooting. 5. Resource files that provide solutions to known errors and linting issues. 6. Project layouts the user prefers.</hint>
version
1.1.0
last_updated
2025-11-04
python_compatibility
3.11+
Opinionated Python Development Skill
Role Identification (Mandatory)
The model must identify its ROLE_TYPE and echo the following statement:
My ROLE_TYPE is "<the role type>". I follow instructions given to "the model" and "<role name>".
Where:
<the role type> is either "orchestrator" or "sub-agent" based on the ROLE_TYPE identification rules in CLAUDE.md
<role name> is "orchestrator" if ROLE_TYPE is orchestrator, or "sub-agent" if ROLE_TYPE is sub-agent
Example for orchestrator:
My ROLE_TYPE is "orchestrator". I follow instructions given to "the model" and "orchestrator".
Example for sub-agent:
My ROLE_TYPE is "sub-agent". I follow instructions given to "the model" and "sub-agent".
Orchestration guide for Python development using specialized agents and modern Python 3.11-3.14 patterns.
Skill Architecture
Bundled Resources (Included in This Skill)
Reference Documentation:
User Project Conventions - Extracted conventions from user's production projects (MANDATORY for new projects)
/shebangpython - Validates correct shebang for all Python scripts
Note: This skill contains command templates in commands/ directory, not the actual slash commands
Reference Documentation:
Modern Python modules (50+ libraries)
Tool and library registry with template variable system
API specifications
Working configurations for pyproject.toml, ruff, mypy, pytest
Docstring Standard: Google style (Args/Returns/Raises sections). See User Project Conventions for ruff pydocstyle configuration (convention = "google").
CRITICAL: Pyproject.toml Template Variables:
All pyproject.toml examples use explicit template variables (e.g., {{project_name_from_directory_or_git_remote}}) instead of generic placeholders. The model MUST replace ALL template variables with actual values before creating files. See Tool & Library Registry sections 18-19 for:
Complete variable reference and sourcing methods
Mandatory rules for file creation
Validation and verification procedures
Script Dependency Trade-offs
Understand the complexity vs portability trade-off when creating Python CLI scripts:
Scripts with dependencies (Typer + Rich via PEP 723):
Benefits:
Less development complexity - Leverage well-tested libraries for argument parsing, formatting, validation
Less code to write - Typer handles CLI boilerplate, Rich handles output formatting
Common Problem: Rich containers (Panel, Table) wrap content at 80 characters in CI/non-TTY environments, breaking URLs, commands, and structured output.
Two Solutions Depending on Context:
Solution 1: Plain Text (No Containers)
For plain text output that shouldn't wrap:
from rich.console import Console
console = Console()
# URLs, paths, commands - never wrap
console.print(long_url, crop=False, overflow="ignore")
Solution 2: Rich Containers (Panel/Table)
For Panel and Table that contain long content, crop=False alone doesn't work because containers calculate their own internal layout. Use get_rendered_width() helper with different patterns for Panel vs Table:
from rich.console import Console, RenderableType
from rich.measure import Measurement
from rich.panel import Panel
from rich.table import Table
def get_rendered_width(renderable: RenderableType) -> int:
"""Get actual rendered width of Rich renderable.
Handles color codes, Unicode, styling, padding, borders.
Works with Panel, Table, or any Rich container.
"""
temp_console = Console(width=9999)
measurement = Measurement.get(temp_console, temp_console.options, renderable)
return int(measurement.maximum)
console = Console()
# Panel: Set Console width (Panel fills Console width)
panel = Panel(long_content)
panel_width = get_rendered_width(panel)
console.width = panel_width # Set Console width, NOT panel.width
console.print(panel, crop=False, overflow="ignore", no_wrap=True, soft_wrap=True)
# Table: Set Table width (Table controls its own width)
table = Table()
table.add_column("Type", style="cyan", no_wrap=True)
table.add_column("Value", style="green", no_wrap=True)
table.add_row("Data", long_content)
table.width = get_rendered_width(table) # Set Table width
console.print(table, crop=False, overflow="ignore", no_wrap=True, soft_wrap=True)
from collections.abc import Sequence
from typing import Protocol
class SupportsAbs[T](Protocol):
def __abs__(self) -> T: ...
def max_by_abs[T: SupportsAbs[float]](*xs: T) -> T:
return max(xs, key=abs)
Value Restrictions (limit to specific types):
def concat[S: (str, bytes)](x: S, y: S) -> S:
return x + y # Type-safe for str OR bytes, but not mixed
Generic Method Chaining (precise return types):
from typing import Self
class Shape:
scale: float = 1.0
def set_scale(self, scale: float) -> Self:
self.scale = scale
return self # Returns precise subclass type
When to Use Protocols
Use protocols for structural subtyping when you need duck typing with type safety. Protocols check whether an object has required methods/attributes regardless of inheritance.
Applicable scenarios:
Accept any object with specific capabilities without requiring inheritance
from typing import Protocol
class SupportsClose(Protocol):
def close(self) -> None: ...
def close_resource(resource: SupportsClose) -> None:
resource.close() # Works with ANY object having close() method
# No inheritance needed - structural match
class FileHandler:
def close(self) -> None:
print("Closing file")
close_resource(FileHandler()) # ✅ Type-safe
Read-Only Attributes (use @property to avoid invariance issues):
from typing import Protocol
class Named(Protocol):
@property
def name(self) -> str: ... # Read-only via property
Recursive Protocols (tree structures):
from typing import Protocol
class TreeLike(Protocol):
value: int
@property
def left(self) -> TreeLike | None: ...
@property
def right(self) -> TreeLike | None: ...
Runtime Checks:
from typing import Protocol, runtime_checkable
@runtime_checkable
class Drawable(Protocol):
def draw(self) -> None: ...
def render(obj: object) -> None:
if isinstance(obj, Drawable): # Runtime check enabled
obj.draw()
WARNING: isinstance() with protocols only verifies attribute existence, NOT type correctness. Use for structural validation, not precise type guarantees.
TypedDict for Dictionary Typing
Use TypedDict for dictionaries with fixed schemas and string keys where each key has a specific value type.
Applicable scenarios:
Dictionaries representing objects with predictable structure
def process_value(value: str | int) -> str:
if isinstance(value, str):
return value.upper() # Narrowed to str
else:
return str(value * 2) # Narrowed to int
None Checks:
def greet(name: str | None) -> str:
if name is not None:
return f"Hello, {name}" # Narrowed to str
return "Hello, stranger"
Type Guards (custom narrowing functions):
from typing import TypeGuard
def is_str_list(val: list[object]) -> TypeGuard[list[str]]:
return all(isinstance(x, str) for x in val)
def process(values: list[object]) -> None:
if is_str_list(values):
# Narrowed to list[str] in this branch
print(" ".join(values))
TypeIs (Python 3.13+, more powerful than TypeGuard):
from typing import TypeIs
def is_str(val: object) -> TypeIs[str]:
return isinstance(val, str)
def process(val: str | int) -> None:
if is_str(val):
print(val.upper()) # Narrowed to str
else:
print(val * 2) # Narrowed to int (complement type)
Key difference: TypeGuard narrows only the if-branch; TypeIs narrows both branches (if-branch to the specified type, else-branch to the complement).
attrs vs dataclasses vs pydantic
Decision Matrix:
Feature
attrs
dataclasses
pydantic
Performance
Fastest (compiled)
Fast (native)
Slower (validation overhead)
Validation
Basic (via converters/validators)
None (requires custom __post_init__)
Comprehensive (built-in)
Immutability
@frozen
frozen=True
frozen=True (v2)
Evolution
Excellent (evolve())
Basic (replace())
Good (model_copy())
Slots
Automatic
Manual (slots=True)
Automatic (v2)
Type Coercion
Manual
None
Automatic
JSON Serialization
Manual
Manual
Native (model_dump_json())
Use When
High-performance, pure Python data
Stdlib-only requirement
External data validation (APIs, configs)
attrs Pattern (high performance, pure Python):
from attrs import define, field
@define
class User:
name: str
age: int = field(validator=lambda i, a, v: v >= 0)
email: str | None = None
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class User:
name: str
age: int
email: str | None = None
def __post_init__(self) -> None:
if self.age < 0:
raise ValueError("Age must be non-negative")
pydantic Pattern (external data validation):
from pydantic import BaseModel, Field, field_validator
class User(BaseModel):
name: str
age: int = Field(ge=0)
email: str | None = None
@field_validator('age')
@classmethod
def validate_age(cls, v: int) -> int:
if v < 0:
raise ValueError('Age must be non-negative')
return v
Recommendation:
Default: Use attrs for internal data structures (best performance, most features)
Stdlib-only requirement: Use dataclasses
External data (APIs, configs, user input): Use pydantic (if already a dependency)
NEVER add pydantic as a dependency solely for dataclasses. Use attrs or stdlib dataclasses instead.
from dataclasses import dataclass
from typing import Generic, TypeVar
T = TypeVar('T')
@dataclass
class Box(Generic[T]):
value: T
int_box: Box[int] = Box(value=42)
str_box: Box[str] = Box(value="hello")
Self Type for Method Chaining:
from typing import Self
class Builder:
def set_name(self, name: str) -> Self:
self.name = name
return self
def set_value(self, value: int) -> Self:
self.value = value
return self
# Type-safe chaining
builder = Builder().set_name("test").set_value(42)
User: "Build a CLI tool to process CSV files"
Orchestrator workflow:
1. Read orchestration guide for agent selection
2. Delegate to @agent-python-cli-architect
"Create CSV processing CLI with Typer+Rich progress bars"
3. Delegate to @agent-python-pytest-architect
"Create test suite for CSV processor"
4. Instruct agent to run: /shebangpython, /modernpython
5. Delegate to @agent-python-code-reviewer
"Review CSV processor implementation"
6. Validate: Code quality checks (linting, formatting) performed and issues addressed per the holistic-linting skill, then uv run pytest
Command Usage
/modernpython
Purpose: Comprehensive reference guide for Python 3.11+ patterns with official PEP citations
When to use:
As reference guide when writing new code
Learning modern Python 3.11-3.14 features and patterns
Understanding official PEPs (585, 604, 695, etc.)
Identifying legacy patterns to avoid
Finding modern alternatives for old code
Note: This is a reference document to READ, not an automated validation tool. Use it to guide your implementation choices.
Usage:
/modernpython
→ Loads comprehensive reference guide
→ Provides Python 3.11+ pattern examples
→ Includes PEP citations with research tool guidance (prefer MCP tools: Ref/exa over WebFetch)
→ Shows legacy patterns to avoid
→ Shows modern alternatives to use
→ Framework-specific guides (Typer, Rich, pytest)
Research tool preference for PEP documentation:
1. mcp__Ref__ref_search_documentation(query="PEP {number} Python enhancement proposal")
2. mcp__exa__get_code_context_exa(query="PEP {number} implementation examples")
3. WebFetch as fallback
> [Web resource access, definitive guide for getting accurate data for high quality results](./references/accessing_online_resources.md)
With file path argument:
/modernpython src/mymodule.py
→ Loads guide for reference while working on specified file
→ Use guide to manually identify and refactor legacy patterns
/shebangpython
Purpose: Validate correct shebang for ALL Python scripts based on their dependencies and execution context
When to use:
Creating any standalone executable Python script
Validating script shebang correctness
Ensuring scripts have proper execution configuration
/shebangpython scripts/deploy.py
→ Analyzes imports to determine dependency type
→ **Corrects shebang** to match script type (edits file if wrong)
→ **Adds PEP 723 metadata** if external dependencies detected (edits file)
→ **Removes PEP 723 metadata** if stdlib-only (edits file)
→ Sets execute bit if needed
→ Provides detailed verification report
If found: Detect and run the correct git hook tool:
# Detect tool (outputs 'prek' or 'pre-commit')
uv run python -c "print(open('.git/hooks/pre-commit').readlines()[1].split()[4].rstrip(':') if __import__('pathlib').Path('.git/hooks/pre-commit').exists() else 'prek')"
# Or use the holistic-linting detection script if available
uv run holistic-linting/scripts/detect-hook-tool.py run --files <files>
Detection logic: reads .git/hooks/pre-commit line 2, token 5 identifies the tool. Defaults to prek.
Note: prek is a Rust-based drop-in replacement for pre-commit. Both tools use the same .pre-commit-config.yaml and have identical CLI interfaces.
Use detected tool with: uv run <detected-tool> run --files <files> for ALL quality checks
This runs the complete toolchain configured in the project
Includes formatting, linting, type checking, and custom validators
Matches exactly what runs in CI and blocks merges
Else check CI pipeline configuration:
# Check for GitLab CI or GitHub Actions
test -f .gitlab-ci.yml || find .github/workflows -name "*.yml" 2>/dev/null
If found: Read the CI config to identify required linting tools and their exact commands
Look for ruff, mypy, basedpyright, pyright, bandit invocations
Note the exact commands and flags used
Execute those specific commands to ensure CI compatibility
Else fallback to tool detection:
Check pyproject.toml[project.optional-dependencies] or [dependency-groups] for dev tools
Use discovered tools with standard configurations
Format-First Workflow
The model always formats before linting.
Reason: Formatting operations (like ruff format) automatically fix many linting issues (whitespace, line length, quote styles). Running linting before formatting wastes context and creates false positives.
Mandatory sequence:
Format: uv run ruff format <files> or via git hook tool (pre-commit/prek)
Lint: uv run ruff check <files> or via git hook tool
Type check: Use project-configured type checker
Test: uv run pytest
When using git hook tool (pre-commit or prek):
# Detect which tool is installed, then run it
# Tool runs hooks in configured order (formatting first)
uv run <detected-tool> run --files <files>
The .pre-commit-config.yaml already specifies correct ordering - trust it.
Type Checker Discovery
The model detects which type checker the project uses:
Reason: Projects standardize on different type checkers (basedpyright, pyright, mypy). Using the wrong one produces incompatible results.
Detection priority:
Check .pre-commit-config.yaml for basedpyright, pyright, or mypy hooks
Check pyproject.toml for [tool.basedpyright], [tool.pyright], or [tool.mypy] sections
Check .gitlab-ci.yml or GitHub Actions for type checker invocations
# If .pre-commit-config.yaml exists (runs all checks in correct order):
# First detect which tool is installed (pre-commit or prek), then:
uv run <detected-tool> run --files <changed_files>
# Else use individual tools in this exact sequence:
uv run ruff format <files> # 1. Format first
uv run ruff check <files> # 2. Lint after formatting
uv run <detected-type-checker> <files> # 3. Type check (basedpyright/pyright/mypy)
uv run pytest # 4. Test
For critical code (payments, auth, security):
Coverage >95%
Mutation testing: uv run mutmut run (>90% score)
Security scan: uv run bandit -r packages/
CI Compatibility Verification:
After local quality gates pass, verify CI will accept the changes:
If .gitlab-ci.yml exists: Check for additional validators not in pre-commit
If .github/workflows/*.yml exists: Check for additional quality gates
Ensure all CI-required checks are executed locally before claiming task completion
Standard Project Structure
All Python projects use this directory layout:
Reason: Consistent structure enables reliable automation and clear separation between user code and dependencies.
This structure is consistent across all projects and enables clear separation of concerns.
Integration
External Reference Example
Complete working example (external): ~/.claude/agents/python-cli-demo.py
This reference implementation demonstrates all recommended patterns:
PEP 723 metadata with correct shebang
Typer + Rich integration
Modern Python 3.11+ (StrEnum, Protocol, TypeVar, Generics)
Annotated syntax for CLI params
Async processing
Comprehensive docstrings
This file is not bundled with this skill and must be available in ~/.claude/agents/ separately. Use as reference when creating CLI tools.
Using Asset Templates
When creating new Python projects, copy standard configuration files from the skill's assets directory to ensure consistency with established conventions:
Reason: Templates implement proven patterns and save setup time.
.pre-commit-config.yaml - Standard git hooks configuration
cp ~/.claude/skills/python3-development/assets/.pre-commit-config.yaml .
# Install hooks using pre-commit or prek (whichever is available)
# Both tools use the same configuration file and have identical interfaces
uv run pre-commit install # or: uv run prek install
These templates implement the patterns documented in User Project Conventions and ensure all projects follow the same standards for version management, linting, formatting, and build configuration.
Common Patterns to Follow (Orchestrator Only)
Delegation Pattern:
Instead of
Use this pattern
Writing Python code directly
Delegate to @agent-python-cli-architect with clear requirements
Skipping validation steps
Complete workflow: implement → test → review → validate
Typer and Rich Examples: Typer and Rich CLI Examples - Executable examples demonstrating solutions to common problems with Rich Console text wrapping in CI/non-TTY environments and Panel/Table content wrapping
Module Reference: Modern Python Modules - Comprehensive guide to 50+ modern Python libraries with deep-dive documentation for each module including usage patterns and best practices
Tool Registry: Tool & Library Registry - Catalog of development tools, their purposes, and usage patterns for linting, testing, and build automation
API Documentation: API Reference - API specifications, integration guides, and programmatic interface documentation
Navigating Large References
To find specific modules in modern-modules.md:
grep -i "^### " references/modern-modules.md
To search for tools by category in tool-library-registry.md:
grep -A 5 "^## " references/tool-library-registry.md
To locate workflow patterns in python-development-orchestration.md: