Added autoapisummary directive

pull/116/head
Ashley Whetter 7 years ago
parent 5a86878811
commit 958fe10103

@ -1,9 +1,135 @@
"""AutoAPI directives"""
from docutils.parsers.rst import Directive
import posixpath
import re
from docutils.parsers.rst import Directive, directives
from docutils.statemachine import ViewList
from docutils import nodes
from sphinx import addnodes
import sphinx.ext.autosummary
from sphinx.util.nodes import nested_parse_with_titles
try:
from sphinx.util.rst import escape
except ImportError:
# sphinx.util.rst is available in sphinx >=1.4.7 only.
# This implementation is taken from sphinx 1.6.5.
def escape(text):
return re.compile(r'([!-/:-@\[-`{-~])').sub(r'\\\1', text)
class AutoapiSummary(Directive):
"""A version of autosummary that uses static analysis."""
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
has_content = True
option_spec = {
'toctree': directives.unchanged,
'nosignatures': directives.flag,
'template': directives.unchanged,
}
def warn(self, msg):
"""Add a warning message.
:param msg: The warning message to add.
:type msg: str
"""
self.warnings.append(
self.state.document.reporter.warning(msg, line=self.lineno)
)
def _get_names(self):
"""Get the names of the objects to include in the table.
:returns: The names of the objects to include.
:rtype: generator(str)
"""
for line in self.content:
line = line.strip()
if line and re.search('^[a-zA-Z0-9]', line):
yield line
def run(self):
self.warnings = []
env = self.state.document.settings.env
mapper = env.autoapi_mapper
objects = [mapper.all_objects[name] for name in self._get_names()]
nodes_ = self._get_table(objects)
if 'toctree' in self.options:
dirname = posixpath.dirname(env.docname)
tree_prefix = self.options['toctree'].strip()
docnames = []
for obj in objects:
docname = posixpath.join(tree_prefix, obj.name)
docname = posixpath.normpath(posixpath.join(dirname, docname))
if docname not in env.found_docs:
self.warn(
'toctree references unknown document {}'.format(docname)
)
docnames.append(docname)
tocnode = addnodes.toctree()
tocnode['includefiles'] = docnames
tocnode['entries'] = [(None, docn) for docn in docnames]
tocnode['maxdepth'] = -1
tocnode['glob'] = None
tocnode = sphinx.ext.autosummary.autosummary_toc('', '', tocnode)
nodes_.append(tocnode)
return self.warnings + nodes_
def _get_row(self, obj):
template = ':{}:`{} <{}>`\\ {}'
if 'nosignatures' in self.options:
template = ':{}:`{} <{}>`'
col1 = template.format(
'obj', obj.short_name, obj.name, escape(obj.signature),
)
col2 = obj.summary
row = nodes.row('')
for text in (col1, col2):
node = nodes.paragraph('')
view_list = ViewList()
view_list.append(text, '<autosummary>')
self.state.nested_parse(view_list, 0, node)
try:
if isinstance(node[0], nodes.paragraph):
node = node[0]
except IndexError:
pass
row.append(nodes.entry('', node))
return row
def _get_table(self, objects):
table_spec = addnodes.tabular_col_spec()
table_spec['spec'] = r'p{0.5\linewidth}p{0.5\linewidth}'
table = sphinx.ext.autosummary.autosummary_table('')
real_table = nodes.table('', classes=['longtable'])
table.append(real_table)
group = nodes.tgroup('', cols=2)
real_table.append(group)
group.append(nodes.colspec('', colwidth=10))
group.append(nodes.colspec('', colwidth=90))
body = nodes.tbody('')
group.append(body)
for obj in objects:
body.append(self._get_row(obj))
return [table_spec, table]
class NestedParse(Directive):

@ -14,7 +14,7 @@ from sphinx.errors import ExtensionError
from docutils.parsers.rst import directives
from .backends import default_file_mapping, default_ignore_patterns, default_backend_mapping
from .directives import NestedParse
from .directives import AutoapiSummary, NestedParse
from .settings import API_ROOT
from .toctree import add_domain_to_toctree
@ -54,11 +54,10 @@ def run_autoapi(app):
normalized_root = os.path.normpath(os.path.join(app.confdir, app.config.autoapi_root))
url_root = os.path.join('/', app.config.autoapi_root)
app.env.autoapi_data = []
sphinx_mapper = default_backend_mapping[app.config.autoapi_type]
sphinx_mapper_obj = sphinx_mapper(app, template_dir=app.config.autoapi_template_dir,
url_root=url_root)
app.env.autoapi_mapper = sphinx_mapper_obj
if app.config.autoapi_file_patterns:
file_patterns = app.config.autoapi_file_patterns
@ -147,11 +146,17 @@ def doctree_read(app, doctree):
)
def clear_env(app, env):
"""Clears the environment of the unpicklable objects that we left behind."""
env.autoapi_mapper = None
def setup(app):
app.connect('builder-inited', run_autoapi)
app.connect('doctree-read', doctree_read)
app.connect('doctree-resolved', add_domain_to_toctree)
app.connect('build-finished', build_finished)
app.connect('env-updated', clear_env)
app.add_config_value('autoapi_type', 'python', 'html')
app.add_config_value('autoapi_root', API_ROOT, 'html')
app.add_config_value('autoapi_ignore', [], 'html')
@ -164,3 +169,4 @@ def setup(app):
app.add_config_value('autoapi_template_dir', [], 'html')
app.add_stylesheet('autoapi.css')
directives.register_directive('autoapi-nested-parse', NestedParse)
directives.register_directive('autoapisummary', AutoapiSummary)

@ -154,6 +154,10 @@ class PythonMapperBase(object):
if pieces:
return '.'.join(pieces)
@property
def signature(self):
return '({})'.format(','.join(self.args))
class SphinxMapperBase(object):
@ -191,6 +195,8 @@ class SphinxMapperBase(object):
self.paths = OrderedDict()
# Mapping of {object id -> Python Object}
self.objects = OrderedDict()
# Mapping of {object id -> Python Object}
self.all_objects = OrderedDict()
# Mapping of {namespace id -> Python Object}
self.namespaces = OrderedDict()
# Mapping of {namespace id -> Python Object}
@ -263,6 +269,9 @@ class SphinxMapperBase(object):
:param obj: Instance of a AutoAPI object
'''
self.objects[obj.id] = obj
self.all_objects[obj.id] = obj
for child in obj.children:
self.all_objects[child.id] = child
def map(self, options=None):
'''Trigger find of serialized sources and build objects'''

@ -243,15 +243,26 @@ class PythonPythonMapper(PythonMapperBase):
return arguments
@property
def summary(self):
for line in self.docstring.splitlines():
line = line.strip()
if line:
return line
return ''
class PythonFunction(PythonPythonMapper):
type = 'function'
is_callable = True
ref_directive = 'func'
class PythonMethod(PythonPythonMapper):
type = 'method'
is_callable = True
ref_directive = 'meth'
class PythonModule(PythonPythonMapper):

@ -6,4 +6,4 @@
{{ obj.docstring|prepare_docstring|indent(3) }}
{% endif %}
{% endif %}
{% endif %}

Loading…
Cancel
Save