blob: c428aff1e827c293dace3813c411bf2ab2d8845f [file] [log] [blame]
Guido van Rossum16d27e31996-08-21 16:28:53 +00001"""Simple code to extract class & function docstrings from a module.
2
3
4"""
5
6import symbol
7import token
8import types
9
10
11def get_docs(fileName):
12 """Retrieve information from the parse tree of a source file.
13
14 fileName
15 Name of the file to read Python source code from.
16 """
17 source = open(fileName).read()
18 import os
19 basename = os.path.basename(os.path.splitext(fileName)[0])
20 import parser
21 ast = parser.suite(source)
22 tup = parser.ast2tuple(ast)
23 return ModuleInfo(tup, basename)
24
25
26class DefnInfo:
27 _docstring = ''
28 _name = ''
29
30 def __init__(self, tree):
31 self._name = tree[2][1]
32
33 def get_docstring(self):
34 return self._docstring
35
36 def get_name(self):
37 return self._name
38
39class SuiteInfoBase(DefnInfo):
40 def __init__(self):
41 self._class_info = {}
42 self._function_info = {}
43
44 def get_class_names(self):
45 return self._class_info.keys()
46
47 def get_class_info(self, name):
48 return self._class_info[name]
49
50 def _extract_info(self, tree):
51 if len(tree) >= 4:
52 found, vars = match(DOCSTRING_STMT_PATTERN, tree[3])
53 if found:
54 self._docstring = eval(vars['docstring'])
55 for node in tree[1:]:
56 if (node[0] == symbol.stmt
57 and node[1][0] == symbol.compound_stmt):
58 if node[1][1][0] == symbol.funcdef:
59 name = node[1][1][2][1]
60 self._function_info[name] = \
61 FunctionInfo(node[1][1])
62 elif node[1][1][0] == symbol.classdef:
63 name = node[1][1][2][1]
64 self._class_info[name] = ClassInfo(node[1][1])
65
66
67class SuiteInfo(SuiteInfoBase):
68 def __init__(self, tree):
69 SuiteInfoBase.__init__(self)
70 self._extract_info(tree)
71
72 def get_function_names(self):
73 return self._function_info.keys()
74
75 def get_function_info(self, name):
76 return self._function_info[name]
77
78
79class FunctionInfo(SuiteInfo):
80 def __init__(self, tree):
81 DefnInfo.__init__(self, tree)
82 suite = tree[-1]
83 if len(suite) >= 4:
84 found, vars = match(DOCSTRING_STMT_PATTERN, suite[3])
85 if found:
86 self._docstring = eval(vars['docstring'])
87 SuiteInfoBase.__init__(self)
88 self._extract_info(suite)
89
90
91class ClassInfo(SuiteInfoBase):
92 def __init__(self, tree):
93 SuiteInfoBase.__init__(self)
94 DefnInfo.__init__(self, tree)
95 self._extract_info(tree[-1])
96
97 def get_method_names(self):
98 return self._function_info.keys()
99
100 def get_method_info(self, name):
101 return self._function_info[name]
102
103
104class ModuleInfo(SuiteInfo):
105 def __init__(self, tree, name="<string>"):
106 self._name = name
107 SuiteInfo.__init__(self, tree)
108 found, vars = match(DOCSTRING_STMT_PATTERN, tree[1])
109 if found:
110 self._docstring = vars["docstring"]
111
112
113from types import ListType, TupleType
114
115def match(pattern, data, vars=None):
116 """
117 """
118 if vars is None:
119 vars = {}
120 if type(pattern) is ListType: # 'variables' are ['varname']
121 vars[pattern[0]] = data
122 return 1, vars
123 if type(pattern) is not TupleType:
124 return (pattern == data), vars
125 if len(data) != len(pattern):
126 return 0, vars
127 for pattern, data in map(None, pattern, data):
128 same, vars = match(pattern, data, vars)
129 if not same:
130 break
131 return same, vars
132
133
134# This pattern will match a 'stmt' node which *might* represent a docstring;
135# docstrings require that the statement which provides the docstring be the
136# first statement in the class or function, which this pattern does not check.
137#
138DOCSTRING_STMT_PATTERN = (
139 symbol.stmt,
140 (symbol.simple_stmt,
141 (symbol.small_stmt,
142 (symbol.expr_stmt,
143 (symbol.testlist,
144 (symbol.test,
145 (symbol.and_test,
146 (symbol.not_test,
147 (symbol.comparison,
148 (symbol.expr,
149 (symbol.xor_expr,
150 (symbol.and_expr,
151 (symbol.shift_expr,
152 (symbol.arith_expr,
153 (symbol.term,
154 (symbol.factor,
155 (symbol.power,
156 (symbol.atom,
157 (token.STRING, ['docstring'])
158 )))))))))))))))),
159 (token.NEWLINE, '')
160 ))
161
162#
163# end of file