diff --git a/pyflakes/checker.py b/pyflakes/checker.py index bd5eba57..6fad51d7 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -409,7 +409,6 @@ class FunctionScope(Scope): @ivar globals: Names declared 'global' in this function. """ - usesLocals = False alwaysUsed = {'__tracebackhide__', '__traceback_info__', '__traceback_supplement__'} @@ -426,7 +425,6 @@ def unusedAssignments(self): """ for name, binding in self.items(): if (not binding.used and name not in self.globals - and not self.usesLocals and isinstance(binding, Assignment)): yield name, binding @@ -708,6 +706,15 @@ def handleNodeLoad(self, node): in_generators = None importStarred = None + if node.id == 'locals' and isinstance(node.parent, ast.Call): + # we are doing locals() call, which marks names currently + # in scope as used. + scope = self.scope + if isinstance(scope, GeneratorScope): + scope = self.scopeStack[-2] + for binding in scope.values(): + binding.used = (self.scope, node) + # try enclosing function scopes and global scope for scope in self.scopeStack[-1::-1]: if isinstance(scope, ClassScope): @@ -1094,10 +1101,6 @@ def NAME(self, node): # Locate the name in locals / function / globals scopes. if isinstance(node.ctx, (ast.Load, ast.AugLoad)): self.handleNodeLoad(node) - if (node.id == 'locals' and isinstance(self.scope, FunctionScope) - and isinstance(node.parent, ast.Call)): - # we are doing locals() call in current scope - self.scope.usesLocals = True elif isinstance(node.ctx, (ast.Store, ast.AugStore)): self.handleNodeStore(node) elif isinstance(node.ctx, ast.Del): diff --git a/pyflakes/test/test_other.py b/pyflakes/test/test_other.py index abf08f9b..b4dc7dc1 100644 --- a/pyflakes/test/test_other.py +++ b/pyflakes/test/test_other.py @@ -1175,7 +1175,7 @@ def a(): b = 1 ''', m.UnusedVariable) - def test_unusedVariableAsLocals(self): + def test_unusedVariableWithLocals(self): """ Using locals() it is perfectly valid to have unused variables """ @@ -1185,6 +1185,29 @@ def a(): return locals() ''') + def test_unusedVariableWithLocalsInComprehension(self): + """ + Using locals() in comprehension it is perfectly valid + to have unused variables + """ + self.flakes(''' + def a(): + b = 1 + return (i for i in locals()) + ''') + + def test_unusedVariableAfterLocals(self): + """ + Warn when an unused variable appears after locals() + """ + self.flakes(''' + def a(): + b = 1 + c = locals() + d = 1 + return c + ''', m.UnusedVariable) + def test_unusedVariableNoLocals(self): """ Using locals() in wrong scope should not matter