I have the following snippet of code:
def wrapper(func):
def wrapped(*args, **kwargs):
func.var = 0
return func(*args, **kwargs)
return wrapped
#wrapper
def f_out():
print(f_out.var)
Could you please explain to me why running f_out() raises:
AttributeError: 'function' object has no attribute 'var'
EDIT
I had to elaborate, as an answer has given me the alternative but this will not work for the situation I want it. Given the following snippet:
def wrapper(func):
def wrapped(*args, **kwargs):
func.var = 0
ret = func(*args, **kwargs)
print(func.var)
return wrapped
#wrapper
def f_out():
f_out.var = 1
f_out()
print(f_out.var)
I get as output:
0
1
Why is this happening?
The correct way is to return the wrapped function and change it before returning it:
def wrapper(func):
def wrapped(*args, **kwargs):
return func(*args, **kwargs)
wrapped.var = 0
return wrapped
#wrapper
def f_out():
print(f_out.var)
You correctly get:
print(f_out())
gives
0
The updated snipped changes the var attribute twice:
first time in the wrapper where it sets the attribute for the original function to 0 and prints it after calling the original function
then when the original function is called from the wrapper, it sets the attribute for the function referenced as f_out to 1. But at that moment, the function referenced as f_out is the wrapped function and no longer the original one.
So when you later print f_out.var you print the attribute for the wrapped function which is 1.
Here is a slightly modified code demonstrating it:
def wrapper(func):
def wrapped(*args, **kwargs):
wrapped.orig = func # keeps a ref to the original function
func.var = 0
ret = func(*args, **kwargs)
print(func.var)
return wrapped
#wrapper
def f_out():
f_out.var = 1
f_out()
print(f_out.var, f_out.orig.var)
It prints
0
1 0
Decorator is wrapping your function when you calling it.
So when you are calling f_out() it returns 1. It is wrapping function that you call, not it's definition.
#wrapper
def f_out()
equal
wrapper(f_out) when you call it.
When you try to print f_out.var it returns the value from function definition.
you have to add return wrapped instead if return func try this:
def wrapper(func):
def wrapped(*args, **kwargs):
func.var = 0
return func(*args, **kwargs)
return wrapped
#wrapper
def f_out():
print(f_out.var)
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
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
This question already has answers here:
How to create a decorator that can be used either with or without parameters?
(16 answers)
Closed 3 years ago.
from functools import wraps
def foo_register(method_name=None):
"""Does stuff."""
def decorator(method):
if method_name is None:
method.gw_method = method.__name__
else:
method.gw_method = method_name
#wraps(method)
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
return decorator
Example: The following decorates my_function with foo_register instead of ever making it to decorator.
#foo_register
def my_function():
print('hi...')
Example: The following works as expected.
#foo_register('say_hi')
def my_function():
print('hi...')
If I want it to work correctly in both applications (one using method.__name__ and one passing the name in), I have to check inside of foo_register to see if the first argument is a decorator, and if so, I have to: return decorator(method_name) (instead of return decorator). This sort of "check to see if it's a callable" seems very hackish. Is there a nicer way to create a multi-use decorator like this?
P.S. I already know that I can require the decorator to be called, but that's not a "solution". I want the API to feel natural. My wife loves decorating, and I don't want to ruin that.
The cleanest way I know of for doing this is the following:
import functools
def decorator(original_function=None, optional_argument1=None, optional_argument2=None, ...):
def _decorate(function):
#functools.wraps(function)
def wrapped_function(*args, **kwargs):
...
return wrapped_function
if original_function:
return _decorate(original_function)
return _decorate
Explanation
When the decorator is called with no optional arguments like this:
#decorator
def function ...
The function is passed as the first argument and decorate returns the decorated function, as expected.
If the decorator is called with one or more optional arguments like this:
#decorator(optional_argument1='some value')
def function ....
Then decorator is called with the function argument with value None, so a function that decorates is returned, as expected.
Python 3
Note that the decorator signature above may be improved with Python 3-specific *, syntax to enforce safe use of keyword arguments. Simply replace the signature of the outermost function with:
def decorator(original_function=None, *, optional_argument1=None, optional_argument2=None, ...):
Through the help of the answers here and elsewhere and a bunch of trial and error I've found that there is actually a far easier and generic way to make decorators take optional arguments. It does check the args it was called with but there isn't any other way to do it.
The key is to decorate your decorator.
Generic decorator decorator code
Here is the decorator decorator (this code is generic and can be used by anyone who needs an optional arg decorator):
def optional_arg_decorator(fn):
def wrapped_decorator(*args):
if len(args) == 1 and callable(args[0]):
return fn(args[0])
else:
def real_decorator(decoratee):
return fn(decoratee, *args)
return real_decorator
return wrapped_decorator
Usage
Using it is as easy as:
Create a decorator like normal.
After the first target function argument, add your optional arguments.
Decorate the decorator with optional_arg_decorator
Example:
#optional_arg_decorator
def example_decorator_with_args(fn, optional_arg = 'Default Value'):
...
return fn
Test cases
For your use case:
So for your case, to save an attribute on the function with the passed-in method name or the __name__ if None:
#optional_arg_decorator
def register_method(fn, method_name = None):
fn.gw_method = method_name or fn.__name__
return fn
Add decorated methods
Now you have a decorator that is usable with or without args:
#register_method('Custom Name')
def custom_name():
pass
#register_method
def default_name():
pass
assert custom_name.gw_method == 'Custom Name'
assert default_name.gw_method == 'default_name'
print 'Test passes :)'
Glenn - I had to do it then. I guess I'm glad that there is not a "magic" way to do it. I hate those.
So, here's my own answer (method names different than above, but same concept):
from functools import wraps
def register_gw_method(method_or_name):
"""Cool!"""
def decorator(method):
if callable(method_or_name):
method.gw_method = method.__name__
else:
method.gw_method = method_or_name
#wraps(method)
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
if callable(method_or_name):
return decorator(method_or_name)
return decorator
Example usage (both versions work the same):
#register_gw_method
def my_function():
print('hi...')
#register_gw_method('say_hi')
def my_function():
print('hi...')
How about
from functools import wraps, partial
def foo_register(method=None, string=None):
if not callable(method):
return partial(foo_register, string=method)
method.gw_method = string or method.__name__
#wraps(method)
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
Enhanced Generic Decorator Decorator Code
Here's my adaption of #Nicole's answer with the following enhancements:
optional kwargs may be passed to the decorated decorator
the decorated decorator may be a bound method
import functools
def optional_arg_decorator(fn):
#functools.wraps(fn)
def wrapped_decorator(*args, **kwargs):
is_bound_method = hasattr(args[0], fn.__name__) if args else False
if is_bound_method:
klass = args[0]
args = args[1:]
# If no arguments were passed...
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
if is_bound_method:
return fn(klass, args[0])
else:
return fn(args[0])
else:
def real_decorator(decoratee):
if is_bound_method:
return fn(klass, decoratee, *args, **kwargs)
else:
return fn(decoratee, *args, **kwargs)
return real_decorator
return wrapped_decorator
Now that this old thread is back at the top anyway, lemme just throw in some Decorator-ception:
def magical_decorator(decorator):
#wraps(decorator)
def inner(*args, **kw):
if len(args) == 1 and not kw and callable(args[0]):
return decorator()(args[0])
else:
return decorator(*args, **kw)
return inner
Now your magical decorator is just a single line away!
#magical_decorator
def foo_register(...):
# bla bla
By the way, this works for any decorator. It just causes #foo to behave (as close as possibly) like #foo().
A generic decorator for decorating decorator definitions, expressing that decorated decorator accepts default arguments, which are set if none are explicitly given.
from functools import wraps
def default_arguments(*default_args, **default_kwargs):
def _dwrapper(decorator):
#wraps(decorator)
def _fwrapper(*args, **kwargs):
if callable(args[0]) and len(args) == 1 and not kwargs:
return decorator(*default_args, **default_kwargs)(args[0])
return decorator(*args, **kwargs)
return _fwrapper
return _dwrapper
It can be used in either of ways.
from functools import lru_cache # memoization decorator from Python 3
# apply decorator to decorator post definition
lru_cache = (default_arguments(maxsize=100)) (lru_cache)
# could also be:
# #default_arguments(maxsize=100)
# class lru_cache(object):
# def __init__(self, maxsize):
# ...
# def __call__(self, wrapped_function):
# ...
#lru_cache # this works
def fibonacci(n):
...
#lru_cache(200) # this also works
def fibonacci(n):
...
If you want this functionality on multiple decorators you can evade the code boilerplate with a decorator for a decorator:
from functools import wraps
import inspect
def decorator_defaults(**defined_defaults):
def decorator(f):
args_names = inspect.getargspec(f)[0]
def wrapper(*new_args, **new_kwargs):
defaults = dict(defined_defaults, **new_kwargs)
if len(new_args) == 0:
return f(**defaults)
elif len(new_args) == 1 and callable(new_args[0]):
return f(**defaults)(new_args[0])
else:
too_many_args = False
if len(new_args) > len(args_names):
too_many_args = True
else:
for i in range(len(new_args)):
arg = new_args[i]
arg_name = args_names[i]
defaults[arg_name] = arg
if len(defaults) > len(args_names):
too_many_args = True
if not too_many_args:
final_defaults = []
for name in args_names:
final_defaults.append(defaults[name])
return f(*final_defaults)
if too_many_args:
raise TypeError("{0}() takes {1} argument(s) "
"but {2} were given".
format(f.__name__,
len(args_names),
len(defaults)))
return wrapper
return decorator
#decorator_defaults(start_val="-=[", end_val="]=-")
def my_text_decorator(start_val, end_val):
def decorator(f):
#wraps(f)
def wrapper(*args, **kwargs):
return "".join([f.__name__, ' ', start_val,
f(*args, **kwargs), end_val])
return wrapper
return decorator
#decorator_defaults(end_val="]=-")
def my_text_decorator2(start_val, end_val):
def decorator(f):
#wraps(f)
def wrapper(*args, **kwargs):
return "".join([f.__name__, ' ', start_val,
f(*args, **kwargs), end_val])
return wrapper
return decorator
#my_text_decorator
def func1a(value):
return value
#my_text_decorator()
def func2a(value):
return value
#my_text_decorator2("-=[")
def func2b(value):
return value
#my_text_decorator(end_val=" ...")
def func3a(value):
return value
#my_text_decorator2("-=[", end_val=" ...")
def func3b(value):
return value
#my_text_decorator("|> ", " <|")
def func4a(value):
return value
#my_text_decorator2("|> ", " <|")
def func4b(value):
return value
#my_text_decorator(end_val=" ...", start_val="|> ")
def func5a(value):
return value
#my_text_decorator2("|> ", end_val=" ...")
def func5b(value):
return value
print(func1a('My sample text')) # func1a -=[My sample text]=-
print(func2a('My sample text')) # func2a -=[My sample text]=-
print(func2b('My sample text')) # func2b -=[My sample text]=-
print(func3a('My sample text')) # func3a -=[My sample text ...
print(func3b('My sample text')) # func3b -=[My sample text ...
print(func4a('My sample text')) # func4a |> My sample text <|
print(func4b('My sample text')) # func4b |> My sample text <|
print(func5a('My sample text')) # func5a |> My sample text ...
print(func5b('My sample text')) # func5b |> My sample text ...
Note: it has the drawback where you can't pass 1 argument as function to decorator.
Note2: if you have tips/notes on how to improve this decorator, you could comment at code review: https://codereview.stackexchange.com/questions/78829/python-decorator-for-optional-arguments-decorator
I was incredibly annoyed by this issue and eventually wrote a library to solve it: decopatch.
It supports two development styles: nested (like in python decorator factories) and flat (one less level of nesting). This is how your example would be implemented in flat mode:
from decopatch import function_decorator, DECORATED
from makefun import wraps
#function_decorator
def foo_register(method_name=None, method=DECORATED):
if method_name is None:
method.gw_method = method.__name__
else:
method.gw_method = method_name
# create a signature-preserving wrapper
#wraps(method)
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
Note that I use makefun.wraps instead of functools.wraps here so that the signature is fully preserved (the wrapper is not called at all if the arguments are invalid).
decopatch supports an additional development style, that I call double-flat, that is dedicated to creating signature-preserving function wrappers like this one. Your example would be implemented like this:
from decopatch import function_decorator, WRAPPED, F_ARGS, F_KWARGS
#function_decorator
def foo_register(method_name=None,
method=WRAPPED, f_args=F_ARGS, f_kwargs=F_KWARGS):
# this is directly the wrapper
if method_name is None:
method.gw_method = method.__name__
else:
method.gw_method = method_name
method(*f_args, **f_kwargs)
Note that in this style, all of your code is executed in calls to method. This might not be desirable - you might wish to perform things once at decoration time only - for this the previous style would be better.
You can check that both styles work:
#foo_register
def my_function():
print('hi...')
#foo_register('say_hi')
def my_function():
print('hi...')
Please check the documentation for details.
Here's another variation, which is fairly concise and doesn't use functools:
def decorator(*args, **kwargs):
def inner_decorator(fn, foo=23, bar=42, abc=None):
'''Always passed <fn>, the function to decorate.
# Do whatever decorating is required.
...
if len(args)==1 and len(kwargs)==0 and callable(args[0]):
return inner_decorator(args[0])
else:
return lambda fn: inner_decorator(fn, *args, **kwargs)
Depending on whether inner_decorator can be called with only one parameter, one can then do #decorator, #decorator(), #decorator(24) etc.
This can be generalised to a 'decorator decorator':
def make_inner_decorator(inner_decorator):
def decorator(*args, **kwargs):
if len(args)==1 and len(kwargs)==0 and callable(args[0]):
return inner_decorator(args[0])
else:
return lambda fn: inner_decorator(fn, *args, **kwargs)
return decorator
#make_inner_decorator
def my_decorator(fn, a=34, b='foo'):
...
#my_decorator
def foo(): ...
#my_decorator()
def foo(): ...
#my_decorator(42)
def foo(): ...
Here is an other solution that work also if the optional argument is a callable:
def test_equal(func=None, optional_value=None):
if func is not None and optional_value is not None:
# prevent user to set func parameter manually
raise ValueError("Don't set 'func' parameter manually")
if optional_value is None:
optional_value = 10 # The default value (if needed)
def inner(function):
def func_wrapper(*args, **kwargs):
# do something
return function(*args, **kwargs) == optional_value
return func_wrapper
if not func:
return inner
return inner(func)
This way both syntax will work:
#test_equal
def does_return_10():
return 10
#test_equal(optional_value=20)
def does_return_20():
return 20
# does_return_10() return True
# does_return_20() return True
Here's my solution, written for python3. It has a different approach from the other ones since it defines a callable class rather than a function.
class flexible_decorator:
def __init__(self, arg="This is default"):
self.arg = arg
def __call__(self, func):
def wrapper(*args, **kwargs):
print("Calling decorated function. arg '%s'" % self.arg)
func(*args, **kwargs)
return wrapper
You still have to explicitly call the decorator
#flexible_decorator()
def f(foo):
print(foo)
#flexible_decorator(arg="This is not default")
def g(bar):
print(bar)
A similar solution like those checking the type and length of the arguments using callable classes
class decor(object):
def __init__(self, *args, **kwargs):
self.decor_args = args
self.decor_kwargs = kwargs
def __call__(self, *call_args, **call_kwargs):
if callable(self.decor_args[0]) and len(self.decor_args) == 1:
func = self.decor_args[0]
return self.__non_param__call__(func, call_args, call_kwargs)
else:
func = call_args[0]
return self.__param__call__(func)
def __non_param__call__(self, func, call_args, call_kwargs):
print "No args"
return func(*call_args, **call_kwargs)
def __param__call__(self, func):
def wrapper(*args, **kwargs):
print "With Args"
return func(*args, **kwargs)
return wrapper
#decor(a)
def test1(a):
print 'test' + a
#decor
def test2(b):
print 'test' + b
I've made a simple package to solve the problem
Installation
Master branch
pip install git+https://github.com/ferrine/biwrap
Latest release
pip install biwrap
Overview
Some wrappers may have optional arguments and we often want to avoid #wrapper() calls and use #wrapper instead.
This works for simple wrapper
import biwrap
#biwrap.biwrap
def hiwrap(fn, hi=True):
def new(*args, **kwargs):
if hi:
print('hi')
else:
print('bye')
return fn(*args, **kwargs)
return new
Defined wrapper can be used in both ways
#hiwrap
def fn(n):
print(n)
fn(1)
#> hi
#> 1
#hiwrap(hi=False)
def fn(n):
print(n)
fn(1)
#> bye
#> 1
biwrap also works for bound methods
class O:
#hiwrap(hi=False)
def fn(self, n):
print(n)
O().fn(1)
#> bye
#> 1
Class methods / properties are supported too
class O:
def __init__(self, n):
self.n = n
#classmethod
#hiwrap
def fn(cls, n):
print(n)
#property
#hiwrap(hi=False)
def num(self):
return self.n
o = O(2)
o.fn(1)
#> hi
#> 1
print(o.num)
#> bye
#> 2
Function like call is OK too
def fn(n):
print(n)
fn = hiwrap(fn, hi=False)
fn(1)
#> bye
#> 1