Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pdoc fails to find docstrings on functions that have been turned into non-function objects by decorators #416

Closed
jeamland opened this issue Jul 22, 2022 · 3 comments
Labels

Comments

@jeamland
Copy link

Problem Description

When a decorator turns a function into something insufficiently function-like, pdoc fails to attach any documentation to it.

Steps to reproduce the behavior:

Start with some code along the lines of this:

from functools import wraps

def vanilla_function(arg: str) -> str:
    "This is a function. It will be documented correctly."
    pass

def function_decorator(f):
    "This is a decorator function. It will be documented correctly."
    @wraps(f)
    def wrapper(*args, **kwargs):
        "This is a wrapper function. It will not be documented but that's fine."
        pass
    return wrapper

@function_decorator
def decorated_function(arg: str) -> str:
    "This is a decorated function. It will be documented correctly."
    pass

class ClassDecorator:
    "This is a class that wraps a function. It will be documented correctly."
    def __init__(self, f):
        self.f = f
    
@ClassDecorator
def another_decorated_function(arg: str) -> str:
    "This is another decorated function. It will not be documented correctly."
    pass

When run through pdoc, this ends up looking a bit like this:

Screen Shot 2022-07-22 at 16 15 30

Note that the last function (another_decorated_function) lacks a docstring. It is possibly correct to have it render as the object it's been turned into but ideally the documentation should follow it.

System Information

> pdoc --version
pdoc: 12.0.2 (+4, commit 441a5c3)
Python: 3.10.2
Platform: Linux-5.4.17-2136.309.5.el8uek.x86_64-x86_64-with-glibc2.28
@jeamland jeamland added the bug label Jul 22, 2022
@jeamland
Copy link
Author

jeamland commented Jul 22, 2022

The following patch makes the docstring show up for another_decorated_function but given that it's potentially stepping on work done in the other function parsers and also may be a bit ungainly I figured it'd warrant some discussion before actually committing it:

diff --git a/pdoc/doc_ast.py b/pdoc/doc_ast.py
index d8c3603..1353e6c 100644
--- a/pdoc/doc_ast.py
+++ b/pdoc/doc_ast.py
@@ -110,6 +110,11 @@ def _walk_tree(
             and isinstance(a.targets[0], ast.Name)
         ):
             name = a.targets[0].id
+        elif isinstance(a, ast.FunctionDef) and a.body:
+            first = a.body[0]
+            if isinstance(first, ast.Expr) and isinstance(first.value, ast.Str):
+                docstrings[a.name] = inspect.cleandoc(first.value.s).strip()
+            continue
         else:
             continue
         if (```

@jeamland
Copy link
Author

For context, I came across this when trying to document some code that was using click, and specifically the subcommand/command group functionality. Functions decorated with @click.group would not be documented correctly.

@mhils
Copy link
Member

mhils commented Jul 22, 2022

Hi @jeamland! My first inclination would have been to say "fix your code", but your click example makes sense and the patch is sufficiently minimal. If that's all that's needed I'd be more than happy to merge a PR! 😃
You can add a snapshot test around here (see https://github.com/mitmproxy/pdoc/blob/main/CONTRIBUTING.md#fixing-snapshot-tests).

@mhils mhils closed this as completed in 3091ed2 Aug 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants