Preserve default arguments of wrapped/decorated Python function in Sphinx documentation

前端 未结 2 803
攒了一身酷
攒了一身酷 2021-01-05 01:00

How can I replace *args and **kwargs with the real signature in the documentation of decorated functions?

Let\'s say I have the following d

相关标签:
2条回答
  • 2021-01-05 01:18

    You ordinarily can't. That is because the variable names used as parameters in the wrapped function are not even present on the wrapped function - so Sphinx do not know about them.

    That is a known complicated issue in Python - so much that recent versions - including not only Python 3, but also Python 2.7 included a __wrapped__ attribute on class decorated that make the proper use from functools.wraps - that way, upon inspecting the decorated function one is able to know about the actual wrrapped function by looking at __wrapped__. Unfortunatelly, Sphinxs ignores the __wrapped__, and show the info on the wrapper function instead.

    SO, one thing to do is certainly to report this as a bug to the Sphinx project itself - it should take __wrapped__ in account.

    A meantime workaround for that would be to change the wrapper function to actually include more information about the wrapped - like its signature - so you could write another function to be called in place of "functools.wraps" for your project, which does just that: pre-pend the function signature to its docstring, if any. Unfortunatelly, retrieving the function signatures in Python older than 3.3 is tricky - (for 3.3 and newer, check https://docs.python.org/3/library/inspect.html#inspect-signature-object ) - but anyway, for a naive form, you could write another version of "wraps" along:

    def wraps(original_func):
       wrap_decorator = functools.wraps(original_func)
       def re_wrapper(func):
           wrapper = wrap_decorator(func)
           poorman_sig = original_func.__code__.co_varnames[
                             :original_func.__code__.co_argcount]
           wrapper.__doc__ = "{} ({})\n\n{}".format (
                original_func.__name__, ", ".join(poorman_sig),
                wrapper.__doc__) 
           return wrapper
       return re_wrapper
    

    And use that instead of "functools.wraps". It would at least add a line with the parameter names, (but not th e defalt values) as first line in the docs.

    ---Hmm..maybe it would be easier just to patch Sphinx to use __wrapped__ before getting this done right.

    0 讨论(0)
  • 2021-01-05 01:31

    I came up with a monkey-patch for functools.wraps. Accordingly, I simply added this to the conf.py script in my project documentation's sphinx source folder:

    # Monkey-patch functools.wraps
    import functools
    
    def no_op_wraps(func):
        """Replaces functools.wraps in order to undo wrapping.
    
        Can be used to preserve the decorated function's signature
        in the documentation generated by Sphinx.
    
        """
        def wrapper(decorator):
            return func
        return wrapper
    
    functools.wraps = no_op_wraps
    

    Hence, when building the html page via make html, functools.wraps is replaced with this decorator no_op_wraps that does absolutely nothing but simply return the original function.

    0 讨论(0)
提交回复
热议问题