|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2010 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 # Original (c) 2005-2008 Divmod, Inc. |
|
6 # |
|
7 # This module is based on pyflakes for Python2 but was heavily hacked to |
|
8 # work with Python3 |
|
9 |
|
10 import builtins |
|
11 import os.path |
|
12 import ast |
|
13 |
|
14 from . import messages |
|
15 |
|
16 class Binding(object): |
|
17 """ |
|
18 Represents the binding of a value to a name. |
|
19 |
|
20 The checker uses this to keep track of which names have been bound and |
|
21 which names have not. See Assignment for a special type of binding that |
|
22 is checked with stricter rules. |
|
23 """ |
|
24 def __init__(self, name, source): |
|
25 self.name = name |
|
26 self.source = source |
|
27 self.used = False |
|
28 |
|
29 def __str__(self): |
|
30 return self.name |
|
31 |
|
32 def __repr__(self): |
|
33 return '<{0} object {1!r} from line {2!r} at 0x{3:x}>'.format( |
|
34 self.__class__.__name__, |
|
35 self.name, |
|
36 self.source.lineno, |
|
37 id(self)) |
|
38 |
|
39 class UnBinding(Binding): |
|
40 ''' |
|
41 Created by the 'del' operator. |
|
42 ''' |
|
43 |
|
44 class Importation(Binding): |
|
45 """ |
|
46 A binding created by an import statement. |
|
47 """ |
|
48 def __init__(self, name, source): |
|
49 self.fullName = name |
|
50 name = name.split('.')[0] |
|
51 super(Importation, self).__init__(name, source) |
|
52 |
|
53 class Argument(Binding): |
|
54 """ |
|
55 Represents binding a name as an argument. |
|
56 """ |
|
57 |
|
58 class Assignment(Binding): |
|
59 """ |
|
60 Represents binding a name with an explicit assignment. |
|
61 |
|
62 The checker will raise warnings for any Assignment that isn't used. Also, |
|
63 the checker does not consider assignments in tuple/list unpacking to be |
|
64 Assignments, rather it treats them as simple Bindings. |
|
65 """ |
|
66 |
|
67 class FunctionDefinition(Binding): |
|
68 """ |
|
69 Represents a function definition. |
|
70 """ |
|
71 pass |
|
72 |
|
73 class ExportBinding(Binding): |
|
74 """ |
|
75 A binding created by an __all__ assignment. If the names in the list |
|
76 can be determined statically, they will be treated as names for export and |
|
77 additional checking applied to them. |
|
78 |
|
79 The only __all__ assignment that can be recognized is one which takes |
|
80 the value of a literal list containing literal strings. For example:: |
|
81 |
|
82 __all__ = ["foo", "bar"] |
|
83 |
|
84 Names which are imported and not otherwise used but appear in the value of |
|
85 __all__ will not have an unused import warning reported for them. |
|
86 """ |
|
87 def names(self): |
|
88 """ |
|
89 Return a list of the names referenced by this binding. |
|
90 """ |
|
91 names = [] |
|
92 if isinstance(self.source, ast.List): |
|
93 for node in self.source.elts: |
|
94 if isinstance(node, (ast.Str, ast.Bytes)): |
|
95 names.append(node.s) |
|
96 elif isinstance(node, ast.Num): |
|
97 names.append(node.n) |
|
98 return names |
|
99 |
|
100 class Scope(dict): |
|
101 """ |
|
102 Class defining the scope base class. |
|
103 """ |
|
104 importStarred = False # set to True when import * is found |
|
105 |
|
106 def __repr__(self): |
|
107 return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self)) |
|
108 |
|
109 def __init__(self): |
|
110 super(Scope, self).__init__() |
|
111 |
|
112 class ClassScope(Scope): |
|
113 """ |
|
114 Class representing a name scope for a class. |
|
115 """ |
|
116 pass |
|
117 |
|
118 class FunctionScope(Scope): |
|
119 """ |
|
120 Class representing a name scope for a function. |
|
121 """ |
|
122 def __init__(self): |
|
123 super(FunctionScope, self).__init__() |
|
124 self.globals = {} |
|
125 |
|
126 class ModuleScope(Scope): |
|
127 """ |
|
128 Class representing a name scope for a module. |
|
129 """ |
|
130 pass |
|
131 |
|
132 # Globally defined names which are not attributes of the builtins module. |
|
133 _MAGIC_GLOBALS = ['__file__', '__builtins__'] |
|
134 |
|
135 class Checker(object): |
|
136 """ |
|
137 Class to check the cleanliness and sanity of Python code. |
|
138 """ |
|
139 nodeDepth = 0 |
|
140 traceTree = False |
|
141 |
|
142 def __init__(self, module, filename = '(none)'): |
|
143 """ |
|
144 Constructor |
|
145 |
|
146 @param module parsed module tree or module source code |
|
147 @param filename name of the module file (string) |
|
148 """ |
|
149 self._deferredFunctions = [] |
|
150 self._deferredAssignments = [] |
|
151 self.dead_scopes = [] |
|
152 self.messages = [] |
|
153 self.filename = filename |
|
154 self.scopeStack = [ModuleScope()] |
|
155 self.futuresAllowed = True |
|
156 |
|
157 if isinstance(module, str): |
|
158 module = ast.parse(module, filename, "exec") |
|
159 self.handleBody(module) |
|
160 self._runDeferred(self._deferredFunctions) |
|
161 # Set _deferredFunctions to None so that deferFunction will fail |
|
162 # noisily if called after we've run through the deferred functions. |
|
163 self._deferredFunctions = None |
|
164 self._runDeferred(self._deferredAssignments) |
|
165 # Set _deferredAssignments to None so that deferAssignment will fail |
|
166 # noisly if called after we've run through the deferred assignments. |
|
167 self._deferredAssignments = None |
|
168 del self.scopeStack[1:] |
|
169 self.popScope() |
|
170 self.check_dead_scopes() |
|
171 |
|
172 def deferFunction(self, callable): |
|
173 ''' |
|
174 Schedule a function handler to be called just before completion. |
|
175 |
|
176 This is used for handling function bodies, which must be deferred |
|
177 because code later in the file might modify the global scope. When |
|
178 `callable` is called, the scope at the time this is called will be |
|
179 restored, however it will contain any new bindings added to it. |
|
180 ''' |
|
181 self._deferredFunctions.append((callable, self.scopeStack[:])) |
|
182 |
|
183 def deferAssignment(self, callable): |
|
184 """ |
|
185 Schedule an assignment handler to be called just after deferred |
|
186 function handlers. |
|
187 """ |
|
188 self._deferredAssignments.append((callable, self.scopeStack[:])) |
|
189 |
|
190 def _runDeferred(self, deferred): |
|
191 """ |
|
192 Run the callables in deferred using their associated scope stack. |
|
193 """ |
|
194 for handler, scope in deferred: |
|
195 self.scopeStack = scope |
|
196 handler() |
|
197 |
|
198 def scope(self): |
|
199 return self.scopeStack[-1] |
|
200 scope = property(scope) |
|
201 |
|
202 def popScope(self): |
|
203 self.dead_scopes.append(self.scopeStack.pop()) |
|
204 |
|
205 def check_dead_scopes(self): |
|
206 """ |
|
207 Look at scopes which have been fully examined and report names in them |
|
208 which were imported but unused. |
|
209 """ |
|
210 for scope in self.dead_scopes: |
|
211 export = isinstance(scope.get('__all__'), ExportBinding) |
|
212 if export: |
|
213 all = scope['__all__'].names() |
|
214 if os.path.split(self.filename)[1] != '__init__.py': |
|
215 # Look for possible mistakes in the export list |
|
216 undefined = set(all) - set(scope) |
|
217 for name in undefined: |
|
218 self.report( |
|
219 messages.UndefinedExport, |
|
220 scope['__all__'].source.lineno, |
|
221 name) |
|
222 else: |
|
223 all = [] |
|
224 |
|
225 # Look for imported names that aren't used. |
|
226 for importation in scope.values(): |
|
227 if isinstance(importation, Importation): |
|
228 if not importation.used and importation.name not in all: |
|
229 self.report( |
|
230 messages.UnusedImport, |
|
231 importation.source.lineno, |
|
232 importation.name) |
|
233 |
|
234 def pushFunctionScope(self): |
|
235 self.scopeStack.append(FunctionScope()) |
|
236 |
|
237 def pushClassScope(self): |
|
238 self.scopeStack.append(ClassScope()) |
|
239 |
|
240 def report(self, messageClass, *args, **kwargs): |
|
241 self.messages.append(messageClass(self.filename, *args, **kwargs)) |
|
242 |
|
243 def handleBody(self, tree): |
|
244 for node in tree.body: |
|
245 self.handleNode(node, tree) |
|
246 |
|
247 def handleNode(self, node, parent): |
|
248 if node: |
|
249 node.parent = parent |
|
250 if self.traceTree: |
|
251 print(' ' * self.nodeDepth + node.__class__.__name__) |
|
252 self.nodeDepth += 1 |
|
253 nodeType = node.__class__.__name__.upper() |
|
254 try: |
|
255 handler = getattr(self, nodeType) |
|
256 handler(node) |
|
257 finally: |
|
258 self.nodeDepth -= 1 |
|
259 if self.traceTree: |
|
260 print(' ' * self.nodeDepth + 'end ' + node.__class__.__name__) |
|
261 |
|
262 def ignore(self, node): |
|
263 pass |
|
264 |
|
265 # ast nodes to be ignored |
|
266 PASS = CONTINUE = BREAK = ELLIPSIS = NUM = STR = BYTES = \ |
|
267 ATTRIBUTES = AND = OR = ADD = SUB = MULT = DIV = \ |
|
268 MOD = POW = LSHIFT = RSHIFT = BITOR = BITXOR = BITAND = FLOORDIV = \ |
|
269 INVERT = NOT = UADD = USUB = INVERT = NOT = UADD = USUB = ignore |
|
270 |
|
271 def addBinding(self, lineno, value, reportRedef = True): |
|
272 '''Called when a binding is altered. |
|
273 |
|
274 @param lineno line of the statement responsible for the change (integer) |
|
275 @param value the optional new value, a Binding instance, associated |
|
276 with the binding; if None, the binding is deleted if it exists |
|
277 @param reportRedef flag indicating if rebinding while unused will be |
|
278 reported (boolean) |
|
279 ''' |
|
280 if (isinstance(self.scope.get(value.name), FunctionDefinition) |
|
281 and isinstance(value, FunctionDefinition)): |
|
282 self.report(messages.RedefinedFunction, |
|
283 lineno, value.name, self.scope[value.name].source.lineno) |
|
284 |
|
285 if not isinstance(self.scope, ClassScope): |
|
286 for scope in self.scopeStack[::-1]: |
|
287 existing = scope.get(value.name) |
|
288 if (isinstance(existing, Importation) |
|
289 and not existing.used |
|
290 and (not isinstance(value, Importation) or value.fullName == existing.fullName) |
|
291 and reportRedef): |
|
292 |
|
293 self.report(messages.RedefinedWhileUnused, |
|
294 lineno, value.name, scope[value.name].source.lineno) |
|
295 |
|
296 if isinstance(value, UnBinding): |
|
297 try: |
|
298 del self.scope[value.name] |
|
299 except KeyError: |
|
300 self.report(messages.UndefinedName, lineno, value.name) |
|
301 else: |
|
302 self.scope[value.name] = value |
|
303 |
|
304 ############################################################ |
|
305 ## individual handler methods below |
|
306 ############################################################ |
|
307 |
|
308 def LIST(self, node): |
|
309 for elt in node.elts: |
|
310 self.handleNode(elt, node) |
|
311 |
|
312 SET = TUPLE = LIST |
|
313 |
|
314 def DICT(self, node): |
|
315 for key in node.keys: |
|
316 self.handleNode(key, node) |
|
317 for val in node.values: |
|
318 self.handleNode(val, node) |
|
319 |
|
320 def WITH(self, node): |
|
321 """ |
|
322 Handle with by checking the target of the statement (which can be an |
|
323 identifier, a list or tuple of targets, an attribute, etc) for |
|
324 undefined names and defining any it adds to the scope and by continuing |
|
325 to process the suite within the statement. |
|
326 """ |
|
327 # Check the "foo" part of a "with foo as bar" statement. Do this no |
|
328 # matter what, since there's always a "foo" part. |
|
329 self.handleNode(node.context_expr, node) |
|
330 |
|
331 if node.optional_vars is not None: |
|
332 self.handleNode(node.optional_vars, node) |
|
333 |
|
334 self.handleBody(node) |
|
335 |
|
336 def GLOBAL(self, node): |
|
337 """ |
|
338 Keep track of globals declarations. |
|
339 """ |
|
340 if isinstance(self.scope, FunctionScope): |
|
341 self.scope.globals.update(dict.fromkeys(node.names)) |
|
342 |
|
343 NONLOCAL = GLOBAL |
|
344 |
|
345 def LISTCOMP(self, node): |
|
346 for generator in node.generators: |
|
347 self.handleNode(generator, node) |
|
348 self.handleNode(node.elt, node) |
|
349 |
|
350 SETCOMP = GENERATOREXP = LISTCOMP |
|
351 |
|
352 def DICTCOMP(self, node): |
|
353 for generator in node.generators: |
|
354 self.handleNode(generator, node) |
|
355 self.handleNode(node.key, node) |
|
356 self.handleNode(node.value, node) |
|
357 |
|
358 def COMPREHENSION(self, node): |
|
359 node.target.parent = node |
|
360 self.handleAssignName(node.target) |
|
361 self.handleNode(node.iter, node) |
|
362 for elt in node.ifs: |
|
363 self.handleNode(elt, node) |
|
364 |
|
365 def FOR(self, node): |
|
366 """ |
|
367 Process bindings for loop variables. |
|
368 """ |
|
369 vars = [] |
|
370 def collectLoopVars(n): |
|
371 if isinstance(n, ast.Name): |
|
372 vars.append(n.id) |
|
373 |
|
374 collectLoopVars(node.target) |
|
375 for varn in vars: |
|
376 if (isinstance(self.scope.get(varn), Importation) |
|
377 # unused ones will get an unused import warning |
|
378 and self.scope[varn].used): |
|
379 self.report(messages.ImportShadowedByLoopVar, |
|
380 node.lineno, varn, self.scope[varn].source.lineno) |
|
381 |
|
382 node.target.parent = node |
|
383 self.handleAssignName(node.target) |
|
384 self.handleNode(node.iter, node) |
|
385 self.handleBody(node) |
|
386 |
|
387 def NAME(self, node): |
|
388 """ |
|
389 Locate the name in locals / function / globals scopes. |
|
390 """ |
|
391 # try local scope |
|
392 importStarred = self.scope.importStarred |
|
393 try: |
|
394 self.scope[node.id].used = (self.scope, node.lineno) |
|
395 except KeyError: |
|
396 pass |
|
397 else: |
|
398 return |
|
399 |
|
400 # try enclosing function scopes |
|
401 for scope in self.scopeStack[-2:0:-1]: |
|
402 importStarred = importStarred or scope.importStarred |
|
403 if not isinstance(scope, FunctionScope): |
|
404 continue |
|
405 try: |
|
406 scope[node.id].used = (self.scope, node.lineno) |
|
407 except KeyError: |
|
408 pass |
|
409 else: |
|
410 return |
|
411 |
|
412 # try global scope |
|
413 importStarred = importStarred or self.scopeStack[0].importStarred |
|
414 try: |
|
415 self.scopeStack[0][node.id].used = (self.scope, node.lineno) |
|
416 except KeyError: |
|
417 if ((not hasattr(builtins, node.id)) |
|
418 and node.id not in _MAGIC_GLOBALS |
|
419 and not importStarred): |
|
420 if (os.path.basename(self.filename) == '__init__.py' and |
|
421 node.id == '__path__'): |
|
422 # the special name __path__ is valid only in packages |
|
423 pass |
|
424 else: |
|
425 self.report(messages.UndefinedName, node.lineno, node.id) |
|
426 |
|
427 def FUNCTIONDEF(self, node): |
|
428 if getattr(node, "decorator_list", None) is not None: |
|
429 for decorator in node.decorator_list: |
|
430 self.handleNode(decorator, node) |
|
431 self.addBinding(node.lineno, FunctionDefinition(node.name, node)) |
|
432 self.LAMBDA(node) |
|
433 |
|
434 def LAMBDA(self, node): |
|
435 for default in node.args.defaults + node.args.kw_defaults: |
|
436 self.handleNode(default, node) |
|
437 |
|
438 def runFunction(): |
|
439 args = [] |
|
440 |
|
441 def addArgs(arglist): |
|
442 for arg in arglist: |
|
443 if isinstance(arg.arg, tuple): |
|
444 addArgs(arg.arg) |
|
445 else: |
|
446 if arg.arg in args: |
|
447 self.report(messages.DuplicateArgument, node.lineno, arg.arg) |
|
448 args.append(arg.arg) |
|
449 |
|
450 def checkUnusedAssignments(): |
|
451 """ |
|
452 Check to see if any assignments have not been used. |
|
453 """ |
|
454 for name, binding in self.scope.items(): |
|
455 if (not binding.used and not name in self.scope.globals |
|
456 and isinstance(binding, Assignment)): |
|
457 self.report(messages.UnusedVariable, |
|
458 binding.source.lineno, name) |
|
459 |
|
460 self.pushFunctionScope() |
|
461 addArgs(node.args.args) |
|
462 addArgs(node.args.kwonlyargs) |
|
463 for name in args: |
|
464 self.addBinding(node.lineno, Argument(name, node), reportRedef=False) |
|
465 if node.args.vararg: |
|
466 self.addBinding(node.lineno, Argument(node.args.vararg, node), |
|
467 reportRedef=False) |
|
468 if node.args.kwarg: |
|
469 self.addBinding(node.lineno, Argument(node.args.kwarg, node), |
|
470 reportRedef=False) |
|
471 if isinstance(node.body, list): |
|
472 self.handleBody(node) |
|
473 else: |
|
474 self.handleNode(node.body, node) |
|
475 self.deferAssignment(checkUnusedAssignments) |
|
476 self.popScope() |
|
477 |
|
478 self.deferFunction(runFunction) |
|
479 |
|
480 def CLASSDEF(self, node): |
|
481 """ |
|
482 Check names used in a class definition, including its decorators, base |
|
483 classes, and the body of its definition. Additionally, add its name to |
|
484 the current scope. |
|
485 """ |
|
486 if getattr(node, "decorator_list", None) is not None: |
|
487 for decorator in node.decorator_list: |
|
488 self.handleNode(decorator, node) |
|
489 for baseNode in node.bases: |
|
490 self.handleNode(baseNode, node) |
|
491 self.addBinding(node.lineno, Binding(node.name, node)) |
|
492 self.pushClassScope() |
|
493 self.handleBody(node) |
|
494 self.popScope() |
|
495 |
|
496 def handleAssignName(self, node): |
|
497 # special handling for ast.Subscript and ast.Starred |
|
498 if isinstance(node, (ast.Subscript, ast.Starred)): |
|
499 node.value.parent = node |
|
500 self.handleAssignName(node.value) |
|
501 if isinstance(node, ast.Subscript): |
|
502 if isinstance(node.slice, ast.Slice): |
|
503 self.handleNode(node.slice.lower, node) |
|
504 self.handleNode(node.slice.upper, node) |
|
505 else: |
|
506 self.handleNode(node.slice.value, node) |
|
507 return |
|
508 |
|
509 # if the name hasn't already been defined in the current scope |
|
510 if isinstance(node, ast.Tuple): |
|
511 for elt in node.elts: |
|
512 elt.parent = node |
|
513 self.handleAssignName(elt) |
|
514 return |
|
515 |
|
516 if isinstance(node, ast.Attribute): |
|
517 self.handleNode(node.value, node) |
|
518 return |
|
519 |
|
520 if isinstance(self.scope, FunctionScope) and node.id not in self.scope: |
|
521 # for each function or module scope above us |
|
522 for scope in self.scopeStack[:-1]: |
|
523 if not isinstance(scope, (FunctionScope, ModuleScope)): |
|
524 continue |
|
525 # if the name was defined in that scope, and the name has |
|
526 # been accessed already in the current scope, and hasn't |
|
527 # been declared global |
|
528 if (node.id in scope |
|
529 and scope[node.id].used |
|
530 and scope[node.id].used[0] is self.scope |
|
531 and node.id not in self.scope.globals): |
|
532 # then it's probably a mistake |
|
533 self.report(messages.UndefinedLocal, |
|
534 scope[node.id].used[1], |
|
535 node.id, |
|
536 scope[node.id].source.lineno) |
|
537 break |
|
538 |
|
539 if isinstance(node.parent, |
|
540 (ast.For, ast.ListComp, ast.GeneratorExp, |
|
541 ast.Tuple, ast.List)): |
|
542 binding = Binding(node.id, node) |
|
543 elif (node.id == '__all__' and |
|
544 isinstance(self.scope, ModuleScope) and |
|
545 isinstance(node.parent, ast.Assign)): |
|
546 binding = ExportBinding(node.id, node.parent.value) |
|
547 else: |
|
548 binding = Assignment(node.id, node) |
|
549 if node.id in self.scope: |
|
550 binding.used = self.scope[node.id].used |
|
551 self.addBinding(node.lineno, binding) |
|
552 |
|
553 def ASSIGN(self, node): |
|
554 self.handleNode(node.value, node) |
|
555 for subnode in node.targets[::-1]: |
|
556 subnode.parent = node |
|
557 if isinstance(subnode, ast.Attribute): |
|
558 self.handleNode(subnode.value, subnode) |
|
559 else: |
|
560 self.handleAssignName(subnode) |
|
561 |
|
562 def AUGASSIGN(self, node): |
|
563 self.handleNode(node.value, node) |
|
564 self.handleNode(node.target, node) |
|
565 |
|
566 def IMPORT(self, node): |
|
567 for alias in node.names: |
|
568 name = alias.asname or alias.name |
|
569 importation = Importation(name, node) |
|
570 self.addBinding(node.lineno, importation) |
|
571 |
|
572 def IMPORTFROM(self, node): |
|
573 if node.module == '__future__': |
|
574 if not self.futuresAllowed: |
|
575 self.report(messages.LateFutureImport, node.lineno, |
|
576 [n.name for n in node.names]) |
|
577 else: |
|
578 self.futuresAllowed = False |
|
579 |
|
580 for alias in node.names: |
|
581 if alias.name == '*': |
|
582 self.scope.importStarred = True |
|
583 self.report(messages.ImportStarUsed, node.lineno, node.module) |
|
584 continue |
|
585 name = alias.asname or alias.name |
|
586 importation = Importation(name, node) |
|
587 if node.module == '__future__': |
|
588 importation.used = (self.scope, node.lineno) |
|
589 self.addBinding(node.lineno, importation) |
|
590 |
|
591 def CALL(self, node): |
|
592 self.handleNode(node.func, node) |
|
593 for arg in node.args: |
|
594 self.handleNode(arg, node) |
|
595 for kw in node.keywords: |
|
596 self.handleNode(kw, node) |
|
597 node.starargs and self.handleNode(node.starargs, node) |
|
598 node.kwargs and self.handleNode(node.kwargs, node) |
|
599 |
|
600 def KEYWORD(self, node): |
|
601 self.handleNode(node.value, node) |
|
602 |
|
603 def BOOLOP(self, node): |
|
604 for val in node.values: |
|
605 self.handleNode(val, node) |
|
606 |
|
607 def BINOP(self, node): |
|
608 self.handleNode(node.left, node) |
|
609 self.handleNode(node.right, node) |
|
610 |
|
611 def UNARYOP(self, node): |
|
612 self.handleNode(node.operand, node) |
|
613 |
|
614 def RETURN(self, node): |
|
615 node.value and self.handleNode(node.value, node) |
|
616 |
|
617 def DELETE(self, node): |
|
618 for tgt in node.targets: |
|
619 self.handleNode(tgt, node) |
|
620 |
|
621 def EXPR(self, node): |
|
622 self.handleNode(node.value, node) |
|
623 |
|
624 def ATTRIBUTE(self, node): |
|
625 self.handleNode(node.value, node) |
|
626 |
|
627 def IF(self, node): |
|
628 self.handleNode(node.test, node) |
|
629 self.handleBody(node) |
|
630 for stmt in node.orelse: |
|
631 self.handleNode(stmt, node) |
|
632 |
|
633 WHILE = IF |
|
634 |
|
635 def RAISE(self, node): |
|
636 node.exc and self.handleNode(node.exc, node) |
|
637 node.cause and self.handleNode(node.cause, node) |
|
638 |
|
639 def TRYEXCEPT(self, node): |
|
640 self.handleBody(node) |
|
641 for handler in node.handlers: |
|
642 self.handleNode(handler, node) |
|
643 for stmt in node.orelse: |
|
644 self.handleNode(stmt, node) |
|
645 |
|
646 def TRYFINALLY(self, node): |
|
647 self.handleBody(node) |
|
648 for stmt in node.finalbody: |
|
649 self.handleNode(stmt, node) |
|
650 |
|
651 def EXCEPTHANDLER(self, node): |
|
652 node.type and self.handleNode(node.type, node) |
|
653 if node.name: |
|
654 node.id = node.name |
|
655 self.handleAssignName(node) |
|
656 self.handleBody(node) |
|
657 |
|
658 def ASSERT(self, node): |
|
659 self.handleNode(node.test, node) |
|
660 node.msg and self.handleNode(node.msg, node) |
|
661 |
|
662 def COMPARE(self, node): |
|
663 self.handleNode(node.left, node) |
|
664 for comparator in node.comparators: |
|
665 self.handleNode(comparator, node) |
|
666 |
|
667 def YIELD(self, node): |
|
668 node.value and self.handleNode(node.value, node) |
|
669 |
|
670 def SUBSCRIPT(self, node): |
|
671 self.handleNode(node.value, node) |
|
672 self.handleNode(node.slice, node) |
|
673 |
|
674 def SLICE(self, node): |
|
675 node.lower and self.handleNode(node.lower, node) |
|
676 node.upper and self.handleNode(node.upper, node) |
|
677 node.step and self.handleNode(node.step, node) |
|
678 |
|
679 def EXTSLICE(self, node): |
|
680 for slice in node.dims: |
|
681 self.handleNode(slice, node) |
|
682 |
|
683 def INDEX(self, node): |
|
684 self.handleNode(node.value, node) |
|
685 |
|
686 def IFEXP(self, node): |
|
687 self.handleNode(node.test, node) |
|
688 self.handleNode(node.body, node) |
|
689 self.handleNode(node.orelse, node) |
|
690 |
|
691 def STARRED(self, node): |
|
692 self.handleNode(node.value, node) |