3
#1 : support for nested function and class definitions.
4
--#2-- : support for directories in command line arguments.
5
#3 : support stats for packages and subpackages
14
setattr(obj,attr,attr+1)
16
def get_module(file_name):
17
# if it's a directory return all modules and submodules recursively
18
if os.path.isdir(file_name):
20
files = dircache.listdir(dir_name)
21
for one_file in files:
22
complete_file_name = os.path.join(dir_name,one_file)
23
for module in get_module(complete_file_name):
26
elif file_name.endswith(".py"):
30
def __init__(self,stats):
35
# when set to True in the previous iteration, the actual token must be a class name and it used to capture that name.
36
# It should be set to false immediatly afterwards.
37
self.capture_class = False
39
# when set to True in the previous iteration, the actual token must be a function name and it used to capture that name.
40
# It should be set to false immediatly afterwards.
41
self.capture_def = False
44
# when reaching 0, we're not in a function definition anymore
48
# when reaching 0, we're not in a class definition anymore
52
# # # # # # # # # # # # # # # # #
53
# when True, this attribute is useful to test if current line is comment or blank
54
self.beginning_of_line = True
56
# used to determine if we should call self.stats.record_newline or not
57
self.current_line_is_code = True
59
###############################################################
60
def __call__(self,token_type,token,start_cl,end_cl,line):
61
self._check_flags(token)
62
self._check_indents(token_type)
64
if self.beginning_of_line :
65
self._flag_line_of_code(token_type,line)
67
if token_type in (tokenize.NL,tokenize.NEWLINE) :
68
if self.current_line_is_code and not (line.strip().startswith("def") or line.strip().startswith("class")):
69
self.stats.record_newline(line)
71
self.beginning_of_line = True # for next iteration
72
self.capture_def = False
73
self.capture_class = False
75
###############################################################
76
def _check_flags(self,token):
78
self.stats.record_def(token)
79
self.capture_def = False
81
if self.capture_class :
82
self.stats.record_class(token)
83
self.capture_class = False
85
if token == "def" and not self.in_def : #don't process nested functions
86
self.capture_def = True
89
elif token == "class" and not self.in_class : #don't process nested classes
90
self.capture_class = True
93
###############################################################
94
def _flag_line_of_code(self,token_type,line):
95
if token_type in (tokenize.STRING, tokenize.COMMENT) or line.strip() == "" :
96
self.current_line_is_code = False
98
self.current_line_is_code = True # for next iteration
100
if token_type not in (tokenize.INDENT, tokenize.DEDENT) :
101
self.beginning_of_line = False # for next iteration
104
###############################################################
105
def _check_indents(self,token_type):
106
if token_type == tokenize.INDENT :
109
if token_type == tokenize.DEDENT :
112
###############################################################
113
def _incr_depth(self,incr=1):
115
self.def_depth += incr
116
if self.def_depth == 0 :
121
self.class_depth += incr
122
if self.class_depth == 0 :
123
self.in_class = False
124
self.stats.end_class()
125
###############################################################
126
def _decr_depth(self):
130
###############################################################
132
def __init__(self,file_name):
133
self.lines = open(file_name).readlines()
134
self.stats = {"#module":{"#loc":0,
135
"#classes":{"#loc":0},
136
"#functions":{"#loc":0},
137
"#module_code":{"#loc":0}
140
self.current_name = None
141
self.current_class = None
142
self.current_def = None
143
self.current_node = None
144
self.current_module = self.stats["#module"]
146
def create_new_stat_dict(self,stat_type,parent=None):
147
d = {"#classes":{"#loc":0},"#loc":0,"#parent":parent}
148
if stat_type == "class":
149
d["#methods"] = {"#loc":0,"#parent":d}
150
d["#class_code"] = {"#loc":0,"#parent":d}
151
elif stat_type == "function":
152
d["#functions"] = {"#loc":0,"#parent":d}
153
elif stat_type == "module":
154
d["#functions"] = {"#loc":0,"#parent":d}
155
d["#module_code"] = {"#loc":0,"#parent":d}
158
def _get_current_parent(self):
159
if self.current_def :
160
parent = self.current_def
162
elif self.current_class :
163
parent = self.current_class
165
elif self.current_module :
166
parent = self.current_module
170
def record_class(self,name):
171
print "recording class",name
172
self.current_name = name
173
parent = self._get_current_parent()
174
self.current_class = parent["#classes"][name] = self.create_new_stat_dict(parent=parent,stat_type="class")
175
self.current_def = None
177
def record_def(self,name):
178
print "recording function or method",name
179
self.current_name = name
180
parent = self._get_current_parent()
181
if parent.get("#methods"):
185
self.current_def = parent[key][name] = self.create_new_stat_dict(parent=parent,stat_type="function")
186
self.current_class = None
188
def record_newline(self,line):
190
self._incr_stat(self.current_def)
191
elif self.current_class:
192
self._incr_stat(self.current_class)
194
self._incr_stat(self.current_module)
197
print "definition of function/method %s ended" % self.current_name
198
self.current_def = "#parent" in self.current_def and self.current_def["#parent"] or None
201
print "definition of class ended"
202
print "self.current_class",self.current_class
203
print "current_parent",pprint.pprint(self._get_current_parent())
204
self.current_class = "#parent" in self.current_class and self.current_class["#parent"] or None
207
tokenize.tokenize(iter(self.lines).next,Reader(self))
209
def _incr_stat(self,stat):
211
if "#parent" in stat :
212
parent = stat.get("#parent")
214
if "#methods" in parent :
215
parent["#methods"]["#loc"] += 1
216
elif "#functions" in parent :
217
parent["#functions"]["#loc"] += 1
218
if self.current_class and not self.current_def :
219
parent["#class_code"]["#loc"] += 1
220
elif not self.current_class and not self.current_def :
221
parent["module_code"]["#loc"] += 1
222
self._incr_stat(stat["#parent"])
225
print "no no no. Use %s like this : python %s path_to_file.py" % (sys.argv[0],sys.argv[0])
228
## file_path = "/home/chaouche/CODE/TEST/PYTHON/sample.py"
229
## file_path = "/home/chaouche/CODE/TEST/PYTHON/simple-sample.py"
230
if len(sys.argv) < 2 :
233
for file_path in sys.argv[1:]:
234
for python_file in get_module(file_path) :
235
print "file :",python_file
236
st = SourceStats(python_file)
238
pprint.pprint(st.stats)
240
if __name__ == "__main__":