1 from __future__ import print_function, absolute_import, division
8 from jinja2 import FileSystemLoader
9 from jinja2.sandbox import SandboxedEnvironment
10 from sphinx.jinja2glue import BuiltinTemplateLoader
11 from sphinx.util.osutil import ensuredir
13 from . import import_by_name
16 def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
17 base_path=None, builder=None, template_dir=None):
19 showed_sources = list(sorted(sources))
20 if len(showed_sources) > 20:
21 showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:]
22 print('[autosummary] generating autosummary for: %s' %
23 ', '.join(showed_sources))
26 print('[autosummary] writing to %s' % output_dir)
28 if base_path is not None:
29 sources = [os.path.join(base_path, filename) for filename in sources]
31 # create our own templating environment
32 template_dirs = [os.path.join(os.path.dirname(__file__), 'templates')]
34 if builder is not None:
35 # allow the user to override the templates
36 template_loader = BuiltinTemplateLoader()
37 template_loader.init(builder, dirs=template_dirs)
40 template_dirs.insert(0, template_dir)
41 template_loader = FileSystemLoader(template_dirs)
42 template_env = SandboxedEnvironment(loader=template_loader)
45 items = find_autosummary_in_files(sources)
47 # keep track of new files
50 for name, path, template_name in sorted(set(items), key=str):
52 # The corresponding autosummary:: directive did not have
56 path = output_dir or os.path.abspath(path)
60 name, obj, parent, mod_name = import_by_name(name)
61 except ImportError as e:
62 print('WARNING [autosummary] failed to import %r: %s' % (name, e), file=sys.stderr)
65 fn = os.path.join(path, name + suffix).replace('::', '.')
67 # skip it if it exists
68 if os.path.isfile(fn):
73 if template_name is None:
74 if obj.tag == 'compounddef' and obj.get('kind') == 'class':
75 template_name = 'doxyclass.rst.in'
77 raise NotImplementedError('No template for %s' % obj)
79 with open(fn, 'w') as f:
80 template = template_env.get_template(template_name)
82 if obj.tag == 'compounddef' and obj.get('kind') == 'class':
83 ns['methods'] = [e.text for e in obj.findall('.//sectiondef[@kind="public-func"]/memberdef[@kind="function"]/name')]
84 ns['enums'] = [e.text for e in obj.findall('.//sectiondef[@kind="public-type"]/memberdef[@kind="enum"]/name')]
85 ns['objtype'] = 'class'
87 raise NotImplementedError(obj)
89 parts = name.split('::')
90 mod_name, obj_name = '::'.join(parts[:-1]), parts[-1]
93 ns['module'] = mod_name
94 ns['objname'] = obj_name
95 ns['name'] = parts[-1]
96 ns['underline'] = len(name) * '='
98 rendered = template.render(**ns)
101 # descend recursively to new files
103 generate_autosummary_docs(new_files, output_dir=output_dir,
104 suffix=suffix, base_path=base_path, builder=builder,
105 template_dir=template_dir)
108 def find_autosummary_in_files(filenames):
109 """Find out what items are documented in source/*.rst.
111 See `find_autosummary_in_lines`.
114 for filename in filenames:
115 with codecs.open(filename, 'r', encoding='utf-8',
116 errors='ignore') as f:
117 lines = f.read().splitlines()
118 documented.extend(find_autosummary_in_lines(lines,
123 def find_autosummary_in_lines(lines, module=None, filename=None):
124 """Find out what items appear in autosummary:: directives in the
127 Returns a list of (name, toctree, template) where *name* is a name
128 of an object and *toctree* the :toctree: path of the corresponding
129 autosummary directive (relative to the root of the file name), and
130 *template* the value of the :template: option. *toctree* and
131 *template* ``None`` if the directive does not have the
132 corresponding options set.
134 autosummary_re = re.compile(r'^(\s*)\.\.\s+autodoxysummary::\s*')
135 autosummary_item_re = re.compile(r'^\s+(~?[_a-zA-Z][a-zA-Z0-9_.:]*)\s*.*?')
136 toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$')
137 template_arg_re = re.compile(r'^\s+:template:\s*(.*?)\s*$')
143 in_autosummary = False
148 m = toctree_arg_re.match(line)
152 toctree = os.path.join(os.path.dirname(filename),
156 m = template_arg_re.match(line)
158 template = m.group(1).strip()
161 if line.strip().startswith(':'):
162 continue # skip options
164 m = autosummary_item_re.match(line)
166 name = m.group(1).strip()
167 if name.startswith('~'):
169 documented.append((name, toctree, template))
172 if not line.strip() or line.startswith(base_indent + " "):
175 in_autosummary = False
177 m = autosummary_re.match(line)
179 in_autosummary = True
180 base_indent = m.group(1)
188 def process_generate_options(app):
189 genfiles = app.config.autosummary_generate
191 if genfiles and not hasattr(genfiles, '__len__'):
192 env = app.builder.env
193 genfiles = [env.doc2path(x, base=None) for x in env.found_docs
194 if os.path.isfile(env.doc2path(x))]
199 ext = app.config.source_suffix[0]
200 genfiles = [genfile + (not genfile.endswith(ext) and ext or '')
201 for genfile in genfiles]
203 generate_autosummary_docs(genfiles, builder=app.builder,
204 suffix=ext, base_path=app.srcdir)