home / skills / alinaqi / claude-bootstrap / python

python skill

/skills/python

This skill helps you bootstrap Python projects with strict typing, testing, and clean architecture guided by ruff, mypy, and pytest.

npx playbooks add skill alinaqi/claude-bootstrap --skill python

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.2 KB
---
name: python
description: Python development with ruff, mypy, pytest - TDD and type safety
---

# Python Skill

*Load with: base.md*

---

## Type Hints

- Use type hints on all function signatures
- Use `typing` module for complex types
- Run `mypy --strict` in CI

```python
def process_user(user_id: int, options: dict[str, Any] | None = None) -> User:
    ...
```

---

## Project Structure

```
project/
├── src/
│   └── package_name/
│       ├── __init__.py
│       ├── core/           # Pure business logic
│       │   ├── __init__.py
│       │   ├── models.py   # Pydantic models / dataclasses
│       │   └── services.py # Pure functions
│       ├── infra/          # Side effects
│       │   ├── __init__.py
│       │   ├── api.py      # FastAPI routes
│       │   └── db.py       # Database operations
│       └── utils/          # Shared utilities
├── tests/
│   ├── unit/
│   └── integration/
├── pyproject.toml
└── CLAUDE.md
```

---

## Tooling (Required)

```toml
# pyproject.toml
[tool.ruff]
line-length = 100
select = ["E", "F", "I", "N", "W", "UP"]

[tool.mypy]
strict = true

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--cov=src --cov-report=term-missing --cov-fail-under=80"
```

---

## Testing with Pytest

```python
# tests/unit/test_services.py
import pytest
from package_name.core.services import calculate_total

class TestCalculateTotal:
    def test_returns_sum_of_items(self):
        # Arrange
        items = [{"price": 10}, {"price": 20}]
        
        # Act
        result = calculate_total(items)
        
        # Assert
        assert result == 30

    def test_returns_zero_for_empty_list(self):
        assert calculate_total([]) == 0

    def test_raises_on_invalid_item(self):
        with pytest.raises(ValueError):
            calculate_total([{"invalid": "item"}])
```

---

## GitHub Actions

```yaml
name: Python Quality Gate

on: [push, pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
          
      - name: Install dependencies
        run: |
          pip install -e ".[dev]"
          
      - name: Lint (Ruff)
        run: ruff check .
        
      - name: Format Check (Ruff)
        run: ruff format --check .
        
      - name: Type Check (mypy)
        run: mypy src/
        
      - name: Test with Coverage
        run: pytest
```

---

## Pre-Commit Hooks

```yaml
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.8.0
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.13.0
    hooks:
      - id: mypy
        additional_dependencies: [pydantic]
        args: [--strict]

  - repo: local
    hooks:
      - id: pytest
        name: pytest
        entry: pytest tests/unit -x --tb=short
        language: system
        pass_filenames: false
        always_run: true
```

Install and setup:
```bash
pip install pre-commit
pre-commit install
```

---

## Patterns

### Pydantic for Data Validation
```python
from pydantic import BaseModel, Field

class CreateUserRequest(BaseModel):
    email: str = Field(..., min_length=5)
    name: str = Field(..., max_length=100)
```

### Dependency Injection
```python
# Don't import dependencies directly in business logic
# Pass them in

# Bad
from .db import database
def get_user(user_id: int) -> User:
    return database.fetch(user_id)

# Good
def get_user(user_id: int, db: Database) -> User:
    return db.fetch(user_id)
```

### Result Pattern (No Exceptions in Core)
```python
from dataclasses import dataclass

@dataclass
class Result[T]:
    value: T | None
    error: str | None
    
    @property
    def is_ok(self) -> bool:
        return self.error is None
```

---

## Python Anti-Patterns

- ❌ `from module import *`
- ❌ Mutable default arguments
- ❌ Bare `except:` clauses
- ❌ Using `type: ignore` without explanation
- ❌ Global variables for state
- ❌ Classes when functions suffice

Overview

This skill provides an opinionated Python development setup focused on test-driven development, type safety, and secure, spec-driven projects. It wires ruff for linting/formatting, mypy for strict static typing, and pytest for automated tests and coverage. The layout and tooling are optimized for maintainable business logic, clear side-effect boundaries, and CI enforcement.

How this skill works

The skill inspects project structure and enforces conventions: src/ package layout with core, infra, and utils layers, plus a tests/ hierarchy. It configures pyproject.toml for ruff and mypy, sets pytest options for coverage, and provides pre-commit hooks and a GitHub Actions pipeline that runs lint, format check, type check, and tests. It emphasizes type hints, dependency injection, Pydantic for validation, and a Result pattern to avoid exceptions in core logic.

When to use it

  • Starting a new Python service or library where long-term maintainability matters
  • Adopting TDD and enforcing behavior with pytest and coverage gates
  • Introducing strict typing to an existing codebase or enforcing mypy --strict in CI
  • Standardizing developer tooling across teams (ruff, pre-commit, GitHub Actions)
  • Building security-sensitive systems that need clear separation of pure logic from side effects

Best practices

  • Annotate all function signatures and use typing for complex types
  • Keep business logic pure under core/, push side effects into infra/
  • Use dependency injection rather than importing globals for easier testing
  • Validate external input with Pydantic models and dataclasses
  • Run ruff formatting and mypy type checks in CI and pre-commit hooks
  • Write focused unit tests and enforce coverage thresholds with pytest

Example use cases

  • Create a new microservice scaffolded with src/package_name and tests/ and CI enforced quality gates
  • Migrate an existing codebase to strict typing and add mypy checks incrementally
  • Implement domain logic as pure functions in core/ and test them in isolation with pytest
  • Validate incoming API payloads with Pydantic and map them to Result-returning core functions
  • Enforce consistent formatting and quick feedback using pre-commit ruff hooks

FAQ

How strict should mypy be for an existing project?

Start with targeted files or directories and gradually enable stricter checks. Use incremental fixes and exclude generated or third-party code until types are introduced.

Can I use this with FastAPI or other frameworks?

Yes. Keep routes and framework code in infra/api.py and keep business rules in core/ to preserve testability and separation of concerns.