summaryrefslogtreecommitdiff
path: root/sphinx/ext/autodoc/directive.py
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2018-01-08 14:38:47 +0900
committerGitHub <noreply@github.com>2018-01-08 14:38:47 +0900
commitb6e38b406541f6eef74e2d19a727522307c96eb9 (patch)
treec2129b90f855c2103134dc0cdf8bddef163adcba /sphinx/ext/autodoc/directive.py
parent42000e0f007ce78c3a40d194ae1a4ac8b3c4674b (diff)
parent1c0a5ee55d45b1332e04011e0d5cc3c7c1c44a6c (diff)
downloadsphinx-git-b6e38b406541f6eef74e2d19a727522307c96eb9.tar.gz
Merge pull request #4310 from tk0miya/refactor_AutoDirective
Refactor AutoDirective
Diffstat (limited to 'sphinx/ext/autodoc/directive.py')
-rw-r--r--sphinx/ext/autodoc/directive.py157
1 files changed, 157 insertions, 0 deletions
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
new file mode 100644
index 000000000..5ea4a9b58
--- /dev/null
+++ b/sphinx/ext/autodoc/directive.py
@@ -0,0 +1,157 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.ext.autodoc.directive
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from docutils import nodes
+from docutils.parsers.rst import Directive
+from docutils.statemachine import ViewList
+from docutils.utils import assemble_option_dict
+
+from sphinx.ext.autodoc import AutoDirective
+from sphinx.util import logging
+from sphinx.util.docutils import switch_source_input
+from sphinx.util.nodes import nested_parse_with_titles
+
+if False:
+ # For type annotation
+ from typing import Any, Dict, List, Set, Type # NOQA
+ from docutils.statemachine import State, StateMachine, StringList # NOQA
+ from docutils.utils import Reporter # NOQA
+ from sphinx.config import Config # NOQA
+ from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.ext.autodoc import Documenter # NOQA
+
+logger = logging.getLogger(__name__)
+
+
+# common option names for autodoc directives
+AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
+ 'show-inheritance', 'private-members', 'special-members',
+ 'ignore-module-all']
+
+
+class DummyOptionSpec(object):
+ """An option_spec allows any options."""
+
+ def __getitem__(self, key):
+ # type: (Any) -> Any
+ return lambda x: x
+
+
+class Options(dict):
+ """A dict/attribute hybrid that returns None on nonexisting keys."""
+ def __getattr__(self, name):
+ # type: (unicode) -> Any
+ try:
+ return self[name.replace('_', '-')]
+ except KeyError:
+ return None
+
+
+class DocumenterBridge(object):
+ """A parameters container for Documenters."""
+
+ def __init__(self, env, reporter, options, lineno):
+ # type: (BuildEnvironment, Reporter, Options, int) -> None
+ self.env = env
+ self.reporter = reporter
+ self.genopt = options
+ self.lineno = lineno
+ self.filename_set = set() # type: Set[unicode]
+ self.warnings = [] # type: List[nodes.Node]
+ self.result = ViewList()
+
+ def warn(self, msg):
+ # type: (unicode) -> None
+ self.warnings.append(self.reporter.warning(msg, line=self.lineno))
+
+
+def process_documenter_options(documenter, config, options):
+ # type: (Type[Documenter], Config, Dict) -> Options
+ """Recognize options of Documenter from user input."""
+ for name in AUTODOC_DEFAULT_OPTIONS:
+ if name not in documenter.option_spec:
+ continue
+ else:
+ negated = options.pop('no-' + name, True) is None
+ if name in config.autodoc_default_flags and not negated:
+ options[name] = None
+
+ return Options(assemble_option_dict(options.items(), documenter.option_spec))
+
+
+def parse_generated_content(state, content, documenter):
+ # type: (State, StringList, Documenter) -> List[nodes.Node]
+ """Parse a generated content by Documenter."""
+ with switch_source_input(state, content):
+ if documenter.titles_allowed:
+ node = nodes.section()
+ # necessary so that the child nodes get the right source/line set
+ node.document = state.document
+ nested_parse_with_titles(state, content, node)
+ else:
+ node = nodes.paragraph()
+ node.document = state.document
+ state.nested_parse(content, 0, node)
+
+ return node.children
+
+
+class AutodocDirective(Directive):
+ """A directive class for all autodoc directives. It works as a dispatcher of Documenters.
+
+ It invokes a Documenter on running. After the processing, it parses and returns
+ the generated content by Documenter.
+ """
+ option_spec = DummyOptionSpec()
+ has_content = True
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = True
+
+ def run(self):
+ # type: () -> List[nodes.Node]
+ env = self.state.document.settings.env
+ reporter = self.state.document.reporter
+
+ try:
+ source, lineno = reporter.get_source_and_line(self.lineno)
+ except AttributeError:
+ source, lineno = (None, None)
+ logger.debug('[autodoc] %s:%s: input:\n%s', source, lineno, self.block_text)
+
+ # look up target Documenter
+ objtype = self.name[4:] # strip prefix (auto-).
+ doccls = AutoDirective._registry[objtype]
+
+ # process the options with the selected documenter's option_spec
+ try:
+ documenter_options = process_documenter_options(doccls, env.config, self.options)
+ except (KeyError, ValueError, TypeError) as exc:
+ # an option is either unknown or has a wrong type
+ msg = reporter.error('An option to %s is either unknown or '
+ 'has an invalid value: %s' % (self.name, exc),
+ line=lineno)
+ return [msg]
+
+ # generate the output
+ params = DocumenterBridge(env, reporter, documenter_options, lineno)
+ documenter = doccls(params, self.arguments[0])
+ documenter.generate(more_content=self.content)
+ if not params.result:
+ return params.warnings
+
+ logger.debug('[autodoc] output:\n%s', '\n'.join(params.result))
+
+ # record all filenames as dependencies -- this will at least
+ # partially make automatic invalidation possible
+ for fn in params.filename_set:
+ self.state.document.settings.record_dependencies.add(fn)
+
+ result = parse_generated_content(self.state, params.result, documenter)
+ return params.warnings + result