Search code examples
pythondecoratordocstring

Using class attributes to modify a docstring with a decorator in Python


I’m trying to create a decorator that is called within a class, which would pull attributes from that class, and use those class attributes to edit the function’s docstring.

My problem is that I have found examples of decorators that edit the docstring of the function (setting the function's __doc__ attribute equal to a new string), and I have also found examples of decorators that pull attributes from the parent class (by passing self into the decorator), but I haven’t been able to find an example of a decorator that is able to do both.

I have tried to combine these two examples, but it isn't working:

def my_decorator(func):
    def wrapper(self, *args, **kwargs):
        name = func.__name__  # pull function name
        cls = self.__class__.__name__ # pull class name
        func.__doc__ = "{} is new for the function {} in class {}".format(
            str(func.__doc__), name, cls) # set them to docstring
        return func(self, *args, **kwargs)
    return wrapper

class Test():
    @my_decorator
    def example(self, examplearg=1):
        """Docstring"""
        pass

With this, I would hope that the following would return "Docstring is now new for the function: example":

Test().example.__doc__

Instead it returns None.

Edit: Note that I am not interested in how to access the name of the class specifically, so much as how to access the class attributes in general (where here self.__class__.__name__ is used as an example).


Solution

  • Turns out that accessing class attributes from within a class is impossible, as the class has yet to be executed when the decorator is called. So the original goal - using a decorator within a class to access class attributes - does not seem to be possible.

    However, thanks to jdehesa for pointing me to a workaround that allows access to the class attributes using a class decorator, here: Can a Python decorator of an instance method access the class?.

    I was able to use the class decorator to alter the specific method's docstring using class attributes like so:

    def class_decorator(cls):
        for name, method in cls.__dict__.items():
            if name == 'example':
                # do something with the method
                method.__doc__ = "{} is new for function {} in class {}".format(method.__doc__, name, cls.__name__)
                # Note that other class attributes such as cls.__base__ 
                # can also be accessed in this way
        return cls
    
    @class_decorator
    class Test():
        def example(self, examplearg=1):
            """Docstring"""
    
    print(Test().example.__doc__)
    # Returns "Docstring is new for function example in class Test"