---
name: ecc-python-patterns
description: Pythonic idioms, PEP 8 standards, type hints, and best practices for building robust, efficient, and maintainable Python code.
---

# Python Patterns

Pythonic idioms, PEP 8 standards, type hints, and best practices.

## Type Hints

Always use type hints for function signatures:

```python
def process_items(items: list[str], threshold: float = 0.5) -> dict[str, int]:
    """Process a list of items with a threshold."""
    result: dict[str, int] = {}
    for item in items:
        if len(item) > threshold:
            result[item] = len(item)
    return result
```

### Use `Self` return type for class methods returning self:
```python
from typing import Self

class Builder:
    def add_item(self, item: str) -> Self:
        self.items.append(item)
        return self
```

### Prefer `|` for Optional/Union:
```python
def find_user(user_id: int | None = None) -> User | None: ...
```

## Project Structure

```
project/
├── src/
│   └── package_name/
│       ├── __init__.py
│       ├── main.py
│       └── utils.py
├── tests/
│   ├── __init__.py
│   └── test_main.py
├── pyproject.toml
├── README.md
└── .gitignore
```

## Error Handling

```python
class AppError(Exception):
    """Base exception for the application."""

def process() -> None:
    try:
        result = risky_operation()
    except SpecificError as e:
        raise AppError(f"Failed to process: {e}") from e
```

## List/Dict Comprehensions

```python
# ✅ Good
squares = [x**2 for x in range(10) if x % 2 == 0]
lookup = {user.id: user for user in users}

# ❌ Avoid deeply nested comprehensions
```

## Context Managers

```python
from contextlib import contextmanager

@contextmanager
def managed_resource():
    resource = acquire()
    try:
        yield resource
    finally:
        release(resource)
```

## Async Patterns

```python
import asyncio
from asyncio import TaskGroup

async def process_all(items: list[str]) -> list[Result]:
    async with TaskGroup() as tg:
        tasks = [tg.create_task(process(item)) for item in items]
    return [t.result() for t in tasks]
```

## Testing

```python
import pytest
from collections.abc import Generator
from unittest.mock import Mock, patch

@pytest.fixture
def mock_db() -> Generator[Mock, None, None]:
    with patch("myapp.database") as mock:
        yield mock

def test_create_user(mock_db: Mock) -> None:
    result = create_user("test@example.com")
    assert result.email == "test@example.com"
```
