Skip to content

Commit 869e6c0

Browse files
committed
Catch usage of undefined name with global statement
When we use undefined name declared by global statement, pyflakes should catch the error but it doesn't. This will fix pyflakes to catch this error. Check test_undefined_globa Fixes #249
1 parent 8a1feac commit 869e6c0

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

pyflakes/checker.py

+26-1
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ def __init__(self):
417417
super(FunctionScope, self).__init__()
418418
# Simplify: manage the special locals as globals
419419
self.globals = self.alwaysUsed.copy()
420+
self.global_names = []
420421
self.returnValue = None # First non-empty return
421422
self.isGenerator = False # Detect a generator
422423

@@ -430,6 +431,13 @@ def unusedAssignments(self):
430431
and isinstance(binding, Assignment)):
431432
yield name, binding
432433

434+
def usedAssignments(self):
435+
for name, binding in self.items():
436+
if (name not in self.globals and
437+
not self.usesLocals and
438+
isinstance(binding, Assignment)):
439+
yield name, binding
440+
433441

434442
class GeneratorScope(Scope):
435443
pass
@@ -1052,7 +1060,8 @@ def GLOBAL(self, node):
10521060
m.message_args[0] != node_name]
10531061

10541062
# Bind name to global scope if it doesn't exist already.
1055-
global_scope.setdefault(node_name, node_value)
1063+
if isinstance(self.scope, FunctionScope):
1064+
self.scope.global_names.append(node_name)
10561065

10571066
# Bind name to non-global scopes, but as already "used".
10581067
node_value.used = (global_scope, node)
@@ -1074,17 +1083,33 @@ def NAME(self, node):
10741083
"""
10751084
Handle occurrence of Name (which can be a load/store/delete access.)
10761085
"""
1086+
global_scope_index = 1 if self._in_doctest() else 0
1087+
global_scope = self.scopeStack[global_scope_index]
10771088
# Locate the name in locals / function / globals scopes.
10781089
if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
10791090
self.handleNodeLoad(node)
10801091
if (node.id == 'locals' and isinstance(self.scope, FunctionScope)
10811092
and isinstance(node.parent, ast.Call)):
10821093
# we are doing locals() call in current scope
10831094
self.scope.usesLocals = True
1095+
if (isinstance(self.scope, FunctionScope) and
1096+
node.id in self.scope.global_names):
1097+
if node.id not in global_scope:
1098+
self.report(messages.UndefinedName, node, node.id)
10841099
elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
10851100
self.handleNodeStore(node)
1101+
if (isinstance(self.scope, FunctionScope) and
1102+
node.id in self.scope.global_names):
1103+
global_scope.setdefault(node.id, Assignment(node.id, node))
10861104
elif isinstance(node.ctx, ast.Del):
10871105
self.handleNodeDelete(node)
1106+
if (isinstance(self.scope, FunctionScope) and
1107+
node.id in self.scope.global_names):
1108+
if not node.id in global_scope:
1109+
self.report(messages.UndefinedName, node, node.id)
1110+
else:
1111+
global_scope.pop(node.id, None)
1112+
self.scope.global_names.remove(node.id)
10881113
else:
10891114
# must be a Param context -- this only happens for names in function
10901115
# arguments, but these aren't dispatched through here

pyflakes/test/test_undefined_names.py

+8
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,14 @@ def f2():
337337
global m
338338
''', m.UndefinedName)
339339

340+
def test_undefined_global(self):
341+
"""Use an undefined name with global statement"""
342+
self.flakes('''
343+
def f():
344+
global m
345+
print(m)
346+
''', m.UndefinedName)
347+
340348
def test_del(self):
341349
"""Del deletes bindings."""
342350
self.flakes('a = 1; del a; a', m.UndefinedName)

0 commit comments

Comments
 (0)