Skip to content

Commit

Permalink
Mark bindings as used by locals()
Browse files Browse the repository at this point in the history
Fixes #136
Fixes #333
  • Loading branch information
jayvdb committed Jul 16, 2018
1 parent 8e2f493 commit b2d1092
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 7 deletions.
15 changes: 9 additions & 6 deletions pyflakes/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,6 @@ class FunctionScope(Scope):
@ivar globals: Names declared 'global' in this function.
"""
usesLocals = False
alwaysUsed = {'__tracebackhide__', '__traceback_info__',
'__traceback_supplement__'}

Expand All @@ -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

Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down
25 changes: 24 additions & 1 deletion pyflakes/test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
"""
Expand All @@ -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
Expand Down

0 comments on commit b2d1092

Please sign in to comment.