3

I'm currently implementing an API on which I need to decorate the class Wrapper, but I want it to keep its docstring to make it available to the API user. Take a look at the following minimal working example :

class ClassDecorator:
    """ClassDecorator docstring
    """
    def __init__(self, enableCache=True):
        self.enableCache = enableCache

    def __call__(self, wrapper):
        def numericalmathfunction(*args, **kwargs):
            func = wrapper(*args, **kwargs)
            return func
        return numericalmathfunction

@ClassDecorator(enableCache=True)
class Wrapper(object):
    """Wrapper docstring
    Instructions on how to use the Wrapper
    """
    def __init__(self, p):
        self.p = p

model = Wrapper(4)
print model.__doc__
print Wrapper.__doc__

This returns

Wrapper docstring
None

Instances of Wrapper do keep the docstring, which is fine, but Wrapper itself does not. If a user wants to learn how to use Wrapper using help(Wrapper), he won't get what he wants.

I know I could just copy paste the dosctring into numericalmathfunction, but the decorator will be used on several classes with different docstrings.

Any ideas on how to make numericalmathfunction systematically inherit the docstrings of the wrapped class ?

Felipe Aguirre
  • 218
  • 1
  • 12
  • 2
    Because the wrapper is now technically the class that is being called first. Look into `@wraps` [here](https://docs.python.org/2/library/functools.html#functools.wraps) – MrAlexBailey Jun 08 '15 at 16:31

1 Answers1

4

Use functools.wraps() to update the attributes of the decorator:

from functools import wraps

class ClassDecorator:
    """ClassDecorator docstring
    """
    def __init__(self, enableCache=True):
        self.enableCache = enableCache

    def __call__(self, wrapper):
        @wraps(wrapper)                              # <-----------
        def numericalmathfunction(*args, **kwargs):
            func = wrapper(*args, **kwargs)
            return func
        return numericalmathfunction

@ClassDecorator(enableCache=True)
class Wrapper(object):
    """Wrapper docstring
    Instructions on how to use the Wrapper
    """
    def __init__(self, p):
        self.p = p

See more standard library documentation for functools.wrap.

ronakg
  • 4,038
  • 21
  • 46
  • Great, it works. Is there a similar solution so that the decorated function inherits class attributes? I already found a solution to this (see [here](http://stackoverflow.com/questions/30711730/decorated-class-looses-acces-to-its-attributes)), but I was wondering if there is something of the likes of `@wraps`. – Felipe Aguirre Jun 08 '15 at 16:56
  • Not that I'm aware of. You can easily write a wrapper decorator based on the answer in that link and use it everywhere. That should work the same way as `@wraps`. – ronakg Jun 08 '15 at 17:01
  • Ok, I'll try that later on to make the code more elegant. For the moment, I already have a working solution. Thanks – Felipe Aguirre Jun 08 '15 at 17:07
  • @FelipeAguirre For some great examples of this type of work, watch [this](https://www.youtube.com/watch?v=sPiWg5jSoZI). The first 30 minutes especially. – MrAlexBailey Jun 08 '15 at 17:14
  • I'll give it a look, but I'm still stuck with python 2 and the tutorial is for python 3. :( – Felipe Aguirre Jun 08 '15 at 17:18