Source code for sphinxcontrib.zopeext.autointerface

"""
=============
autointerface
=============

This Sphinx extension adds an :rst:dir:`autointerface` directive, which can be
used like :rst:dir:`sphinx:autoclass` to document zope interfaces.  Interfaces
are intended to be very different beasts than regular python classes, and as a
result require customized access to documentation, signatures etc.

.. rst:directive:: autointerface

   The :rst:dir:`autointerface` directive has the same form and option as the
   :rst:dir:`sphinx:autoclass` directive::

       .. autointerface:: IClass
          ...

   .. seealso:: :mod:`sphinx.ext.autodoc`

   .. note:: This extension also serves as a simple example of using the sphinx
      version 0.6 :mod:`sphinx.ext.autodoc` refactoring.  Mostly this was
      straight forward, but I stumbled across one "gotcha":

      The `objtype` attribute of the documenters needs to be unique.  Thus, for
      example, :attr:`InterfaceMethodDocumenter.objtype` cannot be `'method'`
      because this would overwrite the entry in :attr:`AutoDirective._registry`
      used to choose the correct documenter.

======================
Implementation Details
======================

.. autosummary::
   
   interface_getattr
   interface_format_args
   InterfaceDocumenter
   InterfaceAttributeDocumenter
   InterfaceMethodDocumenter
   InterfaceDirective
   setup

"""
import sphinx.ext.autodoc
import sphinx.domains.python
import sphinx.roles
from sphinx.locale import l_

import zope.interface.interface

[docs]def interface_getattr(*v): """Behaves like `getattr` but for zope Interface objects which hide the attributes. .. note:: Originally I simply tried to override :meth:`InterfaceDocumenter.special_attrgetter` to deal with the special access needs of :class:`Interface` objects, but found that this is not intended to be overwritten. Instead one should register the special accessor using :func:`app.add_autodoc_attrgetter`. """ obj, name = v[:2] if "__dict__" == name: # Interface objects do not list their members through # __dict__. return dict((n,obj.get(n)) for n in obj.names()) try: return getattr(obj, name) except AttributeError: if name in obj.names(all=True): return obj.get(name) elif 2 < len(v): return v[2] else: raise
[docs]def interface_format_args(obj): r"""Return the signature of an interface method or of an interface.""" sig = "()" if isinstance(obj, zope.interface.interface.InterfaceClass): if '__init__' in obj: sig = interface_format_args(obj.get('__init__')) else: sig = obj.getSignatureString() if sig.startswith('(self, '): sig = "(" + sig[7:] elif sig.startswith('(cls, '): sig = "(" + sig[6:] elif sig in ("(self)", "(cls)"): sig = "()" return sig
[docs]class InterfaceDocumenter(sphinx.ext.autodoc.ClassDocumenter): """A Documenter for :class:`zope.interface.Interface` interfaces. """ objtype = 'interface' # Called 'autointerface' # Since these have very specific tests, we give the classes defined here # very high priority so that they override any other documenters. priority = 100 + sphinx.ext.autodoc.ClassDocumenter.priority @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, zope.interface.interface.InterfaceClass) def format_args(self): return interface_format_args(self.object)
[docs] def get_object_members(self, want_all): """ Return `(members_check_module, members)` where `members` is a list of `(membername, member)` pairs of the members of *self.object*. If *want_all* is True, return all members. Else, only return those members given by *self.options.members* (which may also be none). """ obj = self.object names = obj.names(want_all) return False, [(_name, obj.get(_name)) for _name in names]
[docs]class InterfaceAttributeDocumenter(sphinx.ext.autodoc.AttributeDocumenter): """A Documenter for :class:`zope.interface.interface.Attribute` interface attributes. """ objtype = 'interfaceattribute' # Called 'autointerfaceattribute' directivetype = 'attribute' # Formats as a 'attribute' for now priority = 100 + sphinx.ext.autodoc.AttributeDocumenter.priority @classmethod def can_document_member(cls, member, membername, isattr, parent): res = (isinstance(member, zope.interface.interface.Attribute) and not isinstance(member, zope.interface.interface.Method)) return res def add_content(self, more_content, no_docstring=False): # Revert back to default since the docstring *is* the correct thing to # display here. sphinx.ext.autodoc.ClassLevelDocumenter.add_content( self, more_content, no_docstring)
[docs]class InterfaceMethodDocumenter(sphinx.ext.autodoc.MethodDocumenter): """ A Documenter for :class:`zope.interface.interface.Attribute` interface attributes. """ objtype = 'interfacemethod' # Called 'autointerfacemethod' directivetype = 'method' # Formats as a 'method' for now priority = 100 + sphinx.ext.autodoc.MethodDocumenter.priority @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, zope.interface.interface.Method) def format_args(self): return interface_format_args(self.object) #class InterfaceDirective(sphinx.ext.autodoc.AutoDirective):
[docs]class InterfaceDirective(sphinx.domains.python.PyClasslike): r"""An `'interface'` directive.""" def get_index_text(self, modname, name_cls): if self.objtype == 'interface': if not modname: return '%s (built-in interface)' % name_cls[0] return '%s (%s interface)' % (name_cls[0], modname) else: return ''
def setup(app): app.add_autodoc_attrgetter(zope.interface.interface.InterfaceClass, interface_getattr) app.add_autodocumenter(InterfaceDocumenter) app.add_autodocumenter(InterfaceAttributeDocumenter) app.add_autodocumenter(InterfaceMethodDocumenter) domain = sphinx.domains.python.PythonDomain domain.object_types['interface'] = sphinx.domains.python.ObjType( l_('interface'), 'interface', 'obj') domain.directives['interface'] = InterfaceDirective domain.roles['interface'] = sphinx.domains.python.PyXRefRole()