Class method as decorator in python - python

So, im writing a library for appium tests.
I have a main class that look like this:
class APP():
def __init__(self):
self.variable1 = 1
self.current_view = "main_screen"
def do_operation_A(self):
self.open_side_menu()
do_something
self.current_view = "side_menu"
def do_operation_B(self):
self.open_side_menu()
do_something_else
self.current_view = "side_menu"
def set_landscape(self):
self.open_settings_menu()
configure_landscape
self.current_view = "settings_menu"
The class has a lot of operations so i can do things like app.do_operation_A() or app.set_landscape() without having to first go to each menu manually (resolved inside the class)
To reduce this i want to implement a decorator to do something like this if possible:
class APP():
def __init__(self):
self.variable1 = 1
self.current_view = "main_screen"
#DEFINE_DECORATOR_HERE
#side_menu
def do_operation_A(self):
do_something
#side_menu
def do_operation_B(self):
do_something_else
#settings_menu
def set_landscape(self):
configure_landscape
So i want to implement this decorators to navigate to the corresponding view and also change that variable that i use to check some things in other functions. I have seen some examples with functools.wraps but is not clear to me of how to implement the decorator inside the class to be able to modify this self variables.
Any help?

Using a decorator means that you "wrap" your other function, i.e. you call the decorator and then call the function from inside the decorator.
E.g.:
import functools
def outer(func):
#functools.wraps(func)
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
Upon defining the function, the decorator will be called, returning the inner function.
Whenever you call func, you will in reality call inner, which runs it's own code, including calling the original func function.
So for your use case, you should be able to create decorators similar to:
def settings_menu(func):
#functools.wraps(func)
def inner(self, *args, **kwargs):
self.open_settings_menu()
self.current_view = "settings_menu"
return func(self, *args, **kwargs)
return inner

So a decorator is basically a function that returns another function, right?
def side_menu(func):
def wrapper():
return func()
return wrapper
The wrapper, returned by side_menu, will be called whenever App().do_operationA is called. And whenever that method is called, self is always the first argument. Or rather, the first argument is the instance of App, but whatever. So we could do:
def side_menu(func):
def wrapper(self, *args, **kwargs):
self.open_menu()
func(self, *args, **kwargs)
return wrapper
Now, you don't want the method to present itself as wrapper - you like the name do_operationA. That's where #functools.wraps comes in, it makes things look and work right when decorating.
def side_menu(func):
#functools.wraps
def wrapper(self, *args, **kwargs):
self.open_menu()
func(self, *args, **kwargs)
return wrapper

Related

Implementing __getattr__ inside a class method such as __call__

So I've created a module inspired heavily by amoffat's sh module, where I can import shell programs as functions; unlike sh, my module can do something like git(C = path).commit(m = message) directly, by returning the module class itself as a partial: return partial(bakery, self.program). However, I've lost the ability to run something like ls() without a placeholder method like ls._(), which doesn't look as good. The code in the latter: return output_as_list(args, kwargs).
from functools import partial
def __getattr__(name):
if name == "__path__":
raise AttributeError
return bakery(name)
class bakery:
def __init__(self, program):
self.program = program
def __getattr__(self, subcommand):
return subcommand
#property
def __call__(self):
return partial(bakery, self.program)
My question is this:
Is there a way to tell __call__ you're accessing a dynamic attribute, using a __getattr__ inside it, for example, to implement both the git(C = path).commit(m = message) and ls() scenarios? Or to conditionally return a partial or an output list depending on whether an attribute of __call__ is being accessed?
Edit:
I was wondering if something similar to this might work?
def __call__(self, *args, **kwargs):
class inner_class:
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
def __getattr__(self, subcommand):
return partial(bakery, self.program)
def __call__(self, *args, **kwargs):
return bakery(*self.args, **self.kwargs)._get_output_as_list(*args, **kwargs)
return inner_class(self.program, *args, **kwargs)
Edit 2:
I suppose I could just convert the individual __call__ functions to subclasses, and import from whichever is necessary.

How can I extend a library's decorator?

I would like to extend a library's decorator. I know that I can just call both decorators:
#my_decorator
#lib_decorator
def func():
pass
But I would like to avoid having to pass #lib_decorator to each function each time. I would like my decorator to automatically decorate func() with lib_decorator. How can I do this? Can they be nested?
You can incorporate the lib's decorator within yours. For simple, argument-less decorators, it's rather straight-forward:
def my_decorator():
#lib_decorator # <--- Just include the lib's decorator here
def inner:
func(*args, **kwargs)
return inner
It's a bit trickier for decorators that have arguments. Just remember that your decorator is replacing the decorated function with the inner-most function. So that's the one you need to decorate. So if you call your decorator with args, e.g.
#my_decorator(arg)
def func():
pass
Then decorate the inner function with the lib decorator:
def my_decorator(arg):
def wrapper(func):
#lib_decorator # <--- Just include the lib's decorator here
def inner(*args, **kwargs):
func(*args, **kwargs)
return inner
return wrapper
Or, using the class form of the decorator function:
class my_decorator():
def __init__(self, arg):
pass
def __call__(self, func):
#lib_decorator # <--- Just include the lib's decorator here
def inner(*args, **kwargs):
func(*args, **kwargs)
return inner
You can easily transform a decoration like yours:
#my_decorator
#lib_decorator
def func():
pass
To this simpler decoration, using function composition:
my_composed_decorator = lambda func: my_decorator(lib_decorator(func))
#my_composed_decorator
def func():
pass

Pass parameters to a decorator for a class method is to be decorated

I am trying to define a decorator in flask which will finally be decorating class methods passing parameters of that class instance. Here is an example what I really want to do.
from functools import wraps
def user_permission(myname):
def decorator(f):
#wraps(f)
def decorated(*args,**argws):
if myname == 'My Name':
return f(*args,**argws)
else:
return "Not Permitted"
return decorated
return decorator
And my manager class is defined as:
class Manager(flask.views.MethodView):
def __init__(self,name):
self.name = name
#user_permission(self.my_name)
def post(self):
return "Response"
def get(self):
return "Response"
What I am trying to do is pass the class variables to the decorator. Yes "self" is not defined at that point but "#decorator.user_permission(self.my_name)" is what I am trying actually because I am yet not solved with my problem.
I couldn't find solution from HERE.
Does anybody knows about these stuffs please?
As you say, self is not defined at that point. This could never work, as a decorator is executed when the class is defined, whereas you want something to run when the instance method is called.
However I think you're really overcomplicating this. self is passed to the method itself. So there's no reason to try and make it a parameter to the decorator, since the decorator has access to the method arguments. This would be much simpler:
from functools import wraps
def user_permission():
#wraps(f)
def decorated(self, *args, **kwargs):
if self.myname == 'My Name':
return f(self, *args, **kwargs)
else:
return "Not Permitted"
return decorated
self is just an argument. You don't need a decorator factory.
def user_permission(f):
#wraps(f)
def decorated(self, *args, **kw):
if self.myname == 'My Name':
return f(self, *args, **kw)
else:
return "Not Permitted"
return decorated

Python decorator on instance method

Anyone know what is wrong with this code?
def paginated_instance_method(default_page_size=25):
def wrap(func):
#functools.wraps(func)
def inner(self, page=1, page_size=default_page_size, *args, **kwargs):
objects = func(self=self, *args, **kwargs)
return _paginate(objects, page, page_size)
return inner
return wrap
class Event(object):
...
#paginated_instance_method
def get_attending_users(self, *args, **kwargs):
return User.objects.filter(pk__in=self.attending_list)
I get the following error:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/zarathustra/Virtual_Envs/hinge/hinge_services/hinge/api/decorators.py", line 108, in wrap
def inner(self, page=1, page_size=default_page_size, *args, **kwargs):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'Event' object has no attribute '__name__'
The reason why I thought this would work is because, through trial and error, I got the following decorator working like a charm for classmethods:
def paginated_class_method(default_page_size=25):
def wrap(func):
#functools.wraps(func)
def inner(cls, page=1, page_size=default_page_size, *args, **kwargs):
objects = func(cls=cls, *args, **kwargs)
return _paginate(objects, page, page_size)
return inner
return wrap
paginated_instance_method is not a decorator, it is a function that returns a decorator. So
#paginated_instance_method()
def get_attending_users(self, *args, **kwargs):
(Note the parentheses.)
Your decorator has an extra level of indirection which is throwing things off. When you do this:
#paginated_instance_method
def get_attending_users(self, *args, **kwargs):
return User.objects.filter(pk__in=self.attending_list)
You are doing this:
def get_attending_users(self, *args, **kwargs):
return User.objects.filter(pk__in=self.attending_list)
get_attending_users = paginated_instance_method(get_attending_users)
That is what decorators do. Note that paginated_instance_method is called with get_attending_users as its argument. That means that in your decorator, the argument default_page_size is set to the function get_attending_users. Your decorator returns the function wrap, so get_attending_users is set to that wrap function.
Then when you then call Event().get_attending_users() it calls wrap(self), where self is your Event instance. wrap is expecting the argument to be a function, and tries to return a new function wrapping that function. But the argument isn't a function, it's an Event object, so functools.wrap fails when trying to wrap it.
I have a hunch that what you're trying to do is this:
#paginated_instance_method()
def get_attending_users(self, *args, **kwargs):
return User.objects.filter(pk__in=self.attending_list)
That is, you want paginated_instance_method to take an argument. But even if you want to use the default value of that argument, you still have to actually call paginated_instance_method. Otherwise you just pass the method as the argument, which is not what paginated_instance_method is expecting.
The reason it "worked" for a classmethod is that a classmethod takes the class as the first argument, and a class (unlike an instance) does have a __name__ attribute. However, I suspect that if you test it further you'll find it's not really doing what you want it to do, as it's still wrapping the class rather than the method.
This is really easy, but tricky at first view. Look at pep 318.
#dec2
#dec1
def func(arg1, arg2, ...):
pass
This is equivalent to:
def func(arg1, arg2, ...):
pass
func = dec2(dec1(func))
You have an extra wrapper, which takes a decorator's args to use it in the wrapped functions (closure design pattern). So your decorator will look like this:
#dec(arg=True)
def func(arg1, arg2, ...):
pass
Equivalent to:
def func(arg1, arg2, ...):
pass
func = dec(arg=True)(func)

Difference between decorator classes and decorator functions

I guess that's how they are called, but I will give examples just in case.
Decorator class:
class decorator(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print 'something'
self.func(*args, **kwargs)
Decorator function:
def decorator(func):
def wrapper(*args, **kwargs):
print 'something'
return func(*args, **kwargs)
return wrapper
Is using one or the other just a matter of taste? Is there any practical difference?
If you can write a function to implement your decorator you should prefer it. But not all decorators can easily be written as a function - for example when you want to store some internal state.
class counted(object):
""" counts how often a function is called """
def __init__(self, func):
self.func = func
self.counter = 0
def __call__(self, *args, **kwargs):
self.counter += 1
return self.func(*args, **kwargs)
#counted
def something():
pass
something()
print something.counter
I've seen people (including myself) go through ridiculous efforts to write decorators only with functions. I still have no idea why, the overhead of a class is usually totally negligible.
It is generally just a matter of taste. Most Python programs use duck typing and don't really care whether the thing they're calling is a function or an instance of some other type, so long as it is callable. And anything with a __call__() method is callable.
There are a few advantages to using function-style decorators:
Much cleaner when your decorator doesn't return a wrapper function (i.e., it returns the original function after doing something to it, such as setting an attribute).
No need to explicitly save the reference to the original function, as this is done by the closure.
Most of the tools that help you make decorators, such as functools.wraps() or Michele Simionato's signature-preserving decorator module, work with function-style decorators.
There may be some programs out there somewhere which don't use duck typing, but actually expect a function type, so returning a function to replace a function is theoretically "safer."
For these reasons, I use function-style decorators most of the time. As a counterexample, however, here is a recent instance in which the class-style decorator was more natural for me.
The proposed class decorator implementation has a slight difference with the function implementation : it will fail on methods
class Decorator(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print('something')
self.func(*args, **kwargs)
class A:
#Decorator
def mymethod(self):
print("method")
A().mymethod()
will raise TypeError: mymethod() missing 1 required positional argument: 'self'
To add support of methods, you need to implement the __get__
import types
class Decorator2(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print('something')
self.func(*args, **kwargs)
def __get__(self, instance, owner):
if instance is None:
return self
return types.MethodType(self, instance)
class B:
#Decorator2
def mymethod(self):
print("method")
B().mymethod()
will output
class B:...
something
method
The reason it works is that when you access B().mymethod, the __get__ is called first and supplies the bound method. Then __call__ is called
To conclude, provided you define the __get__, class and function implementation can be used the same way. See python cookbook recipe 9.9 for more information.

Categories