Fix MonkeyPatch.setattr polluting vars(target)

This commit is contained in:
Ganden Schaffner 2023-01-08 00:00:00 -08:00
parent b1b4469ae6
commit 054b335366
No known key found for this signature in database
GPG Key ID: AAF2420F20D8B553
2 changed files with 7 additions and 4 deletions

View File

@ -0,0 +1,3 @@
`monkeypatch.setattr` no longer leaves a leftover item in the target's dictionary after cleanup when patching an inherited attribute of a non-class object. This fixes a bug where a `__get__` descriptor's dynamic lookup was blocked and replaced by a cached static lookup after `monkeypatch` teardown.
Previously `monkeypatch.setattr` avoided leaving leftover items in the target's dictionary when patching class objects but not when patching non-class objects.

View File

@ -223,7 +223,6 @@ class MonkeyPatch:
applies to ``monkeypatch.setattr`` as well.
"""
__tracebackhide__ = True
import inspect
if isinstance(value, Notset):
if not isinstance(target, str):
@ -246,9 +245,10 @@ class MonkeyPatch:
if raising and oldval is notset:
raise AttributeError(f"{target!r} has no attribute {name!r}")
# avoid class descriptors like staticmethod/classmethod
if inspect.isclass(target):
oldval = target.__dict__.get(name, notset)
# Prevent `undo` from polluting `vars(target)` with an object that was not in it
# before monkeypatching, such as inherited attributes or the results of
# descriptor binding.
oldval = vars(target).get(name, notset)
self._setattr.append((target, name, oldval))
setattr(target, name, value)