Skip to content

Commit b2d1092

Browse files
committed
Mark bindings as used by locals()
Fixes #136 Fixes #333
1 parent 8e2f493 commit b2d1092

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

pyflakes/checker.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,6 @@ class FunctionScope(Scope):
409409
410410
@ivar globals: Names declared 'global' in this function.
411411
"""
412-
usesLocals = False
413412
alwaysUsed = {'__tracebackhide__', '__traceback_info__',
414413
'__traceback_supplement__'}
415414

@@ -426,7 +425,6 @@ def unusedAssignments(self):
426425
"""
427426
for name, binding in self.items():
428427
if (not binding.used and name not in self.globals
429-
and not self.usesLocals
430428
and isinstance(binding, Assignment)):
431429
yield name, binding
432430

@@ -708,6 +706,15 @@ def handleNodeLoad(self, node):
708706
in_generators = None
709707
importStarred = None
710708

709+
if node.id == 'locals' and isinstance(node.parent, ast.Call):
710+
# we are doing locals() call, which marks names currently
711+
# in scope as used.
712+
scope = self.scope
713+
if isinstance(scope, GeneratorScope):
714+
scope = self.scopeStack[-2]
715+
for binding in scope.values():
716+
binding.used = (self.scope, node)
717+
711718
# try enclosing function scopes and global scope
712719
for scope in self.scopeStack[-1::-1]:
713720
if isinstance(scope, ClassScope):
@@ -1094,10 +1101,6 @@ def NAME(self, node):
10941101
# Locate the name in locals / function / globals scopes.
10951102
if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
10961103
self.handleNodeLoad(node)
1097-
if (node.id == 'locals' and isinstance(self.scope, FunctionScope)
1098-
and isinstance(node.parent, ast.Call)):
1099-
# we are doing locals() call in current scope
1100-
self.scope.usesLocals = True
11011104
elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
11021105
self.handleNodeStore(node)
11031106
elif isinstance(node.ctx, ast.Del):

pyflakes/test/test_other.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -1175,7 +1175,7 @@ def a():
11751175
b = 1
11761176
''', m.UnusedVariable)
11771177

1178-
def test_unusedVariableAsLocals(self):
1178+
def test_unusedVariableWithLocals(self):
11791179
"""
11801180
Using locals() it is perfectly valid to have unused variables
11811181
"""
@@ -1185,6 +1185,29 @@ def a():
11851185
return locals()
11861186
''')
11871187

1188+
def test_unusedVariableWithLocalsInComprehension(self):
1189+
"""
1190+
Using locals() in comprehension it is perfectly valid
1191+
to have unused variables
1192+
"""
1193+
self.flakes('''
1194+
def a():
1195+
b = 1
1196+
return (i for i in locals())
1197+
''')
1198+
1199+
def test_unusedVariableAfterLocals(self):
1200+
"""
1201+
Warn when an unused variable appears after locals()
1202+
"""
1203+
self.flakes('''
1204+
def a():
1205+
b = 1
1206+
c = locals()
1207+
d = 1
1208+
return c
1209+
''', m.UnusedVariable)
1210+
11881211
def test_unusedVariableNoLocals(self):
11891212
"""
11901213
Using locals() in wrong scope should not matter

0 commit comments

Comments
 (0)