# counter

A heap type with per-module state and full cycle-GC support — the shape every
new extension type should have.

```bash
python -m pip install -e . --no-build-isolation -v
python -m pytest -q
```

```python
import counter

c = counter.Counter("hits")
c.bump()          # 1
c.bump(n=10)      # 11
c.payload = {"x": 1}
print(c)          # <counter.Counter 'hits' count=11>
```

## What to look for in `counter.c`

| Pattern | Where |
|---|---|
| Per-module state instead of C globals | `counter_state`, `get_state` |
| Heap type from a spec | `Counter_spec`, `PyType_FromModuleAndSpec` |
| Traversal including `Py_TYPE(self)` | `Counter_traverse` |
| The four-step deallocation order | `Counter_dealloc` |
| Module state from a method | `Counter_bump` via `defining_class` |
| Module state from a slot function | `Counter_richcompare` via `PyType_GetModuleByDef` |
| Validating setter | `Counter_set_name` |

## Things to try

1. Delete `Py_VISIT(Py_TYPE(self))` from `Counter_traverse` and run
   `test_traverse_reports_exactly_the_object_fields`.
2. Delete `Py_VISIT(self->payload)` and run `test_cycles_are_collected`
   under `python -X dev`. Then think about what a *missing* visit does
   versus an *extra* one.
3. Move `Py_DECREF(tp)` before `tp->tp_free(op)` in the deallocator.
4. Replace the heap type with a static `PyTypeObject` and see what
   `Counter_bump` can no longer reach.
