a new monkeypatch.replace(target, value) call which derives the

monkeypatch location from target (can be class/module/function or
string which is taken as importable python path)
examples:

    monkeypatch.replace(os.path.abspath, lambda x: "")
    monkeypatch.replace("requests.get", ...)
This commit is contained in:
holger krekel
2013-08-07 15:35:27 +02:00
parent 3fddf99661
commit 407283ef81
4 changed files with 106 additions and 3 deletions

View File

@@ -26,6 +26,47 @@ def pytest_funcarg__monkeypatch(request):
notset = object()
if sys.version_info < (3,0):
def derive_obj_and_name(obj):
name = obj.__name__
real_obj = getattr(obj, "im_self", None)
if real_obj is None:
real_obj = getattr(obj, "im_class", None)
if real_obj is None:
real_obj = sys.modules[obj.__module__]
assert getattr(real_obj, name) == obj, \
"could not derive object/name pair"
return name, real_obj
else:
def derive_obj_and_name(obj):
name = obj.__name__
real_obj = getattr(obj, "__self__", None)
if real_obj is None:
current = sys.modules[obj.__module__]
for name in obj.__qualname__.split("."):
real_obj = current
current = getattr(current, name)
assert getattr(real_obj, name) == obj, \
"could not derive object/name pair"
return name, real_obj
def derive_from_string(target):
rest = []
while target:
try:
obj = __import__(target, None, None, "__doc__")
except ImportError:
if "." not in target:
raise
target, name = target.rsplit(".", 1)
rest.append(name)
else:
assert len(rest) >= 1
while len(rest) != 1:
obj = getattr(obj, rest.pop())
return rest[0], obj
class monkeypatch:
""" object keeping a record of setattr/item/env/syspath changes. """
def __init__(self):
@@ -33,9 +74,28 @@ class monkeypatch:
self._setitem = []
self._cwd = None
def replace(self, target, value):
""" derive monkeypatching location from ``target`` and call
setattr(derived_obj, derived_name, value).
This function can usually derive monkeypatch locations
for function, method or class targets. It also accepts
a string which is taken as a python path which is then
tried to be imported. For example the target "os.path.abspath"
will lead to a call to setattr(os.path, "abspath", value)
without the need to import "os.path" yourself.
"""
if isinstance(target, str):
name, obj = derive_from_string(target)
else:
name, obj = derive_obj_and_name(target)
return self.setattr(obj, name, value)
def setattr(self, obj, name, value, raising=True):
""" set attribute ``name`` on ``obj`` to ``value``, by default
raise AttributeEror if the attribute did not exist. """
raise AttributeEror if the attribute did not exist.
"""
oldval = getattr(obj, name, notset)
if raising and oldval is notset:
raise AttributeError("%r has no attribute %r" %(obj, name))