Last active
August 29, 2015 13:56
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- 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