question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

method_decorators are not regular decorators

See original GitHub issue

Hello,

I just spot one weird behavior about the method_decorators feature and I want to know if it was intended or not.

This is how you normally make decorators for object’s methods:

from functools import wraps


def dec1(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        self.dec1 = True
        return func(self, *args, **kwargs)
    return wrapper


def dec2(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        self.dec2 = True
        return func(self, *args, **kwargs)
    return wrapper


class A:
    @dec1
    @dec2
    def foo(self):
        print(vars(self))

a = A()
a.foo()
# outputs: {'dec2': True, 'dec1': True}

As you can see there, you’re supposed to receive self in the arguments. But this particular example doesn’t work at all with the method_decorators simply because the method you receive as func is bind to the instance object. Therefore, you don’t receive self in the arguments and it is not possible to do something with self.

Of course you can still get the Resource’s instance in the __self__ attribute of the function. But it will work only with the first decorator of the list. The next decorators will receive the wrapper method of each previous decorator (and those are not bind to the Resource’s instance).

Example (in Python 3):

from functools import wraps


def dec1(func):
    @wraps(func)
    def wrapper(**kwargs):
        func.__self__.dec1 = True # ok
        return func(**kwargs)
    return wrapper


def dec2(func):
    @wraps(func)
    def wrapper(**kwargs):
        func.__self__.dec2 = True # fails because func is actually the wrapper function of dec1 and doesn't have a __self__ attribute
        return func(**kwargs)
    return wrapper


class A(Resource):
    method_decorators = [dec1, dec2]

    def get(self, **kwargs):
        print(vars(self))

If this behavior is not intended, it can be easily fixed by getting the class’s method and provide the self argument during the call. Here is how to do it:

class Resource(MethodView):
    """
    Represents an abstract RESTful resource. (...)
    """
    representations = None
    method_decorators = []

    def dispatch_request(self, *args, **kwargs):

        meth = getattr(type(self), request.method.lower(), None) # get the class' method that is not bound to the instance self
        if meth is None and request.method == 'HEAD':
            meth = getattr(type(self), 'get', None) # get the class' method
        assert meth is not None, 'Unimplemented method %r' % request.method

        for decorator in self.method_decorators:
            meth = decorator(meth) # no change, we just apply each function to the next decorator

        resp = meth(self, *args, **kwargs) # call the method with the instance object in argument

        (...)

Do you want a PR for this proposal?

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:4
  • Comments:14 (2 by maintainers)

github_iconTop GitHub Comments

3reactions
antoine-lizeecommented, Aug 19, 2016

+1 on the bad naming, and willingness to implement @cecton 's proposal. I’ve been trying to refactor three stacked decorators called on every method of a parent Resource using the method_decorators with no success. (because I’m using self to access resource specific properties like self.authorized_methods). They are absolutely not equivalent to classic python decorators and it might be good to have a note about it in docs if you guys don’t want to change the current behavior.

1reaction
alexregcommented, Dec 12, 2017

This is a real deal-breaker for me too… I’m going to have to abandon the method_decorators approach and manually decorate my methods.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Documentation - Decorators - TypeScript
A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use...
Read more >
python - Why can @decorator not decorate a staticmethod or a ...
classmethod and staticmethod return descriptor objects, not functions. Most decorators are not designed to accept descriptors.
Read more >
Primer on Python Decorators
In this introductory tutorial, we'll look at what Python decorators are and how to create and use them.
Read more >
Decorators not allowed classes expressions #7342 - GitHub
The whole problem seems to be that you can't return directly a class with decorators but if you define the class with a...
Read more >
Decorators do not work as you might expect
Decorators are class and property-based, meaning they are applied and executed once per decorated property when the class gets loaded. This ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found