I am trying to test a function that is decorated. Is there a way to mock a decorator and test function in isolation, when decorator is already applied?
import mock
def decorator(func):
def wrapper(*args, **kwargs):
return 1
return wrapper
def mocked(func):
def wrapper(*args, **kwargs):
return 2
return wrapper
#decorator
def f():
return 0
with mock.patch('test.decorator') as d:
d.side_effect = mocked
assert f() == 2 # error
There is not a simple solution.
This is a similar question: How to strip decorators from a function in python
You can either modify the original code just for testing, or use something like this library: https://pypi.python.org/pypi/undecorated in order to write a helper function to switch from the original wrapper to the testing wrapper:
from undecorated import undecorated
mocked(undecorated(f))()
Related
I'm writing a Django app, and I wanted to write a decorator that will mock the _() function during testing, just adding '_translated' after the string to translate
I basically have my decorator to replace the following instruction within my test:
with mock.patch('module_which_is_being_tested._', lambda s: s+'_translated'):
I didn't find anything similar on the web, so I'm sharing it:
from unittest import mock
import functools
def mock_translation(tested_object):
"""
A decorator that mock the '_()' function during testing
just adds '_translated' after the string to translate
the class/function being tested needs to be passed as parameter
Use:
from my_module.sub_module import my_function_to_be_tested
#mock_translation(my_function_to_be_tested)
def test_my_function_to_be_tested():
pass
"""
def actual_decorator(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
module_name = tested_object.__module__
tested_obj = tested_object
breakpoint()
import_path = module_name + '._'
# with mock.patch('battletech.models._', lambda s: s+'_translated'):
with mock.patch(import_path, lambda s: s+'_translated'):
value = func(*args, **kwargs)
return value
return wrapper
return actual_decorator
I have a python module with lots of functions and I want to apply a decorator for all of them.
Is there a way to patch all of them via monkey-patching to apply this decorator for every function without copy-pasting on line of applying decorator?
In other words, I want to replace this:
#logging_decorator(args)
func_1():
pass
#logging_decorator(args)
func_2():
pass
#logging_decorator(args)
func_3():
pass
#logging_decorator(args)
func_n():
pass
With this:
patch_func():
# get all functions of this module
# apply #logging_decorator to all (or not all) of them
func_1():
pass
func_2():
pass
func_3():
pass
func_n():
pass
I'm really not certain that this is a good idea. Explicit is better than implicit, after all.
With that said, something like this should work, using inspect to find which members of a module can be decorated and using __dict__ to manipulate the module's contents.
import inspect
def decorate_module(module, decorator):
for name, member in inspect.getmembers(module):
if inspect.getmodule(member) == module and callable(member):
if member == decorate_module or member == decorator:
continue
module.__dict__[name] = decorator(member)
Sample usage:
def simple_logger(f):
def wrapper(*args, **kwargs):
print("calling " + f.__name__)
f(*args, **kwargs)
return wrapper
def do_something():
pass
decorate_module(sys.modules[__name__], simple_logger)
do_something()
I ain't gonna be pretty ... but you can list all functions using dir() after their definition. Then I can't think of a way to patch them without a wrapper function.
def patched(func):
#logging_decorator
def newfunc(*args, **kwargs):
return func(*args, **kwargs)
return newfunc
funcs=[f in dir() if not '__' in f]
for f in funcs:
exec(f+'=patched(f)')
decorator code:
from functools import wraps
def wrap2(func):
#wraps(func)
def wrap(*args, **kwargs):
return func(*args, **kwargs)
return wrap
test function :
#wrap2
def f2(x='', y=''):
return 1
def f3(x='', y=''):
return 1
problem: can not use arguments suggestion with tab key on decorated function.
screenshot:
great thanks
the decorator can only keep the doctoring the same even if you use functools.wraps, but can not keep the signature of your original function.
I am using nose test generators feature to run the same test with different contexts. Since it requires the following boiler plate for each test:
class TestSample(TestBase):
def test_sample(self):
for context in contexts:
yield self.check_sample, context
def check_sample(self, context):
"""The real test logic is implemented here"""
pass
I decided to write the following decorator:
def with_contexts(contexts=None):
if contexts is None:
contexts = ['twitter', 'linkedin', 'facebook']
def decorator(f):
#wraps(f)
def wrapper(self, *args, **kwargs):
for context in contexts:
yield f, self, context # The line which causes the error
return wrapper
return decorator
The decorator is used in the following manner:
class TestSample(TestBase):
#with_contexts()
def test_sample(self, context):
"""The real test logic is implemented here"""
var1 = self.some_valid_attribute
When the tests executed an error is thrown specifying that the attribute which is being accessed is not available. However If I change the line which calls the method to the following it works fine:
yield getattr(self, f.__name__), service
I understand that the above snippet creates a bound method where as in the first one self is passed manually to the function. However as far as my understanding goes the first snippet should work fine too. I would appreciate if anyone could clarify the issue.
The title of the question is related to calling instance methods in decorators in general but I have kept the description specific to my context.
You can use functools.partial to tie the wrapped function to self, just like a method would be:
from functools import partial
def decorator(f):
#wraps(f)
def wrapper(self, *args, **kwargs):
for context in contexts:
yield partial(f, self), context
return wrapper
Now you are yielding partials instead, which, when called as yieldedvalue(context), will call f(self, context).
As far as I can tell, some things don't fit together. First, your decorator goes like
def with_contexts(contexts=None):
if contexts is None:
contexts = ['twitter', 'linkedin', 'facebook']
def decorator(f):
#wraps(f)
def wrapper(self, *args, **kwargs):
for context in contexts:
yield f, self, context # The line which causes the error
return wrapper
return decorator
but you use it like
#with_contexts
def test_sample(self, context):
"""The real test logic is implemented here"""
var1 = self.some_valid_attribute
This is wrong: this calls with_context(test_sample), but you need with_context()(test_sample). So do
#with_contexts()
def test_sample(self, context):
"""The real test logic is implemented here"""
var1 = self.some_valid_attribute
even if you don't provide the contexts argument.
Second, you decorate the wrong function: your usage shows that the test function yields the check function for each context. The function you want to wrap does the job of the check function, but you have to name it after the test function.
Applying self to a method can be done with partial as Martijn writes, but it can as well be done the way Python does it under the hood: with
method.__get__(self, None)
or maybe better
method.__get__(self, type(self))
you can achieve the same. (Maybe your original version works as well, with yielding the function to be called and the arguments to use. It was not clear to me that this is the way it works.)
This is what i want to do:
#MyDecorator
def f():
pass
for d in f.decorators:
print d
This is not generally possible without the cooperation of the decorators. For example,
def my_decorator(f):
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
wrapper.decorators = [wrapper]
if hasattr(f, 'decorators'):
wrapper.decorators.extend[f.decorators]
return wrapper
Essentially, all the decorator does is wrap the function as usual and then put a decorators attribute on the wrapper function. It then checks the wrapped function for a similar list and propagates it upwards.
This is pretty useless though
What I think you want is
def my_decorator(f):
def wrapper(args):
return f(args)
wrapper.decorated = f
return wrapper
This will allow you to do stuff like
#my_other_decorator # another decorator following the same pattern
#my_decorator
def foo(args):
print args
foo.decorated(args) # calls the function with the inner decorated function (my_decorator)
foo.decorated.decorated(args) # original function
You can actually abstract this pattern into a decorator
def reversable(decorator):
def wrapper(func):
ret = decorator(func) # manually apply the decorator
ret.decorated = func # save the original function
return ret
return wrapper
Now when you are writing your decorator:
#reversable
def my_decorator(f):
def wrapper(x):
return f(x + 1)
return wrapper
The #MyDecorator syntax is just shorthand for writing the following Python code:
def f():
pass
f = MyDecorator(f)
Written in this form, you can see that the decorators applied to the function are not kept track of in any way. You could make your decorators remember when they're applied (Aaron's answer has a couple good ideas on how to do this), but you'd have to wrap all third-party decorators with your own.