[Interface-dev] subclasses of classes which implement interfaces are not documented by pydoc

glyph at divmod.com glyph at divmod.com
Sat Sep 30 01:18:01 EDT 2006


I noticed this issue has come up on the zope3 list recently as well.

Twisted uses pydoctor for generating documentation, and I know that Zope uses APIDoc, so I understand that this is not a particularly high priority issue.  I can also see that pydoc's implementation is... questionable, at best, due to the way it semi-randomly throws away any error information.

However, I tracked down the problem a bit, so perhaps these investigations will serve useful:

The problem, as described in the topic, can be expressed by this code:

    # dontdocme.py
    from zope.interface import Interface, implements

    class IDestroyDocs(Interface):
        """
        I am a random interface.
        """

    class A:
        """
        I implement a thing.
        """
        implements(IDestroyDocs)

    class B(A):
        """
        Voila: I have no documentation.
        """
        # Without the following line, this class will not work with pydoc:
        # implements(IDestroyDocs)


"help(B)" at an interactive interpreter or "pydoc dontdocme.B" at a commandline will not show any information about B.

If you look down in the guts of pydoc for what's happening, it's due to the fact that attributes of A aren't showing up on B.  I think that in reality this is a bug in pydoc, but it seems that it might break other code. 

>>> pydoc.text.docclass(dontdocme.B)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "C:\Python24\lib\pydoc.py", line 1170, in docclass
    inspect.classify_class_attrs(object))
  File "C:\Python24\lib\inspect.py", line 211, in classify_class_attrs
    obj = getattr(cls, name)
AttributeError: __provides__

Pydoc then erroneously catches AttributeError from this code and falls back to a simplistic str() based approach.  Really this is a special case of the following:

>>> dontdocme.A.__provides__.__get__(None, dontdocme.A)
<zope.interface.declarations.ClassProvides object at 0x00AFCFD0>
>>> dontdocme.A.__provides__.__get__(None, dontdocme.B)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: __provides__

It's not really clear to me why this works this way; IDestroyDocs is implemented by B as well as A due to inheritance, so why aren't its various magical attributes visible there as well?  I realize these are implementation details, but the behavior seems inconsistent unless B also needed to explicitly declare all the interfaces it implemented.



More information about the Interface-dev mailing list