Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track locals() #343

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Mark bindings as used by locals()
Fixes #136
Fixes #333
jayvdb committed Jul 17, 2018
commit dcf7907a6612939ecd0df8880862fee40cd1d3b4
15 changes: 9 additions & 6 deletions pyflakes/checker.py
Original file line number Diff line number Diff line change
@@ -409,7 +409,6 @@ class FunctionScope(Scope):

@ivar globals: Names declared 'global' in this function.
"""
usesLocals = False
alwaysUsed = {'__tracebackhide__', '__traceback_info__',
'__traceback_supplement__'}

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

@@ -710,6 +708,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)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be changed to

if not binding.used:
    binding.used = (self.scope, node)

Then at the end of the scope, any bindings used only by locals() could be a different type of message.

See #333 (comment)


# try enclosing function scopes and global scope
for scope in self.scopeStack[-1::-1]:
if isinstance(scope, ClassScope):
@@ -1096,10 +1103,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):
25 changes: 24 additions & 1 deletion pyflakes/test/test_other.py
Original file line number Diff line number Diff line change
@@ -1185,7 +1185,7 @@ def a(unused_param):
_ = unused_param
''')

def test_unusedVariableAsLocals(self):
def test_unusedVariableWithLocals(self):
"""
Using locals() it is perfectly valid to have unused variables
"""
@@ -1195,6 +1195,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