Repository navigation
Track mutations made through dict.values() and dict.items() on state vars - #7500
Conversation
…vars MutableProxy only re-wrapped nested mutables reached through __getitem__, __iter__, get() and setdefault(). The native dict views handed out the nested dicts unwrapped, so mutating them in a `for item in d.values()` loop changed the backend value without marking the var dirty: the rendered value and any cached var over it went stale. The gap dates to the MutableProxy rewrite in #1748 (v0.2.8). A proxied dict now serves `values()` and `items()` as collections.abc views that read each value back through the proxy, wrapping mutable values under their key so they mark the field dirty and refresh by that key in an async context. The views keep dict-view semantics: live, sized, reversible, searchable, and `isinstance` of ValuesView/ItemsView. Both views iterate through one shared generator that wraps straight from the wrapped dict, at the cost of the existing per-element proxy fast path. Fixes #7477 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MzM34QeM3FSHoTgvC7hs1x
There was a problem hiding this comment.
All reported issues were addressed across 3 files
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
[Medium impact] The PR appears safe to merge, with the previous compatibility issue addressed and no new actionable issues found. SummaryThe PR makes nested writes through state dictionaries’
Reviews (2) · Last reviewed commit: "Give the proxied dict views the native m..." · Reviewed by Greptile |
Merging this PR will degrade performance by 4.27%1 warning
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing |
The views replacing `dict.values()` / `dict.items()` on a proxied dict dropped two parts of the native view API, and carried no type information about the dict they view. `mapping` returns a read-only mapping proxy over the dict's proxy, so the nested values it hands out keep tracking mutations. `repr` delegates to the native view of the wrapped dict, printing exactly what it printed before the views were introduced. The view base is now generic in the dict's key and value types, with `__iter__` and `__reversed__` overloaded on the view's own type: the items view yields `tuple[K, V]` and the values view yields `V`. A test pins the element types with `assert_type`, so pyright checks them in CI. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MzM34QeM3FSHoTgvC7hs1x
All Submissions:
Type of change
Changes To Core Features:
Description
closes #7477
MutableProxyonly re-wrapped nested mutables reached through__getitem__,__iter__,get()andsetdefault(). The nativedict.values()/dict.items()views handed out the nested dicts unwrapped, so a loop likechanged the backend value without marking the var dirty: the rendered value and any cached var over it stayed stale. The gap dates to the
MutableProxyrewrite in #1748 (v0.2.8); before that, the eagerReflexDictconversion did mark the var dirty but rebuilt the container on each write, so only the first mutation in such a loop landed.Fix. A proxied dict now serves
values()anditems()ascollections.abcviews that read each value back through the proxy. Mutable values come out wrapped under their key, so they mark the field dirty and refresh by that key in anasync withcontext. The views keep native dict-view semantics: live, sized, reversible, searchable, usable in set operations, andisinstanceofValuesView/ItemsView. Pure-Python mappings already worked because theirMappingmixin methods get rebound to the proxy; this closes the gap for the C-leveldictmethods, guarded so a non-dict'svalues/itemsattribute is left alone.Both views iterate through one shared generator that wraps straight from the wrapped dict, so they run at the existing per-element proxy fast-path cost rather than the ABC default of
proxy[key]per element:d[k])Tests. Three new tests in
tests/units/istate/test_proxy.pyfail before the fix and pass after it: dirty marking through both views (plus the live / reversed / membership semantics), the issue's cached-var scenario, and key-path refresh for proxies read through views in an async context.🤖 Generated with Claude Code
https://claude.ai/code/session_01MzM34QeM3FSHoTgvC7hs1x
Generated by Claude Code