@@ -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
@@ -430,6 +431,13 @@ def unusedAssignments(self):
430431 and isinstance (binding , Assignment )):
431432 yield name , binding
432433
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+
433441
434442class GeneratorScope (Scope ):
435443 pass
@@ -1052,7 +1060,8 @@ def GLOBAL(self, node):
10521060 m .message_args [0 ] != node_name ]
10531061
10541062 # 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 )
10561065
10571066 # Bind name to non-global scopes, but as already "used".
10581067 node_value .used = (global_scope , node )
@@ -1074,17 +1083,33 @@ def NAME(self, node):
10741083 """
10751084 Handle occurrence of Name (which can be a load/store/delete access.)
10761085 """
1086+ global_scope_index = 1 if self ._in_doctest () else 0
1087+ global_scope = self .scopeStack [global_scope_index ]
10771088 # Locate the name in locals / function / globals scopes.
10781089 if isinstance (node .ctx , (ast .Load , ast .AugLoad )):
10791090 self .handleNodeLoad (node )
10801091 if (node .id == 'locals' and isinstance (self .scope , FunctionScope )
10811092 and isinstance (node .parent , ast .Call )):
10821093 # we are doing locals() call in current scope
10831094 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 )
10841099 elif isinstance (node .ctx , (ast .Store , ast .AugStore )):
10851100 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 ))
10861104 elif isinstance (node .ctx , ast .Del ):
10871105 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 )
10881113 else :
10891114 # must be a Param context -- this only happens for names in function
10901115 # arguments, but these aren't dispatched through here
@@ -1292,13 +1317,21 @@ def TUPLE(self, node):
12921317
12931318 def IMPORT (self , node ):
12941319 for alias in node .names :
1320+ name = alias .name
12951321 if '.' in alias .name and not alias .asname :
1296- importation = SubmoduleImportation (alias . name , node )
1322+ importation = SubmoduleImportation (name , node )
12971323 else :
12981324 name = alias .asname or alias .name
1299- importation = Importation (name , node , alias . name )
1325+ importation = Importation (name , node , name )
13001326 self .addBinding (node , importation )
13011327
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+
13021335 def IMPORTFROM (self , node ):
13031336 if node .module == '__future__' :
13041337 if not self .futuresAllowed :
@@ -1331,6 +1364,13 @@ def IMPORTFROM(self, node):
13311364 module , alias .name )
13321365 self .addBinding (node , importation )
13331366
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+
13341374 def TRY (self , node ):
13351375 handler_names = []
13361376 # List the exception handlers
0 commit comments