Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
0f2b423dc61296d75d8c4dc17c68a161daede01f
[simgrid.git] / docs / source / _ext / autodoxy / autodoxy / autosummary / __init__.py
1 from __future__ import print_function, absolute_import, division
2
3 import re
4 import operator
5 from functools import reduce
6 from itertools import count, groupby
7
8 from docutils import nodes
9 from docutils.statemachine import ViewList
10 from sphinx import addnodes
11 from sphinx.ext.autosummary import Autosummary, autosummary_table
12
13 from autodoxy import get_doxygen_root
14 from autodoxy.autodoxy import DoxygenMethodDocumenter, DoxygenClassDocumenter
15 from autodoxy.xmlutils import format_xml_paragraph
16
17
18 def import_by_name(name, env=None, prefixes=None, i=0):
19     """Get xml documentation for a class/method with a given name.
20     If there are multiple classes or methods with that name, you
21     can use the `i` kwarg to pick which one.
22     """
23     if prefixes is None:
24         prefixes = [None]
25
26     if env is not None:
27         parent = env.ref_context.get('cpp:parent_symbol')
28         parent_symbols = []
29         while parent is not None and parent.identifier is not None:
30             parent_symbols.insert(0, str(parent.identifier))
31             parent = parent.parent
32         prefixes.append('::'.join(parent_symbols))
33
34     tried = []
35     for prefix in prefixes:
36         try:
37             if prefix:
38                 prefixed_name = '::'.join([prefix, name])
39             else:
40                 prefixed_name = name
41             return _import_by_name(prefixed_name, i=i)
42         except ImportError:
43             tried.append(prefixed_name)
44     raise ImportError('no module named %s' % ' or '.join(tried))
45
46
47 def _import_by_name(name, i=0):
48     root = get_doxygen_root()
49     name = name.replace('.', '::')
50
51     if '::' in name:
52         xpath_query = (
53             './/compoundname[text()="%s"]/../'
54             'sectiondef[@kind="public-func"]/memberdef[@kind="function"]/'
55             'name[text()="%s"]/..') % tuple(name.rsplit('::', 1))
56         m = root.xpath(xpath_query)
57         if len(m) > 0:
58             obj = m[i]
59             full_name = '.'.join(name.rsplit('::', 1))
60             return full_name, obj, full_name, ''
61
62         xpath_query = (
63             './/compoundname[text()="%s"]/../'
64             'sectiondef[@kind="public-type"]/memberdef[@kind="enum"]/'
65             'name[text()="%s"]/..') % tuple(name.rsplit('::', 1))
66         m = root.xpath(xpath_query)
67         if len(m) > 0:
68             obj = m[i]
69             full_name = '.'.join(name.rsplit('::', 1))
70             return full_name, obj, full_name, ''
71
72     xpath_query = ('.//compoundname[text()="%s"]/..' % name)
73     m = root.xpath(xpath_query)
74     if len(m) > 0:
75         obj = m[i]
76         return (name, obj, name, '')
77
78     raise ImportError()
79
80
81 def get_documenter(obj, full_name):
82     if obj.tag == 'memberdef' and obj.get('kind') == 'function':
83         return DoxygenMethodDocumenter
84     elif obj.tag == 'compounddef':
85         return DoxygenClassDocumenter
86
87     raise NotImplementedError(obj.tag)
88
89
90 class DoxygenAutosummary(Autosummary):
91     def get_items(self, names):
92         """Try to import the given names, and return a list of
93         ``[(name, signature, summary_string, real_name), ...]``.
94         """
95         env = self.state.document.settings.env
96         items = []
97
98         names_and_counts = reduce(operator.add,
99             [tuple(zip(g, count())) for _, g in groupby(names)]) # type: List[(Str, Int)]
100
101         for name, i in names_and_counts:
102             display_name = name
103             if name.startswith('~'):
104                 name = name[1:]
105                 display_name = name.split('::')[-1]
106
107             try:
108                 real_name, obj, parent, modname = import_by_name(name, env=env, i=i)
109             except ImportError:
110                 self.warn('failed to import %s' % name)
111                 items.append((name, '', '', name))
112                 continue
113
114             self.result = ViewList()  # initialize for each documenter
115             documenter = get_documenter(obj, parent)(self, real_name, id=obj.get('id'))
116             if not documenter.parse_name():
117                 self.warn('failed to parse name %s' % real_name)
118                 items.append((display_name, '', '', real_name))
119                 continue
120             if not documenter.import_object():
121                 self.warn('failed to import object %s' % real_name)
122                 items.append((display_name, '', '', real_name))
123                 continue
124             if documenter.options.members and not documenter.check_module():
125                 continue
126             # -- Grab the signature
127             sig = documenter.format_signature()
128
129             # -- Grab the summary
130             documenter.add_content(None)
131             doc = list(documenter.process_doc([self.result.data]))
132
133             while doc and not doc[0].strip():
134                 doc.pop(0)
135
136             # If there's a blank line, then we can assume the first sentence /
137             # paragraph has ended, so anything after shouldn't be part of the
138             # summary
139             for i, piece in enumerate(doc):
140                 if not piece.strip():
141                     doc = doc[:i]
142                     break
143
144             # Try to find the "first sentence", which may span multiple lines
145             m = re.search(r"^([A-Z].*?\.)(?:\s|$)", " ".join(doc).strip())
146             if m:
147                 summary = m.group(1).strip()
148             elif doc:
149                 summary = doc[0].strip()
150             else:
151                 summary = ''
152
153             items.append((display_name, sig, summary, real_name))
154
155         return items
156
157     def get_tablespec(self):
158         table_spec = addnodes.tabular_col_spec()
159         table_spec['spec'] = 'll'
160
161         table = autosummary_table('')
162         real_table = nodes.table('', classes=['longtable'])
163         table.append(real_table)
164         group = nodes.tgroup('', cols=2)
165         real_table.append(group)
166         group.append(nodes.colspec('', colwidth=10))
167         group.append(nodes.colspec('', colwidth=90))
168         body = nodes.tbody('')
169         group.append(body)
170
171         def append_row(*column_texts):
172             row = nodes.row('')
173             for text in column_texts:
174                 node = nodes.paragraph('')
175                 vl = ViewList()
176                 vl.append(text, '<autosummary>')
177                 self.state.nested_parse(vl, 0, node)
178                 try:
179                     if isinstance(node[0], nodes.paragraph):
180                         node = node[0]
181                 except IndexError:
182                     pass
183                 row.append(nodes.entry('', node))
184             body.append(row)
185         return table, table_spec, append_row
186
187     def get_table(self, items):
188         """Generate a proper list of table nodes for autosummary:: directive.
189
190         *items* is a list produced by :meth:`get_items`.
191         """
192         table, table_spec, append_row = self.get_tablespec()
193         for name, sig, summary, real_name in items:
194             qualifier = 'cpp:any'
195             # required for cpp autolink
196             full_name = real_name.replace('.', '::')
197             col1 = ':%s:`%s <%s>`' % (qualifier, name, full_name)
198             col2 = summary
199             append_row(col1, col2)
200
201         self.result.append('   .. rubric: sdsf', 0)
202         return [table_spec, table]
203
204
205 class DoxygenAutoEnum(DoxygenAutosummary):
206
207     def get_items(self, names):
208         env = self.state.document.settings.env
209         self.name = names[0]
210
211         real_name, obj, parent, modname = import_by_name(self.name, env=env)
212         names = [n.text for n in obj.findall('./enumvalue/name')]
213         descriptions = [format_xml_paragraph(d) for d in obj.findall('./enumvalue/detaileddescription')]
214         return zip(names, descriptions)
215
216     def get_table(self, items):
217         table, table_spec, append_row = self.get_tablespec()
218         for name, description in items:
219             col1 = ':strong:`' + name + '`'
220             while description and not description[0].strip():
221                 description.pop(0)
222             col2 = ' '.join(description)
223             append_row(col1, col2)
224         return [nodes.rubric('', 'Enum: %s' % self.name), table]