Commit e9103d26 authored by Brett Cannon's avatar Brett Cannon

Last big re-organization of importlib._bootstrap. Should actually be able to...

Last big re-organization of importlib._bootstrap. Should actually be able to find something in the file now.
parent ce43ddfe
...@@ -173,7 +173,13 @@ def _check_name(method): ...@@ -173,7 +173,13 @@ def _check_name(method):
return inner return inner
# Finders/loaders ############################################################# def _suffix_list(suffix_type):
"""Return a list of file suffixes based on the imp file type."""
return [suffix[0] for suffix in imp.get_suffixes()
if suffix[2] == suffix_type]
# Loaders #####################################################################
class BuiltinImporter: class BuiltinImporter:
...@@ -241,100 +247,6 @@ class FrozenImporter: ...@@ -241,100 +247,6 @@ class FrozenImporter:
raise raise
def _chained_path_hook(*path_hooks):
"""Create a closure which sequentially checks path hooks to see which ones
(if any) can work with a path."""
def path_hook(entry):
"""Check to see if 'entry' matches any of the enclosed path hooks."""
finders = []
for hook in path_hooks:
try:
finder = hook(entry)
except ImportError:
continue
else:
finders.append(finder)
if not finders:
raise ImportError("no finder found")
else:
return _ChainedFinder(*finders)
return path_hook
class _ChainedFinder:
"""Finder that sequentially calls other finders."""
def __init__(self, *finders):
self._finders = finders
def find_module(self, fullname, path=None):
for finder in self._finders:
result = finder.find_module(fullname, path)
if result:
return result
else:
return None
class _ExtensionFileLoader:
"""Loader for extension modules.
The constructor is designed to work with FileFinder.
"""
def __init__(self, name, path, is_pkg):
"""Initialize the loader.
If is_pkg is True then an exception is raised as extension modules
cannot be the __init__ module for an extension module.
"""
self._name = name
self._path = path
if is_pkg:
raise ValueError("extension modules cannot be packages")
@_check_name
@set_package
@set_loader
def load_module(self, fullname):
"""Load an extension module."""
is_reload = fullname in sys.modules
try:
return imp.load_dynamic(fullname, self._path)
except:
if not is_reload and fullname in sys.modules:
del sys.modules[fullname]
raise
@_check_name
def is_package(self, fullname):
"""Return False as an extension module can never be a package."""
return False
@_check_name
def get_code(self, fullname):
"""Return None as an extension module cannot create a code object."""
return None
@_check_name
def get_source(self, fullname):
"""Return None as extension modules have no source code."""
return None
def _suffix_list(suffix_type):
"""Return a list of file suffixes based on the imp file type."""
return [suffix[0] for suffix in imp.get_suffixes()
if suffix[2] == suffix_type]
class PyLoader: class PyLoader:
"""Loader base class for Python source. """Loader base class for Python source.
...@@ -584,6 +496,136 @@ class PyPycFileLoader(PyPycLoader, PyFileLoader): ...@@ -584,6 +496,136 @@ class PyPycFileLoader(PyPycLoader, PyFileLoader):
raise raise
class _ExtensionFileLoader:
"""Loader for extension modules.
The constructor is designed to work with FileFinder.
"""
def __init__(self, name, path, is_pkg):
"""Initialize the loader.
If is_pkg is True then an exception is raised as extension modules
cannot be the __init__ module for an extension module.
"""
self._name = name
self._path = path
if is_pkg:
raise ValueError("extension modules cannot be packages")
@_check_name
@set_package
@set_loader
def load_module(self, fullname):
"""Load an extension module."""
is_reload = fullname in sys.modules
try:
return imp.load_dynamic(fullname, self._path)
except:
if not is_reload and fullname in sys.modules:
del sys.modules[fullname]
raise
@_check_name
def is_package(self, fullname):
"""Return False as an extension module can never be a package."""
return False
@_check_name
def get_code(self, fullname):
"""Return None as an extension module cannot create a code object."""
return None
@_check_name
def get_source(self, fullname):
"""Return None as extension modules have no source code."""
return None
# Finders #####################################################################
class PathFinder:
"""Meta path finder for sys.(path|path_hooks|path_importer_cache)."""
@classmethod
def _path_hooks(cls, path, hooks=None):
"""Search sequence of hooks for a finder for 'path'.
If 'hooks' is false then use sys.path_hooks.
"""
if not hooks:
hooks = sys.path_hooks
for hook in hooks:
try:
return hook(path)
except ImportError:
continue
else:
raise ImportError("no path hook found for {0}".format(path))
@classmethod
def _path_importer_cache(cls, path, default=None):
"""Get the finder for the path from sys.path_importer_cache.
If the path is not in the cache, find the appropriate finder and cache
it. If None is cached, get the default finder and cache that
(if applicable).
Because of NullImporter, some finder should be returned. The only
explicit fail case is if None is cached but the path cannot be used for
the default hook, for which ImportError is raised.
"""
try:
finder = sys.path_importer_cache[path]
except KeyError:
finder = cls._path_hooks(path)
sys.path_importer_cache[path] = finder
else:
if finder is None and default:
# Raises ImportError on failure.
finder = default(path)
sys.path_importer_cache[path] = finder
return finder
@classmethod
def find_module(cls, fullname, path=None):
"""Find the module on sys.path or 'path'."""
if not path:
path = sys.path
for entry in path:
try:
finder = cls._path_importer_cache(entry)
except ImportError:
continue
loader = finder.find_module(fullname)
if loader:
return loader
else:
return None
class _ChainedFinder:
"""Finder that sequentially calls other finders."""
def __init__(self, *finders):
self._finders = finders
def find_module(self, fullname, path=None):
for finder in self._finders:
result = finder.find_module(fullname, path)
if result:
return result
else:
return None
class FileFinder: class FileFinder:
"""Base class for file finders. """Base class for file finders.
...@@ -643,20 +685,6 @@ class FileFinder: ...@@ -643,20 +685,6 @@ class FileFinder:
return None return None
class ExtensionFileFinder(FileFinder):
"""Importer for extension files."""
_possible_package = False
_loader = _ExtensionFileLoader
def __init__(self, path_entry):
# Assigning to _suffixes here instead of at the class level because
# imp is not imported at the time of class creation.
self._suffixes = _suffix_list(imp.C_EXTENSION)
super().__init__(path_entry)
class PyFileFinder(FileFinder): class PyFileFinder(FileFinder):
"""Importer for source/bytecode files.""" """Importer for source/bytecode files."""
...@@ -683,82 +711,43 @@ class PyPycFileFinder(PyFileFinder): ...@@ -683,82 +711,43 @@ class PyPycFileFinder(PyFileFinder):
self._suffixes += _suffix_list(imp.PY_COMPILED) self._suffixes += _suffix_list(imp.PY_COMPILED)
class PathFinder:
"""Meta path finder for sys.(path|path_hooks|path_importer_cache)."""
@classmethod class ExtensionFileFinder(FileFinder):
def _path_hooks(cls, path, hooks=None):
"""Search sequence of hooks for a finder for 'path'.
If 'hooks' is false then use sys.path_hooks.
""" """Importer for extension files."""
if not hooks:
hooks = sys.path_hooks
for hook in hooks:
try:
return hook(path)
except ImportError:
continue
else:
raise ImportError("no path hook found for {0}".format(path))
@classmethod _possible_package = False
def _path_importer_cache(cls, path, default=None): _loader = _ExtensionFileLoader
"""Get the finder for the path from sys.path_importer_cache.
If the path is not in the cache, find the appropriate finder and cache def __init__(self, path_entry):
it. If None is cached, get the default finder and cache that # Assigning to _suffixes here instead of at the class level because
(if applicable). # imp is not imported at the time of class creation.
self._suffixes = _suffix_list(imp.C_EXTENSION)
super().__init__(path_entry)
Because of NullImporter, some finder should be returned. The only
explicit fail case is if None is cached but the path cannot be used for
the default hook, for which ImportError is raised.
""" # Import itself ###############################################################
try:
finder = sys.path_importer_cache[path]
except KeyError:
finder = cls._path_hooks(path)
sys.path_importer_cache[path] = finder
else:
if finder is None and default:
# Raises ImportError on failure.
finder = default(path)
sys.path_importer_cache[path] = finder
return finder
@classmethod def _chained_path_hook(*path_hooks):
def find_module(cls, fullname, path=None): """Create a closure which sequentially checks path hooks to see which ones
"""Find the module on sys.path or 'path'.""" (if any) can work with a path."""
if not path: def path_hook(entry):
path = sys.path """Check to see if 'entry' matches any of the enclosed path hooks."""
for entry in path: finders = []
for hook in path_hooks:
try: try:
finder = cls._path_importer_cache(entry) finder = hook(entry)
except ImportError: except ImportError:
continue continue
loader = finder.find_module(fullname) else:
if loader: finders.append(finder)
return loader if not finders:
raise ImportError("no finder found")
else: else:
return None return _ChainedFinder(*finders)
# Import itself ###############################################################
class _ImportLockContext:
"""Context manager for the import lock."""
def __enter__(self):
"""Acquire the import lock."""
imp.acquire_lock()
def __exit__(self, exc_type, exc_value, exc_traceback): return path_hook
"""Release the import lock regardless of any raised exceptions."""
imp.release_lock()
_DEFAULT_PATH_HOOK = _chained_path_hook(ExtensionFileFinder, PyPycFileFinder) _DEFAULT_PATH_HOOK = _chained_path_hook(ExtensionFileFinder, PyPycFileFinder)
...@@ -784,6 +773,18 @@ class _DefaultPathFinder(PathFinder): ...@@ -784,6 +773,18 @@ class _DefaultPathFinder(PathFinder):
return super()._path_importer_cache(path, _DEFAULT_PATH_HOOK) return super()._path_importer_cache(path, _DEFAULT_PATH_HOOK)
class _ImportLockContext:
"""Context manager for the import lock."""
def __enter__(self):
"""Acquire the import lock."""
imp.acquire_lock()
def __exit__(self, exc_type, exc_value, exc_traceback):
"""Release the import lock regardless of any raised exceptions."""
imp.release_lock()
_IMPLICIT_META_PATH = [BuiltinImporter, FrozenImporter, _DefaultPathFinder] _IMPLICIT_META_PATH = [BuiltinImporter, FrozenImporter, _DefaultPathFinder]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment