33import os
44import sys
55import imp
6+ import importlib
67import os .path
8+ from warnings import warn
79from types import ModuleType
810
911__all__ = [
@@ -168,6 +170,8 @@ class ImpImporter:
168170 """
169171
170172 def __init__ (self , path = None ):
173+ warn ("This emulation is deprecated, use 'importlib' instead" ,
174+ DeprecationWarning )
171175 self .path = path
172176
173177 def find_module (self , fullname , path = None ):
@@ -232,6 +236,8 @@ class ImpLoader:
232236 code = source = None
233237
234238 def __init__ (self , fullname , file , filename , etc ):
239+ warn ("This emulation is deprecated, use 'importlib' instead" ,
240+ DeprecationWarning )
235241 self .file = file
236242 self .filename = filename
237243 self .fullname = fullname
@@ -366,10 +372,6 @@ def get_importer(path_item):
366372 The returned importer is cached in sys.path_importer_cache
367373 if it was newly created by a path hook.
368374
369- If there is no importer, a wrapper around the basic import
370- machinery is returned. This wrapper is never inserted into
371- the importer cache (None is inserted instead).
372-
373375 The cache (or part of it) can be cleared manually if a
374376 rescan of sys.path_hooks is necessary.
375377 """
@@ -384,66 +386,45 @@ def get_importer(path_item):
384386 except ImportError :
385387 pass
386388 else :
387- try :
388- importer = ImpImporter (path_item )
389- except ImportError :
390- importer = None
389+ importer = None
391390 return importer
392391
393392
394393def iter_importers (fullname = "" ):
395394 """Yield PEP 302 importers for the given module name
396395
397396 If fullname contains a '.', the importers will be for the package
398- containing fullname, otherwise they will be importers for sys.meta_path,
399- sys.path, and Python's "classic" import machinery, in that order. If
400- the named module is in a package, that package is imported as a side
401- effect of invoking this function.
397+ containing fullname, otherwise they will be all registered top level
398+ importers (i.e. those on both sys.meta_path and sys.path_hooks).
402399
403- Non PEP 302 mechanisms (e.g. the Windows registry) used by the
404- standard import machinery to find files in alternative locations
405- are partially supported, but are searched AFTER sys.path. Normally,
406- these locations are searched BEFORE sys.path, preventing sys.path
407- entries from shadowing them.
408-
409- For this to cause a visible difference in behaviour, there must
410- be a module or package name that is accessible via both sys.path
411- and one of the non PEP 302 file system mechanisms. In this case,
412- the emulation will find the former version, while the builtin
413- import mechanism will find the latter.
400+ If the named module is in a package, that package is imported as a side
401+ effect of invoking this function.
414402
415- Items of the following types can be affected by this discrepancy:
416- imp.C_EXTENSION, imp.PY_SOURCE, imp.PY_COMPILED, imp.PKG_DIRECTORY
403+ If no module name is specified, all top level importers are produced.
417404 """
418405 if fullname .startswith ('.' ):
419- raise ImportError ("Relative module names not supported" )
406+ msg = "Relative module name {!r} not supported" .format (fullname )
407+ raise ImportError (msg )
420408 if '.' in fullname :
421409 # Get the containing package's __path__
422- pkg = '.' .join (fullname .split ('.' )[:- 1 ])
423- if pkg not in sys .modules :
424- __import__ (pkg )
425- path = getattr (sys .modules [pkg ], '__path__' , None ) or []
410+ pkg_name = fullname .rpartition ("." )[0 ]
411+ pkg = importlib .import_module (pkg )
412+ path = getattr (sys .modules [pkg ], '__path__' , None )
413+ if path is None :
414+ return
426415 else :
427416 for importer in sys .meta_path :
428417 yield importer
429418 path = sys .path
430419 for item in path :
431420 yield get_importer (item )
432- if '.' not in fullname :
433- yield ImpImporter ()
434421
435422def get_loader (module_or_name ):
436423 """Get a PEP 302 "loader" object for module_or_name
437424
438- If the module or package is accessible via the normal import
439- mechanism, a wrapper around the relevant part of that machinery
440- is returned. Returns None if the module cannot be found or imported.
425+ Returns None if the module cannot be found or imported.
441426 If the named module is not already imported, its containing package
442427 (if any) is imported, in order to establish the package __path__.
443-
444- This function uses iter_importers(), and is thus subject to the same
445- limitations regarding platform-specific special import locations such
446- as the Windows registry.
447428 """
448429 if module_or_name in sys .modules :
449430 module_or_name = sys .modules [module_or_name ]
@@ -457,22 +438,33 @@ def get_loader(module_or_name):
457438 fullname = module_or_name
458439 return find_loader (fullname )
459440
441+
460442def find_loader (fullname ):
461443 """Find a PEP 302 "loader" object for fullname
462444
463- If fullname contains dots, path must be the containing package's __path__.
464- Returns None if the module cannot be found or imported. This function uses
465- iter_importers(), and is thus subject to the same limitations regarding
466- platform-specific special import locations such as the Windows registry .
445+ This is s convenience wrapper around :func:`importlib.find_loader` that
446+ sets the *path* argument correctly when searching for submodules, and
447+ also ensures parent packages (if any) are imported before searching for
448+ submodules .
467449 """
468- for importer in iter_importers (fullname ):
469- if importer is None :
470- continue
471- loader = importer .find_module (fullname )
472- if loader is not None :
473- return loader
474-
475- return None
450+ if fullname .startswith ('.' ):
451+ msg = "Relative module name {!r} not supported" .format (fullname )
452+ raise ImportError (msg )
453+ path = None
454+ pkg_name = fullname .rpartition ("." )[0 ]
455+ if pkg_name :
456+ pkg = importlib .import_module (pkg_name )
457+ path = getattr (pkg , "__path__" , None )
458+ if path is None :
459+ return None
460+ try :
461+ return importlib .find_loader (fullname , path )
462+ except (ImportError , AttributeError , TypeError , ValueError ) as ex :
463+ # This hack fixes an impedance mismatch between pkgutil and
464+ # importlib, where the latter throws other errors for cases where
465+ # pkgutil previously threw ImportError
466+ msg = "Error while finding loader for {!r} ({}: {})"
467+ raise ImportError (msg .format (fullname , type (ex ), ex )) from ex
476468
477469
478470def extend_path (path , name ):
0 commit comments