~divmod-dev/divmod.org/athena-events-806545

« back to all changes in this revision

Viewing changes to Pyflakes/pyflakes/checker.py

  • Committer: exarkun
  • Date: 2010-04-13 14:53:04 UTC
  • Revision ID: svn-v4:866e43f7-fbfc-0310-8f2a-ec88d1da2979:trunk:17946
Merge pyflakes-ast-3005

Author: gbrandl, exarkun
Reviewer: exarkun
Fixes: #3005

Convert pyflakes to use the Python 2.5+ _ast module instead of
the older (now essentially unmaintained) compiler package.

Introduce a number of new tests for various edge cases previously
untested, as well, since this involved changing substantial chunks
of pyflakes internals.

Also add support for certain new constructs which will be added
in Python 2.7, including set comprehensions and dict comprehensions.

Because Python 2.4 does not include the _ast module, this change
effectively drops support for running Pyflakes using Python 2.4.

Show diffs side-by-side

added added

removed removed

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