Python

Writes, debugs, and reviews Python code — runtime traps, packaging, typing, async, tests, performance. Use when Python raises or misbehaves: ModuleNotFoundError, circular imports, …

Iván

@ivangdavila

What This Skill Does

Debugs and reviews Python code across runtime errors, packaging conflicts, concurrency issues, typing, testing, and performance problems. Covers common traps like ModuleNotFoundError, circular imports, mutable defaults, GIL deadlocks, and silent wrong answers.

Replaces manually tracing Python bugs through stack overflow and trial-and-error by providing structured debugging chains for every common failure mode.

When to Use It

  • Debug a traceback whose cause is not obvious within ten seconds
  • Resolve dependency conflicts between pip, uv, poetry, or lockfiles
  • Fix a pytest suite that passes but should not, or mocks patching the wrong module
  • Clear mypy or pyright type errors in a codebase
  • Diagnose a slow script that eats RAM or gets OOM-killed
  • Investigate a hang or deadlock in threads, asyncio, or multiprocessing code

Install

$ openclaw skills install @ivangdavila/py

User preferences live in ~/Clawic/data/py/config.yaml (see Configuration); nothing else is stored on the user's machine. If you have data at an old location (~/py/ or ~/clawic/py/), move it to ~/Clawic/data/py/.

When To Use

  • Writing or reviewing Python — scan Core Rules and Output Gates before committing
  • Debugging wrong results without exceptions: state leaking across calls, aliased mutations, silent coercion
  • Any traceback whose cause is not obvious in ten seconds, plus hangs, memory growth, and segfaults
  • Environment and dependency work: venvs, lockfiles, editable installs, publishing, version upgrades
  • Choosing a concurrency model, a data model, or a typing strategy, and living with the consequences
  • Hardening a script into a tool: arguments, exit codes, logging, timeouts, retries, untrusted input
  • Not for library-specific issues — pandas, numpy, django, fastapi, flask have their own skills

Quick Reference

SituationPlay
Wrong value, no exceptionrepr() at input, midpoint, output; the first wrong one is the bug — suspect aliasing, coercion, stale default → debugging.md
A traceback you have not seen beforeMessage → cause table, then the matching chain → debugging.md
Installed it, still ModuleNotFoundErrorWrong interpreter: check sys.executable, then use python -m pippackaging.md
venvs, lockfiles, uv vs pip vs poetry, publishingApplications pin everything with hashes; libraries declare ranges → packaging.md
cannot import name X from partially initialized moduleCircular import; switch to import a + a.f() before restructuring → imports.md
is vs ==, float rounding, money, NaN, string surgeryis 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, generatorsDefaults 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 inputValidate once at the boundary, plain objects inside → data-modeling.md
Slow, hanging, or racy: GIL, threads, asyncio, multiprocessingPure-Python CPU → processes; I/O → threads or asyncio → concurrency.md
Too slow, or too much memoryProfile first, then fix the top line or nothing → performance.md
Tests pass but should not; mock patches nothing; async tests skippedPatch where the name is USED; autospec=Truetesting.md
mypy or pyright errors, annotating a legacy codebaseFreeze a baseline, then ratchet one package at a time → type-checking.md
Formatting, import order, lint rules, pre-commit, style review commentsOne formatter owns layout; ruff check for bugs; same versions in hook and CI → linting.md
UnicodeDecodeError, CSV and JSON traps, atomic writes, temp filesDeclare encoding="utf-8"; write temp then os.replacefiles.md
Timezones, DST, parsing dates, measuring elapsed timeAware datetimes everywhere; time.monotonic() for durations → datetime.md
Calling git/ffmpeg/anything external, hanging or failing silentlyrun([...], check=True, timeout=…), never shell=True with interpolation → subprocess.md
Writing a script or CLI: arguments, exit codes, pipes, signalssys.exit(main()); stdout for data, stderr for logs → cli.md
Nothing appears in the logs, or deciding what to logFour gates: basicConfig no-op, logger level, handler level, propagation → logging.md
Exception design, chaining, retries, timeoutsraise X from exc; exponential backoff with jitter → errors.md
Calling an HTTP API: sessions, status codes, redirects, streaming a big bodyOne client per process; raise_for_status() before .json()http.md
A local database, cache, or queue in a file; database is lockedWAL plus an explicit busy timeout; one connection per thread → sqlite.md
Untrusted input: pickle, eval, SQL, paths, archives, secretsUnpickling 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 runsRestart and run all; %pip not !pip; strip outputs before committing → notebooks.md
Upgrading Python, choosing a version, a module that vanishedRun the suite with -W error::DeprecationWarning on the OLD version first → versions.md
Anything elseCore Rules below, then reproduce with python -I -c '<the five suspect lines>' and re-add one thing at a time

Each file above is one sub-job and is self-contained: read SKILL.md by default, open exactly one guide when the situation matches.

Core Rules

  1. No mutable defaults: def f(xs=None) then if xs is None: xs = []. Never xs = xs or [] — a caller passing an empty list to be filled gets a fresh list instead; their reference stays empty.
  2. is only for None, True, False, and sentinel objects; == for everything else. Interning makes is on ints and strings pass in tests and fail in production (types.md).
  3. Never mutate the collection you iterate — dicts raise RuntimeError, lists silently skip elements. Iterate a copy (for x in list(xs)) or collect changes and apply after.
  4. Match concurrency to workload: pure-Python CPU → multiprocessing; I/O → threads or asyncio; threads never speed up pure-Python CPU work, because the GIL serializes bytecode (concurrency.md).
  5. except Exception:, never bare except: — bare also catches KeyboardInterrupt and SystemExit, making the process unkillable. Re-raise with bare raise, keeping the original traceback (errors.md).
  6. Files, locks, sockets, connections: always with. CPython's refcounting closes leaked handles by accident; exceptions and other interpreters (PyPy) expose the leak.
  7. Money and exact decimals: decimal.Decimal('1.10') from strings — Decimal(1.1) imports the float error it was meant to avoid. Float comparisons via math.isclose (types.md).
  8. Declare encoding='utf-8' at every I/O boundary — the default follows the platform locale until UTF-8 becomes the default (PEP 686, python >=3.15), so the code breaks first on Windows and in lean containers (files.md).
  9. Guard entry points with if __name__ == "__main__": — top-level code runs on every import and again in every multiprocessing spawn child (imports.md).

Version Floors

The syntax and stdlib floors that shape everyday code. Individual guides carry additional floors — mostly changed defaults and new keyword arguments — inline in this same python >=X.Y form, next to the instruction each one gates. Support windows, removals, and the upgrade procedure: versions.md.

FeatureNeeds
Guaranteed dict insertion order, dataclasses, breakpoint(), from __future__ import annotations, sys.stdout.reconfigurepython >=3.7
Walrus :=, positional-only /, functools.cached_property, TypedDict/Protocol/Literal/Final, basicConfig(force=), self-documenting f"{value=}"python >=3.8
Builtin generics list[int], d1 | d2 (dict merge), zoneinfo, removeprefix/removesuffix, functools.cache, asyncio.to_thread, importlib.resources.files, Path.is_relative_to, graphlib, argparse.BooleanOptionalAction, executor.shutdown(cancel_futures=)python >=3.9
X | Y unions, match, zip(strict=True), dataclass slots=/kw_only=, itertools.pairwise, ParamSpec, EncodingWarning and -X warn_default_encodingpython >=3.10
ExceptionGroup/except*, asyncio.TaskGroup, tomllib, Self, StrEnum, exc.add_note(), contextlib.chdir, full-ISO fromisoformat, NotRequiredpython >=3.11
PEP 695 generics (def f[T]()), @override, itertools.batched, tarfile(filter="data")python >=3.12
Experimental free-threaded build; PEP 594 module removals (cgi, telnetlib, …)python >=3.13
forkserver as the Linux multiprocessing default, lazy annotations (PEP 649)python >=3.14
UTF-8 as the default text encoding everywhere (PEP 686), retiring the locale defaultpython >=3.15

Output Gates

Before delivering Python code, check:

  • No mutable default argument, and no xs or [] where an empty argument is meaningful
  • Every text open, subprocess, and decode boundary declares encoding="utf-8"; every file, lock, socket, and connection is acquired in a with
  • Every network, subprocess, lock, and queue call has a timeout
  • Exceptions: narrowest class caught, chained with from, nothing swallowed without a log line or a written reason
  • Logging through a module logger with lazy %s arguments and no secrets
  • Syntax and stdlib stay within min_python; annotations match type_strictness
  • Nothing user-supplied reaches eval, pickle, a shell string, an SQL string, or a path join without containment
  • The new test was seen failing without the fix in place — change_workflow decides when that run happens, never whether it happened

Configuration

User-dependent variables. Defaults apply until the user states a preference; store them in ~/Clawic/data/py/config.yaml. Never interview the user — record a preference the moment it is stated.

VariableTypeDefaultEffect
min_python3.9 | 3.10 | 3.11 | 3.12 | 3.13 | 3.143.11Gates which Version Floors features may be emitted unguarded, and which fallback appears instead
package_managerpip | uv | poetry | pdm | condapipChooses the install, lock, and venv commands in every example (packaging.md)
type_strictnessnone | gradual | strictgradualHow far annotations go: none skips hints, gradual annotates public boundaries, strict turns on disallow_untyped_defs (type-checking.md)
test_runnerpytest | unittestpytestShape of emitted tests, fixtures, and mocking guidance (testing.md)
target_oslinux | macos | windows | crosscrossPath, encoding, and multiprocessing start-method assumptions; cross flags all three
src_layoutsrc | flatsrcProject scaffolding, editable-install and import advice (packaging.md)
line_lengthnumber (79-120)88Formatting of emitted code and the ruff/black config value (linting.md)
change_workflowtest-first | fix-firsttest-firstOrder of the work: test-first writes the failing test before the fix, fix-first patches first and backfills the test against the reverted change (testing.md)

Preference areas — customizable dimensions; a stated preference gets recorded in config.yaml and applied:

  • Tooling: formatter and linter (ruff vs black+flake8), checker (mypy vs pyright), task runner, notebook vs module workflow — affects every emitted config block (linting.md)
  • Conventions: docstring style (Google/NumPy/reST), naming, module granularity, error-message phrasing — affects generated code and review comments
  • Platform: deployment target (container, serverless, desktop, embedded), CPU architecture, private package index, CI provider — affects packaging.md and versions.md guidance
  • Safety posture: how aggressively to flag pickle, eval, shell=True, missing timeouts and unpinned dependencies — affects security.md and cli.md
  • Dependencies: stdlib-only constraints, banned or mandated libraries (requests vs httpx, pydantic vs dataclasses), tolerance for new transitive dependencies — affects every recommendation
  • Output format: whole file vs minimal diff, how much explanation, whether tests accompany every change — affects the shape of the answer, never the correctness rules
  • Work order: which gates run before a change is proposed rather than after — checker and suite green first, a plan approved before editing, a --dry-run pass on destructive scripts, a profile before any optimization — affects the sequence of every task, never the correctness rules

Traps

TrapWhy it failsDo instead
Bare pip installInstalls into whatever interpreter is first on PATH — the "installed it, still ModuleNotFoundError" looppython -m pip install inside an activated venv (packaging.md)
except Exception: passTurns a crash into a wrong answer three layers awayLog with logger.exception, or write down why this failure is expected (errors.md)
assert user.is_admin as a checkpython -O removes every assert, including that oneRaise explicitly (security.md)
time.time() to measure a durationNTP can step the clock backwards mid-measurement and produce negative elapsed timetime.monotonic() (datetime.md)
datetime.utcnow()Returns a NAIVE datetime holding UTC: compares wrong against aware values, deprecated in python >=3.12datetime.now(timezone.utc) (datetime.md)
f-strings inside logging callsFormats even when the level is disabled, and every line becomes a unique string to the aggregatorlog.info("user %s", uid) (logging.md)
sys.path.append(...) to fix an importWorks from one entry point, breaks under pytest, packaging, and any other cwdEditable install with a src/ layout (imports.md)
shell=True with an interpolated valueCommand injection — and /bin/sh is not bashArgument list; shlex.quote if a shell is unavoidable (subprocess.md)
verify=False to silence an SSL errorDisables peer authentication for every request in the processInstall the CA bundle (security.md)
pickle across a process, user, or network boundaryUnpickling executes attacker-controlled code before you can inspect itJSON/msgpack, or an HMAC-signed payload (security.md)
raise e when re-raisingAppends the current line and hides where the exception really came fromBare raise (errors.md)
Type hints treated as validationNever enforced at runtime: def f(x: int) happily takes a stringChecker in CI plus runtime validation at the boundary (type-checking.md)
x in list or list.pop(0) inside a loopO(n) per operation makes the loop O(n²)set for membership, deque for both ends (collections.md)

Where Experts Disagree

  • asyncio vs threads. asyncio is not "faster Python": for moderate I/O concurrency, ThreadPoolExecutor matches it with far less ceremony. Boundary: high connection counts AND an async-native stack end to end → asyncio; one blocking driver anywhere in the chain forfeits the benefit (concurrency.md).
  • How much typing. The strict school annotates everything and treats Any as a defect; the pragmatic school types boundaries and lets internals be inferred. Boundary: libraries and cross-team APIs → annotate fully; single-owner scripts and glue → boundaries only. Either way, one checker in CI (type-checking.md).
  • Packaging tooling. venv + pip exists on every machine and surprises nobody; uv/poetry/hatch are faster and manage more. Boundary: a project with a real dependency problem, many contributors, or several interpreters justifies the tool; one that installs three libraries does not (packaging.md).
  • EAFP vs LBYL. Python's culture prefers try/except over pre-checking, and it is genuinely more correct under concurrency — the file can disappear between exists() and open(). Boundary: LBYL when the operation is expensive or irreversible, EAFP when the failure is cheap and the race is real.

Related Skills

More Clawic skills, get them at https://clawic.com/skills/py (install if the user confirms):

  • pandas — DataFrame-specific traps and idioms
  • fastapi — async web APIs in Python
  • django — Django ORM and framework patterns
  • profiling — when the question is "why is it slow", measure first
  • debugging — language-agnostic fault isolation (bisection, hypothesis discipline, minimal repro); the Python-specific version of that job is this skill's debugging.md

Feedback

Part of Clawic, the verified skill library. Get this skill: https://clawic.com/skills/py.

Top skills in this category