|
22 | 22 | from __future__ import unicode_literals, absolute_import |
23 | 23 | from __future__ import print_function, division |
24 | 24 |
|
25 | | -from os.path import join, dirname, abspath |
26 | | -from traceback import print_exc |
| 25 | +from os.path import join, dirname, abspath, exists |
27 | 26 |
|
28 | 27 | from jinja2.sandbox import SandboxedEnvironment |
29 | | - |
30 | | -from docutils.parsers.rst import Directive |
31 | | -from docutils.statemachine import ViewList |
32 | 28 | from sphinx.util.osutil import ensuredir |
33 | 29 | from sphinx.jinja2glue import BuiltinTemplateLoader |
34 | 30 |
|
35 | 31 | from . import __version__ |
36 | 32 | from .apinode import APINode |
37 | 33 |
|
38 | 34 |
|
39 | | -class AutoAPI(Directive): |
| 35 | +def get_template_env(app): |
40 | 36 | """ |
41 | | - Autoapi directive. |
| 37 | + Get the template environment. |
42 | 38 |
|
43 | | - This directive will perform the following steps: |
| 39 | + .. note:: |
44 | 40 |
|
45 | | - - Build a :class:`autoapi.apinode.APINode` tree with the module name |
46 | | - specified as argument. |
47 | | - - Render the Jinja2 template with the root node and append the result as |
48 | | - content for this directive. |
49 | | - - Traverse the tree and for each node, except for leaf nodes without public |
50 | | - API, render the Jinja2 template and write the result as a new source |
51 | | - file. |
| 41 | + Template should be loaded as a package_data using |
| 42 | + :py:function:`pkgutil.get_data`, but because we want the user to |
| 43 | + override the default template we need to hook it to the Sphinx loader, |
| 44 | + and thus a file system approach is required as it is implemented like |
| 45 | + that. |
52 | 46 | """ |
53 | | - required_arguments = 1 |
54 | | - optional_arguments = 0 |
55 | | - final_argument_whitespace = False |
56 | | - has_content = True |
57 | | - option_spec = {} |
58 | | - |
59 | | - def autoapi_build(self, tree): |
60 | | - """ |
61 | | - """ |
62 | | - env = self.state.document.settings.env |
63 | | - |
64 | | - # DEBUG |
65 | | - print('=' * 79) |
66 | | - print(env.config.source_suffix) |
67 | | - print(env.srcdir) |
68 | | - print(env.doctreedir) |
69 | | - print(env.found_docs) |
70 | | - print('=' * 79) |
| 47 | + template_dir = [join(dirname(abspath(__file__)), 'template')] |
| 48 | + template_loader = BuiltinTemplateLoader() |
| 49 | + template_loader.init(app.builder, dirs=template_dir) |
| 50 | + template_env = SandboxedEnvironment(loader=template_loader) |
| 51 | + return template_env |
71 | 52 |
|
72 | | - # Get template |
73 | | - # Note: Template should be loaded as a package_data using |
74 | | - # pkgutil.get_data(), but because we want the user to override the |
75 | | - # default template we need to hook it to the Sphinx loader, and thus |
76 | | - # a file system approach is required as it is implemented like that. |
77 | | - template_dir = [join(dirname(abspath(__file__)), 'template')] |
78 | | - template_loader = BuiltinTemplateLoader() |
79 | | - template_loader.init(env.app.builder, dirs=template_dir) |
80 | | - template_env = SandboxedEnvironment(loader=template_loader) |
81 | | - template = template_env.get_template('module.rst') |
82 | | - |
83 | | - # Render root and append it to current node content |
84 | | - autodoc = template.render(node=tree) |
85 | | - self.content.extend( |
86 | | - ViewList(initlist=autodoc.splitlines(), source=tree.name) |
87 | | - ) |
88 | | - |
89 | | - # Render all remaining nodes as separated documents |
90 | | - gen_dir = join(env.srcdir, 'fixme') |
91 | | - ensuredir(gen_dir) |
92 | | - |
93 | | - for name, node in tree.directory.items(): |
94 | | - |
95 | | - # Ignore leaf nodes with public API |
96 | | - # Non-leaf nodes without public API are required to be rendered |
97 | | - # in order to have an index of their subnodes. |
98 | | - if node.is_leaf and not node.has_public_api(): |
99 | | - continue |
100 | 53 |
|
101 | | - out_file = join(gen_dir, name + env.config.source_suffix[0]) |
102 | | - with open(out_file, 'w') as fd: |
103 | | - fd.write(template.render(node=node)) |
| 54 | +def builder_inited(app): |
| 55 | + """ |
| 56 | + autoapi Sphinx extension hook for the ``builder-inited`` event. |
104 | 57 |
|
105 | | - def run(self): |
106 | | - # Get name of the module that is directive argument |
107 | | - module = self.arguments[0] |
| 58 | + This hook will read the configuration value ``autoapi_modules`` and render |
| 59 | + the modules described in it. |
| 60 | + """ |
| 61 | + # Get modules to build documentation for |
| 62 | + modules = app.config.autoapi_modules |
| 63 | + if not modules: |
| 64 | + return |
108 | 65 |
|
109 | | - try: |
110 | | - # Build module tree |
111 | | - tree = APINode(module) |
| 66 | + # Get template environment |
| 67 | + template_env = get_template_env(app) |
112 | 68 |
|
113 | | - # Generate content |
114 | | - self.autoapi_build(tree) |
| 69 | + for module, options in modules.items(): |
115 | 70 |
|
116 | | - except Exception as e: |
117 | | - # Create a warning if the process failed |
118 | | - print_exc() |
119 | | - msg = 'Unable to build autoapi for {}: {}'.format(module, str(e)) |
120 | | - return [ |
121 | | - self.state.document.reporter.warning(msg, line=self.lineno) |
122 | | - ] |
| 71 | + # Get options |
| 72 | + defaults = { |
| 73 | + 'override': True, |
| 74 | + 'template': 'module.rst', |
| 75 | + 'output': module |
| 76 | + } |
| 77 | + if options: |
| 78 | + defaults.update(options) |
123 | 79 |
|
124 | | - return [] |
| 80 | + # Get template |
| 81 | + template = template_env.get_template(defaults['template']) |
| 82 | + |
| 83 | + # Build API tree |
| 84 | + tree = APINode(module) |
| 85 | + |
| 86 | + # Gather nodes to document |
| 87 | + # Ignore leaf nodes without public API |
| 88 | + # Non-leaf nodes without public API are required to be rendered |
| 89 | + # in order to have an index of their subnodes. |
| 90 | + nodes = [ |
| 91 | + (name, node) for name, node in tree.directory.items() |
| 92 | + if node.has_public_api() or not node.is_leaf() |
| 93 | + ] |
| 94 | + if not nodes: |
| 95 | + continue |
| 96 | + |
| 97 | + # Define output directory |
| 98 | + out_dir = join(app.env.srcdir, defaults['output']) |
| 99 | + ensuredir(out_dir) |
| 100 | + |
| 101 | + # Iterate nodes and render them |
| 102 | + for name, node in nodes: |
| 103 | + out_file = join(out_dir, name + app.config.source_suffix[0]) |
| 104 | + |
| 105 | + # Skip file if it override is off and it exists |
| 106 | + if not defaults['override'] and exists(out_file): |
| 107 | + continue |
| 108 | + |
| 109 | + with open(out_file, 'w') as fd: |
| 110 | + fd.write(template.render(node=node)) |
125 | 111 |
|
126 | 112 |
|
127 | 113 | def setup(app): |
| 114 | + """ |
| 115 | + autoapi Sphinx extension setup. |
| 116 | +
|
| 117 | + See http://sphinx-doc.org/extdev/tutorial.html#the-setup-function |
| 118 | + """ |
128 | 119 | # autodoc is required |
129 | 120 | app.setup_extension('sphinx.ext.autodoc') |
130 | | - app.add_directive('autoapi', AutoAPI) |
131 | | - return {'version': __version__, 'parallel_read_safe': True} |
| 121 | + app.add_config_value('autoapi_modules', {}, True) |
| 122 | + app.connect(b'builder-inited', builder_inited) |
| 123 | + return {'version': __version__} |
132 | 124 |
|
133 | 125 |
|
134 | | -__all__ = ['AutoAPI'] |
| 126 | +__all__ = ['builder_inited', 'setup'] |
0 commit comments