Skip to content

Commit bd0a4d6

Browse files
Danny Seplerdannysepler
Danny Sepler
authored andcommitted
Global variables must be assigned to be used
1 parent dbb1843 commit bd0a4d6

File tree

2 files changed

+50
-5
lines changed

2 files changed

+50
-5
lines changed

Diff for: pyflakes/checker.py

+21-5
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,11 @@ class Binding(object):
322322
the node that this binding was last used.
323323
"""
324324

325-
def __init__(self, name, source):
325+
def __init__(self, name, source, *, assigned=True):
326326
self.name = name
327327
self.source = source
328328
self.used = False
329+
self.assigned = assigned
329330

330331
def __str__(self):
331332
return self.name
@@ -1129,6 +1130,12 @@ def addBinding(self, node, value):
11291130
break
11301131
existing = scope.get(value.name)
11311132

1133+
global_scope = self.scopeStack[-1]
1134+
if (existing and global_scope.get(value.name) == existing and
1135+
not existing.assigned):
1136+
# make sure the variable is in the global scope before setting as assigned
1137+
existing.assigned = True
1138+
11321139
if (existing and not isinstance(existing, Builtin) and
11331140
not self.differentForks(node, existing.source)):
11341141

@@ -1207,6 +1214,10 @@ def handleNodeLoad(self, node):
12071214
continue
12081215

12091216
binding = scope.get(name, None)
1217+
1218+
if getattr(binding, 'assigned', None) is False:
1219+
self.report(messages.UndefinedName, node, name)
1220+
12101221
if isinstance(binding, Annotation) and not self._in_postponed_annotation:
12111222
continue
12121223

@@ -1276,12 +1287,19 @@ def handleNodeStore(self, node):
12761287
continue
12771288
# if the name was defined in that scope, and the name has
12781289
# been accessed already in the current scope, and hasn't
1279-
# been declared global
1290+
# been assigned globally
12801291
used = name in scope and scope[name].used
12811292
if used and used[0] is self.scope and name not in self.scope.globals:
12821293
# then it's probably a mistake
12831294
self.report(messages.UndefinedLocal,
12841295
scope[name].used[1], name, scope[name].source)
1296+
1297+
# and we can remove UndefinedName messages already reported for this name
1298+
self.messages = [
1299+
m for m in self.messages if not
1300+
isinstance(m, messages.UndefinedName) or
1301+
m.message_args[0] != name]
1302+
12851303
break
12861304

12871305
parent_stmt = self.getParent(node)
@@ -2002,11 +2020,9 @@ def GLOBAL(self, node):
20022020

20032021
# One 'global' statement can bind multiple (comma-delimited) names.
20042022
for node_name in node.names:
2005-
node_value = Assignment(node_name, node)
2023+
node_value = Assignment(node_name, node, assigned=False)
20062024

20072025
# Remove UndefinedName messages already reported for this name.
2008-
# TODO: if the global is not used in this scope, it does not
2009-
# become a globally defined name. See test_unused_global.
20102026
self.messages = [
20112027
m for m in self.messages if not
20122028
isinstance(m, messages.UndefinedName) or

Diff for: pyflakes/test/test_undefined_names.py

+29
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,35 @@ def c(): bar
335335
def b(): global bar; bar = 1
336336
''')
337337

338+
def test_unassigned_global_is_undefined(self):
339+
"""
340+
If a "global" is never given a value, it is undefined
341+
"""
342+
self.flakes('''
343+
def a():
344+
global fu
345+
fu
346+
''', m.UndefinedName)
347+
348+
self.flakes('''
349+
global fu
350+
fu
351+
''', m.UndefinedName)
352+
353+
def test_scope_defined_global(self):
354+
"""
355+
If a "global" is defined inside of a function only,
356+
outside of the function it is undefined
357+
"""
358+
self.flakes('''
359+
global fu
360+
def a():
361+
fu = 1
362+
fu
363+
a()
364+
fu
365+
''', m.UndefinedName)
366+
338367
def test_definedByGlobalMultipleNames(self):
339368
"""
340369
"global" can accept multiple names.

0 commit comments

Comments
 (0)