Skip to content

Commit d97cd36

Browse files
rustyconoverclaude
andcommitted
docs: document reassign-to-persist contract on AggregateFunction.update
State is persisted only for groups whose dict entry was *assigned* during update() (plus groups carried from a prior batch). An in-place mutation of a group first seen in the current batch is silently dropped, so finalize() sees only initial_state() — every result NULL for single-group/single-batch aggregates. Spell out the immutable-reassign idiom with do/don't examples. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent f6b65aa commit d97cd36

1 file changed

Lines changed: 16 additions & 0 deletions

File tree

‎vgi/aggregate_function.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,22 @@ def update(
337337
The ``states`` dict is pre-populated with ``initial_state()`` for
338338
all new group_ids. ``group_ids`` is parallel to each column array.
339339
340+
IMPORTANT — reassign to persist. Treat state as immutable: to record
341+
a change you MUST write it back with ``states[gid] = new_state``. The
342+
framework only persists a group whose entry you *assigned* during this
343+
call (plus groups already saved from an earlier batch). Mutating the
344+
existing object in place — e.g. ``states[gid].items.append(x)`` — is
345+
NOT detected for a group first seen in this batch, so its data is
346+
silently dropped and ``finalize()`` sees only ``initial_state()``. This
347+
bites single-group / single-batch aggregates hardest. Do::
348+
349+
s = states[gid]
350+
states[gid] = MyState(items=s.items + new_items) # reassign
351+
352+
not::
353+
354+
states[gid].items.extend(new_items) # in-place: may be lost
355+
340356
"""
341357
...
342358

0 commit comments

Comments
 (0)