I have such kind of wrapper
def external_services_mock(f):
#patch('service.remote_call1')
#patch('service.remote_call2')
def wrapper(self, remote_call2_mock, remote_call1_mock, *args, **kwargs):
remote_call1_mock.return_value = None
def test_mocks():
return remote_call1_mock, remote_call2_mock
f(self, test_mocks, *args, **kwargs)
return wrapper
and test:
#external_services_mock
def test_add_callback(self, test_mocks):
remote_call1_mock, remote_call2_mock = test_mocks()
// do smth
// assert smth
django_nose runner ignores wrapped tests, and normally runs regular
Django version 2.0.2
django_nose 1.4.5
any ideas?
Using functools.wraps to preserve the name of the wrapped test seems to fix.
from functools import wraps
def external_services_mock(f):
#patch('service.remote_call1')
#patch('service.remote_call2')
#wraps(f)
def wrapper(self, remote_call2_mock, remote_call1_mock, *args, **kwargs):
remote_call1_mock.return_value = None
def test_mocks():
return remote_call1_mock, remote_call2_mock
f(self, test_mocks, *args, **kwargs)
return wrapper
Related
I have a decorator:
def decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
print('something', **kwargs)
# kwargs.pop('task_id', None)
func(*args, **kwargs)
print('something', **kwargs)
return wrapper
I need to filter the kwargs in such a way that they can be passed to the calling function without the task_id, but not removed. Since after I will need to fully use kwargs. Does anyone know how can this be done?
You can add task_id as a separate keyword argument, so it will be automatically excluded from other kwargs:
def decorator(func):
#wraps(func)
def wrapper(*args, task_id=None, **kwargs):
func(*args, **kwargs)
return wrapper
When inside the function (wrapper), kwargs is just a dict. So you can remove a key while returning a new dictionary:
def decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
print('something', **kwargs)
func(*args, **{kw: kwargs[kw] for kw in kwargs if kw != "task_id"})
print('something', **kwargs)
return wrapper
I have one variant, but it looks so bad. So I did:
def decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
print('something', **kwargs)
new_kwargs = kwargs,copy()
new_kwargs.pop('task_id', None)
func(*args, **new_kwargs)
print('something', **kwargs)
return wrapper
If anyone have better variant, it will be good.
I don't really know enough (yet) about python to figure this out myself, so that's why I wanted to try here. Is there any way to make these almost identical #wraps functions take up less space? I've got 5 of these in total, and 100 lines for 5x the same thing sounds like a waste. I originally found this on some website, but I can't seem to find it anymore.
The functions:
def a_required(func):
#wraps(func)
def decorated_view(*args, **kwargs):
if request.method in EXEMPT_METHODS:
return func(*args, **kwargs)
elif current_app.config.get('LOGIN_DISABLED'):
return func(*args, **kwargs)
elif not current_user.is_authenticated or not current_user["Keys"]["A"]:
return current_app.login_manager.unauthorized()
return func(*args, **kwargs)
return decorated_view
def b_required(func):
#wraps(func)
def decorated_view(*args, **kwargs):
if request.method in EXEMPT_METHODS:
return func(*args, **kwargs)
elif current_app.config.get('LOGIN_DISABLED'):
return func(*args, **kwargs)
elif not current_user.is_authenticated or not current_user["Keys"]["B"]:
return current_app.login_manager.unauthorized()
return func(*args, **kwargs)
return decorated_view
This is for a Flask website, with pages that would only be accesible to users with the right rights.
You can write a function that returns a decorator, and call it like so:
def required(req):
def wrapper(func):
#wraps(func)
def decorated_view(*args, **kwargs):
# put your decorated_view code here
# swapping out the hard coded `current_user["Keys"]["B"]`
# for `current_user["Keys"][req]`
print("executing decorator with", req)
return func(*args, **kwargs)
return decorated_view
return wrapper
#required("B")
def foo():
print("inside foo function")
#required("A")
def bar():
print("inside bar function")
Then executing these functions looks like:
>>> foo()
executing decorator with B
inside foo function
>>> bar()
executing decorator with A
inside bar function
The function required returns a dynamic decorator that changes its behavior depending on the value req we pass to it. This way, the decorated_view function can access the appropriate value of req depending on how we invoked required(...).
try this:
def required(req):
def wrapper(func):
#wraps(func)
def decorated_view(*args, **kwargs):
if not current_user.is_authenticated or not current_user["Keys"][req]:
return current_app.login_manager.unauthorized()
return func(*args, **kwargs)
return decorated_view
return wrapper
#required("A")
def method1():
pass
#required("B")
def method2():
pass
I am attempting to write a quick decorator to manage logging returns of various functions. I am not super well versed in decorators so any help you can provide would be very helpful!
from functools import update_wrapper
from typing import Any, Optional
from logging import getLogger
from time import perf_counter
from datetime import datetime
class logger:
def __init__(self, func:callable, response:str = "debug"):
self.logger = getLogger()
self.func = func
self.response = response
update_wrapper(self, func)
def __call__(self, *args, **kwargs):
return getattr(self, self.response)
def debug(self, *args, **kwargs):
self.logger.debug(f"Running {__name__} with id: {id(self)} at {datetime.now()}")
start = perf_counter()
value = self.func(*args, **kwargs)
end = perf_counter()
self.logger.debug(f"""Completed {__name__} with id: {id(self)} at {datetime.now()}.
Total Time to run: {end - start:.6f}s""")
return value
def info(self, *args, **kwargs):
self.logger.info(f"Running {__name__} at {datetime.now()}.")
return self.func(*args, **kwargs)
#logger(response="debug")
def stuff(x):
return x*x
stuff(2)
The error I am receiving is:
TypeError: __init__() missing 1 required positional argument: 'func',
clearly, it doesn't like the required callable and the response requirement. However, I see in all other class-based decorator setups that func needs to be called as part of the __init__ and I have also seen you can pass decorators addition information. What am I doing wrong here?
EDIT:
The purpose of getattr(self, self.response) is so that the function returned by __call__ is either the function along with the debug or info logging. This allows me to utilize the decorator #logging for both logging and debug, yet yields two different results depending on the response value specified in the decorator (i.e #logging(response="info")).
Solution:
class logger:
def __init__(self, response:str = "debug"):
self.logger = getLogger()
self.response = response
def __call__(self, func:callable):
update_wrapper(self, func)
self.func = func
return getattr(self, self.response)
def debug(self, *args, **kwargs):
self.logger.debug(f"Running {self.func.__name__} (type:{type(self.func)}) with id: {id(self)} at {datetime.now()}")
start = perf_counter()
value = self.func(*args, **kwargs)
end = perf_counter()
self.logger.debug(f"""Completed {self.func.__name__} with id: {id(self)} at {datetime.now()}.
Total Time to run: {end - start:.6f}s""")
return value
def info(self, *args, **kwargs):
self.logger.info(f"Running {self.func.__name__} at {datetime.now()}.")
return self.func(*args, **kwargs)
I don't know what your code should do, in particular it is not clear (to me) which kind of arguments should be passed to getattr(self, self.response)(*args, **kwargs). I am saying this to understand the proper workflow of the decorator.
So your code will never work. Here some possible examples of decoration:
the __call__way: #logger(response="debug")
class logger_1:
def __init__(self, response:str = "debug"):
print(response)
def __call__(self, func):
self.func = func
return self # ? depends on what are you doing
def debug(self, *args, **kwargs):
# ...
def info(self, *args, **kwargs):
#...
#logger_1(response="debug")
def stuff(x):
return x*x
A level more of "abstraction": #logger(response="debug").('some_parameter').debug_method
class logger_2:
def __init__(self, response:str = "debug"):
print(response)
def __call__(self, *args, **kwargs):
self.updated_response = getattr(self, self.response)(*args, **kwargs) # just an example
return self
def debug_method(self, func):
self.func = func
# ...
return func
def debug(self, *args, **kwargs):
# ...
def info(self, *args, **kwargs):
#...
#logger_2(response="debug")('some_parameter').debug_method
def stuff(x):
return x*x
NB: logger_2(response="debug").('some_parameter').debug_method is not taking argument because it waits to be "feed" with the target function stuff
These are examples of syntax which constraint the workflow, so you need to be careful when design your decorator
I would like to create two objects such that methods for some of this objects will be repeated for other. Then I try to do this:
def backup(method):
def wrapper(self, *args, **kwargs):
method(self, *args, **kwargs)
getattr(self.another_tester, method.__name__)(*args, **kwargs)
return wrapper
class Tester():
def __init__(self, name):
self.name = name
def select_backup(self, tester):
self.another_tester = tester
#backup
def foo1(self, stop=False):
print("I am tester {}, do method foo1".format(self.name))
#backup
def foo2(self, stop=False):
print("I am tester {}, do method foo2".format(self.name))
#backup
def foo3(self, stop=False):
print("I am tester {}, do method foo3".format(self.name))
tester1 = Tester("A")
tester2 = Tester("B")
tester1.select_backup(tester2)
tester2.select_backup(tester1)
tester1.foo1()
tester2.foo2()
tester1.foo3()
I get RuntimeError: maximum recursion depth exceeded while calling a Python object for such code. class Tester have a lot of different methods (foo1, foo2, foo3, ...), and I would like to backup (repeat) each method. So I use decorator.
I can change decorator:
def backup(method):
def wrapper(self, *args, **kwargs):
method(self, *args, **kwargs)
try:
kwargs["stop"]
except KeyError:
getattr(self.another_tester, method.__name__)(stop=True, *args, **kwargs)
return wrapper
It's work, but I think there is more pythonic way to do it. Can anyone offer such way?
If your pair of objects are always instances of the same class (or at least, never have different implementations of the decorated method), you can change the decorator so that it directly calls the original method on the other instance, rather than getting the decorated version via getattr:
def backup(method):
def wrapper(self, *args, **kwargs):
method(self, *args, **kwargs)
method(self.another_tester, *args, **kwargs)
return wrapper
If you want to be able to support pairs of objects of different classes (and different method implementations), things need to be a bit more complicated. You can save a reference to the original undecorated method as an attribute on the wrapper function, then look it up when you need it:
def backup(method):
def wrapper(self, *args, **kwargs):
method(self, *args, **kwargs)
other_method = getattr(self.another_tester, method.__name__).orig_method
other_method(self.another_tester, *args, **kwargs)
wrapper.orig_method = method
return wrapper
Does the Python standard library have a shortcut for writing decorators which accept arguments?
For example, if I want to write a decorator like with_timeout(timeout):
#with_timeout(10.0)
def cook_eggs(eggs):
while not eggs.are_done():
eggs.cook()
I have to write something like:
def with_timeout(timeout):
_func = [None]
def with_timeout_helper(*args, **kwargs):
with Timeout(timeout):
return _func[0](*args, **kwargs)
def with_timeout_return(f):
return functools.wraps(f)(with_timeout_helper)
return with_timeout_return
But that's awfully verbose. Is there a shortcut which makes decorators which accept arguments easier to write?
Note: I realize that it's also possible to use three nested functions to implement decorators with arguments… But that feels just a bit suboptimal too.
For example, possibly something like a #decorator_with_arguments function:
#decorator_with_arguments
def timeout(f, timeout):
#functools.wraps(f)
def timeout_helper(*args, **kwargs):
with Timeout(timeout):
return f(*args, **kwargs)
return timeout_helper
I tend to write my decorators as classes to be honest
class TestWithArgs(object):
def __init__(self, *deco_args, **deco_kwargs):
self.deco_args = deco_args
self.deco_kwargs = deco_kwargs
def __call__(self, func):
def _wrap(self, *args, **kwargs):
print "Blah blah blah"
return func(*args, **kwargs)
return _wrap
Its nothing if not slightly clearer
I know you said it feels suboptimal but I still feel that using three nested models is the cleanest solution. The inner two functions are just the 'normal' way of defining a decorator for a function that takes arguments (see example in python's docs for #wraps). The outer one is really just a function that takes and argument and returns a decorator.
def with_timeout(timeout):
def decorator(f):
#wraps(f)
def wrapper(*args, **kwargs):
with Timeout(timeout):
return f(*args, **kwargs)
return wrapper
return decorator
Based on Jakob's suggestion, I've implemented a small Decorator class, which I feel does a fairly decent job:
class Decorator(object):
def __call__(self, f):
self.f = f
return functools.wraps(f)(lambda *a, **kw: self.wrap(*a, **kw))
def wrap(self, *args, **kwrags):
raise NotImplemented("Subclasses of Decorator must implement 'wrap'")
class with_timeout(Decorator):
def __init__(self, timeout):
self.timeout = timeout
def wrap(self, *args, **kwargs):
with Timeout(timeout):
return self.f(*args, **kwargs)
First, we can define a little meta-decorator:
def decorator_with_arguments(wrapper):
return lambda *args, **kwargs: lambda func: wrapper(func, *args, **kwargs)
That allows us to create decorators that accept arguments like so:
#decorator_with_arguments
def my_wrapper(func, *decorator_args, **decorator_kwargs):
def wrapped(*call_args, **call_kwargs):
print "from decorator:", decorator_args, decorator_kwargs
func(*call_args, **call_kwargs)
return wrapped
Which can then be used normally:
#my_wrapper(1, 2, 3)
def test(*args, **kwargs):
print "passed directly:", args, kwargs
test(4, 5, 6)
Adding functools.wraps decoration is left as an exercise :)
Another take, without using lambdas:
def decorator_with_arguments(f):
#functools.wraps(f)
def with_arguments_helper(*args, **kwargs):
def decorator(g):
return f(g, *args, **kwargs)
return decorator
return with_arguments_helper