Skip to content

Commit 72f208f

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 45fc732 commit 72f208f

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

pyflakes/checker.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@ def __init__(self):
435435
super(FunctionScope, self).__init__()
436436
# Simplify: manage the special locals as globals
437437
self.globals = self.alwaysUsed.copy()
438+
self.global_names = []
438439
self.returnValue = None # First non-empty return
439440
self.isGenerator = False # Detect a generator
440441

@@ -1016,6 +1017,15 @@ def handleForwardAnnotation():
10161017
def ignore(self, node):
10171018
pass
10181019

1020+
def store_global_scope(self, name, value):
1021+
"""This store name in global scope if it is in global_names"""
1022+
if isinstance(self.scope, FunctionScope):
1023+
global_scope_index = 1 if self._in_doctest() else 0
1024+
global_scope = self.scopeStack[global_scope_index]
1025+
if name in self.scope.global_names:
1026+
global_scope.setdefault(name,
1027+
Assignment(name, value))
1028+
10191029
# "stmt" type nodes
10201030
DELETE = PRINT = FOR = ASYNCFOR = WHILE = IF = WITH = WITHITEM = \
10211031
ASYNCWITH = ASYNCWITHITEM = TRYFINALLY = EXEC = \
@@ -1122,7 +1132,8 @@ def GLOBAL(self, node):
11221132
m.message_args[0] != node_name]
11231133

11241134
# Bind name to global scope if it doesn't exist already.
1125-
global_scope.setdefault(node_name, node_value)
1135+
if isinstance(self.scope, FunctionScope):
1136+
self.scope.global_names.append(node_name)
11261137

11271138
# Bind name to non-global scopes, but as already "used".
11281139
node_value.used = (global_scope, node)
@@ -1144,17 +1155,33 @@ def NAME(self, node):
11441155
"""
11451156
Handle occurrence of Name (which can be a load/store/delete access.)
11461157
"""
1158+
global_scope_index = 1 if self._in_doctest() else 0
1159+
global_scope = self.scopeStack[global_scope_index]
11471160
# Locate the name in locals / function / globals scopes.
11481161
if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
11491162
self.handleNodeLoad(node)
11501163
if (node.id == 'locals' and isinstance(self.scope, FunctionScope)
11511164
and isinstance(node.parent, ast.Call)):
11521165
# we are doing locals() call in current scope
11531166
self.scope.usesLocals = True
1167+
if (isinstance(self.scope, FunctionScope) and
1168+
node.id in self.scope.global_names):
1169+
if node.id not in global_scope:
1170+
self.report(messages.UndefinedName, node, node.id)
11541171
elif isinstance(node.ctx, (ast.Store, ast.AugStore, ast.Param)):
11551172
self.handleNodeStore(node)
1173+
if (isinstance(self.scope, FunctionScope) and
1174+
node.id in self.scope.global_names):
1175+
global_scope.setdefault(node.id, Assignment(node.id, node))
11561176
elif isinstance(node.ctx, ast.Del):
11571177
self.handleNodeDelete(node)
1178+
if (isinstance(self.scope, FunctionScope) and
1179+
node.id in self.scope.global_names):
1180+
if node.id not in global_scope:
1181+
self.report(messages.UndefinedName, node, node.id)
1182+
else:
1183+
global_scope.pop(node.id, None)
1184+
self.scope.global_names.remove(node.id)
11581185
else:
11591186
# Unknown context
11601187
raise RuntimeError("Got impossible expression context: %r" % (node.ctx,))
@@ -1366,13 +1393,16 @@ def TUPLE(self, node):
13661393

13671394
def IMPORT(self, node):
13681395
for alias in node.names:
1396+
name = alias.name
13691397
if '.' in alias.name and not alias.asname:
1370-
importation = SubmoduleImportation(alias.name, node)
1398+
importation = SubmoduleImportation(name, node)
13711399
else:
13721400
name = alias.asname or alias.name
13731401
importation = Importation(name, node, alias.name)
13741402
self.addBinding(node, importation)
13751403

1404+
self.store_global_scope(name, alias)
1405+
13761406
def IMPORTFROM(self, node):
13771407
if node.module == '__future__':
13781408
if not self.futuresAllowed:
@@ -1405,6 +1435,8 @@ def IMPORTFROM(self, node):
14051435
module, alias.name)
14061436
self.addBinding(node, importation)
14071437

1438+
self.store_global_scope(name, alias)
1439+
14081440
def TRY(self, node):
14091441
handler_names = []
14101442
# List the exception handlers

pyflakes/test/test_undefined_names.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,14 @@ def f2():
353353
global m
354354
''', m.UndefinedName)
355355

356+
def test_undefined_global(self):
357+
"""Use an undefined name with global statement"""
358+
self.flakes('''
359+
def f():
360+
global m
361+
print(m)
362+
''', m.UndefinedName)
363+
356364
def test_del(self):
357365
"""Del deletes bindings."""
358366
self.flakes('a = 1; del a; a', m.UndefinedName)

0 commit comments

Comments
 (0)