Ticket #2718: unused-in-function-2718-4.diff

File unused-in-function-2718-4.diff, 14.0 kB (added by jml, 9 months ago)

Latest version of patch.

  • pyflakes/checker.py

    old new  
    88from pyflakes import messages 
    99 
    1010 
     11 
    1112class Binding(object): 
    1213    """ 
     14    Represents the binding of a value to a name. 
     15 
     16    The checker uses this to keep track of which names have been bound and 
     17    which names have not. See L{Assignment} for a special type of binding that 
     18    is checked with stricter rules. 
     19 
    1320    @ivar used: pair of (L{Scope}, line-number) indicating the scope and 
    1421                line number that this binding was last used 
    1522    """ 
     23 
    1624    def __init__(self, name, source): 
    1725        self.name = name 
    1826        self.source = source 
    1927        self.used = False 
    2028 
     29 
    2130    def __str__(self): 
    2231        return self.name 
    2332 
     33 
    2434    def __repr__(self): 
    2535        return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__, 
    2636                                                        self.name, 
    2737                                                        self.source.lineno, 
    2838                                                        id(self)) 
    2939 
     40 
     41 
    3042class UnBinding(Binding): 
    3143    '''Created by the 'del' operator.''' 
    3244 
     
    3547        name = name.split('.')[0] 
    3648        super(Importation, self).__init__(name, source) 
    3749 
     50 
     51 
     52class Argument(Binding): 
     53    """ 
     54    Represents binding a name as an argument. 
     55    """ 
     56 
     57 
     58 
    3859class Assignment(Binding): 
    39     pass 
     60    """ 
     61    Represents binding a name with an explicit assignment. 
     62 
     63    The checker will raise warnings for any Assignment that isn't used. Also, 
     64    the checker does not consider assignments in tuple/list unpacking to be 
     65    Assignments, rather it treats them as simple Bindings. 
     66    """ 
     67 
     68 
    4069 
    4170class FunctionDefinition(Binding): 
    4271    pass 
    4372 
    4473 
     74 
    4575class Scope(dict): 
    4676    importStarred = False       # set to True when import * is found 
    4777 
     78 
    4879    def __repr__(self): 
    4980        return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self)) 
    5081 
     82 
    5183    def __init__(self): 
    5284        super(Scope, self).__init__() 
    5385 
     86 
     87 
    5488class ClassScope(Scope): 
    5589    pass 
    5690 
     
    79113 
    80114 
    81115class Checker(object): 
     116    """ 
     117    I check the cleanliness and sanity of Python code. 
     118    """ 
     119 
    82120    nodeDepth = 0 
    83121    traceTree = False 
    84122 
    85123    def __init__(self, tree, filename='(none)'): 
    86         self.deferred = [] 
     124        self._deferredFunctions = [] 
     125        self._deferredAssignments = [] 
    87126        self.dead_scopes = [] 
    88127        self.messages = [] 
    89128        self.filename = filename 
    90129        self.scopeStack = [ModuleScope()] 
    91130        self.futuresAllowed = True 
    92  
    93131        self.handleChildren(tree) 
    94         for handler, scope in self.deferred: 
    95             self.scopeStack = scope 
    96             handler() 
     132        self._runDeferred(self._deferredFunctions) 
     133        # Set _deferredFunctions to None so that deferFunction will fail 
     134        # noisily if called after we've run through the deferred functions. 
     135        self._deferredFunctions = None 
     136        self._runDeferred(self._deferredAssignments) 
     137        # Set _deferredAssignments to None so that deferAssignment will fail 
     138        # noisly if called after we've run through the deferred assignments. 
     139        self._deferredAssignments = None 
    97140        del self.scopeStack[1:] 
    98141        self.popScope() 
    99142        self.check_dead_scopes() 
    100143 
    101     def defer(self, callable): 
    102         '''Schedule something to be called after just before completion. 
     144 
     145    def deferFunction(self, callable): 
     146        ''' 
     147        Schedule a function handler to be called just before completion. 
    103148 
    104149        This is used for handling function bodies, which must be deferred 
    105150        because code later in the file might modify the global scope. When 
    106151        `callable` is called, the scope at the time this is called will be 
    107152        restored, however it will contain any new bindings added to it. 
    108153        ''' 
    109         self.deferred.append( (callable, self.scopeStack[:]) ) 
     154        self._deferredFunctions.append((callable, self.scopeStack[:])) 
     155 
     156 
     157    def deferAssignment(self, callable): 
     158        """ 
     159        Schedule an assignment handler to be called just before completion. 
     160        """ 
     161        self._deferredAssignments.append((callable, self.scopeStack[:])) 
     162 
     163 
     164    def _runDeferred(self, deferred): 
     165        """ 
     166        Run the callables in C{deferred} using their associated scope stack. 
     167        """ 
     168        for handler, scope in deferred: 
     169            self.scopeStack = scope 
     170            handler() 
     171 
    110172 
    111173    def scope(self): 
    112174        return self.scopeStack[-1] 
     
    132194 
    133195    def handleChildren(self, tree): 
    134196        for node in tree.getChildNodes(): 
    135             self.handleNode(node
     197            self.handleNode(node, tree
    136198 
    137     def handleNode(self, node): 
     199    def handleNode(self, node, parent): 
     200        node.parent = parent 
    138201        if self.traceTree: 
    139202            print '  ' * self.nodeDepth + node.__class__.__name__ 
    140203        self.nodeDepth += 1 
     
    208271        # Of course these are assignments, not references, so we have to 
    209272        # handle them as a special case here. 
    210273 
    211         self.handleNode(node.expr
     274        self.handleNode(node.expr, node
    212275 
    213276        if isinstance(node.vars, ast.AssTuple): 
    214277            varNodes = node.vars.nodes 
     
    232295 
    233296    def LISTCOMP(self, node): 
    234297        for qual in node.quals: 
    235             self.handleNode(qual
    236         self.handleNode(node.expr
     298            self.handleNode(qual, node
     299        self.handleNode(node.expr, node
    237300 
    238301    GENEXPRINNER = LISTCOMP 
    239302 
     
    305368 
    306369    def LAMBDA(self, node): 
    307370        for default in node.defaults: 
    308             self.handleNode(default
     371            self.handleNode(default, node
    309372 
    310373        def runFunction(): 
    311374            args = [] 
     
    322385            self.pushFunctionScope() 
    323386            addArgs(node.argnames) 
    324387            for name in args: 
    325                 self.addBinding(node.lineno, Assignment(name, node), reportRedef=False) 
    326             self.handleNode(node.code) 
     388                self.addBinding(node.lineno, Argument(name, node), reportRedef=False) 
     389            self.handleNode(node.code, node) 
     390            def checkUnusedAssignments(): 
     391                """ 
     392                Check to see if any assignments have not been used. 
     393                """ 
     394                for name, binding in self.scope.iteritems(): 
     395                    if (not binding.used and not name in self.scope.globals 
     396                        and isinstance(binding, Assignment)): 
     397                        self.report(messages.UnusedVariable, 
     398                                    binding.source.lineno, name) 
     399            self.deferAssignment(checkUnusedAssignments) 
    327400            self.popScope() 
    328401 
    329         self.defer(runFunction) 
     402        self.deferFunction(runFunction) 
    330403 
    331404 
    332405    def CLASS(self, node): 
     
    337410        """ 
    338411        if getattr(node, "decorators", None) is not None: 
    339412            self.handleChildren(node.decorators) 
    340         self.addBinding(node.lineno, Assignment(node.name, node)) 
     413        self.addBinding(node.lineno, Binding(node.name, node)) 
    341414        for baseNode in node.bases: 
    342             self.handleNode(baseNode
     415            self.handleNode(baseNode, node
    343416        self.pushClassScope() 
    344417        self.handleChildren(node.code) 
    345418        self.popScope() 
     
    372445                                    scope[node.name].source.lineno) 
    373446                        break 
    374447 
    375             self.addBinding(node.lineno, Assignment(node.name, node)) 
     448            if isinstance(node.parent, 
     449                          (ast.For, ast.ListCompFor, ast.GenExprFor, 
     450                           ast.AssTuple, ast.AssList)): 
     451                binding = Binding(node.name, node) 
     452            else: 
     453                binding = Assignment(node.name, node) 
     454            if node.name in self.scope: 
     455                binding.used = self.scope[node.name].used 
     456            self.addBinding(node.lineno, binding) 
    376457 
    377458    def ASSIGN(self, node): 
    378         self.handleNode(node.expr
     459        self.handleNode(node.expr, node
    379460        for subnode in node.nodes[::-1]: 
    380             self.handleNode(subnode
     461            self.handleNode(subnode, node
    381462 
    382463    def IMPORT(self, node): 
    383464        for name, alias in node.names: 
  • pyflakes/messages.py

    old new  
    7171    def __init__(self, filename, lineno, names): 
    7272        Message.__init__(self, filename, lineno) 
    7373        self.message_args = (names,) 
     74 
     75 
     76class UnusedVariable(Message): 
     77    """ 
     78    Indicates that a variable has been explicity assigned to but not actually 
     79    used. 
     80    """ 
     81 
     82    message = 'local variable %r is assigned to but never used' 
     83    def __init__(self, filename, lineno, names): 
     84        Message.__init__(self, filename, lineno) 
     85        self.message_args = (names,) 
  • pyflakes/test/test_imports.py

    old new  
    401401        import fu 
    402402        def a(): 
    403403            fu = 3 
     404            return fu 
    404405        fu 
    405406        ''') 
    406407 
  • pyflakes/test/test_other.py

    old new  
    7575 
    7676 
    7777 
     78class TestUnusedAssignment(harness.Test): 
     79    """ 
     80    Tests for warning about unused assignments. 
     81    """ 
     82 
     83    def test_unusedVariable(self): 
     84        """ 
     85        Warn when a variable in a function is assigned a value that's never 
     86        used. 
     87        """ 
     88        self.flakes(''' 
     89        def a(): 
     90            b = 1 
     91        ''', m.UnusedVariable) 
     92 
     93 
     94    def test_assignToGlobal(self): 
     95        """ 
     96        Assigning to a global and then not using that global is perfectly 
     97        acceptable. Do not mistake it for an unused local variable. 
     98        """ 
     99        self.flakes(''' 
     100        b = 0 
     101        def a(): 
     102            global b 
     103            b = 1 
     104        ''') 
     105 
     106 
     107    def test_assignToMember(self): 
     108        """ 
     109        Assigning to a member of another object and then not using that member 
     110        variable is perfectly acceptable. Do not mistake it for an unused 
     111        local variable. 
     112        """ 
     113        # XXX: Adding this test didn't generate a failure. Maybe not 
     114        # necessary? 
     115        self.flakes(''' 
     116        class b: 
     117            pass 
     118        def a(): 
     119            b.foo = 1 
     120        ''') 
     121 
     122 
     123    def test_assignInForLoop(self): 
     124        """ 
     125        Don't warn when a variable in a for loop is assigned to but not used. 
     126        """ 
     127        self.flakes(''' 
     128        def f(): 
     129            for i in range(10): 
     130                pass 
     131        ''') 
     132 
     133 
     134    def test_assignInListComprehension(self): 
     135        """ 
     136        Don't warn when a variable in a for loop is assigned to but not used. 
     137        """ 
     138        self.flakes(''' 
     139        def f(): 
     140            [None for i in range(10)] 
     141        ''') 
     142 
     143 
     144    def test_generatorComprehension(self): 
     145        """ 
     146        Don't warn when a variable in a for loop is assigned to but not used. 
     147        """ 
     148        self.flakes(''' 
     149        def f(): 
     150            (None for i in range(10)) 
     151        ''') 
     152 
     153 
     154    def test_assignmentInsideLoop(self): 
     155        """ 
     156        Don't warn when a variable assignment occurs lexically after its use. 
     157        """ 
     158        self.flakes(''' 
     159        def f(): 
     160            x = None 
     161            for i in range(10): 
     162                if i > 2: 
     163                    return x 
     164                x = i * 2 
     165        ''') 
     166 
     167 
     168    def test_tupleUnpacking(self): 
     169        """ 
     170        Don't warn when a variable included in tuple unpacking is unused. It's 
     171        very common for variables in a tuple unpacking assignment to be unused 
     172        in good Python code, so warning will only create false positives. 
     173        """ 
     174        self.flakes(''' 
     175        def f(): 
     176            (x, y) = 1, 2 
     177        ''') 
     178 
     179 
     180    def test_listUnpacking(self): 
     181        """ 
     182        Don't warn when a variable included in list unpacking is unused. 
     183        """ 
     184        self.flakes(''' 
     185        def f(): 
     186            [x, y] = [1, 2] 
     187        ''') 
     188 
     189 
     190    def test_closedOver(self): 
     191        """ 
     192        Don't warn when the assignment is used in an inner function. 
     193        """ 
     194        self.flakes(''' 
     195        def barMaker(): 
     196            foo = 5 
     197            def bar(): 
     198                return foo 
     199            return bar 
     200        ''') 
     201 
     202 
     203    def test_doubleClosedOver(self): 
     204        """ 
     205        Don't warn when the assignment is used in an inner function, even if 
     206        that inner function itself is in an inner function. 
     207        """ 
     208        self.flakes(''' 
     209        def barMaker(): 
     210            foo = 5 
     211            def bar(): 
     212                def baz(): 
     213                    return foo 
     214            return bar 
     215        ''') 
     216 
     217 
     218 
    78219class Python25Test(harness.Test): 
    79220    """ 
    80221    Tests for checking of syntax only available in Python 2.5 and newer. 
  • pyflakes/test/test_undefined_names.py

    old new  
    113113        def fun(): 
    114114            a 
    115115            a = 2 
     116            return a 
    116117        ''', m.UndefinedLocal) 
    117118 
    118119    def test_laterRedefinedGlobalFromNestedScope2(self): 
     
    128129                def fun2(): 
    129130                    a 
    130131                    a = 2 
     132                    return a 
    131133        ''', m.UndefinedLocal) 
    132134 
    133135 
     
    146148                    def c(): 
    147149                        x 
    148150                        x = 3 
     151                        return x 
     152                    return x 
     153                return x 
    149154        ''', m.UndefinedLocal).messages[0] 
    150155        self.assertEqual(exc.message_args, ('x', 5)) 
    151156 
     
    161166                def fun2(): 
    162167                    a 
    163168                    a = 1 
     169                    return a 
     170                return a 
    164171        ''', m.UndefinedLocal) 
    165172 
    166173    def test_nestedClass(self): 
     
    183190            class C: 
    184191                bar = foo 
    185192            foo = 456 
    186  
     193            return foo 
    187194        f() 
    188195        ''', m.UndefinedName) 
    189196 
jethro@divmod.org