Skip to content

Instantly share code, notes, and snippets.

@nnemkin
Last active August 29, 2015 13:56
Show Gist options
  • Save nnemkin/9065600 to your computer and use it in GitHub Desktop.
Save nnemkin/9065600 to your computer and use it in GitHub Desktop.
Sphinx extension example. It doesn't really work as is but gives you hints on what you can extend.
# -*- coding: utf-8 -*-
#
# SWT documentation build configuration file, created by
# sphinx-quickstart on Tue Feb 26 18:19:43 2013.
#
# The author of this program disclaims copyright.
import sys, os
import re
from docutils import nodes
from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.domains.python import PythonDomain, PyClasslike, PyClassmember
from sphinx.ext.autodoc import (Documenter, ClassDocumenter,
ExceptionDocumenter, AttributeDocumenter)
# General configuration
needs_sphinx = '1.2'
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
'sphinxcontrib.fulltoc']
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
rst_epilog = """
.. |platform_attribute| replace:: This field is not intended to be referenced by clients.
"""
# Autodoc
autodoc_member_order = 'groupwise'
autodoc_default_flags = ['members', 'undoc-members', 'show-inheritance']
autosummary_generate = True
inheritance_graph_attrs = dict(rankdir='TD')
# Custom extension
class SwtClassDocumenterMixin(object):
def add_directive_header(self, sig):
if self.doc_as_attr:
self.directivetype = 'attribute'
Documenter.add_directive_header(self, sig)
# add inheritance info, if wanted
if not self.doc_as_attr and self.options.show_inheritance:
bases = ', '.join(b.__module__ == '__builtin__' and
b.__name__ or
u'%s.%s' % (b.__module__, b.__name__)
for b in self.object.__bases__
if b is not object)
if bases:
self.add_line(' :bases: ' + bases, '<autodoc>')
class SwtClassDocumenter(SwtClassDocumenterMixin, ClassDocumenter):
priority = ClassDocumenter.priority + 0.1
class SwtExceptionDocumenter(SwtClassDocumenterMixin, ExceptionDocumenter):
priority = ExceptionDocumenter.priority + 0.1
class SwtAttributeDocumenter(AttributeDocumenter):
"""
AttributeDocumenter that understands Cython-generated property docstrings
of the form:
name: 'type'
"""
priority = AttributeDocumenter.priority + 0.1
member_order = 45
property_sig_re = re.compile(r"^\s*([\w.]+): '([\w.]+)'\s*$")
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
if type(member).__name__ == 'wrapper_descriptor' and member.__name__ == '__init__' and member != object.__init__:
return False
return super(SwtAttributeDocumenter, cls).can_document_member(
member, membername, isattr, parent)
def _find_signature(self, encoding=None):
docstrings = Documenter.get_doc(self, encoding)
if len(docstrings) != 1:
return
doclines = docstrings[0]
setattr(self, '__new_doclines', doclines)
if not doclines:
return
# match first line of docstring against signature RE
match = self.property_sig_re.match(doclines[0])
if not match:
return
base, retann = match.groups()
# the base name must match ours
if not self.objpath or base != self.objpath[-1]:
return
# re-prepare docstring to ignore indentation after signature
docstrings = Documenter.get_doc(self, encoding, 2)
doclines = docstrings[0]
# ok, now jump over remaining empty lines and set the remaining
# lines as the new doclines
i = 1
while i < len(doclines) and not doclines[i].strip():
i += 1
setattr(self, '__new_doclines', doclines[i:])
return retann
def get_doc(self, encoding=None, ignore=1):
lines = getattr(self, '__new_doclines', None)
if lines is not None:
return [lines]
return Documenter.get_doc(self, encoding, ignore)
def format_signature(self):
if self.retann is None and self.env.config.autodoc_docstring_signature:
# only act if a signature is not explicitly given already, and if
# the feature is enabled
self.retann = self._find_signature()
return ' ' + (self.retann or '')
class SwtPyClasslike(PyClasslike):
option_spec = PyClasslike.option_spec.copy()
option_spec['bases'] = directives.unchanged
def handle_signature(self, sig, signode):
result = super(SwtPyClasslike, self).handle_signature(sig, signode)
bases = self.options.get('bases')
if bases:
baselist = addnodes.desc_parameterlist()
for base in bases.split(','):
base = base.strip()
baselist += addnodes.pending_xref(
'', refdomain='py', reftype='class', reftarget=base)
baselist[-1] += nodes.literal(base, base)
signode += baselist
return result
class SwtPyClassAttribute(PyClassmember):
"""Interpret signatures prepared by SwtAttributeDocumenter"""
def get_signature_prefix(self, sig):
return self.retann + ' '
def handle_signature(self, sig, signode):
if self.objtype == 'attribute':
parts = sig.split()
if len(parts) != 2:
raise ValueError
sig, self.retann = parts
return super(SwtPyClassAttribute, self).handle_signature(sig, signode)
class SwtPythonDomain(PythonDomain):
directives = PythonDomain.directives.copy()
directives['class'] = SwtPyClasslike
directives['exception'] = SwtPyClasslike
directives['attribute'] = SwtPyClassAttribute
def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode):
node = super(SwtPythonDomain, self).resolve_xref(
env, fromdocname, builder, type, target, node, contnode)
if node and 'refuri' in node:
if type == 'obj':
modname = node.get('py:module')
clsname = node.get('py:class')
searchmode = node.hasattr('refspecific') and 1 or 0
matches = self.find_obj(env, modname, clsname, target, type, searchmode)
type = matches[0][1][1]
if type in ('class', 'exc', 'exception', 'module'):
node['refuri'] = node['refuri'].split('#', 1)[0]
return node
def skip(app, what, name, obj, skip, options):
if name == '__init__':
return False
return skip
def setup(app):
app.override_domain(SwtPythonDomain)
app.add_autodocumenter(SwtClassDocumenter)
app.add_autodocumenter(SwtExceptionDocumenter)
app.add_autodocumenter(SwtAttributeDocumenter)
app.connect('autodoc-skip-member', skip)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment