Skip to content

Commit dcf7907

Browse files
committed
Mark bindings as used by locals()
Fixes #136 Fixes #333
1 parent b1b2f36 commit dcf7907

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

Diff for: 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

@@ -428,7 +427,6 @@ def unusedAssignments(self):
428427
if (not binding.used
429428
and name != '_' # see issue #202
430429
and name not in self.globals
431-
and not self.usesLocals
432430
and isinstance(binding, Assignment)):
433431
yield name, binding
434432

@@ -710,6 +708,15 @@ def handleNodeLoad(self, node):
710708
in_generators = None
711709
importStarred = None
712710

711+
if node.id == 'locals' and isinstance(node.parent, ast.Call):
712+
# we are doing locals() call, which marks names currently
713+
# in scope as used.
714+
scope = self.scope
715+
if isinstance(scope, GeneratorScope):
716+
scope = self.scopeStack[-2]
717+
for binding in scope.values():
718+
binding.used = (self.scope, node)
719+
713720
# try enclosing function scopes and global scope
714721
for scope in self.scopeStack[-1::-1]:
715722
if isinstance(scope, ClassScope):
@@ -1096,10 +1103,6 @@ def NAME(self, node):
10961103
# Locate the name in locals / function / globals scopes.
10971104
if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
10981105
self.handleNodeLoad(node)
1099-
if (node.id == 'locals' and isinstance(self.scope, FunctionScope)
1100-
and isinstance(node.parent, ast.Call)):
1101-
# we are doing locals() call in current scope
1102-
self.scope.usesLocals = True
11031106
elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
11041107
self.handleNodeStore(node)
11051108
elif isinstance(node.ctx, ast.Del):

Diff for: pyflakes/test/test_other.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -1185,7 +1185,7 @@ def a(unused_param):
11851185
_ = unused_param
11861186
''')
11871187

1188-
def test_unusedVariableAsLocals(self):
1188+
def test_unusedVariableWithLocals(self):
11891189
"""
11901190
Using locals() it is perfectly valid to have unused variables
11911191
"""
@@ -1195,6 +1195,29 @@ def a():
11951195
return locals()
11961196
''')
11971197

1198+
def test_unusedVariableWithLocalsInComprehension(self):
1199+
"""
1200+
Using locals() in comprehension it is perfectly valid
1201+
to have unused variables
1202+
"""
1203+
self.flakes('''
1204+
def a():
1205+
b = 1
1206+
return (i for i in locals())
1207+
''')
1208+
1209+
def test_unusedVariableAfterLocals(self):
1210+
"""
1211+
Warn when an unused variable appears after locals()
1212+
"""
1213+
self.flakes('''
1214+
def a():
1215+
b = 1
1216+
c = locals()
1217+
d = 1
1218+
return c
1219+
''', m.UnusedVariable)
1220+
11981221
def test_unusedVariableNoLocals(self):
11991222
"""
12001223
Using locals() in wrong scope should not matter

0 commit comments

Comments
 (0)