I want to pass a list of values to a decorator. Every function which is decorated by the decorator passes different list of values. I am using the decorator python library
Here is what I was trying -
from decorator import decorator
def dec(func, *args):
// Do something with the *args - I guess *args contains the arguments
return func()
dec = decorator(dec)
#dec(['first_name', 'last_name'])
def my_function_1():
// Do whatever needs to be done
#dec(['email', 'zip'])
def my_function_2():
// Do whatever needs to be done
However, this does not work. It gives an error - AttributeError: 'list' object has no attribute 'func_globals'
How can I do this?
You can implement it without decorator library
def custom_decorator(*args, **kwargs):
# process decorator params
def wrapper(func):
def dec(*args, **kwargs):
return func(*args, **kwargs)
return dec
return wrapper
#custom_decorator(['first_name', 'last_name'])
def my_function_1():
pass
#custom_decorator(['email', 'zip'])
def my_function_2():
pass
Related
I'm learning about function decorators and am trying to apply some of the concepts I've learned so far. For the below function i've created a decorator function but it doesnt behave as I would expect.
Can someone help me understand why I can't access the default value (or any provided arg) of the outer function so that I can use it in the inner function? I keep getting a TypeError: type function doesn't define __round__ method.
def mathTool(defaultVal=5):
def innerFunc(*args):
return round(*args, defaultVal)
return innerFunc
print(funcB()) //triggers TypeError
print(funcB(2)) //triggers TypeError
def funcA(A):
return A/2
#mathTool()
def funcB(B):
return B/2
def funcAB(A, B):
return A*B/2
func_dec = mathTool(defaultVal=7)(funcAB)
print(func_dec(5, 9)) //triggers TypeError
Decorators take exactly one argument: the function. And their syntax is this:
#deco
def func():
...
When you see a decorator which takes arguments, as in #deco(arg), that is actually a function which returns a decorator.
Therefore:
def mathTool(defaultVal=5):
def decorator(func):
def innerFunc(*args):
args = args + (defaultVal,)
return func(*args)
return innerFunc
return decorator
#mathTool(7)
def whatever(a, b):
...
Now, calling whatever(1) will pass 1 and 7 to the function.
I have given arbitraly class A with method m and I would like to create decorator that can be added to new function f and this decorator will allow to execute f every time that m is called but to execute f before m and f should intercept arguments of m
I need help defining pre_execution decorator - I have something but it's not working and I can't figure out how to make it work
#a.py
class A:
def m(self, x):
return x+1
#mydecorator.py -- need help with code in this file
def pre_execution(klass, method):
old_fn = getattr(klass, method)
def inner(fn, *args):
# #wraps(fn)
def iin(*args, **kwargs):
fn(*args, **kwargs)
return old_fn(*args, **kwargs)
return iin
setattr(klass, method, inner)
return inner
# main.py
from a import A
from mydecorator import pre_execution
if __name__ == "__main__":
#pre_execution(A, 'm')
def f(*args, **kwargs):
print "in"
print "my code using args and kwargs"
print "out"
a = A()
print a.m(1) == 2
print a.m(1)
expected output:
in
my code using args and kwargs
out
True
I think what you want is
def pre_execution(klass, method):
old_method = getattr(klass, method)
def patch_klass(f):
def new_method(*args, **kwargs):
f(*args, **kwargs)
return old_method(*args, **kwargs)
setattr(klass, method, new_method)
return f
return patch_klass
pre_execution saves a reference to the original method, then defines a function that will be returned and called on f. This function defines a new method that calls f before calling the original method. patch_klass then replaces the old method with the new in. It also returns the original function unmodified, in case you want to use f elsewhere.
$ python tmp.py
in
my code using args and kwargs
out
True
in
my code using args and kwargs
out
2
Note that this works in Python 2 or 3.
I have decorator1 and decorator2 functions. And I am using these to decorate a function.
#decorator1("some", "args")
#decorator2(1,2)
#decorator1()
def my_func(): print("my func")
When I call wrapped functions iteratively over and over again, output becomes like this:
for my_func.__wrapped__:
decorator1
decorator2
decorator1
my func
for my_func.__wrapped__.__wrapped__():
decorator2
decorator1
my func
Problem is every wrapped function's name is my_func. I want to check if a function in this chain is instance of lets say decorator1. I want to know this because I will use arguments of decorators. (I already know them by using __closure__ cells.)
Clarification
I decided to give and example to show my purpose.
#route("/index")
def index(): pass
#route("/settings")
#need_permission("admin")
def settings: pass
#route("/blog")
#need_permission("admin", "user")
def blog(): pass
I can get all these route functions somewhere else, and I want to extract which one needs which permissions.
Here my findings:
>>> blog()
route blog
permissions admin user
>>> blog.__closure__[0].cell_contents
('/blog',)
>>> blog.__closure__[1].cell_contents()
permissions admin user
>>> blog.__closure__[0].cell_contents.__closure__[0].cell_contents
('admin', 'user')
>>> blog.__closure__[0].cell_contents.__closure__[1].cell_contents()
>>>
I just want to extract the tuples that hold permissions. I can apply my decorators in some specific order and extract easily or I need to implement my DecoratorApplier function as #Poolka pointed out. If there is no way of knowing like first option, i will follow second.
instance of a decorator - decorators implemented with functions and regular functions are all just functions of type function.
Not sure what exactly you wanna get. The code below is how I would approach the problem. Basically I add attribute primal_type to all the functions involved into decoration carnival to store the name of the functions/decorators. I do this with another decorator named DecoratorApplier. The code seems to perform something related to the issue in the question.
EDIT
Added clarification didn't make everything clear. I guess it's not good practrice to mix function and decorator logics in that way. Maybe there is another option to get desired info inside the function? Anyway two modified versions of my original approach below (oda stands for optional decorator arguments).
(1) - with DecoratorApplier
import functools
def decorator_1(*d1_args, **d1_kwargs):
def decorator(func):
#functools.wraps(func)
def wrapped(*args, **kwargs):
print('inside decorator_1', d1_args, d1_kwargs)
return func(*args, **kwargs)
return wrapped
return decorator
def decorator_2(*d2_args, **d2_kwargs):
def decorator(func):
#functools.wraps(func)
def wrapped(*args, **kwargs):
print('inside decorator_2', d2_args, d2_kwargs)
return func(*args, **kwargs)
return wrapped
return decorator
class DecoratorApplier:
def __init__(self, *decorators):
self.decorators = decorators
def __call__(self, func):
func.oda = dict()
for decorator in self.decorators:
func = decorator[0](*decorator[1], **decorator[2])(func)
(
func
.oda
.setdefault(decorator[0].__name__, list())
.extend([decorator[1], decorator[2]])
)
return func
#DecoratorApplier(
(decorator_1, (1, 2), {'x': 10, 'y': 20}),
(decorator_2, tuple(), dict()))
def f_1():
print('inside f_1')
print(f_1.oda)
return
if __name__ == '__main__':
f_1()
(2) - with modifying original decorators
import functools
def decorator_1(*d1_args, **d1_kwargs):
def decorator(func):
#functools.wraps(func)
def wrapped(*args, **kwargs):
print('inside decorator_1', d1_args, d1_kwargs)
(
kwargs
.setdefault('oda', dict())
.setdefault('decorator_1', list())
.extend([d1_args, d1_kwargs])
)
return func(*args, **kwargs)
return wrapped
return decorator
def decorator_2(*d2_args, **d2_kwargs):
def decorator(func):
#functools.wraps(func)
def wrapped(*args, **kwargs):
print('inside decorator_2', d2_args, d2_kwargs)
(
kwargs
.setdefault('oda', dict())
.setdefault('decorator_2', list())
.extend([d2_args, d2_kwargs])
)
return func(*args, **kwargs)
return wrapped
return decorator
#decorator_1(1, 2, x=10, y=20)
#decorator_2()
def f_1(oda=None):
print('inside f_1')
print(' oda', oda)
return
if __name__ == '__main__':
f_1()
I am new to decorators but ideally I wan to use them to simply define a bunch of class functions within class OptionClass, each representing some particular option with a name and description and if it's required. I don't want to modify the operation of the class function at all if that makes sense, I only want to use the decorator to define name, description, and if it's required.
Problem 1: I construct an OptionClass() and I want to call it's option_1. When I do this I receive a TypeError as the call decorator is not receiving an instance of OptionClass. Why is this? When I call option_1 passing the instance of OptionClass() it works. How do I call option_1 without needing to always pass the instance as self.
The error when received is:
Traceback (most recent call last):
File "D:/OneDrive_P/OneDrive/projects/python/examples/dec_ex.py", line 110, in <module>
print(a.option_1("test")) # TypeError: option1() missing 1 required positional argument: 'test_text'
File "D:/OneDrive_P/OneDrive/projects/python/examples/dec_ex.py", line 80, in __call__
return self.function_ptr(*args, **kwargs)
TypeError: option_1() missing 1 required positional argument: 'test_text'
Problem 2: How would I run or call methods on the decorator to set_name, set_description, set_required?
Problem 3: Although this is a sample I intend to code an option class using async functions and decorate them. Do I need to make the decorator call be async def __call__() or is it fine since it's just returning the function?
class option_decorator(object):
def __init__(self, function_pt):
self.function_ptr = function_pt
self.__required = True
self.__name = ""
self.__description = ""
def set_name(self, text):
self.__name = text
def set_description(self, text):
self.__description = text
def set_required(self,flag:bool):
self.__required = flag
def __bool__(self):
"""returns if required"""
return self.__required
def __call__(self, *args, **kwargs):
return self.function_ptr(*args, **kwargs)
def __str__(self):
"""prints a description and name of the option """
return "{} - {}".format(self.__name, self.__description)
class OptionClass(object):
"""defines a bunch of options"""
#option_decorator
def option_1(self,test_text):
return("option {}".format(test_text))
#option_decorator
def option_2(self):
print("option 2")
def get_all_required(self):
"""would return a list of option functions within the class that have their decorator required flag set to true"""
pass
def get_all_available(self):
"""would return all options regardless of required flag set"""
pass
def print_all_functions(self):
"""would call str(option_1) and print {} - {} for example"""
pass
a = OptionClass()
print(a.option_1("test")) # TypeError: option1() missing 1 required positional argument: 'test_text'
print(a.option_1(a,"test")) #Prints: option test
Problem 1
You implemented the method wrapper as a custom callable instead of as a normal function object. This means that you must implement the __get__() descriptor that transforms a function into a method yourself. (If you had used a function this would already be present.)
from types import MethodType
class Dec:
def __init__(self, f):
self.f = f
def __call__(self, *a, **kw):
return self.f(*a, **kw)
def __get__(self, obj, objtype=None):
return self if obj is None else MethodType(self, obj)
class Foo:
#Dec
def opt1(self, text):
return 'foo' + text
>>> Foo().opt1('two')
'footwo'
See the Descriptor HowTo Guide
Problem 2
The callable option_decorator instance replaces the function in the OptionClass dict. That means that mutating the callable instance affects all instances of OptionClass that use that callable object. Make sure that's what you want to do, because if you want to customize the methods per-instance, you'll have to build this differently.
You could access it in class definition like
class OptionClass(object):
"""defines a bunch of options"""
#option_decorator
def option_1(self,test_text):
return("option {}".format(test_text))
option_1.set_name('foo')
Problem 3
The __call__ method in your example isn't returning a function. It's returning the result of the function_ptr invocation. But that will be a coroutine object if you define your options using async def, which you would have to do anyway if you're using the async/await syntax in the function body. This is similar to the way that yield transforms a function into a function that returns a generator object.
My idea is to decorate the function with a decorator and then return other objects. Because I can not specify the return type in the function, so I would like to know if there is any way to specify in the decorator, thanks very much. here is the sample code:
class DemoObject(object):
"""
DEMO OBJECT
"""
def say(self):
print 'i am in demo object'
def demo_decorator(f):
def wrapper(*args, **kwargs):
return DemoObject()
return wrapper
#demo_decorator
def demo_fun():
a = 1
b = 2
demo_fun().say()
I want specify the demo_fun return types but not use ":rtype: xxx" in demo_fun docs, tks.