dict iterator __reduce__ to resume from current position - #8384
Conversation
Pickling a partially-consumed dict / dict-view iterator restarted from the beginning because __reduce__ materialized every entry and ignored the iterator's position. Walk from the current position (mirroring next) so only the not-yet-yielded entries are captured, matching CPython, which reduces both directions to iter(remaining). Fixing the reverse iterator also uncovered two pre-existing bugs in reverse iteration itself, both from prev_entry saturating at 0: a hole just above index 0 made the entry at 0 yield twice, and deleting the first-inserted key made reversed() loop forever. prev_entry now returns the found entry's actual index and stops cleanly at index 0, and the reverse iterator/reduce detect exhaustion from that index. Verified against CPython 3.14 across all pickle protocols, dict states (including deletions), iterator kinds, and consumption counts. No regressions in test_dict, test_dictviews, test_ordered_dict, test_collections, test_userdict, test_iter, or test_copy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughDict and dict-view iterator reduction now serializes remaining entries from the stored position. Reverse iterators traverse dictionary storage directly, update positions from returned indices, and terminate correctly when probing reaches an empty slot at index zero. ChangesDict iterator traversal
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Collapse the builtins import block left multi-line after removing the now-unused builtins_reversed import. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The dict iterator __reduce__ fix makes SyncManager dict proxies pickle correctly, so this test now passes under spawn and forkserver. Remove the stale expectedFailure marker that was causing an UNEXPECTED SUCCESS CI failure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📦 Library DependenciesThe following Lib/ modules were modified. Here are their dependencies: [x] test: cpython/Lib/test/test_float.py (TODO: 3) dependencies: dependent tests: (no tests depend on float) [x] test: cpython/Lib/test/test_termios.py (TODO: 5) dependencies: dependent tests: (8 tests)
[x] test: cpython/Lib/test/test_format.py (TODO: 6) dependencies: dependent tests: (no tests depend on format) [x] lib: cpython/Lib/threading.py dependencies:
dependent tests: (163 tests)
[x] lib: cpython/Lib/pty.py dependencies:
dependent tests: (4 tests)
[x] lib: cpython/Lib/sqlite3 dependencies:
dependent tests: (2 tests)
Legend:
|
youknowone
left a comment
There was a problem hiding this comment.
thank you! we are getting more and more loop in dict. we probably need to redesign dict one day...
…#8384) * Fix dict iterator __reduce__ to resume from current position Pickling a partially-consumed dict / dict-view iterator restarted from the beginning because __reduce__ materialized every entry and ignored the iterator's position. Walk from the current position (mirroring next) so only the not-yet-yielded entries are captured, matching CPython, which reduces both directions to iter(remaining). Fixing the reverse iterator also uncovered two pre-existing bugs in reverse iteration itself, both from prev_entry saturating at 0: a hole just above index 0 made the entry at 0 yield twice, and deleting the first-inserted key made reversed() loop forever. prev_entry now returns the found entry's actual index and stops cleanly at index 0, and the reverse iterator/reduce detect exhaustion from that index. Verified against CPython 3.14 across all pickle protocols, dict states (including deletions), iterator kinds, and consumption counts. No regressions in test_dict, test_dictviews, test_ordered_dict, test_collections, test_userdict, test_iter, or test_copy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Apply cargo fmt to dict.rs imports Collapse the builtins import block left multi-line after removing the now-unused builtins_reversed import. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Unmark TestSyncManagerTypes.test_dict as expectedFailure The dict iterator __reduce__ fix makes SyncManager dict proxies pickle correctly, so this test now passes under spawn and forkserver. Remove the stale expectedFailure marker that was causing an UNEXPECTED SUCCESS CI failure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Pickling a partially-consumed dict / dict-view iterator restarted from the beginning because reduce materialized every entry and ignored the iterator's position. Walk from the current position (mirroring next) so only the not-yet-yielded entries are captured, matching CPython, which reduces both directions to iter(remaining).
Fixing the reverse iterator also uncovered two pre-existing bugs in reverse iteration itself, both from prev_entry saturating at 0: a hole just above index 0 made the entry at 0 yield twice, and deleting the first-inserted key made reversed() loop forever. prev_entry now returns the found entry's actual index and stops cleanly at index 0, and the reverse iterator/reduce detect exhaustion from that index.
Verified against CPython 3.14 across all pickle protocols, dict states (including deletions), iterator kinds, and consumption counts. No regressions in test_dict, test_dictviews, test_ordered_dict, test_collections, test_userdict, test_iter, or test_copy.
Assisted-by: Claude Code:claude-opus-4-8
Summary by CodeRabbit