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

Jim Fulton jim at zope.com
Sun Oct 1 09:23:30 EDT 2006


glyph at divmod.com wrote:
> 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:

Thanks for digging into this.

> 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.

Yes. A common mistake in class-introspection code is to assume that is a class
has a key in it's dictionary (or in the dictionary of one of its ancestors,
that it has a corresponding attribute.  This isn't true in general.

>>>> 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.

This is a good question.  You are right that __provides__ is an internal
attribute.  If one were to use the proper, providedBy on A and B, you would get
equivalent answers.  For example:

   print list(providedBy(A))
   print list(providedBy(B))

prints 2 empty lists.

The intent is that declaring that a class provides an interface says
nothing about it's subclasses. (Some people don't agree with this
intent.)

When you make any declarations about a class, internal descriptors
are set up for it.  In the example above, a __provides__ descriptor
for A is established that returns an empty ClassProvides specification.
To prevent inheritence, the internal __provides__ descriptor bothers to
prevent inheritance by raising an AttributeError if it is used on a class
other than the one it was defined for. I suppose that, alternatively it
could either:

- Return an empty ClassProvides specification or

- Initialize the class' interface meta data.

The former change is easier and almost certainly adequate.

This would work around the common bug in introspection
systems. (A Zope-internal introspection facility had the same bug.)

Would you mind submitting a bug report:
https://launchpad.net/products/zope.interface/+bugs

Jim

-- 
Jim Fulton           mailto:jim at zope.com       Python Powered!
CTO                  (540) 361-1714            http://www.python.org
Zope Corporation     http://www.zope.com       http://www.zope.org


More information about the Interface-dev mailing list