1 # -*- coding: utf-8 -*- |
1 # -*- coding: utf-8 -*- |
2 |
2 |
3 # Copyright (c) 2011 - 2012 Detlev Offenbach <detlev@die-offenbachs.de> |
3 # Copyright (c) 2011 - 2012 Detlev Offenbach <detlev@die-offenbachs.de> |
4 # |
4 # |
5 # Original (c) 2005-2008 Divmod, Inc. |
5 # Original (c) 2005-2010 Divmod, Inc. |
6 # |
6 # |
7 # This module is based on pyflakes for Python2 but was heavily hacked to |
7 # This module is based on pyflakes for Python2 but was heavily hacked to |
8 # work within eric5 |
8 # work within eric5 |
9 |
9 |
10 import __builtin__ |
10 import __builtin__ |
11 import os.path |
11 import os.path |
12 import compiler |
12 import _ast |
13 from compiler import ast |
|
14 |
13 |
15 from py2flakes import messages |
14 from py2flakes import messages |
|
15 |
|
16 # utility function to iterate over an AST node's children, adapted |
|
17 # from Python 2.6's standard ast module |
|
18 try: |
|
19 import ast |
|
20 iter_child_nodes = ast.iter_child_nodes |
|
21 except (ImportError, AttributeError): |
|
22 def iter_child_nodes(node, astcls=_ast.AST): |
|
23 """ |
|
24 Yield all direct child nodes of *node*, that is, all fields that are nodes |
|
25 and all items of fields that are lists of nodes. |
|
26 """ |
|
27 for name in node._fields: |
|
28 field = getattr(node, name, None) |
|
29 if isinstance(field, astcls): |
|
30 yield field |
|
31 elif isinstance(field, list): |
|
32 for item in field: |
|
33 yield item |
|
34 |
16 |
35 |
17 class Binding(object): |
36 class Binding(object): |
18 """ |
37 """ |
19 Represents the binding of a value to a name. |
38 Represents the binding of a value to a name. |
20 |
39 |
238 |
258 |
239 def report(self, messageClass, *args, **kwargs): |
259 def report(self, messageClass, *args, **kwargs): |
240 self.messages.append(messageClass(self.filename, *args, **kwargs)) |
260 self.messages.append(messageClass(self.filename, *args, **kwargs)) |
241 |
261 |
242 def handleChildren(self, tree): |
262 def handleChildren(self, tree): |
243 for node in tree.getChildNodes(): |
263 for node in iter_child_nodes(tree): |
244 self.handleNode(node, tree) |
264 self.handleNode(node, tree) |
|
265 |
|
266 def isDocstring(self, node): |
|
267 """ |
|
268 Determine if the given node is a docstring, as long as it is at the |
|
269 correct place in the node tree. |
|
270 """ |
|
271 return isinstance(node, _ast.Str) or \ |
|
272 (isinstance(node, _ast.Expr) and |
|
273 isinstance(node.value, _ast.Str)) |
245 |
274 |
246 def handleNode(self, node, parent): |
275 def handleNode(self, node, parent): |
247 node.parent = parent |
276 node.parent = parent |
248 if self.traceTree: |
277 if self.traceTree: |
249 print ' ' * self.nodeDepth + node.__class__.__name__ |
278 print ' ' * self.nodeDepth + node.__class__.__name__ |
250 self.nodeDepth += 1 |
279 self.nodeDepth += 1 |
|
280 if self.futuresAllowed and not \ |
|
281 (isinstance(node, _ast.ImportFrom) or self.isDocstring(node)): |
|
282 self.futuresAllowed = False |
251 nodeType = node.__class__.__name__.upper() |
283 nodeType = node.__class__.__name__.upper() |
252 if nodeType not in ('STMT', 'FROM'): |
|
253 self.futuresAllowed = False |
|
254 try: |
284 try: |
255 handler = getattr(self, nodeType) |
285 handler = getattr(self, nodeType) |
256 handler(node) |
286 handler(node) |
257 finally: |
287 finally: |
258 self.nodeDepth -= 1 |
288 self.nodeDepth -= 1 |
260 print ' ' * self.nodeDepth + 'end ' + node.__class__.__name__ |
290 print ' ' * self.nodeDepth + 'end ' + node.__class__.__name__ |
261 |
291 |
262 def ignore(self, node): |
292 def ignore(self, node): |
263 pass |
293 pass |
264 |
294 |
265 STMT = PRINT = PRINTNL = TUPLE = LIST = ASSTUPLE = ASSATTR = \ |
295 # "stmt" type nodes |
266 ASSLIST = GETATTR = SLICE = SLICEOBJ = IF = CALLFUNC = DISCARD = \ |
296 RETURN = DELETE = PRINT = WHILE = IF = WITH = RAISE = TRYEXCEPT = \ |
267 RETURN = ADD = MOD = SUB = NOT = UNARYSUB = INVERT = ASSERT = COMPARE = \ |
297 TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren |
268 SUBSCRIPT = AND = OR = TRYEXCEPT = RAISE = YIELD = DICT = LEFTSHIFT = \ |
298 |
269 RIGHTSHIFT = KEYWORD = TRYFINALLY = WHILE = EXEC = MUL = DIV = POWER = \ |
299 CONTINUE = BREAK = PASS = ignore |
270 FLOORDIV = BITAND = BITOR = BITXOR = LISTCOMPFOR = LISTCOMPIF = \ |
300 |
271 AUGASSIGN = BACKQUOTE = UNARYADD = GENEXPR = GENEXPRFOR = GENEXPRIF = \ |
301 # "expr" type nodes |
272 IFEXP = handleChildren |
302 BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = COMPARE = \ |
273 |
303 CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = handleChildren |
274 CONST = PASS = CONTINUE = BREAK = ELLIPSIS = ignore |
304 |
|
305 NUM = STR = ELLIPSIS = ignore |
|
306 |
|
307 # "slice" type nodes |
|
308 SLICE = EXTSLICE = INDEX = handleChildren |
|
309 |
|
310 # expression contexts are node instances too, though being constants |
|
311 LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore |
|
312 |
|
313 # same for operators |
|
314 AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \ |
|
315 BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \ |
|
316 EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore |
|
317 |
|
318 # additional node types |
|
319 COMPREHENSION = EXCEPTHANDLER = KEYWORD = handleChildren |
275 |
320 |
276 def addBinding(self, lineno, value, reportRedef=True): |
321 def addBinding(self, lineno, value, reportRedef=True): |
277 ''' |
322 ''' |
278 Called when a binding is altered. |
323 Called when a binding is altered. |
279 |
324 |
305 except KeyError: |
351 except KeyError: |
306 self.report(messages.UndefinedName, lineno, value.name) |
352 self.report(messages.UndefinedName, lineno, value.name) |
307 else: |
353 else: |
308 self.scope[value.name] = value |
354 self.scope[value.name] = value |
309 |
355 |
310 def WITH(self, node): |
|
311 """ |
|
312 Handle 'with' by checking the target of the statement (which can be an |
|
313 identifier, a list or tuple of targets, an attribute, etc) for |
|
314 undefined names and defining any it adds to the scope and by continuing |
|
315 to process the suite within the statement. |
|
316 """ |
|
317 # Check the "foo" part of a "with foo as bar" statement. Do this no |
|
318 # matter what, since there's always a "foo" part. |
|
319 self.handleNode(node.expr, node) |
|
320 |
|
321 if node.vars is not None: |
|
322 self.handleNode(node.vars, node) |
|
323 |
|
324 self.handleChildren(node.body) |
|
325 |
|
326 |
|
327 def GLOBAL(self, node): |
356 def GLOBAL(self, node): |
328 """ |
357 """ |
329 Keep track of globals declarations. |
358 Keep track of globals declarations. |
330 """ |
359 """ |
331 if isinstance(self.scope, FunctionScope): |
360 if isinstance(self.scope, FunctionScope): |
332 self.scope.globals.update(dict.fromkeys(node.names)) |
361 self.scope.globals.update(dict.fromkeys(node.names)) |
333 |
362 |
334 def LISTCOMP(self, node): |
363 def LISTCOMP(self, node): |
335 for qual in node.quals: |
364 # handle generators before element |
336 self.handleNode(qual, node) |
365 for gen in node.generators: |
337 self.handleNode(node.expr, node) |
366 self.handleNode(gen, node) |
338 |
367 self.handleNode(node.elt, node) |
339 GENEXPRINNER = LISTCOMP |
368 |
|
369 GENERATOREXP = SETCOMP = LISTCOMP |
|
370 |
|
371 # dictionary comprehensions; introduced in Python 2.7 |
|
372 def DICTCOMP(self, node): |
|
373 for gen in node.generators: |
|
374 self.handleNode(gen, node) |
|
375 self.handleNode(node.key, node) |
|
376 self.handleNode(node.value, node) |
340 |
377 |
341 def FOR(self, node): |
378 def FOR(self, node): |
342 """ |
379 """ |
343 Process bindings for loop variables. |
380 Process bindings for loop variables. |
344 """ |
381 """ |
345 vars = [] |
382 vars = [] |
346 def collectLoopVars(n): |
383 def collectLoopVars(n): |
347 if hasattr(n, 'name'): |
384 if isinstance(n, _ast.Name): |
348 vars.append(n.name) |
385 vars.append(n.id) |
|
386 elif isinstance(n, _ast.expr_context): |
|
387 return |
349 else: |
388 else: |
350 for c in n.getChildNodes(): |
389 for c in iter_child_nodes(n): |
351 collectLoopVars(c) |
390 collectLoopVars(c) |
352 |
391 |
353 collectLoopVars(node.assign) |
392 collectLoopVars(node.target) |
354 for varn in vars: |
393 for varn in vars: |
355 if (isinstance(self.scope.get(varn), Importation) |
394 if (isinstance(self.scope.get(varn), Importation) |
356 # unused ones will get an unused import warning |
395 # unused ones will get an unused import warning |
357 and self.scope[varn].used): |
396 and self.scope[varn].used): |
358 self.report(messages.ImportShadowedByLoopVar, |
397 self.report(messages.ImportShadowedByLoopVar, |
362 |
401 |
363 def NAME(self, node): |
402 def NAME(self, node): |
364 """ |
403 """ |
365 Locate the name in locals / function / globals scopes. |
404 Locate the name in locals / function / globals scopes. |
366 """ |
405 """ |
367 # try local scope |
406 if isinstance(node.ctx, (_ast.Load, _ast.AugLoad)): |
368 importStarred = self.scope.importStarred |
407 # try local scope |
369 try: |
408 importStarred = self.scope.importStarred |
370 self.scope[node.name].used = (self.scope, node.lineno) |
|
371 except KeyError: |
|
372 pass |
|
373 else: |
|
374 return |
|
375 |
|
376 # try enclosing function scopes |
|
377 |
|
378 for scope in self.scopeStack[-2:0:-1]: |
|
379 importStarred = importStarred or scope.importStarred |
|
380 if not isinstance(scope, FunctionScope): |
|
381 continue |
|
382 try: |
409 try: |
383 scope[node.name].used = (self.scope, node.lineno) |
410 self.scope[node.id].used = (self.scope, node.lineno) |
384 except KeyError: |
411 except KeyError: |
385 pass |
412 pass |
386 else: |
413 else: |
387 return |
414 return |
388 |
415 |
389 # try global scope |
416 # try enclosing function scopes |
390 |
417 |
391 importStarred = importStarred or self.scopeStack[0].importStarred |
418 for scope in self.scopeStack[-2:0:-1]: |
392 try: |
419 importStarred = importStarred or scope.importStarred |
393 self.scopeStack[0][node.name].used = (self.scope, node.lineno) |
420 if not isinstance(scope, FunctionScope): |
394 except KeyError: |
421 continue |
395 if ((not hasattr(__builtin__, node.name)) |
422 try: |
396 and node.name not in _MAGIC_GLOBALS |
423 scope[node.id].used = (self.scope, node.lineno) |
397 and not importStarred): |
424 except KeyError: |
398 if (os.path.basename(self.filename) == '__init__.py' and |
|
399 node.name == '__path__'): |
|
400 # the special name __path__ is valid only in packages |
|
401 pass |
425 pass |
402 else: |
426 else: |
403 self.report(messages.UndefinedName, node.lineno, node.name) |
427 return |
404 |
428 |
405 |
429 |
406 def FUNCTION(self, node): |
430 # try global scope |
407 if getattr(node, "decorators", None) is not None: |
431 |
408 self.handleChildren(node.decorators) |
432 importStarred = importStarred or self.scopeStack[0].importStarred |
|
433 try: |
|
434 self.scopeStack[0][node.id].used = (self.scope, node.lineno) |
|
435 except KeyError: |
|
436 if ((not hasattr(__builtin__, node.id)) |
|
437 and node.id not in _MAGIC_GLOBALS |
|
438 and not importStarred): |
|
439 if (os.path.basename(self.filename) == '__init__.py' and |
|
440 node.id == '__path__'): |
|
441 # the special name __path__ is valid only in packages |
|
442 pass |
|
443 else: |
|
444 self.report(messages.UndefinedName, node.lineno, node.id) |
|
445 elif isinstance(node.ctx, (_ast.Store, _ast.AugStore)): |
|
446 # if the name hasn't already been defined in the current scope |
|
447 if isinstance(self.scope, FunctionScope) and node.id not in self.scope: |
|
448 # for each function or module scope above us |
|
449 for scope in self.scopeStack[:-1]: |
|
450 if not isinstance(scope, (FunctionScope, ModuleScope)): |
|
451 continue |
|
452 # if the name was defined in that scope, and the name has |
|
453 # been accessed already in the current scope, and hasn't |
|
454 # been declared global |
|
455 if (node.id in scope |
|
456 and scope[node.id].used |
|
457 and scope[node.id].used[0] is self.scope |
|
458 and node.id not in self.scope.globals): |
|
459 # then it's probably a mistake |
|
460 self.report(messages.UndefinedLocal, |
|
461 scope[node.id].used[1], |
|
462 node.id, |
|
463 scope[node.id].source.lineno) |
|
464 break |
|
465 |
|
466 if isinstance(node.parent, |
|
467 (_ast.For, _ast.comprehension, _ast.Tuple, _ast.List)): |
|
468 binding = Binding(node.id, node) |
|
469 elif (node.id == '__all__' and |
|
470 isinstance(self.scope, ModuleScope)): |
|
471 binding = ExportBinding(node.id, node.parent.value) |
|
472 else: |
|
473 binding = Assignment(node.id, node) |
|
474 if node.id in self.scope: |
|
475 binding.used = self.scope[node.id].used |
|
476 self.addBinding(node.lineno, binding) |
|
477 elif isinstance(node.ctx, _ast.Del): |
|
478 if isinstance(self.scope, FunctionScope) and \ |
|
479 node.id in self.scope.globals: |
|
480 del self.scope.globals[node.id] |
|
481 else: |
|
482 self.addBinding(node.lineno, UnBinding(node.id, node)) |
|
483 else: |
|
484 # must be a Param context -- this only happens for names in function |
|
485 # arguments, but these aren't dispatched through here |
|
486 raise RuntimeError( |
|
487 "Got impossible expression context: %r" % (node.ctx,)) |
|
488 |
|
489 def FUNCTIONDEF(self, node): |
|
490 if hasattr(node, 'decorators'): |
|
491 for deco in node.decorators: |
|
492 self.handleNode(deco, node) |
|
493 else: |
|
494 for deco in node.decorator_list: |
|
495 self.handleNode(deco, node) |
409 self.addBinding(node.lineno, FunctionDefinition(node.name, node)) |
496 self.addBinding(node.lineno, FunctionDefinition(node.name, node)) |
410 self.LAMBDA(node) |
497 self.LAMBDA(node) |
411 |
498 |
412 def LAMBDA(self, node): |
499 def LAMBDA(self, node): |
413 for default in node.defaults: |
500 for default in node.args.defaults: |
414 self.handleNode(default, node) |
501 self.handleNode(default, node) |
415 |
502 |
416 def runFunction(): |
503 def runFunction(): |
417 args = [] |
504 args = [] |
418 |
505 |
419 def addArgs(arglist): |
506 def addArgs(arglist): |
420 for arg in arglist: |
507 for arg in arglist: |
421 if isinstance(arg, tuple): |
508 if isinstance(arg, _ast.Tuple): |
422 addArgs(arg) |
509 addArgs(arg.elts) |
423 else: |
510 else: |
424 if arg in args: |
511 if arg.id in args: |
425 self.report(messages.DuplicateArgument, node.lineno, arg) |
512 self.report(messages.DuplicateArgument, node.lineno, arg.id) |
426 args.append(arg) |
513 args.append(arg.id) |
427 |
514 |
428 self.pushFunctionScope() |
515 self.pushFunctionScope() |
429 addArgs(node.argnames) |
516 addArgs(node.args.args) |
|
517 # vararg/kwarg identifiers are not Name nodes |
|
518 if node.args.vararg: |
|
519 args.append(node.args.vararg) |
|
520 if node.args.kwarg: |
|
521 args.append(node.args.kwarg) |
430 for name in args: |
522 for name in args: |
431 self.addBinding(node.lineno, Argument(name, node), reportRedef=False) |
523 self.addBinding(node.lineno, Argument(name, node), reportRedef=False) |
432 self.handleNode(node.code, node) |
524 if isinstance(node.body, list): |
|
525 # case for FunctionDefs |
|
526 for stmt in node.body: |
|
527 self.handleNode(stmt, node) |
|
528 else: |
|
529 # case for Lambdas |
|
530 self.handleNode(node.body, node) |
|
531 |
433 def checkUnusedAssignments(): |
532 def checkUnusedAssignments(): |
434 """ |
533 """ |
435 Check to see if any assignments have not been used. |
534 Check to see if any assignments have not been used. |
436 """ |
535 """ |
437 for name, binding in self.scope.iteritems(): |
536 for name, binding in self.scope.iteritems(): |
442 self.deferAssignment(checkUnusedAssignments) |
541 self.deferAssignment(checkUnusedAssignments) |
443 self.popScope() |
542 self.popScope() |
444 |
543 |
445 self.deferFunction(runFunction) |
544 self.deferFunction(runFunction) |
446 |
545 |
447 |
546 def CLASSDEF(self, node): |
448 def CLASS(self, node): |
|
449 """ |
547 """ |
450 Check names used in a class definition, including its decorators, base |
548 Check names used in a class definition, including its decorators, base |
451 classes, and the body of its definition. Additionally, add its name to |
549 classes, and the body of its definition. Additionally, add its name to |
452 the current scope. |
550 the current scope. |
453 """ |
551 """ |
454 if getattr(node, "decorators", None) is not None: |
552 # decorator_list is present as of Python 2.6 |
455 self.handleChildren(node.decorators) |
553 for deco in getattr(node, 'decorator_list', []): |
|
554 self.handleNode(deco, node) |
456 for baseNode in node.bases: |
555 for baseNode in node.bases: |
457 self.handleNode(baseNode, node) |
556 self.handleNode(baseNode, node) |
|
557 self.pushClassScope() |
|
558 for stmt in node.body: |
|
559 self.handleNode(stmt, node) |
|
560 self.popScope() |
458 self.addBinding(node.lineno, Binding(node.name, node)) |
561 self.addBinding(node.lineno, Binding(node.name, node)) |
459 self.pushClassScope() |
|
460 self.handleChildren(node.code) |
|
461 self.popScope() |
|
462 |
|
463 |
|
464 def ASSNAME(self, node): |
|
465 if node.flags == 'OP_DELETE': |
|
466 if isinstance(self.scope, FunctionScope) and node.name in self.scope.globals: |
|
467 del self.scope.globals[node.name] |
|
468 else: |
|
469 self.addBinding(node.lineno, UnBinding(node.name, node)) |
|
470 else: |
|
471 # if the name hasn't already been defined in the current scope |
|
472 if isinstance(self.scope, FunctionScope) and node.name not in self.scope: |
|
473 # for each function or module scope above us |
|
474 for scope in self.scopeStack[:-1]: |
|
475 if not isinstance(scope, (FunctionScope, ModuleScope)): |
|
476 continue |
|
477 # if the name was defined in that scope, and the name has |
|
478 # been accessed already in the current scope, and hasn't |
|
479 # been declared global |
|
480 if (node.name in scope |
|
481 and scope[node.name].used |
|
482 and scope[node.name].used[0] is self.scope |
|
483 and node.name not in self.scope.globals): |
|
484 # then it's probably a mistake |
|
485 self.report(messages.UndefinedLocal, |
|
486 scope[node.name].used[1], |
|
487 node.name, |
|
488 scope[node.name].source.lineno) |
|
489 break |
|
490 |
|
491 if isinstance(node.parent, |
|
492 (ast.For, ast.ListCompFor, ast.GenExprFor, |
|
493 ast.AssTuple, ast.AssList)): |
|
494 binding = Binding(node.name, node) |
|
495 elif (node.name == '__all__' and |
|
496 isinstance(self.scope, ModuleScope) and |
|
497 isinstance(node.parent, ast.Assign)): |
|
498 binding = ExportBinding(node.name, node.parent.expr) |
|
499 else: |
|
500 binding = Assignment(node.name, node) |
|
501 if node.name in self.scope: |
|
502 binding.used = self.scope[node.name].used |
|
503 self.addBinding(node.lineno, binding) |
|
504 |
562 |
505 def ASSIGN(self, node): |
563 def ASSIGN(self, node): |
506 self.handleNode(node.expr, node) |
564 self.handleNode(node.value, node) |
507 for subnode in node.nodes[::-1]: |
565 for target in node.targets: |
508 self.handleNode(subnode, node) |
566 self.handleNode(target, node) |
|
567 |
|
568 def AUGASSIGN(self, node): |
|
569 # AugAssign is awkward: must set the context explicitly and visit twice, |
|
570 # once with AugLoad context, once with AugStore context |
|
571 node.target.ctx = _ast.AugLoad() |
|
572 self.handleNode(node.target, node) |
|
573 self.handleNode(node.value, node) |
|
574 node.target.ctx = _ast.AugStore() |
|
575 self.handleNode(node.target, node) |
509 |
576 |
510 def IMPORT(self, node): |
577 def IMPORT(self, node): |
511 for name, alias in node.names: |
578 for alias in node.names: |
512 name = alias or name |
579 name = alias.asname or alias.name |
513 importation = Importation(name, node) |
580 importation = Importation(name, node) |
514 self.addBinding(node.lineno, importation) |
581 self.addBinding(node.lineno, importation) |
515 |
582 |
516 def FROM(self, node): |
583 def IMPORTFROM(self, node): |
517 if node.modname == '__future__': |
584 if node.module == '__future__': |
518 if not self.futuresAllowed: |
585 if not self.futuresAllowed: |
519 self.report(messages.LateFutureImport, node.lineno, [n[0] for n in node.names]) |
586 self.report(messages.LateFutureImport, node.lineno, |
|
587 [n.name for n in node.names]) |
520 else: |
588 else: |
521 self.futuresAllowed = False |
589 self.futuresAllowed = False |
522 |
590 |
523 for name, alias in node.names: |
591 for alias in node.names: |
524 if name == '*': |
592 if alias.name == '*': |
525 self.scope.importStarred = True |
593 self.scope.importStarred = True |
526 self.report(messages.ImportStarUsed, node.lineno, node.modname) |
594 self.report(messages.ImportStarUsed, node.lineno, node.module) |
527 continue |
595 continue |
528 name = alias or name |
596 name = alias.asname or alias.name |
529 importation = Importation(name, node) |
597 importation = Importation(name, node) |
530 if node.modname == '__future__': |
598 if node.module == '__future__': |
531 importation.used = (self.scope, node.lineno) |
599 importation.used = (self.scope, node.lineno) |
532 self.addBinding(node.lineno, importation) |
600 self.addBinding(node.lineno, importation) |
533 |
601 |
534 # |
602 # |
535 # eflag: FileType = Python2 |
603 # eflag: FileType = Python2 |