@@ -417,6 +417,7 @@ def __init__(self):
417
417
super (FunctionScope , self ).__init__ ()
418
418
# Simplify: manage the special locals as globals
419
419
self .globals = self .alwaysUsed .copy ()
420
+ self .global_names = []
420
421
self .returnValue = None # First non-empty return
421
422
self .isGenerator = False # Detect a generator
422
423
@@ -430,6 +431,13 @@ def unusedAssignments(self):
430
431
and isinstance (binding , Assignment )):
431
432
yield name , binding
432
433
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
+
433
441
434
442
class GeneratorScope (Scope ):
435
443
pass
@@ -1052,7 +1060,8 @@ def GLOBAL(self, node):
1052
1060
m .message_args [0 ] != node_name ]
1053
1061
1054
1062
# 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 )
1056
1065
1057
1066
# Bind name to non-global scopes, but as already "used".
1058
1067
node_value .used = (global_scope , node )
@@ -1074,17 +1083,33 @@ def NAME(self, node):
1074
1083
"""
1075
1084
Handle occurrence of Name (which can be a load/store/delete access.)
1076
1085
"""
1086
+ global_scope_index = 1 if self ._in_doctest () else 0
1087
+ global_scope = self .scopeStack [global_scope_index ]
1077
1088
# Locate the name in locals / function / globals scopes.
1078
1089
if isinstance (node .ctx , (ast .Load , ast .AugLoad )):
1079
1090
self .handleNodeLoad (node )
1080
1091
if (node .id == 'locals' and isinstance (self .scope , FunctionScope )
1081
1092
and isinstance (node .parent , ast .Call )):
1082
1093
# we are doing locals() call in current scope
1083
1094
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 )
1084
1099
elif isinstance (node .ctx , (ast .Store , ast .AugStore )):
1085
1100
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 ))
1086
1104
elif isinstance (node .ctx , ast .Del ):
1087
1105
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 )
1088
1113
else :
1089
1114
# must be a Param context -- this only happens for names in function
1090
1115
# arguments, but these aren't dispatched through here
@@ -1292,13 +1317,21 @@ def TUPLE(self, node):
1292
1317
1293
1318
def IMPORT (self , node ):
1294
1319
for alias in node .names :
1320
+ name = alias .name
1295
1321
if '.' in alias .name and not alias .asname :
1296
- importation = SubmoduleImportation (alias . name , node )
1322
+ importation = SubmoduleImportation (name , node )
1297
1323
else :
1298
1324
name = alias .asname or alias .name
1299
- importation = Importation (name , node , alias . name )
1325
+ importation = Importation (name , node , name )
1300
1326
self .addBinding (node , importation )
1301
1327
1328
+ if isinstance (self .scope , FunctionScope ):
1329
+ global_scope_index = 1 if self ._in_doctest () else 0
1330
+ global_scope = self .scopeStack [global_scope_index ]
1331
+ if name in self .scope .global_names :
1332
+ global_scope .setdefault (name ,
1333
+ Assignment (name , alias ))
1334
+
1302
1335
def IMPORTFROM (self , node ):
1303
1336
if node .module == '__future__' :
1304
1337
if not self .futuresAllowed :
@@ -1331,6 +1364,13 @@ def IMPORTFROM(self, node):
1331
1364
module , alias .name )
1332
1365
self .addBinding (node , importation )
1333
1366
1367
+ if isinstance (self .scope , FunctionScope ):
1368
+ global_scope_index = 1 if self ._in_doctest () else 0
1369
+ global_scope = self .scopeStack [global_scope_index ]
1370
+ if name in self .scope .global_names :
1371
+ global_scope .setdefault (name ,
1372
+ Assignment (name , alias ))
1373
+
1334
1374
def TRY (self , node ):
1335
1375
handler_names = []
1336
1376
# List the exception handlers
0 commit comments