Skip to content

Support for read-only value descriptors #76

@rysson

Description

@rysson

Expected Behavior

Detect if read-only descriptor is data or method descriptor.

Generate documentation.

Actual Behavior

 File "/HOME/.local/lib/python3.7/site-packages/pdoc/__init__.py", line 1020, in __init__
    assert callable(obj)

in Class.__init__ (L822) inspect.isroutine(obj) is checked and in Function.__init__ (L1020) callable(obj) is asserted.

Problem is when obj is read-only value descriptor (has __get__, no __set__ and __get__() returns non-callable value).

From inspect.py (Python 3.7):

def isroutine(object):
    """Return true if the object is any kind of function or method."""
    return (isbuiltin(object)
            or isfunction(object)
            or ismethod(object)
            or ismethoddescriptor(object))

Follow documentation inspect.ismethoddescriptor(object):

An object passing this test has a __get__() method but not a __set__() method...

From inspect.py (Python 3.7):

def ismethoddescriptor(object):
    if isclass(object) or ismethod(object) or isfunction(object):
        # mutual exclusion
        return False
    tp = type(object)
    return hasattr(tp, "__get__") and not hasattr(tp, "__set__")

Then assert failed on read-only value descriptors (like peewee.BackrefAccessor) and documentation is not generated.

Steps to Reproduce

  1. Use read-only value descriptor, for example (foo.py):
class Power:
    def __get__(self, instance, instance_type=None):
        if instance is not None:
            return instance.value ** 2
        return self

class Value:
    power = Power()
    def __init__(self, value):
        self.value = value
  1. Run pdoc --html foo.py

Additional info

  • pdoc version: 0.6.3

  • minimal workaround I've found, change Class.__init__() (L822):

--- pdoc-0.6.3/__init__.py  2019-07-04 07:34:53.857974401 +0200
+++ pdoc/__init__.py  2019-07-04 07:34:33.366110353 +0200
@@ -819,7 +819,7 @@
         for name, obj in public_objs:
             if name in self.doc and self.doc[name].docstring:
                 continue
-            if inspect.isroutine(obj):
+            if inspect.isroutine(obj) and callable(obj):
                 self.doc[name] = Function(
                     name, self.module, obj, cls=self,
                     method=not self._method_type(self.obj, name))

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions