Gracefully reset the StateProxy internals on error#6169
Conversation
If we catch an error during `StateProxy.__aenter__`, we have to manually unwind the internal state changes to allow the proxy to be used again.
Greptile SummaryThis PR fixes a correctness bug in Key changes:
Issue found: The new Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant BG as Background Task
participant SP as StateProxy
participant Lock as asyncio.Lock
participant MCM as modify_state ctx mgr
BG->>SP: async with state_proxy (aenter)
SP->>Lock: acquire()
Lock-->>SP: acquired
SP->>MCM: modify_state().__aenter__()
alt aenter raises Exception/CancelledError
MCM-->>SP: raises error
SP->>SP: __aexit__(*sys.exc_info()) [cleanup]
SP->>Lock: release()
SP-->>BG: re-raises error
BG->>SP: async with state_proxy (retry — works!)
else aenter succeeds
MCM-->>SP: mutable_state
SP->>SP: _self_mutable = True, __wrapped__ = substate
SP-->>BG: returns self (mutable)
BG->>SP: async with state_proxy (aexit)
SP->>MCM: __aexit__() [emit delta, clean state]
alt aexit raises CancelledError ⚠️
MCM-->>SP: CancelledError (BaseException)
Note over SP: contextlib.suppress(Exception)<br/>does NOT catch CancelledError!
SP-->>BG: lock never released — DEADLOCK
else aexit succeeds
MCM-->>SP: ok
SP->>Lock: release()
end
end
Last reviewed commit: fd05a8b |
If we catch an error during
StateProxy.__aenter__, we have to manually unwind the internal state changes to allow the proxy to be used again.