pdoc3 / pdoc

:snake: :arrow_right: :scroll: Auto-generate API documentation for Python projects
https://pdoc3.github.io/pdoc/
GNU Affero General Public License v3.0
1.14k stars 146 forks source link

pdoc3 skips a decorated function (callable object) #221

Open ShuhuaGao opened 4 years ago

ShuhuaGao commented 4 years ago

Expected Behavior

Generate the documentation for add3

Actual Behavior

Nothing is generated

Steps to Reproduce

  1. Create a script "pdoc_tt.py" as follows
    
    from numba import cuda

@cuda.jit def add3(a: int, b: int) -> int: """Add two integers

Args:
    a (int): one integer
    b (int): the other integer

Returns:
    int: the sum
"""
return a + b

add3 is a callable object of type 'numba.cuda.compiler.AutoJitCUDAKernel`

set the two attributes below manually, since AutoJitCUDAKernel does not preserve them

add3.module = name add3.doc = add3.py_func.doc # the right hand side is just the original doc

print(callable(add3)) print(add3.module) print(add3.doc)

The package `numba` is found [here](https://numba.pydata.org/numba-doc/dev/index.html)

2. run command `pdoc3 --html pdoc_tt.py`
3. We see the following output in the terminal

True pdoc_tt Add two integers

Args:
    a (int): one integer
    b (int): the other integer

Returns:
    int: the sum

As we see, `add3` is indeed `callable`, and its `__module__` and `__doc__` are also right. However, there is no documentation generated for `add3` in the Html "pdoc_tt.html".

### Additional info
Adding `__pdoc__ = {'add3': True}` makes no difference.
- pdoc version: pdoc.exe 0.8.1 (on Windows 10)
ShuhuaGao commented 4 years ago

Well, it seems the reason is the following method in pdoc3:

def _is_function(obj):
    return inspect.isroutine(obj) and callable(obj)

Here, for the above add3 of type numba.cuda.compiler.AutoJitCUDAKernel, callable yields true while inspect.isroutine gives false. My question is why and is used here for the two conditions. That is, what is the problem if we use or instead?

kernc commented 4 years ago

My question is why and is used here for the two conditions. That is, what is the problem if we use or instead?

When in doubt, best to flip the line and see what, if any, tests it breaks. (Hint: all of them.) The line was introduces as part of https://github.com/pdoc3/pdoc/pull/77, addressing read-only value descriptors (https://github.com/pdoc3/pdoc/issues/76).

The function ensures the passed object is callable and indeed a routine, as opposed to an arbitrary object which just happens to be callable (its class defines __call__), as is the case here.

If you can think of a way to adapt this condition check to also cover your case, a PR is welcome! https://github.com/pdoc3/pdoc/blob/64405338c0b58207f80d264b07cd7fe17694e2d5/pdoc/__init__.py#L611-L613

Pdoc3, though, diligently calls inspect.unwrap() on detected objects: https://github.com/pdoc3/pdoc/blob/64405338c0b58207f80d264b07cd7fe17694e2d5/pdoc/__init__.py#L604 so really it's numba at fault, for they should update_wrapper(). This way, you also wouldn't need to manually preserve __module__ and __doc__. I see this is already filed as https://github.com/numba/numba/issues/5902. :+1: It also seems to be an easy, good first issue, so welcome to give them a hand!

ShuhuaGao commented 4 years ago

Hi, kernc,

Thank you for your help. I have tried the or logic instead as follows:

def _is_function(obj):
    return inspect.isroutine(obj) or callable(obj)

aside from setting __module__ and __doc__ manually.

However, it still failed to generate the documentation. I guess more changes are needed for this workaround, which is a little beyond my capability. For now, I will wait for the update from numba.