Skip to main content

loom-python

Python language expertise for idiomatic, production-quality code.

Quellinformationen

Repository
cosmix/loom
Letzte Quellaktivität
24. September 2026 um 17:08
Erkannte Sprache von SKILL.md
Englisch
Sterne
61
Forks
4

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
loom-python
description
Python language expertise for idiomatic, production-quality code.
triggers
["python","py","pip","uv","poetry","virtualenv","pytest","pydantic","fastapi","django","flask","pandas","numpy","dataclass","type hints","asyncio","mypy","ruff","black","sklearn","pytorch","tensorflow","jupyter","pipenv","conda"]
# Python Language Expertise ## Overview Idiomatic, production-grade Python: modern typing, async, packaging, and the web/data/ML ecosystem. Assumes competence — the value is in what is easy to get wrong (interning, mutable defaults, blocking the event loop, GIL, dataclass/Pydantic boundaries), not syntax tutorials. ## Tooling (uv / ruff / mypy) Ruff is a strong default for linting and formatting. `uv` is a capable project and environment manager, but preserve the repository's existing package manager and lockfile unless a migration is explicitly requested. **`uv`** — a single tool for project environments, locking, Python installation, and pip-compatible workflows. Use it for new work or repositories that already use it; do not treat it as an implicit replacement for Poetry or another established workflow. | Task | Command | | --- | --- | | New project | `uv init` (writes `pyproject.toml`) | | Add / remove dep | `uv add httpx` / `uv remove httpx` (updates `pyproject.toml` + `uv.lock`) | | Add dev dep | `uv add --dev pytest` | | Sync env from lock | `uv sync` (creates `.venv`, exact/reproducible) | | Run in project env | `uv run pytest` (auto-syncs first; no manual activate) | | Install a Python | `uv python install 3.12` | | Ephemeral tool | `uvx ruff check` (= `uv tool run`) | | pip shim | `uv pip install ...` (drop-in, no project file) | - In a uv-managed project, use `uv add`/`uv remove` so the manifest and `uv.lock` stay in sync; use `uv sync --frozen` in CI to fail on drift. **`ruff`** — single linter+formatter replacing flake8, isort, pyupgrade, pydocstyle, autoflake, and much of bandit. - `ruff check --fix` (lint+autofix), `ruff format` (near-identical to black, replaces it). - Config lives under `[tool.ruff.lint]` since 0.1 — top-level `select` is deprecated and warns. - Useful rule groups: `E`/`W` pycodestyle, `F` pyflakes, `I` isort, `UP` pyupgrade (keeps typing current), `B` bugbear (catches mutable defaults, `lru_cache` on methods), `SIM`, `S` bandit-security, `ASYNC`, `PL` pylint, `RUF`. Suppress with `# noqa: E501` (never bare `# noqa`). **`mypy --strict`** for type checking; `pyright`/`basedpyright` (what Pylance runs) is faster and stricter on inference. Always scope ignores: `# type: ignore[arg-type]`, never bare `# type: ignore`. ## Type Hints - 3.9+: built-in generics `list[T]`, `dict[K, V]`. 3.10+: `X | Y`, `X | None` (not `Union`/`Optional`). - Import `Callable`/`Sequence`/`Mapping`/`Iterator` from `collections.abc`, not `typing`. - **Accept abstract, return concrete:** take `Iterable`/`Sequence`/`Mapping` (max caller flexibility); return `list`/`dict` (callers know exactly what they get). - `Self` (3.11+) for fluent builders / alternate constructors. `TypedDict` for structured dicts (JSON payloads, `**kwargs`), `Protocol` for structural typing, `ParamSpec` for signature-preserving decorators — see Expert Practices. ```python from collections.abc import Iterable from typing import TypedDict, NotRequired, Self class MovieRow(TypedDict): # dict shape checkable by mypy title: str year: NotRequired[int] # 3.11+: optional key def deduplicate(items: Iterable[str]) -> list[str]: # abstract in, concrete out return list(dict.fromkeys(items)) class Builder: def with_name(self, name: str) -> Self: self._name = name return self ``` ## Async / Await Prefer `asyncio.TaskGroup` (3.11+) over `gather()`: on failure it cancels remaining sibling tasks, awaits them, and re-raises all errors as an `ExceptionGroup` (handled with `except*`). `gather()` does NOT cancel siblings on failure (they "won't be cancelled and will continue to run"), and `return_exceptions=True` silently mixes return values and exception objects into one list. Reserve `gather()` for genuinely independent fire-and-forget results. ⚠ **Never block the event loop.** A coroutine that calls blocking IO (`requests`, sync DB drivers, `open().read()`), `time.sleep()`, or CPU-heavy work freezes *every* task on that loop. Offload: `await asyncio.to_thread(fn, *args)` (3.9+) for blocking IO; `loop.run_in_executor(process_pool, fn)` for CPU work; `await asyncio.sleep()` not `time.sleep()`. See Async Gotchas for the full set. ```python import asyncio async def process_batch(urls: list[str]) -> list[dict]: async with asyncio.TaskGroup() as tg: tasks = [tg.create_task(fetch_data(url)) for url in urls] return [t.result() for t in tasks] # ExceptionGroup raised on any failure try: await process_batch(urls) except* ConnectionError as eg: # subgroup handling for exc in eg.exceptions: logger.error("fetch failed: %s", exc) rows = await asyncio.to_thread(cursor.execute, sql) # sync driver off the loop ``` ## Dataclasses vs Pydantic (the validation boundary) **Choose by trust, not habit.** | | `@dataclass` (stdlib) / `attrs` | Pydantic v2 `BaseModel` | | --- | --- | --- | | Validation | **None** — type hints are not enforced at runtime | Validates + coerces on construction | | Use for | Internal, already-trusted structs; hot paths | Untrusted edges: API bodies, config, external JSON | | Cost | Free | Per-instance validation (Rust core, but non-zero) | Don't trust a dataclass with external input (it will happily hold `age="banana"`); don't pay Pydantic's validation cost for internal structs constructed in a tight loop. For perf-critical validated decode, `msgspec` is the fastest option. ```python from dataclasses import dataclass, field from pydantic import BaseModel, Field, field_validator @dataclass(slots=True) # internal; slots=True saves memory + blocks typos class Config: host: str port: int = 8080 tags: list[str] = field(default_factory=list) # NEVER tags: list = [] class UserCreate(BaseModel): # untrusted edge email: str = Field(min_length=5) name: str = Field(max_length=100) @field_validator("email") @classmethod def norm_email(cls, v: str) -> str: if "@" not in v: raise ValueError("invalid email") # NEVER assert (-O strips it) return v.lower() ``` ⚠ Plain `@dataclass` sets `__hash__ = None` (unhashable — cannot be a set member / dict key). Use `frozen=True` for a hashable value object. `slots=True`/`kw_only=True` are 3.10+. ## Packaging (`pyproject.toml`) ```toml [project] name = "mypackage" version = "0.1.0" requires-python = ">=3.11" dependencies = ["httpx>=0.25", "pydantic>=2.0"] [dependency-groups] # PEP 735; `uv add --dev` targets this dev = ["pytest>=8", "pytest-asyncio>=0.23", "mypy>=1.9", "ruff>=0.4"] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.ruff.lint] # MUST be nested since ruff 0.1; top-level is deprecated select = ["E4", "E7", "E9", "F", "I", "UP", "B", "SIM"] [tool.mypy] strict = true [tool.pytest.ini_options] asyncio_mode = "auto" # else async tests are silently UNCOLLECTED (default "strict") ``` ## Web Frameworks ### FastAPI Type hints drive validation, serialization, and OpenAPI. Use `Annotated[..., Depends(...)]` for injection; `response_model` to shape/filter output. ```python from typing import Annotated from fastapi import FastAPI, Depends, HTTPException, status app = FastAPI() async def get_db() -> AsyncIterator[AsyncSession]: # yield-dependency: cleanup after response async with AsyncSession(engine) as s: yield s @app.post("/users/", response_model=User, status_code=status.HTTP_201_CREATED) async def create_user(user: UserCreate, db: Annotated[AsyncSession, Depends(get_db)]) -> User: return await UserService(db).create(user) ``` ⚠ Gotchas: - **`def` vs `async def` path ops:** a `def` handler runs in FastAPI's threadpool (blocking libs OK). An `async def` handler runs on the event loop — any blocking call inside stalls *all* requests. Pick `def` for sync ORMs, `async def` only with async libs. - `response_model` strips fields not on the model — the primary guard against leaking hashes/tokens; don't return the raw ORM object. - `Depends` results are cached per-request; use `BackgroundTasks` for post-response work. ### Django ```python from django.db import models, transaction class User(models.Model): email = models.EmailField(unique=True) class Meta: indexes = [models.Index(fields=["email"])] with transaction.atomic(): # rolls back on exception user = User.objects.create(email=e) Profile.objects.create(user=user) ``` ⚠ QuerySets are lazy (evaluated on iteration/`len`/slice). Kill N+1 with `.select_related()` (FK/1:1 join) and `.prefetch_related()` (M2M/reverse). Batch with `bulk_create`/`bulk_update`. `.only()`/`.defer()` to trim columns. ## Data & ML (dense reference) The APIs are well-known; these are the correctness/perf traps that bite in review. **pandas** - **Vectorize.** `df["c"] = df["a"] * df["b"]`, `.map`/`np.where`/masks — never `iterrows`/`itertuples`/row-wise `apply` (Python-level loop, 10–100× slower). - **Assign via `.loc`**, never chained (`df[m]["c"] = ...`) — raises `SettingWithCopyWarning` and may no-op. pandas 3.0 makes Copy-on-Write the default, ending the ambiguity. - Memory: `read_csv(usecols=, dtype=)`; nullable `Int64`/`boolean` for NA-bearing ints; `category` dtype for low-cardinality strings; chunk with `read_csv(chunksize=)` or move to `polars`/`dask`/`pyarrow` at scale. - `merge` silently fans out on non-unique keys (row explosion) — validate with `merge(..., validate="one_to_many")`. **numpy** - Slices are **views**, not copies — `b = a[1:3]; b[0] = 9` mutates `a`. `.copy()` to break aliasing. - Broadcasting + ufuncs over Python loops; watch silent `int` overflow on fixed-width dtypes. - Use `rng = np.random.default_rng(seed)` (Generator API), not legacy global `np.random.seed`/`np.random.rand`. **scikit-learn** - Wrap preprocessing + estimator in a `Pipeline` so scalers/encoders fit on the **train fold only** inside CV — fitting a scaler on the full dataset before splitting leaks test statistics and inflates scores. `ColumnTransformer` for heterogeneous columns. Set `random_state` for reproducibility. **PyTorch** - Training step order: `optimizer.zero_grad()` → forward → `loss.backward()` → `optimizer.step()`. Forgetting `zero_grad` accumulates gradients. - Eval: `model.eval()` (disables dropout, freezes BatchNorm running stats) **and** `torch.inference_mode()` / `torch.no_grad()` (no autograd graph). Both are needed; they do different things. - Accumulate metrics as `loss.item()`/`.detach()` — keeping tensors alive retains the whole graph → memory blowup. `DataLoader(num_workers>0, pin_memory=True)` for GPU throughput; move batches with `.to(device, non_blocking=True)`. ## Testing with pytest Async fixtures must use `@pytest_asyncio.fixture` (a plain `@pytest.fixture` on `async def` yields an un-awaited coroutine under strict mode). Session/module-scoped async fixtures need `loop_scope=`; the `event_loop` fixture was removed in pytest-asyncio 1.0. Built-in fixtures worth reaching for: - `tmp_path` (per-test `pathlib.Path` dir), `monkeypatch` (`setattr`/`setenv`/`chdir`/`syspath_prepend`, all auto-undone at teardown — never patch globals by hand), `capsys`/`caplog`, `pytest.raises(ValueError, match="regex")`. - `conftest.py` fixtures are auto-discovered by every test in that directory tree — no import. Fixture `scope=` is `function` (default) / `class` / `module` / `session`; `autouse=True` applies without being requested. - Run tips: `-x` stop on first fail, `-k EXPR` select, `--lf` last-failed, `-q`, parametrize `ids=` for readable names. ```python import pytest, pytest_asyncio @pytest_asyncio.fixture async def db_session() -> AsyncIterator[AsyncSession]: async with AsyncSession(engine) as s: yield s @pytest.mark.parametrize("email,valid", [("a@b.com", True), ("bad", False)]) def test_email(email: str, valid: bool) -> None: if valid: User(email=email) else: with pytest.raises(ValueError): User(email=email) def test_env(monkeypatch): monkeypatch.setenv("API_KEY", "test") # auto-restored after the test assert load_config().key == "test" ``` ## Loom Test Runner Adapter
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen