Skip to content

fix: add missing list/dict methods to LockedListProxy and LockedDictProxy#4760

Open
lucasgomide wants to merge 1 commit intomainfrom
lg-proxy-list-error
Open

fix: add missing list/dict methods to LockedListProxy and LockedDictProxy#4760
lucasgomide wants to merge 1 commit intomainfrom
lg-proxy-list-error

Conversation

@lucasgomide
Copy link
Contributor

@lucasgomide lucasgomide commented Mar 7, 2026

We introduced a breaking change in 1.10.x when a Flow tries to access its state using .index (for example)

LockedListProxy and LockedDictProxy subclass list and dict but initialize the parent as empty (super().__init__()), delegating all access to an internal self._list / self._dict. However, several inherited methods were not overridden, causing them to silently operate on the empty parent instead of the real data.

Most critically, list.index() always raises ValueError because it searches the empty parent list. Other broken methods include count() (always returns 0), sort()/reverse() (no-ops), copy() (returns empty), and arithmetic operators (+, *).

All mutating operations acquire the lock; read-only operations delegate directly to the underlying collection, consistent with existing pattern.


Note

Medium Risk
Touches core flow state proxy behavior and locking for common container operations, which could subtly affect concurrency or operator semantics. Changes are narrowly scoped and covered by new unit tests for the previously broken methods.

Overview
Fixes broken container behavior in flow state proxies. LockedListProxy and LockedDictProxy now override additional list/dict methods and operators (e.g. index, count, sort/reverse, copy, +/*, and dict union |/|=) so they delegate to the underlying self._list/self._dict instead of the empty parent container.

Adds regression coverage. New tests in test_flow.py validate the corrected behavior (including in-place mutation operators not deadlocking and non-mutating operators returning new containers).

Written by Cursor Bugbot for commit 497400f. This will update automatically on new commits. Configure here.

@lucasgomide lucasgomide force-pushed the lg-proxy-list-error branch from 2b78458 to 4789721 Compare March 7, 2026 18:41
…roxy

LockedListProxy and LockedDictProxy subclass `list` and `dict` but
initialize the parent as empty (`super().__init__()`), delegating all
access to an internal `self._list` / `self._dict`. However, several
inherited methods were not overridden, causing them to silently operate
on the empty parent instead of the real data.

Most critically, `list.index()` always raises `ValueError` because it
searches the empty parent list. Other broken methods include `count()`
(always returns 0), `sort()`/`reverse()` (no-ops), `copy()` (returns
empty), and arithmetic operators (`+`, `*`).

All mutating operations acquire the lock; read-only operations delegate
directly to the underlying collection, consistent with existing pattern.
@lucasgomide lucasgomide force-pushed the lg-proxy-list-error branch from 4789721 to 497400f Compare March 7, 2026 18:45
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

def __iadd__(self, other: Iterable[T]) -> list[T]:
with self._lock:
self._list += list(other)
return self._list
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In-place operators return raw collection, breaking proxy identity

Medium Severity

__iadd__, __imul__, and __ior__ return the raw internal self._list/self._dict instead of self. After augmented assignment on a proxy reference (e.g., items = flow.state.items; items += [10]), the variable silently becomes a plain list/dict, losing all thread-safety guarantees from the lock. The primary flow.state.items += [10] path works only because StateProxy re-wraps on every access, but direct proxy holders lose protection.

Additional Locations (2)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant