NameError for simple nonlocal binding - python

I'm trying to use a decorator to give a function access to a particular variable whenever it's called. For instance,
def make_x_available(fn):
def decorated():
x = 5
return fn()
return decorated
#make_x_available
def print_x():
print(x)
5 here stands for an arbitrary expression to be evaluated each time the function fn is called, and to be bound to the name x.
Instead of print_x walking up the stack to find x's value of 5 as I expected, it raises NameError: name 'x' is not defined.
How would I go about achieving this? And why is this error caused?

You could add x as an argument to the function:
def make_x_available(fn):
def decorated():
x = 5
return fn(x)
return decorated
#make_x_available
def print_x(x):
print(x)
print_x()
Output
5
Alternatively, you can use keyword arguments:
def make_x_available(fn):
def decorated(*args, **kwargs):
kwargs['x'] = 5
return fn(*args, **kwargs)
return decorated
#make_x_available
def print_x(*args, **kwargs):
print(kwargs['x'])
print_x()
Output
5

Not sure what it could be used for, but this would work:
from functools import wraps
def make_x_available(fn):
#wraps(fn)
def decorated(*args, **kwargs):
decorated.x = 5
return fn(*args, **kwargs)
return decorated
#make_x_available
def print_x():
print(print_x.x)
print_x()
prints:
5
Note: When you write a decorator, always use functools.wraps to keep the name, docstring, and other attributes of the decorated function.

Related

The decorated function is not passed to the decorator [duplicate]

This question already has an answer here:
python decorator arguments with # syntax
(1 answer)
Closed 2 years ago.
Following is a useless example to show that I do not manage to decorate my add function with my_decorator, giving some extra arguments:
def my_decorator(fn=None, message=None, **options):
assert fn is not None, 'fn is not found'
print('function is found')
print(message)
return fn
# This syntax works (but does not make sense...)
#my_decorator
def multiply(a, b):
return a * b
# This syntax does not work
#my_decorator(message='surprise')
def add(a, b):
return a + b
# This no sugar-syntax works
add = my_decorator(add, message='surprise') # Works
In the second example, fn is seen as None, and that raise the AssertionError, but works with "no-sugar" syntax!
What is wrong in my code? (Python 2.7. Yes, we will migrate it ...)
First, the minimum requirement to create a decorator is to implement a function (the decorator) which take a function in parameter.
For instance, you can write:
def my_decorator(f):
def wrapper(*args, **kwargs):
print("f() is called")
return f(*args, **kwargs)
return wrapper
# You can use your decorator w/o parameter:
#my_decorator
def multiply(a, b):
return a * b
You get:
multiply(5, 8)
f() is called
40
As you can see, it works but your decorator take no parameter.
So you need to create a decorator factory:
def my_decorator_factory(message):
def my_decorator(f):
def wrapper(*args, **kwargs):
print("f() is called, saying: " + message)
return f(*args, **kwargs)
return wrapper
return my_decorator
# You can use your decorator factory with a parameter:
#my_decorator_factory(message="hi")
def multiply(a, b):
return a * b
You get:
multiply(5, 8)
f() is called, saying: hi
40
But, you want the ability to call you decorator factory like a decorator (w/o parameter).
So you need to change the signature of your decorator factory to allow the calls like:
my_decorator_factory(message="something") # or
my_decorator_factory(function)
here, you need to check that the first parameter is a function of something else:
import inspect
def my_decorator_factory(message=None):
if inspect.isfunction(message):
return my_decorator_factory(message=None)(message)
else:
def my_decorator(f):
def wrapper(*args, **kwargs):
print("f() is called, saying: " + (message or "(empty)"))
return f(*args, **kwargs)
return wrapper
return my_decorator
#my_decorator_factory
def multiply(a, b):
return a * b
#my_decorator_factory(message='surprise')
def add(a, b):
return a + b
You get:
multiply(5, 8)
f() is called, saying: (empty)
40
add(3, 2)
f() is called, saying: surprise
5
You may consider using Wrapt to implement "good" decorators/decorator factories.
my_decorator(message='surprise') returns None because it uses fn=None implicitly.
You then try to apply None to add, which as you noticed cannot work.
You need a parametrized function that returns a decorator. Informally, this is called a parametrized decorator.
Here's an example:
def make_decorator(message):
def decorator(func):
def new_func(*args, **kwargs):
print(message)
return func(*args, **kwargs)
return new_func
return decorator
#make_decorator(message='surprise')
def add(a, b):
return a + b
x = add(1, 2) # prints 'surprise' and sets x to 3
Note that
#make_decorator(message='surprise')
def add(a, b):
return a + b
is equivalent to
def add(a, b):
return a + b
decorator = make_decorator(message='surprise')
add = decorator(add)

Python 3.5 Decorators and function fields

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)

How to gain information about a function inside a decorator with python?

Using a python decorator as follows
def decoratorFunctionWithArguments(msg):
def wrap(f):
def wrapped_f(*args, **kwargs):
print ...
return f(*args, **kwargs)
return wrapped_f
return wrap
I want to print out the following attributes of the function f:
Name of the function
Name of the module this function was called.
Line of the module this function was called
I probably need the inspect module, but I am not quite sure how to use what method to achieve my goal. Or maybe I need to evaluate a traceback object?
This information can be retrieved in CPython with sys._getframe:
import sys
def mywrapper(msg):
def wrap(f):
def wrapped_f(*args, **kwargs):
f_back = sys._getframe().f_back
print f.__name__, f_back.f_code.co_filename, f_back.f_lineno
return f(*args, **kwargs)
return wrapped_f
return wrap
#mywrapper("message")
def f(x):
return x*x
f(3)
This will output the name of the function and the module and line number from where it is called.

Python: wrapping recursive functions

How can I wrap a recursive function, recursive calls included? For example, given foo and wrap:
def foo(x):
return foo(x - 1) if x > 0 else 1
def wrap(f):
def wrapped(*args, **kwargs):
print "f was called"
return f(*args, **kwargs)
return wrapped
wrap(foo)(x) will only output "f was called" with the first call. Recursive calls still address foo().
I don't mind monkey patching, or poking around internals. I'm not planning to add this code to the next nuclear warhead handling program, so even if it's a bad idea, I'd like to achieve the effect.
Edit: for example, would patching foo.func_globals to override foo.__name__ work? If it always does, any side-effects I should be minding?
It works if you use your wrapper function as a decorator.
def wrap(f):
def wrapped(*args, **kwargs):
print "f was called"
return f(*args, **kwargs)
return wrapped
#wrap
def foo(x):
return foo(x - 1) if x > 0 else 1
Reason being that in your example, you're only calling the result of the wrap function once. If you use it as a decorator it actually replaces the definition of foo in the module namespace with the decorated function, so its internal call resolves to the wrapped version.
Wrap the function with a class, rather than a function:
>>> def foo(x):
... return foo(x-1) if x > 0 else 1
...
>>> class Wrap(object):
... def __init__(self, f): self.f = f
... def __call__(self, *args, **kwargs):
... print "f called"
... return self.f(*args, **kwargs)
...
>>> foo = Wrap(foo)
>>> foo(4)
f called
1

Making decorators with optional arguments [duplicate]

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

Categories

Resources