Skip to content

Commit a9bdb17

Browse files
committed
Closes #249
1 parent 8a1feac commit a9bdb17

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

pyflakes/checker.py

Lines changed: 26 additions & 1 deletion
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

Lines changed: 8 additions & 0 deletions
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)