fix: clone forward list to prevent shared mutation across form subclasses (#1333)#1410
fix: clone forward list to prevent shared mutation across form subclasses (#1333)#1410jpic merged 4 commits intoyourlabs:masterfrom
Conversation
…oss form subclasses Closes yourlabs#1333. Port of upstream PR yourlabs#1358. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e plan Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix: clone forward list to prevent shared mutation across form subclasses (yourlabs#1333)
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe pull request addresses a bug where form subclasses sharing a base widget instance could silently mutate each other's Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
Summary
Port and cleanup of #1358, fixing #1333.
Problem
WidgetMixin.forwardis a mutable list. Because Django deep-copies widget instances from class-level field definitions rather than re-instantiating them, all form subclasses sharing a common base class reference the same list object. Any__init__that appends towidget.forwardsilently mutates every sibling form instantiated from the same base.Changes
src/dal/widgets.pyWidgetMixin.__init__: replaceself.forward = forward or []withlist(forward) if forward else []— always produces a fresh list, never shares the caller's reference.WidgetMixin.__deepcopy__: new method that delegates tosuper().__deepcopy__()then re-copies.forward, so widget clones are also isolated.test_project/tests/test_widgets.pyForwardCloningTest::test_forwards_are_cloned: two subclasses of a shared base form each append a differentforward.Const; asserts each sees exactly one entry with the correct destination key, directly reproducing the original bug.Difference from #1358
One minor simplification:
list(forward).copy()→list(forward)(the.copy()call is redundant sincelist()already copies).Tested against
Django 5.2 and 6.0, Python 3.11–3.14. All existing tests pass.
Summary by CodeRabbit