Working around decorator and getattr - python

I have problem solving this question, I have the following class:
class test:
#auth
def method1(self, x):
return x
#auth
def method2(self, x, y):
return x+y
def method3(self, z):
return z
I applied the decorator in both methods, follow:
class auth:
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
self.f(*args, **kwargs)
So far no problem, however I need (NEED) to use the following code:
def run():
klass = globals()["test"]()
method1 = getattr(klass, "method1")
print(method1.__code__.co_varnames)
# should print (self, x)
method2 = getattr(klass, "method2")
print(method2.__code__.co_varnames)
# should print (self, x, y)
method3 = getattr(klass, "method3")
print(method3.__code__.co_varnames)
# i get (self, z) < without decorator
But I get now:
AttributeError: 'auth' object has no attribute '__code__'
What makes sense if we think that the signature of method "method1 and method2" is now "auth".
So how do I get the arguments with or without decorators.
I started reading about the "inspect" but there are many reports about being slow.

The "original" method is stored in the f attribute of the auth object. Instead of method1.__code__.co_varnames use method1.f.__code__.co_varnames

Annotations just contain an object and are not the object itsself, it is an object of class auth and not function. To access the function itsself, you can write methodN.f.__code__.co_varnames or assign a copy of the __dict__ object of the function to the auth-object in __init__ itsself.
class auth:
def __init__(self, f):
self.__dict__.update(f.__dict__)
# now the initialisations
self.f = f
def __call__(self, *args, **kwargs):
self.f(*args, **kwargs)
Edit:
You should initialize the members/call super after updating the dict, because f could be overriden by the update, eg. you define another decorator-class and it has also a member f.

Related

Import class method and use as method in wrapper

I have a class in a separate file called test_class.py. It looks something like this:
class test():
def __init__(self, X, Y):
self.X = X
self.Y = Y
def get_sum(self):
return self.X + self.Y
I would now like to create a wrapper class that also has the get_sum() function as a method. This is what I'm trying (inspired and motivated by the problem in this post: https://stackoverflow.com/a/7089719):
from test_class import test
tmp = test(1,3)
tmp.get_sum()
This outputs 4, so far so good. Now I try to create the wrapper as follows:
class Wrapper(object):
def __init__(self, method_name, module_name):
self.get_sum = method_name
self.test = module_name
def __call__(self, *args, **kwargs):
method = __import__(self.module, globals(), locals(), [self.get_sum,])
return method(*args, **kwargs)
wr = Wrapper('get_sum', 'test')
wr.get_sum()
Which just results in the following error TypeError: 'str' object is not callable
I know this is because wr.get_sum evaluates to a string, but I don't know how to ensure I have access to the methods defined in my test class.

Using a class as a decorator? [duplicate]

This question already has answers here:
How can I decorate an instance method with a decorator class?
(2 answers)
Closed 2 years ago.
I thought the following would work as a decorator
class D:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
class A:
#D
def f(self, x):
pass
a=A()
a.f(1)
but I get TypeError: f() missing 1 required positional argument: 'x'
What's going on and is there a way a can use a class as a decorator like this?
The thing is that besides the decorator mechanism, there is the mechanism that Python uses so that functions inside class bodies behave as instance methods: it is the "descriptor protocol". That is actually simple: all function objects have a __get__ method (but not __set__ or __del__) method, which make of them "non data descriptors". When Python retrieves the attribute from an instance, __get__ is called with the instance as a parameter - the __get__ method them have to return a callable that will work as the method, and has to know which was the instance called:
# example only - DO NOT DO THIS but for learning purposes,
# due to concurrency problems:
class D:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
return self.func(self.instance, *args, **kwargs)
def __get__(self, instance, owner):
self.instance = instance
return self
class A:
#D
def f(self, x):
print(self, x)
a=A()
a.f(1)
This will print "<main.A object at 0x...> 1"
However, as it is easily perceivable this only allows the decorated method to be called in a single instance at once - even non parallel code that owns more than an instance of "A" could have the method called with the wrong instance in mind. That is, this sequence:
In [127]: a1 = A()
In [128]: a2 = A()
In [129]: f1 = a1.f
In [130]: f2 = a2.f
In [131]: f1()
will end up calling "a2.f()" not "a1.f()"
To avoid this, what you have to return is a callable from __get__ that won't need to retrieve the instance as a class attribute. One way to do that is to create a partial callable and include that - however, note that since this is a necessary step, there is no need for the decorator class itself to have the "run wrapper + original code" function in the __call__ method - it could have any name:
from functools import partial
class D:
def __init__(self, func):
self.func = func
def __call__(self, *args, _instance=None, **kwargs):
if _instance:
return self.func(_instance, *args, **kwargs)
else:
return self.func(*args, **kwargs)
def __get__(self, instance, owner):
return partial(self.__call__, _instance=instance)
class A:
#D
def f(self, x):
print(self, x)
a=A()
a.f(1)

python use self in decorator for class method [duplicate]

How do I pass a class field to a decorator on a class method as an argument? What I want to do is something like:
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization("some_attr", self.url)
def get(self):
do_work()
It complains that self does not exist for passing self.url to the decorator. Is there a way around this?
Yes. Instead of passing in the instance attribute at class definition time, check it at runtime:
def check_authorization(f):
def wrapper(*args):
print args[0].url
return f(*args)
return wrapper
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization
def get(self):
print 'get'
>>> Client('http://www.google.com').get()
http://www.google.com
get
The decorator intercepts the method arguments; the first argument is the instance, so it reads the attribute off of that. You can pass in the attribute name as a string to the decorator and use getattr if you don't want to hardcode the attribute name:
def check_authorization(attribute):
def _check_authorization(f):
def wrapper(self, *args):
print getattr(self, attribute)
return f(self, *args)
return wrapper
return _check_authorization
A more concise example might be as follows:
#/usr/bin/env python3
from functools import wraps
def wrapper(method):
#wraps(method)
def _impl(self, *method_args, **method_kwargs):
method_output = method(self, *method_args, **method_kwargs)
return method_output + "!"
return _impl
class Foo:
#wrapper
def bar(self, word):
return word
f = Foo()
result = f.bar("kitty")
print(result)
Which will print:
kitty!
from re import search
from functools import wraps
def is_match(_lambda, pattern):
def wrapper(f):
#wraps(f)
def wrapped(self, *f_args, **f_kwargs):
if callable(_lambda) and search(pattern, (_lambda(self) or '')):
f(self, *f_args, **f_kwargs)
return wrapped
return wrapper
class MyTest(object):
def __init__(self):
self.name = 'foo'
self.surname = 'bar'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'foo')
def my_rule(self):
print 'my_rule : ok'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'bar')
def my_rule2(self):
print 'my_rule2 : ok'
test = MyTest()
test.my_rule()
test.my_rule2()
ouput:
my_rule2 : ok
Another option would be to abandon the syntactic sugar and decorate in the __init__ of the class.
def countdown(number):
def countdown_decorator(func):
def func_wrapper():
for index in reversed(range(1, number+1)):
print(index)
func()
return func_wrapper
return countdown_decorator
class MySuperClass():
def __init__(self, number):
self.number = number
self.do_thing = countdown(number)(self.do_thing)
def do_thing(self):
print('im doing stuff!')
myclass = MySuperClass(3)
myclass.do_thing()
which would print
3
2
1
im doing stuff!
I know this issue is quite old, but the below workaround hasn't been proposed before. The problem here is that you can't access self in a class block, but you can in a class method.
Let's create a dummy decorator to repeat a function some times.
import functools
def repeat(num_rep):
def decorator_repeat(func):
#functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(num_rep):
value = func(*args, **kwargs)
return
return wrapper_repeat
return decorator_repeat
class A:
def __init__(self, times, name):
self.times = times
self.name = name
def get_name(self):
#repeat(num_rep=self.times)
def _get_name():
print(f'Hi {self.name}')
_get_name()
I know this is an old question, but this solution has not been mentioned yet, hopefully it may help someone even today, after 8 years.
So, what about wrapping a wrapper? Let's assume one cannot change the decorator neither decorate those methods in init (they may be #property decorated or whatever). There is always a possibility to create custom, class-specific decorator that will capture self and subsequently call the original decorator, passing runtime attribute to it.
Here is a working example (f-strings require python 3.6):
import functools
# imagine this is at some different place and cannot be changed
def check_authorization(some_attr, url):
def decorator(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"checking authorization for '{url}'...")
return func(*args, **kwargs)
return wrapper
return decorator
# another dummy function to make the example work
def do_work():
print("work is done...")
###################
# wrapped wrapper #
###################
def custom_check_authorization(some_attr):
def decorator(func):
# assuming this will be used only on this particular class
#functools.wraps(func)
def wrapper(self, *args, **kwargs):
# get url
url = self.url
# decorate function with original decorator, pass url
return check_authorization(some_attr, url)(func)(self, *args, **kwargs)
return wrapper
return decorator
#############################
# original example, updated #
#############################
class Client(object):
def __init__(self, url):
self.url = url
#custom_check_authorization("some_attr")
def get(self):
do_work()
# create object
client = Client(r"https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments")
# call decorated function
client.get()
output:
checking authorisation for 'https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments'...
work is done...
You can't. There's no self in the class body, because no instance exists. You'd need to pass it, say, a str containing the attribute name to lookup on the instance, which the returned function can then do, or use a different method entirely.
It will be very useful to have a general-purpose utility, that can turn any decorator for functions, into decorator for methods. I thought about it for an hour, and actually come up with one:
from typing import Callable
Decorator = Callable[[Callable], Callable]
def decorate_method(dec_for_function: Decorator) -> Decorator:
def dec_for_method(unbounded_method) -> Callable:
# here, `unbounded_method` will be a unbounded function, whose
# invokation must have its first arg as a valid `self`. When it
# return, it also must return an unbounded method.
def decorated_unbounded_method(self, *args, **kwargs):
#dec_for_function
def bounded_method(*args, **kwargs):
return unbounded_method(self, *args, **kwargs)
return bounded_method(*args, **kwargs)
return decorated_unbounded_method
return dec_for_method
The usage is:
# for any decorator (with or without arguments)
#some_decorator_with_arguments(1, 2, 3)
def xyz(...): ...
# use it on a method:
class ABC:
#decorate_method(some_decorator_with_arguments(1, 2, 3))
def xyz(self, ...): ...
Test:
def dec_for_add(fn):
"""This decorator expects a function: (x,y) -> int.
If you use it on a method (self, x, y) -> int, it will fail at runtime.
"""
print(f"decorating: {fn}")
def add_fn(x,y):
print(f"Adding {x} + {y} by using {fn}")
return fn(x,y)
return add_fn
#dec_for_add
def add(x,y):
return x+y
add(1,2) # OK!
class A:
#dec_for_add
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# TypeError: add_fn() takes 2 positional arguments but 3 were given
# A().f(1,2)
class A:
#decorate_method(dec_for_add)
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# Now works!!
A().f(1,2)

Using class as decorator for another class's method

I have an issue with using a class to decorate another class' method. Code is as follows:
class decorator(object):
def __init__(self, func):
self.func = func
def __call__(self, *args):
return self.func(*args)
class test(object):
#decorator
def func(self, x, y):
print x, y
t = test()
t.func(1, 2)
It shows this error
TypeError: func() takes exactly 3 arguments (2 given).
If called using:
t.func(t, 1, 2)
then it passes. But then if the decorator is taken away, then this line will have issue again.
Why this is happening and how to solve it?
Edit: second version of the code to show the self in decorator.__call__ should be different than the self in test.func:
class decorator(object):
def __init__(self, func):
self.func = func
def __call__(self, *args):
return self.func(*args)
class test(object):
def __init__(self):
self.x = 1
self.y = 2
#decorator
def func(self):
print self
print self.x, self.y
t = test()
t.func()
This shows the same error. But
t.func(t)
works but not ideal.
To work as a method, an object in a class needs to implement part of the descriptor protocol. That is, it should have a __get__ method that returns a callable object which has been "bound" to the instance the method was looked up on.
Here's one way that you could make that work, using a wrapper function:
class decorator(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
def wrapper(*args):
return self.func(instance, *args) # note, self here is the descriptor object
return wrapper
You could instead return an instance of some other class from __get__, rather than a function, and use the __call__ method of that other class to implement the wrapper. If you're not using a closure though, you'd need to pass the instance to the wrapper class explicitly (as well as the function, since self.func won't work outside the descriptor class).

Python: Accessing class instance's `self` in decorator [duplicate]

How do I pass a class field to a decorator on a class method as an argument? What I want to do is something like:
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization("some_attr", self.url)
def get(self):
do_work()
It complains that self does not exist for passing self.url to the decorator. Is there a way around this?
Yes. Instead of passing in the instance attribute at class definition time, check it at runtime:
def check_authorization(f):
def wrapper(*args):
print args[0].url
return f(*args)
return wrapper
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization
def get(self):
print 'get'
>>> Client('http://www.google.com').get()
http://www.google.com
get
The decorator intercepts the method arguments; the first argument is the instance, so it reads the attribute off of that. You can pass in the attribute name as a string to the decorator and use getattr if you don't want to hardcode the attribute name:
def check_authorization(attribute):
def _check_authorization(f):
def wrapper(self, *args):
print getattr(self, attribute)
return f(self, *args)
return wrapper
return _check_authorization
A more concise example might be as follows:
#/usr/bin/env python3
from functools import wraps
def wrapper(method):
#wraps(method)
def _impl(self, *method_args, **method_kwargs):
method_output = method(self, *method_args, **method_kwargs)
return method_output + "!"
return _impl
class Foo:
#wrapper
def bar(self, word):
return word
f = Foo()
result = f.bar("kitty")
print(result)
Which will print:
kitty!
from re import search
from functools import wraps
def is_match(_lambda, pattern):
def wrapper(f):
#wraps(f)
def wrapped(self, *f_args, **f_kwargs):
if callable(_lambda) and search(pattern, (_lambda(self) or '')):
f(self, *f_args, **f_kwargs)
return wrapped
return wrapper
class MyTest(object):
def __init__(self):
self.name = 'foo'
self.surname = 'bar'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'foo')
def my_rule(self):
print 'my_rule : ok'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'bar')
def my_rule2(self):
print 'my_rule2 : ok'
test = MyTest()
test.my_rule()
test.my_rule2()
ouput:
my_rule2 : ok
Another option would be to abandon the syntactic sugar and decorate in the __init__ of the class.
def countdown(number):
def countdown_decorator(func):
def func_wrapper():
for index in reversed(range(1, number+1)):
print(index)
func()
return func_wrapper
return countdown_decorator
class MySuperClass():
def __init__(self, number):
self.number = number
self.do_thing = countdown(number)(self.do_thing)
def do_thing(self):
print('im doing stuff!')
myclass = MySuperClass(3)
myclass.do_thing()
which would print
3
2
1
im doing stuff!
I know this issue is quite old, but the below workaround hasn't been proposed before. The problem here is that you can't access self in a class block, but you can in a class method.
Let's create a dummy decorator to repeat a function some times.
import functools
def repeat(num_rep):
def decorator_repeat(func):
#functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(num_rep):
value = func(*args, **kwargs)
return
return wrapper_repeat
return decorator_repeat
class A:
def __init__(self, times, name):
self.times = times
self.name = name
def get_name(self):
#repeat(num_rep=self.times)
def _get_name():
print(f'Hi {self.name}')
_get_name()
I know this is an old question, but this solution has not been mentioned yet, hopefully it may help someone even today, after 8 years.
So, what about wrapping a wrapper? Let's assume one cannot change the decorator neither decorate those methods in init (they may be #property decorated or whatever). There is always a possibility to create custom, class-specific decorator that will capture self and subsequently call the original decorator, passing runtime attribute to it.
Here is a working example (f-strings require python 3.6):
import functools
# imagine this is at some different place and cannot be changed
def check_authorization(some_attr, url):
def decorator(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"checking authorization for '{url}'...")
return func(*args, **kwargs)
return wrapper
return decorator
# another dummy function to make the example work
def do_work():
print("work is done...")
###################
# wrapped wrapper #
###################
def custom_check_authorization(some_attr):
def decorator(func):
# assuming this will be used only on this particular class
#functools.wraps(func)
def wrapper(self, *args, **kwargs):
# get url
url = self.url
# decorate function with original decorator, pass url
return check_authorization(some_attr, url)(func)(self, *args, **kwargs)
return wrapper
return decorator
#############################
# original example, updated #
#############################
class Client(object):
def __init__(self, url):
self.url = url
#custom_check_authorization("some_attr")
def get(self):
do_work()
# create object
client = Client(r"https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments")
# call decorated function
client.get()
output:
checking authorisation for 'https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments'...
work is done...
You can't. There's no self in the class body, because no instance exists. You'd need to pass it, say, a str containing the attribute name to lookup on the instance, which the returned function can then do, or use a different method entirely.
It will be very useful to have a general-purpose utility, that can turn any decorator for functions, into decorator for methods. I thought about it for an hour, and actually come up with one:
from typing import Callable
Decorator = Callable[[Callable], Callable]
def decorate_method(dec_for_function: Decorator) -> Decorator:
def dec_for_method(unbounded_method) -> Callable:
# here, `unbounded_method` will be a unbounded function, whose
# invokation must have its first arg as a valid `self`. When it
# return, it also must return an unbounded method.
def decorated_unbounded_method(self, *args, **kwargs):
#dec_for_function
def bounded_method(*args, **kwargs):
return unbounded_method(self, *args, **kwargs)
return bounded_method(*args, **kwargs)
return decorated_unbounded_method
return dec_for_method
The usage is:
# for any decorator (with or without arguments)
#some_decorator_with_arguments(1, 2, 3)
def xyz(...): ...
# use it on a method:
class ABC:
#decorate_method(some_decorator_with_arguments(1, 2, 3))
def xyz(self, ...): ...
Test:
def dec_for_add(fn):
"""This decorator expects a function: (x,y) -> int.
If you use it on a method (self, x, y) -> int, it will fail at runtime.
"""
print(f"decorating: {fn}")
def add_fn(x,y):
print(f"Adding {x} + {y} by using {fn}")
return fn(x,y)
return add_fn
#dec_for_add
def add(x,y):
return x+y
add(1,2) # OK!
class A:
#dec_for_add
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# TypeError: add_fn() takes 2 positional arguments but 3 were given
# A().f(1,2)
class A:
#decorate_method(dec_for_add)
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# Now works!!
A().f(1,2)

Categories

Resources