@@ -147,6 +147,17 @@ def redefines(self, other):
147147 return isinstance (other , Definition ) and self .name == other .name
148148
149149
150+ class StarImportation (Importation ):
151+ """A binding created by an 'from x import *' statement."""
152+
153+ def __init__ (self , name , source ):
154+ super (StarImportation , self ).__init__ ('*' , source )
155+ # Each star importation needs a unique name, and
156+ # may not be the module name otherwise it will be deemed imported
157+ self .name = name + '.*'
158+ self .fullName = name
159+
160+
150161class Argument (Binding ):
151162 """
152163 Represents binding a name as an argument.
@@ -358,17 +369,29 @@ def checkDeadScopes(self):
358369 if isinstance (scope , ClassScope ):
359370 continue
360371
361- if isinstance (scope .get ('__all__' ), ExportBinding ):
362- all_names = set (scope ['__all__' ].names )
372+ all_binding = scope .get ('__all__' )
373+ if all_binding and not isinstance (all_binding , ExportBinding ):
374+ all_binding = None
375+
376+ if all_binding :
377+ all_names = set (all_binding .names )
378+ undefined = all_names .difference (scope )
379+ else :
380+ all_names = undefined = []
381+
382+ if undefined :
363383 if not scope .importStarred and \
364384 os .path .basename (self .filename ) != '__init__.py' :
365385 # Look for possible mistakes in the export list
366- undefined = all_names .difference (scope )
367386 for name in undefined :
368387 self .report (messages .UndefinedExport ,
369388 scope ['__all__' ].source , name )
370- else :
371- all_names = []
389+
390+ # mark all import '*' as used by the undefined in __all__
391+ if scope .importStarred :
392+ for binding in scope .values ():
393+ if isinstance (binding , StarImportation ):
394+ binding .used = all_binding
372395
373396 # Look for imported names that aren't used.
374397 for value in scope .values ():
@@ -504,8 +527,24 @@ def handleNodeLoad(self, node):
504527 in_generators = isinstance (scope , GeneratorScope )
505528
506529 # look in the built-ins
507- if importStarred or name in self .builtIns :
530+ if name in self .builtIns :
508531 return
532+
533+ if importStarred :
534+ from_list = []
535+
536+ for scope in self .scopeStack [- 1 ::- 1 ]:
537+ for binding in scope .values ():
538+ if isinstance (binding , StarImportation ):
539+ # mark '*' imports as used for each scope
540+ binding .used = (self .scope , node )
541+ from_list .append (binding .fullName )
542+
543+ # report * usage, with a list of possible sources
544+ from_list = ', ' .join (sorted (from_list ))
545+ self .report (messages .ImportStarUsage , node , name , from_list )
546+ return
547+
509548 if name == '__path__' and os .path .basename (self .filename ) == '__init__.py' :
510549 # the special name __path__ is valid only in packages
511550 return
@@ -976,17 +1015,19 @@ def IMPORTFROM(self, node):
9761015 self .futuresAllowed = False
9771016
9781017 for alias in node .names :
1018+ name = alias .asname or alias .name
9791019 if alias .name == '*' :
9801020 # Only Python 2, local import * is a SyntaxWarning
9811021 if not PY2 and not isinstance (self .scope , ModuleScope ):
9821022 self .report (messages .ImportStarNotPermitted ,
9831023 node , node .module )
9841024 continue
1025+
9851026 self .scope .importStarred = True
9861027 self .report (messages .ImportStarUsed , node , node .module )
987- continue
988- name = alias . asname or alias . name
989- importation = Importation (name , node )
1028+ importation = StarImportation ( node . module , node )
1029+ else :
1030+ importation = Importation (name , node )
9901031 if node .module == '__future__' :
9911032 importation .used = (self .scope , node )
9921033 self .addBinding (node , importation )
0 commit comments