home / skills / martinffx / claude-code-atelier / modern-python

modern-python skill

/plugins/atelier-python/skills/modern-python

This skill helps you write modern Python code with 3.10+ features, including typing, pattern matching, and async patterns for robust, scalable apps.

This is most likely a fork of the atelier-python-modern-python skill from martinffx
npx playbooks add skill martinffx/claude-code-atelier --skill modern-python

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

Files (4)
SKILL.md
3.8 KB
---
name: python:modern-python
description: Modern Python language features and typing patterns. Use when writing type hints, using generics, implementing pattern matching, working with async/await, or leveraging Python 3.10+ features.
user-invocable: false
---

# Modern Python Features

Modern Python 3.10+ language features, type hints, and patterns.

## Type Hints

### Basic Types

```python
def greet(name: str) -> str:
    return f"Hello {name}"

age: int = 25
prices: list[float] = [9.99, 19.99]
mapping: dict[str, int] = {"a": 1}
```

### Union Types (Python 3.10+)

```python
# Modern syntax
def process(value: int | str) -> bool:
    ...

# Optional
def get_user(id: int) -> User | None:
    ...

# Multiple types
Result = int | str | bool
```

### Generic Types

```python
from typing import TypeVar, Generic

T = TypeVar("T")

class Repository(Generic[T]):
    def get(self, id: int) -> T | None:
        ...

    def save(self, entity: T) -> T:
        ...

user_repo = Repository[User]()
```

### Protocol (Structural Typing)

```python
from typing import Protocol

class Drawable(Protocol):
    """Structural type - any class with draw()"""
    def draw(self) -> None:
        ...

def render(obj: Drawable) -> None:
    """Works with any object that has draw()"""
    obj.draw()
```

## Pattern Matching (Python 3.10+)

### Basic Matching

```python
def handle_command(command: str):
    match command:
        case "start":
            start_process()
        case "stop":
            stop_process()
        case "status":
            return get_status()
        case _:
            raise ValueError("Unknown command")
```

### Matching with Values

```python
def handle_http_status(status: int):
    match status:
        case 200:
            return "OK"
        case 404:
            return "Not Found"
        case 500 | 502 | 503:
            return "Server Error"
        case _:
            return "Unknown"
```

### Structural Pattern Matching

```python
def process_point(point):
    match point:
        case (0, 0):
            return "Origin"
        case (x, 0):
            return f"On X-axis at {x}"
        case (0, y):
            return f"On Y-axis at {y}"
        case (x, y):
            return f"At ({x}, {y})"
```

## Async/Await

### Async Functions

```python
async def fetch_data(url: str) -> dict:
    """Async function"""
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        return response.json()

# Call async function
data = await fetch_data("https://api.example.com")
```

### Concurrent Execution

```python
import asyncio

async def process_all(urls: list[str]):
    """Process multiple URLs concurrently"""
    tasks = [fetch_data(url) for url in urls]
    results = await asyncio.gather(*tasks)
    return results
```

### Async Context Managers

```python
class AsyncDatabase:
    async def __aenter__(self):
        await self.connect()
        return self

    async def __aexit__(self, *args):
        await self.disconnect()

async with AsyncDatabase() as db:
    await db.query(...)
```

## Dataclasses and attrs

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class Point:
    x: float
    y: float

    def distance_from_origin(self) -> float:
        return (self.x ** 2 + self.y ** 2) ** 0.5
```

## Walrus Operator

```python
# Assign and use in one expression
if (n := len(items)) > 10:
    print(f"Too many items: {n}")

# In comprehensions
results = [y for x in items if (y := process(x)) is not None]
```

## Modern String Formatting

```python
name = "Alice"
age = 30

# f-strings
message = f"Hello {name}, you are {age} years old"

# f-strings with expressions
message = f"In 5 years you'll be {age + 5}"

# Debug f-strings (Python 3.8+)
print(f"{name=}")  # name='Alice'
```

See references/ for advanced typing, async patterns, and pattern matching.

Overview

This skill surfaces modern Python 3.10+ language features, typing patterns, and idiomatic constructs for cleaner, safer code. It focuses on practical usage of type hints, generics, structural typing, pattern matching, async/await, dataclasses, and recent syntax additions like the walrus operator and f-strings. Use it to make code more expressive, easier to type-check, and more concurrent-ready.

How this skill works

The skill inspects code patterns and suggests modern replacements and idioms: concise type hints (PEP 585/604), TypeVar-based generics, Protocols for structural typing, and pattern matching for control flow. It recommends async patterns (async/await, asyncio.gather, async context managers), dataclass usage for immutability and convenience, and newer syntax such as the walrus operator and debug f-strings. Suggestions emphasize readability, static-checker compatibility, and runtime safety.

When to use it

  • When adding or improving type annotations across a codebase to enable mypy/pyright checks.
  • When designing generic APIs, repositories, or libraries that should be type-safe and reusable.
  • When refactoring complex if/elif chains into structural pattern matching for clarity.
  • When converting blocking I/O to asynchronous code using async/await and concurrency patterns.
  • When modeling domain objects with immutable or convenient representations (dataclasses/attrs).

Best practices

  • Prefer builtin collection generics (list[int], dict[str, T]) from PEP 585 over typing.List in new code.
  • Use union types (A | B) and Optional[T] (T | None) for clearer signatures and better type-checker output.
  • Favor Protocols for duck-typed interfaces instead of nominal inheritance when only behavior matters.
  • Use pattern matching for expressive dispatch, but keep patterns simple and well-documented for maintainability.
  • Adopt async/await with structured concurrency (asyncio.gather, task groups) and proper cancellation handling.
  • Use frozen dataclasses for value objects and avoid mutable defaults; prefer attrs when you need advanced validators.

Example use cases

  • Annotate public API functions and repository interfaces with generics so callers get precise types.
  • Replace nested if/else chains that inspect tuple/list shapes with structural pattern matching for clarity.
  • Implement concurrent HTTP fetchers using asyncio.gather and async context managers for pooled clients.
  • Define drawable Protocols so unrelated classes can be passed to render functions without inheritance.
  • Model configuration or coordinate types as frozen dataclasses to guarantee immutability and easy comparisons.

FAQ

When should I use Protocol instead of an abstract base class?

Use Protocol when you want structural typing (any object with required methods fits) and avoid coupling to a common base; use ABCs when you need enforced inheritance or shared implementation.

Is pattern matching always better than if/elif chains?

Not always. Pattern matching shines for shape-based dispatch and readable branches, but for very simple boolean checks or when patterns become complex, traditional control flow may be clearer.