"""Python cheatsheet - modern Python patterns and idioms."""

# @mode: code
# @section: types
# @size: large
# @priority: 12
# @title: Python: Type Hints

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

ages: dict[str, int] = {"Alice": 30, "Bob": 25}
items: list[str | int] = [1, "two", 3]
coords: tuple[float, float] = (10.5, 20.3)
maybe: str | None = None
result: int | str = process_data()

from typing import TypeVar, Protocol
T = TypeVar('T')
def first(items: list[T]) -> T | None:
    return items[0] if items else None

class Drawable(Protocol):  # Structural subtyping
    def draw(self) -> None: ...

UserId = int  # Type alias
UserDict = dict[UserId, str]

# @section: dataclass
# @size: large
# @priority: 11
# @title: Python: Data Classes

from dataclasses import dataclass, field
from typing import ClassVar

@dataclass
class User:
    name: str
    email: str
    age: int = 0
    tags: list[str] = field(default_factory=list)
    _id_counter: ClassVar[int] = 0
    
    def __post_init__(self):
        self.email = self.email.lower()
        User._id_counter += 1
        self.id = User._id_counter
    
    @property
    def display_name(self) -> str:
        return f"{self.name} ({self.age})"

user = User("Alice", "ALICE@EXAMPLE.COM", age=30)
print(user.display_name)  # Alice (30)

# @section: patterns
# @size: large
# @priority: 7
# @title: Python: Modern Patterns

# Pattern matching (3.10+)
match command:
    case ["move", x, y]: player.move(int(x), int(y))
    case ["attack", target]: player.attack(target)
    case {"action": "quit"}: return False
    case _: print("Unknown command")

# Walrus operator (3.8+)
while (line := input()) != "quit":
    process(line)
if (n := len(items)) > 10:
    print(f"Too many items: {n}")

# F-string debugging (3.8+)
value = 42
print(f"{value=}")      # value=42
print(f"{value=:.2f}")  # value=42.00

# Dictionary merge (3.9+) and unpacking
defaults = {"color": "blue", "size": 10}
merged = defaults | {"size": 20, "weight": 5}
first, *middle, last = range(10)
combined = {**dict1, **dict2, "key": "value"}

# @section: async
# @size: large
# @priority: 5
# @title: Python: Async/Await

import asyncio
from typing import AsyncIterator; from contextlib import asynccontextmanager

async def fetch_data(url: str) -> dict:
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            return await resp.json()

async def process_urls(urls: list[str]) -> list[dict]:
    tasks = [fetch_data(url) for url in urls]
    return await asyncio.gather(*tasks)

async def paginate(url: str) -> AsyncIterator[dict]:
    page = 1
    while data := await fetch_data(f"{url}?page={page}"):
        if not data["items"]: break
        yield from data["items"]
        page += 1

@asynccontextmanager
async def managed_resource():
    resource = await acquire_resource()
    try: yield resource
    finally: await release_resource(resource)