Skill: ivangdavila/py (version 1.0.1)

--- name: Python slug: python version: 1.0.1 description: Write reliable Python avoiding mutable defaults, import traps, and common runtime surprises. metadata: {"clawdbot":{"emoji":"🐍","requires":{"bins":["python3"]},"os":["linux","darwin","win32"]}} --- ## Quick Reference | Topic | File | |-------|------| | Dynamic typing, type hints, duck typing | `types.md` | | List/dict/set gotchas, comprehensions | `collections.md` | | Args/kwargs, closures, decorators, generators | `functions.md` | | Inheritance, descriptors, metaclasses | `classes.md` | | GIL, threading, asyncio, multiprocessing | `concurrency.md` | | Circular imports, packages, __init__.py | `imports.md` | | Pytest, mocking, fixtures | `testing.md` | ## Critical Rules - `def f(items=[])` shares list across all calls — use `items=None` then `items = items or []` - `is` checks identity, `==` checks equality — `"a" * 100 is "a" * 100` may be False - Modifying list while iterating skips elements — iterate over copy: `for x in list(items):` - GIL prevents true parallel Python threads — use multiprocessing for CPU-bound - Bare `except:` catches `SystemExit` and `KeyboardInterrupt` — use `except Exception:` - `UnboundLocalError` when assigning to outer scope variable — use `nonlocal` or `global` - `open()` without context manager leaks handles — always use `with open():` - Circular imports fail silently or partially — import inside function to break cycle - `0.1 + 0.2 != 0.3` — floating point, use `decimal.Decimal` for money - Generator exhausted after one iteration — can't reuse, recreate or use `itertools.tee` - Class attributes with mutables shared across instances — define in `__init__` instead - `__init__` is not constructor — `__new__` creates instance, `__init__` initializes - Default encoding is platform-dependent — always specify `encoding='utf-8'`

path: classes.md
content: # Class Traps

- Class attribute shared — `class A: items = []` shared by all instances
- `__init__` returns None — can't `return value`, use `__new__` for singletons
- Mutable class default — same as function default trap, use `None` + init
- `super()` in multiple inheritance — follows MRO, not just parent
- `__slots__` breaks `__dict__` — can't add arbitrary attributes
- `@property` setter needs getter — define getter first with `@property`
- `__eq__` without `__hash__` — makes class unhashable, can't use in sets
- Private `__name` mangling — `_ClassName__name`, not truly private


path: collections.md
content: # Collection Traps

- Modify list while iterating — skips elements, iterate over copy `list(x)`
- Dict keys mutated — if key object changes hash, dict lookup fails silently
- `dict.get()` returns None — can't distinguish missing key from None value
- List slice creates copy — `a[:]` is new list, but shallow (nested objects shared)
- `in` on list is O(n) — use `set` for membership testing
- Empty list/dict is falsy — `if not my_list:` catches empty AND None
- `+=` on tuple fails — tuples immutable, but `a = a + (x,)` works
- `list * n` shares refs — `[[]] * 3` creates 3 refs to SAME inner list


path: concurrency.md
content: # Concurrency Traps

## GIL
- Only one thread executes Python — CPU-bound = no speedup from threads
- I/O releases GIL — threads help for I/O-bound (network, disk)
- Use `multiprocessing` for CPU — separate processes, no GIL

## Threading
- Race conditions — shared mutable state without locks corrupts data
- Daemon threads killed abruptly — no cleanup on main thread exit
- `threading.local()` for thread-local — but watch for leaks in pools

## Asyncio
- `await` in sync function fails — must be in `async def`
- Blocking call in async — `time.sleep()` blocks event loop, use `asyncio.sleep()`
- Forgotten `await` — coroutine never runs, just returns coroutine object
- `asyncio.run()` in running loop — fails, use `await` or `create_task`


path: functions.md
content: # Function Traps

- Mutable default arg — evaluated ONCE at definition, shared across calls
- `*args` after keyword-only — `def f(*, name)` forces keyword
- Late binding closures — `lambda: i` captures variable, not value
- `return` in finally — overrides return/exception from try block
- Generator exhausted — can only iterate once, `list(gen)` to reuse
- `yield` makes function generator — returns iterator, not value
- `@decorator` without parens — `@deco` vs `@deco()` are different
- `global` vs `nonlocal` — `global` for module, `nonlocal` for enclosing


path: imports.md
content: # Import Traps

- Circular import — partially initialized module, `ImportError` or `None` attrs
- `from x import *` — pollutes namespace, shadows builtins
- Relative import outside package — `from . import x` fails in script
- `__init__.py` side effects — runs on ANY import from package
- Import caching — `importlib.reload()` needed for changes, but breaks refs
- Module runs on import — top-level code executes, use `if __name__ == "__main__"`
- `sys.path` modification — affects ALL imports, prefer proper packaging


path: testing.md
content: # Testing Traps

- `mock.patch` path — patch where imported, not where defined
- Mock not reset — `mock.reset_mock()` between tests or use `autospec`
- `assert` stripped in `-O` — never use `assert` for validation
- Fixture scope confusion — `function` runs each test, `module` once
- `pytest.raises` without match — catches wrong exception silently
- Async test needs plugin — `pytest-asyncio` + `@pytest.mark.asyncio`
- Mocking `datetime.now()` — mock `datetime` module, not method


path: types.md
content: # Type Traps

- Mutable default args — `def f(x=[])` shares list across calls, use `None`
- `is` vs `==` — `is` checks identity, `==` checks value; `a is b` fails for equal objects
- Integer caching — `256 is 256` is True, `257 is 257` may be False
- Float comparison — `0.1 + 0.2 != 0.3`, use `math.isclose()`
- `None` check — use `is None`, not `== None`
- Type hints don't enforce — `def f(x: int)` accepts strings at runtime
- `bool` subclass of `int` — `True + True == 2`, `False == 0`


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)