5

I'm trying to document a decorator, but am not sure where the doc string should go. Technically, it's the inner wrapper that contains the parameters I want to document, but a user is going to be applying the outer function name as the decorator.

For example,

def change_case(func):
    """Does the doc string go here
    """
    def _wrapper(s, case=None):
        """Or, does the doc string go here?
        """
        if case == 'Upper':
            s = s.upper()
        elif case == 'Lower':
            s = s.lower()
        return func(s)

    return _wrapper

@change_case
def echo(s):
    return s

echo('Test', case='Upper')

In the above, does the doc string go after change_case() or _wrapper(). I'm leaning towards the former.

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
Chris
  • 1,313
  • 2
  • 13
  • 26
  • 1
    @unutbu I don't think that dupe is right. The OP is asking where the docstring for the **decorator** should go, not how to preserve the docstring of the decorated function. – Aran-Fey May 28 '18 at 15:32
  • @Aran-Fey: [Different questions may be closed as dupes if understanding the answer to one answers the other](https://meta.stackexchange.com/questions/217401/does-the-new-guidance-on-duplicate-questions-suggest-closing-a-question-as-dupli). – unutbu May 28 '18 at 15:47
  • 4
    @unutbu Sorry, I don't see the connection. How does knowing that you have to use `functools.wraps` on your inner function help you figure out where the docstring for the decorator goes? Even if you understand that `@wraps` overwrites the inner function's docstring, it could still be correct to put the docstring into the inner function just because static code analyzers like IDEs will pick it up. – Aran-Fey May 28 '18 at 15:54

1 Answers1

8

Put the documentation for the actually decorator in the top-level decorator function. When a user is attempting to use your decorator, this is where they would expect to find the documentation for it. For example, take the functools.singledispatch decorator from the standard library:

>>> from functools import singledispatch
>>>
>>> print(singledispatch.__doc__) # the decorator has it's documentation in the actually decorator function
Single-dispatch generic function decorator.

    Transforms a function into a generic function, which can have different
    behaviours depending upon the type of its first argument. The decorated
    function acts as the default implementation, and additional
    implementations can be registered using the register() attribute of the
    generic function.


>>>

However, you should also use functools.wraps to transfer any documentation contained in the decorated function to the wrapper function:

>>> from functools import wraps
>>>
>>> def dec(func):
...     @wraps(func)
...     def wrap(*args, **kwargs):
...         return func(*args, **kwargs)
...     return wrap
...
>>> @dec
... def func():
...     """Docstring here"""
...
>>> print(func.__doc__)
Docstring here
>>>
Christian Dean
  • 22,138
  • 7
  • 54
  • 87