| Wrong value, no exception | repr() at input, midpoint, output; the first wrong one is the bug — suspect aliasing, coercion, stale default → debugging.md |
| A traceback you have not seen before | Message → cause table, then the matching chain → debugging.md |
Installed it, still ModuleNotFoundError | Wrong interpreter: check sys.executable, then use python -m pip → packaging.md |
| venvs, lockfiles, uv vs pip vs poetry, publishing | Applications pin everything with hashes; libraries declare ranges → packaging.md |
cannot import name X from partially initialized module | Circular import; switch to import a + a.f() before restructuring → imports.md |
is vs ==, float rounding, money, NaN, string surgery | is only for None/True/False/sentinels; Decimal from strings → types.md |
| Aliasing, copies, ordering, membership cost | [[]]*3 shares one list; x in list is O(n) → collections.md |
| State leaks across calls: defaults, closures, decorators, generators | Defaults evaluate once, at def time → functions.md |
Class design: shared attributes, hashability, MRO, __slots__ | __eq__ without __hash__ makes instances unhashable → classes.md |
| dict vs dataclass vs NamedTuple vs pydantic; validating input | Validate once at the boundary, plain objects inside → data-modeling.md |
| Slow, hanging, or racy: GIL, threads, asyncio, multiprocessing | Pure-Python CPU → processes; I/O → threads or asyncio → concurrency.md |
| Too slow, or too much memory | Profile first, then fix the top line or nothing → performance.md |
| Tests pass but should not; mock patches nothing; async tests skipped | Patch where the name is USED; autospec=True → testing.md |
| mypy or pyright errors, annotating a legacy codebase | Freeze a baseline, then ratchet one package at a time → type-checking.md |
| Formatting, import order, lint rules, pre-commit, style review comments | One formatter owns layout; ruff check for bugs; same versions in hook and CI → linting.md |
UnicodeDecodeError, CSV and JSON traps, atomic writes, temp files | Declare encoding="utf-8"; write temp then os.replace → files.md |
| Timezones, DST, parsing dates, measuring elapsed time | Aware datetimes everywhere; time.monotonic() for durations → datetime.md |
| Calling git/ffmpeg/anything external, hanging or failing silently | run([...], check=True, timeout=…), never shell=True with interpolation → subprocess.md |
| Writing a script or CLI: arguments, exit codes, pipes, signals | sys.exit(main()); stdout for data, stderr for logs → cli.md |
| Nothing appears in the logs, or deciding what to log | Four gates: basicConfig no-op, logger level, handler level, propagation → logging.md |
| Exception design, chaining, retries, timeouts | raise X from exc; exponential backoff with jitter → errors.md |
| Calling an HTTP API: sessions, status codes, redirects, streaming a big body | One client per process; raise_for_status() before .json() → http.md |
A local database, cache, or queue in a file; database is locked | WAL plus an explicit busy timeout; one connection per thread → sqlite.md |
| Untrusted input: pickle, eval, SQL, paths, archives, secrets | Unpickling executes code; parameterize SQL; contain paths → security.md |
| "Is there something in the stdlib for this?" | Counter, deque, bisect, itertools, pathlib, secrets, sqlite3 → stdlib.md |
| Notebook works for you and nobody else; results change between runs | Restart and run all; %pip not !pip; strip outputs before committing → notebooks.md |
| Upgrading Python, choosing a version, a module that vanished | Run the suite with -W error::DeprecationWarning on the OLD version first → versions.md |
| Anything else | Core Rules below, then reproduce with python -I -c '<the five suspect lines>' and re-add one thing at a time |