Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
0b8d66ac2d401068bbc227f744c48266ed52126f
[simgrid.git] / docs / source / _ext / autodoxy / autodoxy / autosummary / generate.py
1 from __future__ import print_function, absolute_import, division
2
3 import codecs
4 import os
5 import re
6 import sys
7
8 from jinja2 import FileSystemLoader
9 from jinja2.sandbox import SandboxedEnvironment
10 from sphinx.jinja2glue import BuiltinTemplateLoader
11 from sphinx.util.osutil import ensuredir
12
13 from . import import_by_name
14
15
16 def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
17                               base_path=None, builder=None, template_dir=None):
18
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))
24
25     if output_dir:
26         print('[autosummary] writing to %s' % output_dir)
27
28     if base_path is not None:
29         sources = [os.path.join(base_path, filename) for filename in sources]
30
31     # create our own templating environment
32     template_dirs = [os.path.join(os.path.dirname(__file__), 'templates')]
33
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)
38     else:
39         if template_dir:
40             template_dirs.insert(0, template_dir)
41         template_loader = FileSystemLoader(template_dirs)
42     template_env = SandboxedEnvironment(loader=template_loader)
43
44     # read
45     items = find_autosummary_in_files(sources)
46
47     # keep track of new files
48     new_files = []
49
50     for name, path, template_name in sorted(set(items), key=str):
51         if path is None:
52             # The corresponding autosummary:: directive did not have
53             # a :toctree: option
54             continue
55
56         path = output_dir or os.path.abspath(path)
57         ensuredir(path)
58
59         try:
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)
63             continue
64
65         fn = os.path.join(path, name + suffix).replace('::', '.')
66
67         # skip it if it exists
68         if os.path.isfile(fn):
69             continue
70
71         new_files.append(fn)
72
73         if template_name is None:
74             if obj.tag == 'compounddef' and obj.get('kind') == 'class':
75                 template_name = 'doxyclass.rst.in'
76             else:
77                 raise NotImplementedError('No template for %s' % obj)
78
79         with open(fn, 'w') as f:
80             template = template_env.get_template(template_name)
81             ns = {}
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'
86             else:
87                 raise NotImplementedError(obj)
88
89             parts = name.split('::')
90             mod_name, obj_name = '::'.join(parts[:-1]), parts[-1]
91
92             ns['fullname'] = name
93             ns['module'] = mod_name
94             ns['objname'] = obj_name
95             ns['name'] = parts[-1]
96             ns['underline'] = len(name) * '='
97
98             rendered = template.render(**ns)
99             f.write(rendered)
100
101     # descend recursively to new files
102     if 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)
106
107
108 def find_autosummary_in_files(filenames):
109     """Find out what items are documented in source/*.rst.
110
111     See `find_autosummary_in_lines`.
112     """
113     documented = []
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,
119                                                         filename=filename))
120     return documented
121
122
123 def find_autosummary_in_lines(lines, module=None, filename=None):
124     """Find out what items appear in autosummary:: directives in the
125     given lines.
126
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.
133     """
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*$')
138
139     documented = []
140
141     toctree = None
142     template = None
143     in_autosummary = False
144     base_indent = ""
145
146     for line in lines:
147         if in_autosummary:
148             m = toctree_arg_re.match(line)
149             if m:
150                 toctree = m.group(1)
151                 if filename:
152                     toctree = os.path.join(os.path.dirname(filename),
153                                            toctree)
154                 continue
155
156             m = template_arg_re.match(line)
157             if m:
158                 template = m.group(1).strip()
159                 continue
160
161             if line.strip().startswith(':'):
162                 continue  # skip options
163
164             m = autosummary_item_re.match(line)
165             if m:
166                 name = m.group(1).strip()
167                 if name.startswith('~'):
168                     name = name[1:]
169                 documented.append((name, toctree, template))
170                 continue
171
172             if not line.strip() or line.startswith(base_indent + " "):
173                 continue
174
175             in_autosummary = False
176
177         m = autosummary_re.match(line)
178         if m:
179             in_autosummary = True
180             base_indent = m.group(1)
181             toctree = None
182             template = None
183             continue
184
185     return documented
186
187
188 def process_generate_options(app):
189     genfiles = app.config.autosummary_generate
190
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))]
195
196     if not genfiles:
197         return
198
199     ext = app.config.source_suffix[0]
200     genfiles = [genfile + (not genfile.endswith(ext) and ext or '')
201                 for genfile in genfiles]
202
203     generate_autosummary_docs(genfiles, builder=app.builder,
204                               suffix=ext, base_path=app.srcdir)