1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
# -*- 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, AutodocReporter
from sphinx.util import logging
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']
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):
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
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]
try:
# use a custom reporter that correctly assigns lines to source
# filename/description and lineno
old_reporter = state.memo.reporter
state.memo.reporter = AutodocReporter(content, state.memo.reporter)
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
finally:
state.memo.reporter = old_reporter
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
|