Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a pythonic way to skip decoration on a subclass' method?

I have an class which decorates some methods using a decorator from another library. Specifically, the class subclasses flask-restful resources, decorates the http methods with httpauth.HTTPBasicAuth().login_required(), and does some sensible defaults on a model service.

On most subclasses I want the decorator applied; therefore I'd rather remove it than add it in the subclasses.

My thought is to have a private method which does the operations and a public method which is decorated. The effects of decoration can be avoided by overriding the public method to call the private one and not decorating this override. Mocked example below.

I am curious to know if there's a better way to do this. Is there a shortcut for 'cancelling decorators' in python that gives this effect?

Or can you recommend a better approach?

Some other questions have suitable answers for this, e.g. Is there a way to get the function a decorator has wrapped?. But my question is about broader design - i am interested in any pythonic way to run the operations in decorated methods without the effects of decoration. E.g. my example is one such way but there may be others.

def auth_required(fn):
    def new_fn(*args, **kwargs):
        print('Auth required for this resource...')
        fn(*args, **kwargs)
    return new_fn

class Resource:
    name = None

    @auth_required
    def get(self):
        self._get()

    def _get(self):
        print('Getting %s' %self.name)

class Eggs(Resource):
    name = 'Eggs'

class Spam(Resource):
    name = 'Spam'

    def get(self):
        self._get()
        # super(Spam, self)._get()

eggs = Eggs()
spam = Spam()

eggs.get()
# Auth required for this resource...
# Getting Eggs

spam.get()
# Getting Spam
like image 417
Jotham Apaloo Avatar asked Feb 02 '16 23:02

Jotham Apaloo


People also ask

Are decorators Pythonic?

Decorators are a very powerful and useful tool in Python since it allows programmers to modify the behaviour of a function or class. Decorators allow us to wrap another function in order to extend the behaviour of the wrapped function, without permanently modifying it.

Which decorator function is used to create a class method?

To decorate a function with a class, we must use the @syntax followed by our class name above the function definition. Following convention, we will use camel-case for our class name. In the class definition, we define two methods: the init constructor and the magic (or dunder) call method.

What are method decorators in Python?

A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.

What are method decorators?

A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form @expression , where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.


1 Answers

Flask-HTTPAuth uses functools.wraps in the login_required decorator:

def login_required(self, f):
    @wraps(f)
    def decorated(*args, **kwargs):
        ...

From Python 3.2, as this calls update_wrapper, you can access the original function via __wrapped__:

To allow access to the original function for introspection and other purposes (e.g. bypassing a caching decorator such as lru_cache()), this function automatically adds a __wrapped__ attribute to the wrapper that refers to the function being wrapped.

If you're writing your own decorators, as in your example, you can also use @wraps to get the same functionality (as well as keeping the docstrings, etc.).

See also Is there a way to get the function a decorator has wrapped?

like image 177
jonrsharpe Avatar answered Oct 02 '22 14:10

jonrsharpe