1 import doctest
2 import os
3 import re
4
5 import logger
6 from model import System, Module, Class, Function, parseFile, processModuleAst
7
8
9
10 if getattr(doctest, 'DocTestParser', False):
11
12 get_doctests = doctest.DocTestParser().get_examples
13 else:
14
15 get_doctests = doctest._extract_examples
16
17
19 """Compile a regex pattern using default or user mapping.
20 """
21
22
23 mapping = {'ALPHA': r'[-.,?!\w]', 'WORD': r'[-.,?!\s\w]',
24 'START': r'(^|\s)', 'END': r'([.,?!\s]|$)'}
25
26 if user_map:
27 mapping = mapping.copy()
28 mapping.update(user_map)
29
30 - def sub(text, mapping):
31 for From, To in mapping.iteritems():
32 text = text.replace(From, To)
33 return text
34
35 pattern = sub(pattern, mapping)
36
37 return re.compile(pattern, re.LOCALE | re.VERBOSE)
38
40 if end is None:
41 end = start
42 return compile_regex(r'''(START %(start)s ALPHA %(end)s END) |
43 (START %(start)s ALPHA WORD* ALPHA %(end)s END)'''\
44 % {'start': start, 'end': end}, mapping)
45
47 return inline_markup(start, end, mapping={'ALPHA': r'[-.,?!\s\w]',
48 'START': r'(\n|^)[\ \t]*',
49 'END': r''})
50
51 supported_formats = {
52
53 'reST': [
54 inline_markup(r'\*'),
55 inline_markup(r'\*\*'),
56 inline_markup(r'``'),
57 inline_markup(r'\(', r'_\)',
58 {'ALPHA': r'\w', 'WORD': r'[-.\w]'}),
59 inline_markup(r'\(`', r'`_\)'),
60 line_markup(r':'),
61 line_markup(r'[*+-]', r''),
62 line_markup(r'((\d+) | ([a-zA-Z]+) [.\)])', r''),
63 line_markup(r'\( ((\d+) | ([a-zA-Z]+)) \)', r''),
64 ],
65
66
67 'epytext': [
68 re.compile(r'[BCEGILMSUX]\{.*\}'),
69 line_markup(r'@[a-z]+([\ \t][a-zA-Z]+)?:', r''),
70 line_markup(r'-', r''),
71 line_markup(r'\d+(\.\d+)*', r''),
72 ],
73
74
75 'javadoc': [
76 re.compile(r'<[a-zA-z]+[^>]*>'),
77 line_markup(r'@[a-z][a-zA-Z]*\s', r''),
78 re.compile(r'{@ ((docRoot) | (inheritDoc) | (link) | (linkplain) |'\
79 ' (value)) [^}]* }', re.VERBOSE),
80 ],
81 }
82
83
95
96
98 """Information about the structure of a Python module.
99
100 * Collects modules, classes, methods, functions and associated docstrings
101 * Based on mwh's docextractor.model module
102 """
104 """Initialize Code Parser object.
105
106 :Parameters:
107 `pyfile` : str
108 Path to a Python module to parse.
109 `log` : logger.Producer instance
110 Logger to use during code parsing.
111 """
112 if log:
113 self.log = log.codeparser
114 else:
115 self.log = logger.default.codeparser
116 self.modules = []
117 self.classes = []
118 self.methods = []
119 self.method_func = []
120 self.functions = []
121 self.docstrings = []
122 self.docstrings_by_format = {}
123 self.formatted_docstrings_count = 0
124 self.doctests_count = 0
125 self.unittests_count = 0
126
127
128 for format in supported_formats:
129 self.docstrings_by_format[format] = []
130
131 (path, filename) = os.path.split(pyfile)
132 (module, ext) = os.path.splitext(filename)
133 self.log("Inspecting file: " + pyfile)
134
135 self.system = System()
136 try:
137 processModuleAst(parseFile(pyfile), module, self.system)
138 except Exception, e:
139 self.log("Code parsing error occured:\n***\n%s\n***" % str(e))
140 return
141
142 for obj in self.system.orderedallobjects:
143 fullname = obj.fullName()
144 if isinstance(obj, Module):
145 self.modules.append(fullname)
146 if isinstance(obj, Class):
147 if 'unittest.TestCase' in obj.bases or 'TestCase' in obj.bases:
148 self.unittests_count += 1
149 self.classes.append(fullname)
150 if isinstance(obj, Function):
151 self.method_func.append(fullname)
152 if isinstance(obj.docstring, str) and obj.docstring.strip():
153 self.docstrings.append(fullname)
154
155 formatted = False
156 for format in supported_formats:
157 if use_format(obj.docstring, format):
158 self.docstrings_by_format[format].append(fullname)
159 formatted = True
160 if formatted:
161 self.formatted_docstrings_count += 1
162
163
164 if get_doctests(obj.docstring):
165 self.doctests_count += 1
166
167 for method_or_func in self.method_func:
168 method_found = 0
169 for cls in self.classes:
170 if method_or_func.startswith(cls):
171 self.methods.append(method_or_func)
172 method_found = 1
173 break
174 if not method_found:
175 self.functions.append(method_or_func)
176
177 self.log("modules: " + ",".join(self.modules))
178 self.log("classes: " + ",".join(self.classes))
179 self.log("methods: " + ",".join(self.methods))
180 self.log("functions: " + ",".join(self.functions))
181 self.log("docstrings: %s" % self.docstrings_by_format)
182 self.log("number of doctests: %d" % self.doctests_count)
183
185 """Return number of objects found in this module.
186
187 Objects include:
188 * module
189 * classes
190 * methods
191 * functions
192 """
193 module_count = len(self.modules)
194 cls_count = len(self.classes)
195 method_count = len(self.methods)
196 func_count = len(self.functions)
197 return module_count + cls_count + method_count + func_count
198
200 """Return number of docstrings found in this module.
201 """
202 return len(self.docstrings)
203
205 """Return number of docstrings of given type found in this module.
206 """
207 return len(self.docstrings_by_format[type])
208
210 """Return list of functions called by functions/methods
211 defined in this module.
212 """
213 return self.system.func_called.keys()
214
215 functions_called = property(_functions_called)
216