Ticket #2718: unused-in-function-2718-4.diff
| File unused-in-function-2718-4.diff, 14.0 kB (added by jml, 9 months 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 … … 79 113 80 114 81 115 class Checker(object): 116 """ 117 I check the cleanliness and sanity of Python code. 118 """ 119 82 120 nodeDepth = 0 83 121 traceTree = False 84 122 85 123 def __init__(self, tree, filename='(none)'): 86 self.deferred = [] 124 self._deferredFunctions = [] 125 self._deferredAssignments = [] 87 126 self.dead_scopes = [] 88 127 self.messages = [] 89 128 self.filename = filename 90 129 self.scopeStack = [ModuleScope()] 91 130 self.futuresAllowed = True 92 93 131 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 97 140 del self.scopeStack[1:] 98 141 self.popScope() 99 142 self.check_dead_scopes() 100 143 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. 103 148 104 149 This is used for handling function bodies, which must be deferred 105 150 because code later in the file might modify the global scope. When 106 151 `callable` is called, the scope at the time this is called will be 107 152 restored, however it will contain any new bindings added to it. 108 153 ''' 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 110 172 111 173 def scope(self): 112 174 return self.scopeStack[-1] … … 132 194 133 195 def handleChildren(self, tree): 134 196 for node in tree.getChildNodes(): 135 self.handleNode(node )197 self.handleNode(node, tree) 136 198 137 def handleNode(self, node): 199 def handleNode(self, node, parent): 200 node.parent = parent 138 201 if self.traceTree: 139 202 print ' ' * self.nodeDepth + node.__class__.__name__ 140 203 self.nodeDepth += 1 … … 208 271 # Of course these are assignments, not references, so we have to 209 272 # handle them as a special case here. 210 273 211 self.handleNode(node.expr )274 self.handleNode(node.expr, node) 212 275 213 276 if isinstance(node.vars, ast.AssTuple): 214 277 varNodes = node.vars.nodes … … 232 295 233 296 def LISTCOMP(self, node): 234 297 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) 237 300 238 301 GENEXPRINNER = LISTCOMP 239 302 … … 305 368 306 369 def LAMBDA(self, node): 307 370 for default in node.defaults: 308 self.handleNode(default )371 self.handleNode(default, node) 309 372 310 373 def runFunction(): 311 374 args = [] … … 322 385 self.pushFunctionScope() 323 386 addArgs(node.argnames) 324 387 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) 327 400 self.popScope() 328 401 329 self.defer (runFunction)402 self.deferFunction(runFunction) 330 403 331 404 332 405 def CLASS(self, node): … … 337 410 """ 338 411 if getattr(node, "decorators", None) is not None: 339 412 self.handleChildren(node.decorators) 340 self.addBinding(node.lineno, Assignment(node.name, node))413 self.addBinding(node.lineno, Binding(node.name, node)) 341 414 for baseNode in node.bases: 342 self.handleNode(baseNode )415 self.handleNode(baseNode, node) 343 416 self.pushClassScope() 344 417 self.handleChildren(node.code) 345 418 self.popScope() … … 372 445 scope[node.name].source.lineno) 373 446 break 374 447 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) 376 457 377 458 def ASSIGN(self, node): 378 self.handleNode(node.expr )459 self.handleNode(node.expr, node) 379 460 for subnode in node.nodes[::-1]: 380 self.handleNode(subnode )461 self.handleNode(subnode, node) 381 462 382 463 def IMPORT(self, node): 383 464 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 113 113 def fun(): 114 114 a 115 115 a = 2 116 return a 116 117 ''', m.UndefinedLocal) 117 118 118 119 def test_laterRedefinedGlobalFromNestedScope2(self): … … 128 129 def fun2(): 129 130 a 130 131 a = 2 132 return a 131 133 ''', m.UndefinedLocal) 132 134 133 135 … … 146 148 def c(): 147 149 x 148 150 x = 3 151 return x 152 return x 153 return x 149 154 ''', m.UndefinedLocal).messages[0] 150 155 self.assertEqual(exc.message_args, ('x', 5)) 151 156 … … 161 166 def fun2(): 162 167 a 163 168 a = 1 169 return a 170 return a 164 171 ''', m.UndefinedLocal) 165 172 166 173 def test_nestedClass(self): … … 183 190 class C: 184 191 bar = foo 185 192 foo = 456 186 193 return foo 187 194 f() 188 195 ''', m.UndefinedName) 189 196
