@@ -467,6 +467,10 @@ class BuiltinImporter:
467467
468468 """
469469
470+ @classmethod
471+ def module_repr (cls , module ):
472+ return "<module '{}' (built-in)>" .format (module .__name__ )
473+
470474 @classmethod
471475 def find_module (cls , fullname , path = None ):
472476 """Find the built-in module.
@@ -520,6 +524,10 @@ class FrozenImporter:
520524
521525 """
522526
527+ @classmethod
528+ def module_repr (cls , m ):
529+ return "<module '{}' (frozen)>" .format (m .__name__ )
530+
523531 @classmethod
524532 def find_module (cls , fullname , path = None ):
525533 """Find a frozen module."""
@@ -533,7 +541,10 @@ def load_module(cls, fullname):
533541 """Load a frozen module."""
534542 is_reload = fullname in sys .modules
535543 try :
536- return _imp .init_frozen (fullname )
544+ m = _imp .init_frozen (fullname )
545+ # Let our own module_repr() method produce a suitable repr.
546+ del m .__file__
547+ return m
537548 except :
538549 if not is_reload and fullname in sys .modules :
539550 del sys .modules [fullname ]
@@ -875,6 +886,79 @@ def get_source(self, fullname):
875886 return None
876887
877888
889+ class _NamespacePath :
890+ """Represents a namespace package's path. It uses the module name
891+ to find its parent module, and from there it looks up the parent's
892+ __path__. When this changes, the module's own path is recomputed,
893+ using path_finder. For top-leve modules, the parent module's path
894+ is sys.path."""
895+
896+ def __init__ (self , name , path , path_finder ):
897+ self ._name = name
898+ self ._path = path
899+ self ._last_parent_path = tuple (self ._get_parent_path ())
900+ self ._path_finder = path_finder
901+
902+ def _find_parent_path_names (self ):
903+ """Returns a tuple of (parent-module-name, parent-path-attr-name)"""
904+ parent , dot , me = self ._name .rpartition ('.' )
905+ if dot == '' :
906+ # This is a top-level module. sys.path contains the parent path.
907+ return 'sys' , 'path'
908+ # Not a top-level module. parent-module.__path__ contains the
909+ # parent path.
910+ return parent , '__path__'
911+
912+ def _get_parent_path (self ):
913+ parent_module_name , path_attr_name = self ._find_parent_path_names ()
914+ return getattr (sys .modules [parent_module_name ], path_attr_name )
915+
916+ def _recalculate (self ):
917+ # If the parent's path has changed, recalculate _path
918+ parent_path = tuple (self ._get_parent_path ()) # Make a copy
919+ if parent_path != self ._last_parent_path :
920+ loader , new_path = self ._path_finder (self ._name , parent_path )
921+ # Note that no changes are made if a loader is returned, but we
922+ # do remember the new parent path
923+ if loader is None :
924+ self ._path = new_path
925+ self ._last_parent_path = parent_path # Save the copy
926+ return self ._path
927+
928+ def __iter__ (self ):
929+ return iter (self ._recalculate ())
930+
931+ def __len__ (self ):
932+ return len (self ._recalculate ())
933+
934+ def __repr__ (self ):
935+ return "_NamespacePath({0!r})" .format (self ._path )
936+
937+ def __contains__ (self , item ):
938+ return item in self ._recalculate ()
939+
940+ def append (self , item ):
941+ self ._path .append (item )
942+
943+
944+ class NamespaceLoader :
945+ def __init__ (self , name , path , path_finder ):
946+ self ._path = _NamespacePath (name , path , path_finder )
947+
948+ @classmethod
949+ def module_repr (cls , module ):
950+ return "<module '{}' (namespace)>" .format (module .__name__ )
951+
952+ @set_package
953+ @set_loader
954+ @module_for_loader
955+ def load_module (self , module ):
956+ """Load a namespace module."""
957+ _verbose_message ('namespace module loaded with path {!r}' , self ._path )
958+ module .__path__ = self ._path
959+ return module
960+
961+
878962# Finders #####################################################################
879963
880964class PathFinder :
@@ -915,20 +999,47 @@ def _path_importer_cache(cls, path):
915999 sys .path_importer_cache [path ] = finder
9161000 return finder
9171001
1002+ @classmethod
1003+ def _get_loader (cls , fullname , path ):
1004+ """Find the loader or namespace_path for this module/package name."""
1005+ # If this ends up being a namespace package, namespace_path is
1006+ # the list of paths that will become its __path__
1007+ namespace_path = []
1008+ for entry in path :
1009+ finder = cls ._path_importer_cache (entry )
1010+ if finder is not None :
1011+ if hasattr (finder , 'find_loader' ):
1012+ loader , portions = finder .find_loader (fullname )
1013+ else :
1014+ loader = finder .find_module (fullname )
1015+ portions = []
1016+ if loader is not None :
1017+ # We found a loader: return it immediately.
1018+ return (loader , namespace_path )
1019+ # This is possibly part of a namespace package.
1020+ # Remember these path entries (if any) for when we
1021+ # create a namespace package, and continue iterating
1022+ # on path.
1023+ namespace_path .extend (portions )
1024+ else :
1025+ return (None , namespace_path )
1026+
9181027 @classmethod
9191028 def find_module (cls , fullname , path = None ):
9201029 """Find the module on sys.path or 'path' based on sys.path_hooks and
9211030 sys.path_importer_cache."""
9221031 if path is None :
9231032 path = sys .path
924- for entry in path :
925- finder = cls ._path_importer_cache (entry )
926- if finder is not None :
927- loader = finder .find_module (fullname )
928- if loader :
929- return loader
1033+ loader , namespace_path = cls ._get_loader (fullname , path )
1034+ if loader is not None :
1035+ return loader
9301036 else :
931- return None
1037+ if namespace_path :
1038+ # We found at least one namespace path. Return a
1039+ # loader which can create the namespace package.
1040+ return NamespaceLoader (fullname , namespace_path , cls ._get_loader )
1041+ else :
1042+ return None
9321043
9331044
9341045class FileFinder :
@@ -942,8 +1053,8 @@ class FileFinder:
9421053
9431054 def __init__ (self , path , * details ):
9441055 """Initialize with the path to search on and a variable number of
945- 3-tuples containing the loader, file suffixes the loader recognizes, and
946- a boolean of whether the loader handles packages."""
1056+ 3-tuples containing the loader, file suffixes the loader recognizes,
1057+ and a boolean of whether the loader handles packages."""
9471058 packages = []
9481059 modules = []
9491060 for loader , suffixes , supports_packages in details :
@@ -964,6 +1075,19 @@ def invalidate_caches(self):
9641075
9651076 def find_module (self , fullname ):
9661077 """Try to find a loader for the specified module."""
1078+ # Call find_loader(). If it returns a string (indicating this
1079+ # is a namespace package portion), generate a warning and
1080+ # return None.
1081+ loader , portions = self .find_loader (fullname )
1082+ assert len (portions ) in [0 , 1 ]
1083+ if loader is None and len (portions ):
1084+ msg = "Not importing directory {}: missing __init__"
1085+ _warnings .warn (msg .format (portions [0 ]), ImportWarning )
1086+ return loader
1087+
1088+ def find_loader (self , fullname ):
1089+ """Try to find a loader for the specified module, or the namespace
1090+ package portions. Returns (loader, list-of-portions)."""
9671091 tail_module = fullname .rpartition ('.' )[2 ]
9681092 try :
9691093 mtime = _os .stat (self .path ).st_mtime
@@ -987,17 +1111,17 @@ def find_module(self, fullname):
9871111 init_filename = '__init__' + suffix
9881112 full_path = _path_join (base_path , init_filename )
9891113 if _path_isfile (full_path ):
990- return loader (fullname , full_path )
1114+ return ( loader (fullname , full_path ), [ base_path ] )
9911115 else :
992- msg = "Not importing directory {}: missing __init__"
993- _warnings . warn ( msg . format ( base_path ), ImportWarning )
1116+ # A namespace package, return the path
1117+ return ( None , [ base_path ] )
9941118 # Check for a file w/ a proper suffix exists.
9951119 for suffix , loader in self .modules :
9961120 if cache_module + suffix in cache :
9971121 full_path = _path_join (self .path , tail_module + suffix )
9981122 if _path_isfile (full_path ):
999- return loader (fullname , full_path )
1000- return None
1123+ return ( loader (fullname , full_path ), [] )
1124+ return ( None , [])
10011125
10021126 def _fill_cache (self ):
10031127 """Fill the cache of potential modules and packages for this directory."""
0 commit comments