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