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