Python __getattribute__ and wrapper of method - python

I want to call a wrapper method of function with argument just before I call specific method.
So I guess I have to ovewrite __getattribute__ method.
Here is an example of code:
def wrapper(func):
return func * 2
class A(object):
def test(self, arg):
return arg + 1
def __getattribute__(self, name):
if name in ['test']:
return wrapper(super(A, self).__getattribute__(name))
return super(A, self).__getattribute__(name)
The matter is that getattribute is called when test return the value. What I want is to be able to catch test with the arguments and define the wrapper method like this:
def wrapper(func, *args, **kwargs):
print "do some stuff"
return func(*args, **kwargs)

Use a factory function to return your wrapper:
def decorate(f):
def wrapper(*args, **kw):
return f(*args, **kw) * 2
return wrapper
Here f is closed over by wrapper(), so it can access the name when called.
Then return this in the __getattribute__ hook:
def __getattribute__(self, name):
result = super(A, self).__getattribute__(name)
if name in ('test',):
return decorate(result)
return result
Of course, you could also just apply decorate as a decorator on test then:
class A(object):
#decorate
def test(self, arg):
return arg + 1

If I understand you correctly, you can use a decorator.
def wrapper(func):
def _wrapper(*args, **kwargs):
return func(*args, **kwargs) * 2
return _wrapper
class A(object):
#wrapper
def test(self, arg):
return arg + 1

Related

Python decorators in classes with extra parameters

I read this
one answer
and this
another answer
but I'm not doing it right with parameters
class decoratortest:
def dec(func):
def wrapper(self,*args,**kwargs):
func(*args,**kwargs)
func(*args,**kwargs)
return wrapper
#dec
def printer(self,a):
print(a)
def dectest(self):
self.printer('hi')
x = decoratortest()
x.dectest()
I get the usual positional error argument. What's the right syntax so I can print hi twice?
For the future, this worked:
class decoratortest:
def dec(func):
def wrapper(self,*args,**kwargs):
func(self,*args,**kwargs)
func(self,*args,**kwargs)
return wrapper
#dec
def printer(self,a):
print(a)
def dectest(self):
self.printer('hi')
x = decoratortest()
x.dectest()
very tricky, you dont' type self in the decorator, but you do in the underlying wrapper and func items.
You have to pass self explicitly, since func is a reference to a regular function object, not the method object that self.printer produces (via the descriptor protocol):
def dec(func):
def wrapper(self, *args, **kwargs):
func(self, *args, **kwargs)
func(self, *args, **kwargs)
return wrapper
Or you can put your decorator outside of the class
def dec(func):
def wrapper(*args, **kwargs):
func(*args, **kwargs)
func(*args, **kwargs)
return wrapper
class decoratortest:
#dec
def printer(self, a):
print(a)
def dectest(self):
self.printer('hi')
x = decoratortest()
x.dectest()
Output:
hi
hi
class decoratortest:
def dec(func):
def wrapper(self,*args,**kwargs):
func(*args,**kwargs)
func(*args,**kwargs)
return wrapper
#dec
def printer(self,a='hi'):
print(a)
def dectest(self):
self.printer('hi')
x = decoratortest()
x.dectest()
Added a default value for decorator when argument is not specified and it worked.

Decorate class method with another class method

Say I have a class, and I want to decorate one of its methods with another. Like in the example:
class Foobar:
def foo(self, func):
def wrapped(*args, **kwargs):
print(self + " is doing something")
func(*args, **kwargs)
return wrapped
#foo
def bar(self, *args, **kwargs):
print("Foobar")
Of course, when I make an instance of Foobar and run bar, an exception will be raised because the function gets passed to self and there's nothing for func. But I can't just decorate foo with staticmethod because self is needed to run the wrapped function.
This is quite a dilemma. Can someone help?
You can't have self in the foo() method, but you can get it just by specifying it in the wrapped() function:
class Foobar:
def foo(func):
print('foo is running')
def wrapped(self, *args, **kwargs):
print(str(self) + " is doing something")
func(self, *args, **kwargs)
return wrapped
#foo
def bar(self, *args, **kwargs):
print("Foobar")
print(self, args, kwargs)
print('Make Foobar')
f = Foobar()
f.bar(1, f=42)
print('Done')
Just to be clear, I've added some print() statements so you can see the order of operations:
foo is running
Make Foobar
<__main__.Foobar object at 0x03BE5210> is doing something
Foobar
<__main__.Foobar object at 0x03BE5210> (1,) {'f': 42}
Done

Decorators as classes

Trying to rewrite a decorator as a Class isn't working as expected. My actual decorator is:
def check(callback):
def decorator(function):
def wrapper(*args, **kwargs):
result = function(*args, **kwargs)
cb_result = callback()
return result
return wrapper
return decorator
My approach to class format is
class Check(object):
def __init__(self, *args, **kwargs):
self._args = args
self._kwargs = kwargs
def __call__(self, *call_args, **call_kwargs):
function = call_args[0]
return self.__param__call__(function)
def __param__call__(self, function):
def wrapper(*args, **kwargs):
result = function(*args, **kwargs)
cb_result = callback()
return result
return wrapper
I expect to use the decorator as in:
#Check(callback=a_function_callback)
def my_function():
...
What is my mistake while rewriting it as a class, I'm also always trying to keep backwards compatibility (aka Python 2.7 compliance).
You should accept callback as a parameter in your Check class' __init__ method, so that your wrapper function can actually reference it as a callback function:
class Check(object):
def __init__(self, callback):
self.callback = callback
def __call__(self, func):
return self.__param__call__(func)
def __param__call__(self, func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
cb_result = self.callback()
return result
return wrapper

How to pass self into a decorator?

How do I pass self.key below into the decorator?
class CacheMix(object):
def __init__(self, *args, **kwargs):
super(CacheMix, self).__init__(*args, **kwargs)
key_func = Constructor(
memoize_for_request=True,
params={'updated_at': self.key}
)
#cache_response(key_func=key_func)
def list(self, *args, **kwargs):
pass
class ListView(CacheMix, generics.ListCreateAPIView):
key = 'test_key'
I get the error:
'self' is not defined
Here's an example of doing it with a class decorator as I tried to describe to you in the comments. I filled-in a few undefined references in your question and used a super-simplified version of your cache_response function decorator, but hopefully this will convey the idea concretely enough for you to be able adapt it to your real code.
import inspect
import types
class Constructor(object):
def __init__(self, memoize_for_request=True, params=None):
self.memoize_for_request = memoize_for_request
self.params = params
def __call__(self):
def key_func():
print('key_func called with params:')
for k, v in self.params.items():
print(' {}: {!r}'.format(k, v))
key_func()
def cache_response(key_func):
def decorator(fn):
def decorated(*args, **kwargs):
key_func()
fn(*args, **kwargs)
return decorated
return decorator
def example_class_decorator(cls):
key_func = Constructor( # define key_func here using cls.key
memoize_for_request=True,
params={'updated_at': cls.key} # use decorated class's attribute
)
# create and apply cache_response decorator to marked methods
# (in Python 3 use types.FunctionType instead of types.UnboundMethodType)
decorator = cache_response(key_func)
for name, fn in inspect.getmembers(cls):
if isinstance(fn, types.UnboundMethodType) and hasattr(fn, 'marked'):
setattr(cls, name, decorator(fn))
return cls
def decorate_me(fn):
setattr(fn, 'marked', 1)
return fn
class CacheMix(object):
def __init__(self, *args, **kwargs):
super(CacheMix, self).__init__(*args, **kwargs)
#decorate_me
def list(self, *args, **kwargs):
classname = self.__class__.__name__
print('list() method of {} object called'.format(classname))
#example_class_decorator
class ListView(CacheMix):
key = 'test_key'
listview = ListView()
listview.list()
Output:
key_func called with params:
updated_at: 'test_key'
list() method of ListView object called
I just found out that if you write the decorator function like so:
def decorator(the_func):
#wraps(the_func)
def wrapper(*args, **kwargs):
the_func(*args, **kwargs)
return wrapper
and decorate any method which takes self as an argument, self will appear in args. Therefore you can do this:
from functools import wraps
class myClass:
def __init__(self):
self.myValue = "Hello"
def decorator(the_func):
#wraps(the_func)
def wrapper(*args, **kwargs):
print(args[0].myValue)
the_func(*args, **kwargs)
return wrapper
#decorator
def myFunction(self):
print("World")
Call it like you normally would
foo = myClass()
foo.myFunction()
and you should get
Hello
World

Using the same decorator (with arguments) with functions and methods

I have been trying to create a decorator that can be used with both functions and methods in python. This on it's own is not that hard, but when creating a decorator that takes arguments, it seems to be.
class methods(object):
def __init__(self, *_methods):
self.methods = _methods
def __call__(self, func):
def inner(request, *args, **kwargs):
print request
return func(request, *args, **kwargs)
return inner
def __get__(self, obj, type=None):
if obj is None:
return self
new_func = self.func.__get__(obj, type)
return self.__class__(new_func)
The above code wraps the function/method correctly, but in the case of a method, the request argument is the instance it is operating on, not the first non-self argument.
Is there a way to tell if the decorator is being applied to a function instead of a method, and deal accordingly?
To expand on the __get__ approach. This can be generalized into a decorator decorator.
class _MethodDecoratorAdaptor(object):
def __init__(self, decorator, func):
self.decorator = decorator
self.func = func
def __call__(self, *args, **kwargs):
return self.decorator(self.func)(*args, **kwargs)
def __get__(self, instance, owner):
return self.decorator(self.func.__get__(instance, owner))
def auto_adapt_to_methods(decorator):
"""Allows you to use the same decorator on methods and functions,
hiding the self argument from the decorator."""
def adapt(func):
return _MethodDecoratorAdaptor(decorator, func)
return adapt
In this way you can just make your decorator automatically adapt to the conditions it is used in.
def allowed(*allowed_methods):
#auto_adapt_to_methods
def wrapper(func):
def wrapped(request):
if request not in allowed_methods:
raise ValueError("Invalid method %s" % request)
return func(request)
return wrapped
return wrapper
Notice that the wrapper function is called on all function calls, so don't do anything expensive there.
Usage of the decorator:
class Foo(object):
#allowed('GET', 'POST')
def do(self, request):
print "Request %s on %s" % (request, self)
#allowed('GET')
def do(request):
print "Plain request %s" % request
Foo().do('GET') # Works
Foo().do('POST') # Raises
The decorator is always applied to a function object -- have the decorator print the type of its argument and you'll be able to confirm that; and it should generally return a function object, too (which is already a decorator with the proper __get__!-) although there are exceptions to the latter.
I.e, in the code:
class X(object):
#deco
def f(self): pass
deco(f) is called within the class body, and, while you're still there, f is a function, not an instance of a method type. (The method is manufactured and returned in f's __get__ when later f is accessed as an attribute of X or an instance thereof).
Maybe you can better explain one toy use you'd want for your decorator, so we can be of more help...?
Edit: this goes for decorators with arguments, too, i.e.
class X(object):
#deco(23)
def f(self): pass
then it's deco(23)(f) that's called in the class body, f is still a function object when passed as the argument to whatever callable deco(23) returns, and that callable should still return a function object (generally -- with exceptions;-).
Since you're already defining a __get__ to use your decorator on the Bound Method, you could pass a flag telling it if it's being used on a method or function.
class methods(object):
def __init__(self, *_methods, called_on_method=False):
self.methods = _methods
self.called_on_method
def __call__(self, func):
if self.called_on_method:
def inner(self, request, *args, **kwargs):
print request
return func(request, *args, **kwargs)
else:
def inner(request, *args, **kwargs):
print request
return func(request, *args, **kwargs)
return inner
def __get__(self, obj, type=None):
if obj is None:
return self
new_func = self.func.__get__(obj, type)
return self.__class__(new_func, called_on_method=True)
Here is a general way I found to detect whether a decorated callable is a function or method:
import functools
class decorator(object):
def __init__(self, func):
self._func = func
self._obj = None
self._wrapped = None
def __call__(self, *args, **kwargs):
if not self._wrapped:
if self._obj:
self._wrapped = self._wrap_method(self._func)
self._wrapped = functools.partial(self._wrapped, self._obj)
else:
self._wrapped = self._wrap_function(self._func)
return self._wrapped(*args, **kwargs)
def __get__(self, obj, type=None):
self._obj = obj
return self
def _wrap_method(self, method):
#functools.wraps(method)
def inner(self, *args, **kwargs):
print('Method called on {}:'.format(type(self).__name__))
return method(self, *args, **kwargs)
return inner
def _wrap_function(self, function):
#functools.wraps(function)
def inner(*args, **kwargs):
print('Function called:')
return function(*args, **kwargs)
return inner
Example usage:
class Foo(object):
#decorator
def foo(self, foo, bar):
print(foo, bar)
#decorator
def foo(foo, bar):
print(foo, bar)
foo(12, bar=42) # Function called: 12 42
foo(12, 42) # Function called: 12 42
obj = Foo()
obj.foo(12, bar=42) # Method called on Foo: 12 42
obj.foo(12, 42) # Method called on Foo: 12 42
A partial (specific) solution I have come up with relies on exception handling. I am attempting to create a decorator to only allow certain HttpRequest methods, but make it work with both functions that are views, and methods that are views.
So, this class will do what I want:
class methods(object):
def __init__(self, *_methods):
self.methods = _methods
def __call__(self, func):
#wraps(func)
def inner(*args, **kwargs):
try:
if args[0].method in self.methods:
return func(*args, **kwargs)
except AttributeError:
if args[1].method in self.methods:
return func(*args, **kwargs)
return HttpResponseMethodNotAllowed(self.methods)
return inner
Here are the two use cases: decorating a function:
#methods("GET")
def view_func(request, *args, **kwargs):
pass
and decorating methods of a class:
class ViewContainer(object):
# ...
#methods("GET", "PUT")
def object(self, request, pk, *args, **kwargs):
# stuff that needs a reference to self...
pass
Is there a better solution than to use exception handling?

Categories