|
1 """Simple code to extract class & function docstrings from a module. |
|
2 |
|
3 This code is used as an example in the library reference manual in the |
|
4 section on using the parser module. Refer to the manual for a thorough |
|
5 discussion of the operation of this code. |
|
6 """ |
|
7 |
|
8 import os |
|
9 import parser |
|
10 import symbol |
|
11 import token |
|
12 import types |
|
13 |
|
14 from types import ListType, TupleType |
|
15 |
|
16 |
|
17 def get_docs(fileName): |
|
18 """Retrieve information from the parse tree of a source file. |
|
19 |
|
20 fileName |
|
21 Name of the file to read Python source code from. |
|
22 """ |
|
23 source = open(fileName).read() |
|
24 basename = os.path.basename(os.path.splitext(fileName)[0]) |
|
25 ast = parser.suite(source) |
|
26 return ModuleInfo(ast.totuple(), basename) |
|
27 |
|
28 |
|
29 class SuiteInfoBase: |
|
30 _docstring = '' |
|
31 _name = '' |
|
32 |
|
33 def __init__(self, tree = None): |
|
34 self._class_info = {} |
|
35 self._function_info = {} |
|
36 if tree: |
|
37 self._extract_info(tree) |
|
38 |
|
39 def _extract_info(self, tree): |
|
40 # extract docstring |
|
41 if len(tree) == 2: |
|
42 found, vars = match(DOCSTRING_STMT_PATTERN[1], tree[1]) |
|
43 else: |
|
44 found, vars = match(DOCSTRING_STMT_PATTERN, tree[3]) |
|
45 if found: |
|
46 self._docstring = eval(vars['docstring']) |
|
47 # discover inner definitions |
|
48 for node in tree[1:]: |
|
49 found, vars = match(COMPOUND_STMT_PATTERN, node) |
|
50 if found: |
|
51 cstmt = vars['compound'] |
|
52 if cstmt[0] == symbol.funcdef: |
|
53 name = cstmt[2][1] |
|
54 self._function_info[name] = FunctionInfo(cstmt) |
|
55 elif cstmt[0] == symbol.classdef: |
|
56 name = cstmt[2][1] |
|
57 self._class_info[name] = ClassInfo(cstmt) |
|
58 |
|
59 def get_docstring(self): |
|
60 return self._docstring |
|
61 |
|
62 def get_name(self): |
|
63 return self._name |
|
64 |
|
65 def get_class_names(self): |
|
66 return self._class_info.keys() |
|
67 |
|
68 def get_class_info(self, name): |
|
69 return self._class_info[name] |
|
70 |
|
71 def __getitem__(self, name): |
|
72 try: |
|
73 return self._class_info[name] |
|
74 except KeyError: |
|
75 return self._function_info[name] |
|
76 |
|
77 |
|
78 class SuiteFuncInfo: |
|
79 # Mixin class providing access to function names and info. |
|
80 |
|
81 def get_function_names(self): |
|
82 return self._function_info.keys() |
|
83 |
|
84 def get_function_info(self, name): |
|
85 return self._function_info[name] |
|
86 |
|
87 |
|
88 class FunctionInfo(SuiteInfoBase, SuiteFuncInfo): |
|
89 def __init__(self, tree = None): |
|
90 self._name = tree[2][1] |
|
91 SuiteInfoBase.__init__(self, tree and tree[-1] or None) |
|
92 |
|
93 |
|
94 class ClassInfo(SuiteInfoBase): |
|
95 def __init__(self, tree = None): |
|
96 self._name = tree[2][1] |
|
97 SuiteInfoBase.__init__(self, tree and tree[-1] or None) |
|
98 |
|
99 def get_method_names(self): |
|
100 return self._function_info.keys() |
|
101 |
|
102 def get_method_info(self, name): |
|
103 return self._function_info[name] |
|
104 |
|
105 |
|
106 class ModuleInfo(SuiteInfoBase, SuiteFuncInfo): |
|
107 def __init__(self, tree = None, name = "<string>"): |
|
108 self._name = name |
|
109 SuiteInfoBase.__init__(self, tree) |
|
110 if tree: |
|
111 found, vars = match(DOCSTRING_STMT_PATTERN, tree[1]) |
|
112 if found: |
|
113 self._docstring = vars["docstring"] |
|
114 |
|
115 |
|
116 def match(pattern, data, vars=None): |
|
117 """Match `data' to `pattern', with variable extraction. |
|
118 |
|
119 pattern |
|
120 Pattern to match against, possibly containing variables. |
|
121 |
|
122 data |
|
123 Data to be checked and against which variables are extracted. |
|
124 |
|
125 vars |
|
126 Dictionary of variables which have already been found. If not |
|
127 provided, an empty dictionary is created. |
|
128 |
|
129 The `pattern' value may contain variables of the form ['varname'] which |
|
130 are allowed to match anything. The value that is matched is returned as |
|
131 part of a dictionary which maps 'varname' to the matched value. 'varname' |
|
132 is not required to be a string object, but using strings makes patterns |
|
133 and the code which uses them more readable. |
|
134 |
|
135 This function returns two values: a boolean indicating whether a match |
|
136 was found and a dictionary mapping variable names to their associated |
|
137 values. |
|
138 """ |
|
139 if vars is None: |
|
140 vars = {} |
|
141 if type(pattern) is ListType: # 'variables' are ['varname'] |
|
142 vars[pattern[0]] = data |
|
143 return 1, vars |
|
144 if type(pattern) is not TupleType: |
|
145 return (pattern == data), vars |
|
146 if len(data) != len(pattern): |
|
147 return 0, vars |
|
148 for pattern, data in map(None, pattern, data): |
|
149 same, vars = match(pattern, data, vars) |
|
150 if not same: |
|
151 break |
|
152 return same, vars |
|
153 |
|
154 |
|
155 # This pattern identifies compound statements, allowing them to be readily |
|
156 # differentiated from simple statements. |
|
157 # |
|
158 COMPOUND_STMT_PATTERN = ( |
|
159 symbol.stmt, |
|
160 (symbol.compound_stmt, ['compound']) |
|
161 ) |
|
162 |
|
163 |
|
164 # This pattern will match a 'stmt' node which *might* represent a docstring; |
|
165 # docstrings require that the statement which provides the docstring be the |
|
166 # first statement in the class or function, which this pattern does not check. |
|
167 # |
|
168 DOCSTRING_STMT_PATTERN = ( |
|
169 symbol.stmt, |
|
170 (symbol.simple_stmt, |
|
171 (symbol.small_stmt, |
|
172 (symbol.expr_stmt, |
|
173 (symbol.testlist, |
|
174 (symbol.test, |
|
175 (symbol.and_test, |
|
176 (symbol.not_test, |
|
177 (symbol.comparison, |
|
178 (symbol.expr, |
|
179 (symbol.xor_expr, |
|
180 (symbol.and_expr, |
|
181 (symbol.shift_expr, |
|
182 (symbol.arith_expr, |
|
183 (symbol.term, |
|
184 (symbol.factor, |
|
185 (symbol.power, |
|
186 (symbol.atom, |
|
187 (token.STRING, ['docstring']) |
|
188 )))))))))))))))), |
|
189 (token.NEWLINE, '') |
|
190 )) |