Search code examples
pythondecoratorclass-methodpython-decorators

How do I use the python-decorator package to decorate classmethods?


I'm have a decorator that I want to use to decorate class methods. In the following example, the @mydec decorator works fine on its own, however it does not preserve the function signature when using help() or pydoc. In order to fix this, I looked at using @decorator python-decorator package:

import functools
import decorator


@decorator.decorator
def mydec(func):
    @functools.wraps(func)
    def inner(cls, *args, **kwargs):
        # do some stuff
        return func(cls, *args, **kwargs)
    return inner


class Foo(object):
    @classmethod
    @mydec
    def bar(cls, baz='test', qux=None):
        print (baz, qux)


Foo.bar()

Unfortunately, this results in the following exception:

Traceback (most recent call last):
  File "/tmp/test.py", line 21, in <module>
    Foo.bar()
  File "<string>", line 2, in bar
TypeError: mydec() takes exactly 1 argument (4 given)

Solution

  • You do not need to provide your own wrapper anymore, just use @decorator.decorator on the inner function, which takes one extra first positional argument, the function wrapped:

    @decorator.decorator
    def mydec(func, cls, *args, **kwargs):
        # do some stuff
        return func(cls, *args, **kwargs)
    

    The decorator package doesn't use a closure for decorators and instead passes in the wrapped function as an argument.

    Demo:

    >>> @decorator.decorator
    ... def mydec(func, cls, *args, **kwargs):
    ...     # do some stuff
    ...     return func(cls, *args, **kwargs)
    ... 
    >>> class Foo(object):
    ...     @classmethod
    ...     @mydec
    ...     def bar(cls, baz='test', qux=None):
    ...         print (baz, qux)
    ... 
    >>> Foo.bar()
    ('test', None)