Skip to content

Commit 517e8de

Browse files
Danny Seplerdannysepler
authored andcommitted
Global variables must be assigned to be used
1 parent 853cce9 commit 517e8de

File tree

2 files changed

+50
-5
lines changed

2 files changed

+50
-5
lines changed

pyflakes/checker.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -250,10 +250,11 @@ class Binding:
250250
the node that this binding was last used.
251251
"""
252252

253-
def __init__(self, name, source):
253+
def __init__(self, name, source, *, assigned=True):
254254
self.name = name
255255
self.source = source
256256
self.used = False
257+
self.assigned = assigned
257258

258259
def __str__(self):
259260
return self.name
@@ -1007,6 +1008,12 @@ def addBinding(self, node, value):
10071008
break
10081009
existing = scope.get(value.name)
10091010

1011+
global_scope = self.scopeStack[-1]
1012+
if (existing and global_scope.get(value.name) == existing and
1013+
not existing.assigned):
1014+
# make sure the variable is in the global scope before setting as assigned
1015+
existing.assigned = True
1016+
10101017
if (existing and not isinstance(existing, Builtin) and
10111018
not self.differentForks(node, existing.source)):
10121019

@@ -1089,6 +1096,10 @@ def handleNodeLoad(self, node):
10891096
continue
10901097

10911098
binding = scope.get(name, None)
1099+
1100+
if getattr(binding, 'assigned', None) is False:
1101+
self.report(messages.UndefinedName, node, name)
1102+
10921103
if isinstance(binding, Annotation) and not self._in_postponed_annotation:
10931104
scope[name].used = True
10941105
continue
@@ -1159,12 +1170,19 @@ def handleNodeStore(self, node):
11591170
continue
11601171
# if the name was defined in that scope, and the name has
11611172
# been accessed already in the current scope, and hasn't
1162-
# been declared global
1173+
# been assigned globally
11631174
used = name in scope and scope[name].used
11641175
if used and used[0] is self.scope and name not in self.scope.globals:
11651176
# then it's probably a mistake
11661177
self.report(messages.UndefinedLocal,
11671178
scope[name].used[1], name, scope[name].source)
1179+
1180+
# and remove UndefinedName messages already reported for this name
1181+
self.messages = [
1182+
m for m in self.messages if not
1183+
isinstance(m, messages.UndefinedName) or
1184+
m.message_args[0] != name]
1185+
11681186
break
11691187

11701188
parent_stmt = self.getParent(node)
@@ -1848,11 +1866,9 @@ def GLOBAL(self, node):
18481866

18491867
# One 'global' statement can bind multiple (comma-delimited) names.
18501868
for node_name in node.names:
1851-
node_value = Assignment(node_name, node)
1869+
node_value = Assignment(node_name, node, assigned=False)
18521870

18531871
# Remove UndefinedName messages already reported for this name.
1854-
# TODO: if the global is not used in this scope, it does not
1855-
# become a globally defined name. See test_unused_global.
18561872
self.messages = [
18571873
m for m in self.messages if not
18581874
isinstance(m, messages.UndefinedName) or

pyflakes/test/test_undefined_names.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,35 @@ def c(): bar
298298
def b(): global bar; bar = 1
299299
''')
300300

301+
def test_unassigned_global_is_undefined(self):
302+
"""
303+
If a "global" is never given a value, it is undefined
304+
"""
305+
self.flakes('''
306+
def a():
307+
global fu
308+
fu
309+
''', m.UndefinedName)
310+
311+
self.flakes('''
312+
global fu
313+
fu
314+
''', m.UndefinedName)
315+
316+
def test_scope_defined_global(self):
317+
"""
318+
If a "global" is defined inside of a function only,
319+
outside of the function it is undefined
320+
"""
321+
self.flakes('''
322+
global fu
323+
def a():
324+
fu = 1
325+
fu
326+
a()
327+
fu
328+
''', m.UndefinedName)
329+
301330
def test_definedByGlobalMultipleNames(self):
302331
"""
303332
"global" can accept multiple names.

0 commit comments

Comments
 (0)