Fix bug 733
[osm/N2VC.git] / modules / libjuju / docs / _extensions / automembersummary.py
1 # Copyright 2014-2015 Canonical Limited.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15
16 import importlib
17 import inspect
18 import textwrap
19
20 from docutils import nodes
21 from docutils.statemachine import ViewList
22 from sphinx.errors import SphinxError
23 from sphinx.util.compat import Directive
24 from sphinx.util.nodes import nested_parse_with_titles
25
26
27 class AutoMemberSummary(Directive):
28 required_arguments = 1
29
30 def run(self):
31 module_name = self.arguments[0]
32
33 try:
34 module = importlib.import_module(module_name)
35 except ImportError:
36 raise SphinxError("Unable to generate reference docs for %s, "
37 "could not import" % (module_name))
38
39 divider = '+{:-<80}+'.format('')
40 row = '| {:<78} |'.format
41 lines = []
42 for member_name, member in inspect.getmembers(module):
43 if not self._filter(module_name, member_name, member):
44 continue
45 summary = textwrap.wrap(self._get_summary(member), 78) or ['']
46 link = '`{} <#{}>`_'.format(member_name,
47 '.'.join([module_name,
48 member_name]))
49 methods = ['* `{} <#{}>`_'.format(n,
50 '.'.join([module_name,
51 member_name,
52 n]))
53 for n, m in inspect.getmembers(member)
54 if not n.startswith('_') and inspect.isfunction(m)]
55
56 lines.append(divider)
57 lines.append(row(link))
58 lines.append(divider)
59 for line in summary:
60 lines.append(row(line))
61 if methods:
62 lines.append(row(''))
63 lines.append(row('Methods:'))
64 lines.append(row(''))
65 for i, method in enumerate(methods):
66 lines.append(row(method))
67 lines.append(divider)
68 content = '\n'.join(lines)
69
70 result = self._parse(content, '<automembersummary>')
71 return result
72
73 def _get_summary(self, member):
74 doc = (member.__doc__ or '').splitlines()
75
76 # strip any leading blank lines
77 while doc and not doc[0].strip():
78 doc.pop(0)
79
80 # strip anything after the first blank line
81 for i, piece in enumerate(doc):
82 if not piece.strip():
83 doc = doc[:i]
84 break
85
86 return " ".join(doc).strip()
87
88 def _filter(self, module_name, member_name, member):
89 if member_name.startswith('_'):
90 return False
91 if hasattr(member, '__module__'):
92 # skip imported classes & functions
93 return member.__module__.startswith(module_name)
94 elif hasattr(member, '__name__'):
95 # skip imported modules
96 return member.__name__.startswith(module_name)
97 else:
98 return False # skip instances
99 return True
100
101 def _parse(self, rst_text, annotation):
102 result = ViewList()
103 for line in rst_text.split("\n"):
104 result.append(line, annotation)
105 node = nodes.paragraph()
106 node.document = self.state.document
107 nested_parse_with_titles(self.state, result, node)
108 return node.children
109
110
111 def setup(app):
112 app.add_directive('automembersummary', AutoMemberSummary)