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):
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:
......@@ -241,100 +247,6 @@ class FrozenImporter:
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:
"""Loader base class for Python source.
......@@ -584,6 +496,136 @@ class PyPycFileLoader(PyPycLoader, PyFileLoader):
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:
"""Base class for file finders.
......@@ -643,20 +685,6 @@ class FileFinder:
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):
"""Importer for source/bytecode files."""
......@@ -683,82 +711,43 @@ class PyPycFileFinder(PyFileFinder):
self._suffixes += _suffix_list(imp.PY_COMPILED)
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.
class ExtensionFileFinder(FileFinder):
"""
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))
"""Importer for extension files."""
@classmethod
def _path_importer_cache(cls, path, default=None):
"""Get the finder for the path from sys.path_importer_cache.
_possible_package = False
_loader = _ExtensionFileLoader
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).
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)
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
# Import itself ###############################################################
@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:
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 = cls._path_importer_cache(entry)
finder = hook(entry)
except ImportError:
continue
loader = finder.find_module(fullname)
if loader:
return loader
else:
finders.append(finder)
if not finders:
raise ImportError("no finder found")
else:
return None
# Import itself ###############################################################
class _ImportLockContext:
"""Context manager for the import lock."""
def __enter__(self):
"""Acquire the import lock."""
imp.acquire_lock()
return _ChainedFinder(*finders)
def __exit__(self, exc_type, exc_value, exc_traceback):
"""Release the import lock regardless of any raised exceptions."""
imp.release_lock()
return path_hook
_DEFAULT_PATH_HOOK = _chained_path_hook(ExtensionFileFinder, PyPycFileFinder)
......@@ -784,6 +773,18 @@ class _DefaultPathFinder(PathFinder):
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]
......
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