Ticket #2718: unused-in-function-2718-3.diff
| File unused-in-function-2718-3.diff, 13.1 kB (added by jml, 1 year ago) |
|---|
-
pyflakes/checker.py
old new 8 8 from pyflakes import messages 9 9 10 10 11 11 12 class Binding(object): 12 13 """ 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 13 20 @ivar used: pair of (L{Scope}, line-number) indicating the scope and 14 21 line number that this binding was last used 15 22 """ 23 16 24 def __init__(self, name, source): 17 25 self.name = name 18 26 self.source = source 19 27 self.used = False 20 28 29 21 30 def __str__(self): 22 31 return self.name 23 32 33 24 34 def __repr__(self): 25 35 return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__, 26 36 self.name, 27 37 self.source.lineno, 28 38 id(self)) 29 39 40 41 30 42 class UnBinding(Binding): 31 43 '''Created by the 'del' operator.''' 32 44 … … 35 47 name = name.split('.')[0] 36 48 super(Importation, self).__init__(name, source) 37 49 50 51 52 class Argument(Binding): 53 """ 54 Represents binding a name as an argument. 55 """ 56 57 58 38 59 class 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 40 69 41 70 class FunctionDefinition(Binding): 42 71 pass 43 72 44 73 74 45 75 class Scope(dict): 46 76 importStarred = False # set to True when import * is found 47 77 78 48 79 def __repr__(self): 49 80 return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self)) 50 81 82 51 83 def __init__(self): 52 84 super(Scope, self).__init__() 53 85 86 87 54 88 class ClassScope(Scope): 55 89 pass 56 90 … … 72 106 pass 73 107 74 108 109 75 110 class Checker(object): 76 111 nodeDepth = 0 77 112 traceTree = False 78 113 79 114 def __init__(self, tree, filename='(none)'): 80 self.deferred = [] 115 self._deferredFunctions = [] 116 self._deferredAssignments = [] 81 117 self.dead_scopes = [] 82 118 self.messages = [] 83 119 self.filename = filename 84 120 self.scopeStack = [ModuleScope()] 85 121 self.futuresAllowed = True 86 87 122 self.handleChildren(tree) 88 for handler, scope in self.deferred: 89 self.scopeStack = scope 90 handler() 123 self._runDeferred(self._deferredFunctions) 124 self._runDeferred(self._deferredAssignments) 91 125 del self.scopeStack[1:] 92 126 self.popScope() 93 127 self.check_dead_scopes() 94 128 95 def defer(self, callable): 96 '''Schedule something to be called after just before completion. 129 130 def deferFunction(self, callable): 131 ''' 132 Schedule a function handler to be called after just before completion. 97 133 98 134 This is used for handling function bodies, which must be deferred 99 135 because code later in the file might modify the global scope. When 100 136 `callable` is called, the scope at the time this is called will be 101 137 restored, however it will contain any new bindings added to it. 102 138 ''' 103 self.deferred.append( (callable, self.scopeStack[:]) ) 139 self._deferredFunctions.append((callable, self.scopeStack[:])) 140 141 142 def deferAssignment(self, callable): 143 """ 144 Schedule an assignment handler to be called after just before 145 completion. 146 """ 147 self._deferredAssignments.append((callable, self.scopeStack[:])) 148 149 150 def _runDeferred(self, deferred): 151 """ 152 Run the callables in C{deferred} using their associated scope stack. 153 """ 154 for handler, scope in deferred: 155 self.scopeStack = scope 156 handler() 157 104 158 105 159 def scope(self): 106 160 return self.scopeStack[-1] … … 126 180 127 181 def handleChildren(self, tree): 128 182 for node in tree.getChildNodes(): 129 self.handleNode(node )183 self.handleNode(node, tree) 130 184 131 def handleNode(self, node): 185 def handleNode(self, node, parent=None): 186 node.parent = parent 132 187 if self.traceTree: 133 188 print ' ' * self.nodeDepth + node.__class__.__name__ 134 189 self.nodeDepth += 1 … … 226 281 227 282 def LISTCOMP(self, node): 228 283 for qual in node.quals: 229 self.handleNode(qual )230 self.handleNode(node.expr )284 self.handleNode(qual, node) 285 self.handleNode(node.expr, node) 231 286 232 287 GENEXPRINNER = LISTCOMP 233 288 … … 299 354 300 355 def LAMBDA(self, node): 301 356 for default in node.defaults: 302 self.handleNode(default )357 self.handleNode(default, node) 303 358 304 359 def runFunction(): 305 360 args = [] … … 316 371 self.pushFunctionScope() 317 372 addArgs(node.argnames) 318 373 for name in args: 319 self.addBinding(node.lineno, Assignment(name, node), reportRedef=False) 320 self.handleNode(node.code) 374 self.addBinding(node.lineno, Argument(name, node), reportRedef=False) 375 self.handleNode(node.code, node) 376 def checkUnusedAssignments(): 377 """ 378 Check to see if any assignments have not been used. 379 """ 380 for name, binding in self.scope.iteritems(): 381 if (not binding.used and not name in self.scope.globals 382 and isinstance(binding, Assignment)): 383 self.report(messages.UnusedVariable, 384 binding.source.lineno, name) 385 self.deferAssignment(checkUnusedAssignments) 321 386 self.popScope() 322 387 323 self.defer (runFunction)388 self.deferFunction(runFunction) 324 389 325 390 def CLASS(self, node): 326 self.addBinding(node.lineno, Assignment(node.name, node))391 self.addBinding(node.lineno, Binding(node.name, node)) 327 392 for baseNode in node.bases: 328 self.handleNode(baseNode )393 self.handleNode(baseNode, node) 329 394 self.pushClassScope() 330 395 self.handleChildren(node.code) 331 396 self.popScope() … … 357 422 scope[node.name].source.lineno) 358 423 break 359 424 360 self.addBinding(node.lineno, Assignment(node.name, node)) 425 if isinstance(node.parent, 426 (ast.For, ast.ListCompFor, ast.GenExprFor, 427 ast.AssTuple, ast.AssList)): 428 binding = Binding(node.name, node) 429 else: 430 binding = Assignment(node.name, node) 431 if node.name in self.scope: 432 binding.used = self.scope[node.name].used 433 self.addBinding(node.lineno, binding) 361 434 362 435 def ASSIGN(self, node): 363 self.handleNode(node.expr )436 self.handleNode(node.expr, node) 364 437 for subnode in node.nodes[::-1]: 365 self.handleNode(subnode )438 self.handleNode(subnode, node) 366 439 367 440 def IMPORT(self, node): 368 441 for name, alias in node.names: -
pyflakes/messages.py
old new 71 71 def __init__(self, filename, lineno, names): 72 72 Message.__init__(self, filename, lineno) 73 73 self.message_args = (names,) 74 75 76 class 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 401 401 import fu 402 402 def a(): 403 403 fu = 3 404 return fu 404 405 fu 405 406 ''') 406 407 -
pyflakes/test/test_other.py
old new 75 75 76 76 77 77 78 class 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 78 219 class Python25Test(harness.Test): 79 220 """ 80 221 Tests for checking of syntax only available in Python 2.5 and newer. -
pyflakes/test/test_undefined_names.py
old new 91 91 def fun(): 92 92 a 93 93 a = 2 94 return a 94 95 ''', m.UndefinedLocal) 95 96 96 97 def test_laterRedefinedGlobalFromNestedScope2(self): … … 106 107 def fun2(): 107 108 a 108 109 a = 2 110 return a 109 111 ''', m.UndefinedLocal) 110 112 111 113 … … 124 126 def c(): 125 127 x 126 128 x = 3 129 return x 130 return x 131 return x 127 132 ''', m.UndefinedLocal).messages[0] 128 133 self.assertEqual(exc.message_args, ('x', 5)) 129 134 … … 139 144 def fun2(): 140 145 a 141 146 a = 1 147 return a 148 return a 142 149 ''', m.UndefinedLocal) 143 150 144 151 def test_nestedClass(self): … … 161 168 class C: 162 169 bar = foo 163 170 foo = 456 164 171 return foo 165 172 f() 166 173 ''', m.UndefinedName) 167 174
