"""
=============
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]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()