fix(monkeypatch): don't leave inherited attributes in the instance dict - #14969
Open
Irahan2 wants to merge 1 commit into
Open
fix(monkeypatch): don't leave inherited attributes in the instance dict#14969Irahan2 wants to merge 1 commit into
Irahan2 wants to merge 1 commit into
Conversation
`MonkeyPatch.setattr()` recorded the old value with `getattr()`, which follows the MRO. When the attribute was inherited rather than owned by the instance, `undo()` assigned that inherited value back onto the instance, adding a `__dict__` entry that had not been there before. For a plain class attribute this only leaves the target in a different state than it was found in. For an inherited non-data descriptor it is worse: the value computed during teardown is stored on the instance and shadows the descriptor, so every later lookup returns that frozen value. Look the old value up in the instance `__dict__` instead, which is what `setattr()` and `undo()` actually operate on -- but only when no data descriptor is in the way. Data descriptors intercept the assignment, so for those the `getattr()` value remains the right thing to restore. Closes pytest-dev#10644. Co-authored-by: Claude <noreply@anthropic.com>
Irahan2
force-pushed
the
fix-10644-monkeypatch-inherited-attr
branch
from
September 4, 2026 10:16
612cb41 to
0c601d5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #10644.
Problem
MonkeyPatch.setattr()records the old value withgetattr(), which follows the MRO. When the patched attribute is inherited rather than owned by the instance,undo()assigns that inherited value back onto the instance, creating a__dict__entry that was never there:For a plain class attribute this only leaves the target in a different state than it was found in. For an inherited non-data descriptor it is worse: the value computed during teardown is stored on the instance and shadows the descriptor, so the attribute is frozen for every later lookup.
Patching a method on an instance hits the same path, leaving a bound method behind in
vars(obj)after teardown.Fix
Look the old value up in the instance
__dict__— which is whatsetattr()andundo()actually operate on — mirroring the handlingsetattr()already has for classes.The lookup is guarded by a data-descriptor check, and that guard is load-bearing. A data descriptor intercepts the assignment, so the attribute never reaches the instance
__dict__and there is nothing there to delete on undo. Without the guard,monkeypatch.setattr()on a property fails during undo withAttributeError: property 'x' of 'Sample' object has no deleter, and__slots__attributes break the same way.test_undo_data_descriptor_on_instanceandtest_undo_slot_attribute_on_instancecover both.Notes
Only
setattr()is changed.delattr()has the same shape but cannot reach this state: deleting an inherited attribute from an instance raisesAttributeError, so nothing is recorded. Its separate undo-ordering issue is #14909.Verified locally on Python 3.13 (Windows): the three new tests reproducing the bug fail without the change and pass with it, and the full test suite is unaffected.
Checklist
closes #XYZWto the PR description and/or commits.changelogdirectory.AUTHORSin alphabetical order.AI/LLM assistance
Per the AI/LLM-Assisted Contributions Policy: this change was developed with AI assistance (Claude Code), credited in the
Co-authored-bytrailer on the commit. Happy to explain any part of it or adjust the approach.