I have a following decorator.
def allow_disable_in_tests(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
version = ??? # name of the func of method’s class name
need_to_switch_off_in_tests = cache.get('switch_off_in_tests', version=version)
if settings.IM_IN_TEST_MODE and need_to_switch_off_in_tests:
return None
value = func(*args, **kwargs)
return value
return wrapper
There are 2 types of objects this decorator might accept as a func
:
Standalone function.
Method of the class(bound method, static method and class method all possible)
Question is how to get from inside decorator name of the:
function in case of func
is standalone function.
method's class name in case of func
is method of the class
This will be used in version
.
Decorator should be able to work with both types of objects.
Thanks
You can use __qualname__
and __module__
to derive this information. __qualname__
will describe where the class is defined within a module according to the class or function it was defined in.
However, you are putting test logic in production code, which is a bit of a code smell. You'd be better off using the monkey patching features of your testing framework to patch these functions when running your test suite. For example, with pytest:
import pytest
from functools import wraps
from inspect import signature
class FuncPatch:
def __init__(self, parent, name, retval=None):
self.parent = parent
self.name = name
self.retval = retval
def get_things_to_patch():
import mymodule
return (
FuncPatch(mymodule, 'my_func'),
FuncPatch(mymodule.MyClass, 'method'),
FuncPatch(mymodule.MyClass, 'static'),
FuncPatch(mymodule.MyClass, 'class_', retval='special'),
)
def create_test_function(func, retval, decorator=None):
func = getattr(func, '__func__', func) # unwrap if classmethod or normal method
sig = signature(func)
@wraps(func)
def f(*args, **kwargs):
# check func was called with correct params raises TypeError if wrong
sig.bind(*args, **kwargs)
return retval
if decorator:
f = decorator(f)
return f
@pytest.fixture
def patch_all_the_things(monkeypatch):
for patch in get_things_to_patch():
decorator = None
if (isinstance(patch.parent, type)
and not callable(patch.parent.__dict__[patch.name])
):
# quick hack to detect staticmethod or classmethod
decorator = type(patch.parent.__dict__[patch.name])
to_patch = getattr(patch.parent, patch.name)
func = create_test_function(to_patch, patch.retval, decorator)
monkeypatch.setattr(patch.parent, patch.name, func)
# things to test
def my_func():
return 'my_func'
class MyClass:
@staticmethod
def static():
return 'static'
@classmethod
def class_(cls):
return 'class'
def method(self):
return 'method'
# actual tests
def test_my_func(patch_all_the_things):
assert my_func() is None
def test_my_class(patch_all_the_things):
assert MyClass().method() is None
assert MyClass.method(MyClass()) is None
assert MyClass.static() is None
assert MyClass.class_() == 'special'
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.