Fix MonkeyPatch.setattr/delattr binding descriptors when raising=False

This commit is contained in:
Ganden Schaffner 2023-01-09 00:00:00 -08:00
parent d5f04b526b
commit 82557504ba
No known key found for this signature in database
GPG Key ID: AAF2420F20D8B553
2 changed files with 15 additions and 12 deletions

View File

@ -0,0 +1,3 @@
Fix bug where `monkeypatch.setattr` and `monkeypatch.delattr` bind descriptors (possibly causing unwanted side-effects) to implement their `hasattr` checks even when their checks are disabled by `raising=False`.
Note that they still must bind descriptors to implement their checks if `raising=True`.

View File

@ -241,8 +241,7 @@ class MonkeyPatch:
"import string"
)
oldval = getattr(target, name, notset)
if raising and oldval is notset:
if raising and not hasattr(target, name):
raise AttributeError(f"{target!r} has no attribute {name!r}")
# Prevent `undo` from polluting `vars(target)` with an object that was not in it
@ -268,7 +267,6 @@ class MonkeyPatch:
``raising`` is set to False.
"""
__tracebackhide__ = True
import inspect
if isinstance(name, Notset):
if not isinstance(target, str):
@ -279,16 +277,18 @@ class MonkeyPatch:
)
name, target = derive_importpath(target, raising)
if not hasattr(target, name):
if raising:
raise AttributeError(f"{target!r} has no attribute {name!r}")
else:
oldval = getattr(target, name, notset)
# Avoid class descriptors like staticmethod/classmethod.
if inspect.isclass(target):
oldval = target.__dict__.get(name, notset)
self._setattr.append((target, name, oldval))
if raising and not hasattr(target, name):
raise AttributeError(f"{target!r} has no attribute {name!r}")
# Prevent `undo` from overwriting class descriptors (like
# staticmethod/classmethod) with the results of descriptor binding.
oldval = vars(target).get(name, notset)
try:
delattr(target, name)
except AttributeError:
pass
else:
self._setattr.append((target, name, oldval))
def setitem(self, dic: MutableMapping[K, V], name: K, value: V) -> None:
"""Set dictionary entry ``name`` to value."""