Skip to content

Commit 1bda547

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
1 parent 8a1feac commit 1bda547

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

pyflakes/checker.py

+34-2
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

@@ -959,6 +960,15 @@ def handleForwardAnnotation():
959960
def ignore(self, node):
960961
pass
961962

963+
def store_global_scope(self, name):
964+
"""This store name in global scope if it is in global_names"""
965+
if isinstance(self.scope, FunctionScope):
966+
global_scope_index = 1 if self._in_doctest() else 0
967+
global_scope = self.scopeStack[global_scope_index]
968+
if name in self.scope.global_names:
969+
global_scope.setdefault(name,
970+
Assignment(name, alias))
971+
962972
# "stmt" type nodes
963973
DELETE = PRINT = FOR = ASYNCFOR = WHILE = IF = WITH = WITHITEM = \
964974
ASYNCWITH = ASYNCWITHITEM = RAISE = TRYFINALLY = EXEC = \
@@ -1052,7 +1062,8 @@ def GLOBAL(self, node):
10521062
m.message_args[0] != node_name]
10531063

10541064
# Bind name to global scope if it doesn't exist already.
1055-
global_scope.setdefault(node_name, node_value)
1065+
if isinstance(self.scope, FunctionScope):
1066+
self.scope.global_names.append(node_name)
10561067

10571068
# Bind name to non-global scopes, but as already "used".
10581069
node_value.used = (global_scope, node)
@@ -1074,17 +1085,33 @@ def NAME(self, node):
10741085
"""
10751086
Handle occurrence of Name (which can be a load/store/delete access.)
10761087
"""
1088+
global_scope_index = 1 if self._in_doctest() else 0
1089+
global_scope = self.scopeStack[global_scope_index]
10771090
# Locate the name in locals / function / globals scopes.
10781091
if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
10791092
self.handleNodeLoad(node)
10801093
if (node.id == 'locals' and isinstance(self.scope, FunctionScope)
10811094
and isinstance(node.parent, ast.Call)):
10821095
# we are doing locals() call in current scope
10831096
self.scope.usesLocals = True
1097+
if (isinstance(self.scope, FunctionScope) and
1098+
node.id in self.scope.global_names):
1099+
if node.id not in global_scope:
1100+
self.report(messages.UndefinedName, node, node.id)
10841101
elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
10851102
self.handleNodeStore(node)
1103+
if (isinstance(self.scope, FunctionScope) and
1104+
node.id in self.scope.global_names):
1105+
global_scope.setdefault(node.id, Assignment(node.id, node))
10861106
elif isinstance(node.ctx, ast.Del):
10871107
self.handleNodeDelete(node)
1108+
if (isinstance(self.scope, FunctionScope) and
1109+
node.id in self.scope.global_names):
1110+
if node.id not in global_scope:
1111+
self.report(messages.UndefinedName, node, node.id)
1112+
else:
1113+
global_scope.pop(node.id, None)
1114+
self.scope.global_names.remove(node.id)
10881115
else:
10891116
# must be a Param context -- this only happens for names in function
10901117
# arguments, but these aren't dispatched through here
@@ -1292,13 +1319,16 @@ def TUPLE(self, node):
12921319

12931320
def IMPORT(self, node):
12941321
for alias in node.names:
1322+
name = alias.name
12951323
if '.' in alias.name and not alias.asname:
1296-
importation = SubmoduleImportation(alias.name, node)
1324+
importation = SubmoduleImportation(name, node)
12971325
else:
12981326
name = alias.asname or alias.name
12991327
importation = Importation(name, node, alias.name)
13001328
self.addBinding(node, importation)
13011329

1330+
self.store_global_scope(name)
1331+
13021332
def IMPORTFROM(self, node):
13031333
if node.module == '__future__':
13041334
if not self.futuresAllowed:
@@ -1331,6 +1361,8 @@ def IMPORTFROM(self, node):
13311361
module, alias.name)
13321362
self.addBinding(node, importation)
13331363

1364+
self.store_global_scope(name)
1365+
13341366
def TRY(self, node):
13351367
handler_names = []
13361368
# List the exception handlers

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)