mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
Issue #14605: Expose importlib.abc.FileLoader and
importlib.machinery.(FileFinder, SourceFileLoader, _SourcelessFileLoader, ExtensionFileLoader). This exposes all of importlib's mechanisms that will become public on the sys module.
This commit is contained in:
parent
8c5e920ae3
commit
938d44d59c
17 changed files with 3246 additions and 3076 deletions
|
@ -13,6 +13,9 @@ work. One should use importlib as the public-facing version of this module.
|
|||
# reference any injected objects! This includes not only global code but also
|
||||
# anything specified at the class level.
|
||||
|
||||
# XXX Make sure all public names have no single leading underscore and all
|
||||
# others do.
|
||||
|
||||
|
||||
# Bootstrap-related code ######################################################
|
||||
|
||||
|
@ -283,7 +286,7 @@ def _check_name(method):
|
|||
|
||||
"""
|
||||
def _check_name_wrapper(self, name, *args, **kwargs):
|
||||
if self._name != name:
|
||||
if self.name != name:
|
||||
raise ImportError("loader cannot handle %s" % name, name=name)
|
||||
return method(self, name, *args, **kwargs)
|
||||
_wrap(_check_name_wrapper, method)
|
||||
|
@ -423,7 +426,7 @@ class FrozenImporter:
|
|||
class _LoaderBasics:
|
||||
|
||||
"""Base class of common code needed by both SourceLoader and
|
||||
_SourcelessFileLoader."""
|
||||
SourcelessFileLoader."""
|
||||
|
||||
def is_package(self, fullname):
|
||||
"""Concrete implementation of InspectLoader.is_package by checking if
|
||||
|
@ -608,7 +611,7 @@ class SourceLoader(_LoaderBasics):
|
|||
return self._load_module(fullname)
|
||||
|
||||
|
||||
class _FileLoader:
|
||||
class FileLoader:
|
||||
|
||||
"""Base file loader class which implements the loader protocol methods that
|
||||
require file system usage."""
|
||||
|
@ -616,13 +619,13 @@ class _FileLoader:
|
|||
def __init__(self, fullname, path):
|
||||
"""Cache the module name and the path to the file found by the
|
||||
finder."""
|
||||
self._name = fullname
|
||||
self._path = path
|
||||
self.name = fullname
|
||||
self.path = path
|
||||
|
||||
@_check_name
|
||||
def get_filename(self, fullname):
|
||||
"""Return the path to the source file as found by the finder."""
|
||||
return self._path
|
||||
return self.path
|
||||
|
||||
def get_data(self, path):
|
||||
"""Return the data from path as raw bytes."""
|
||||
|
@ -630,7 +633,7 @@ class _FileLoader:
|
|||
return file.read()
|
||||
|
||||
|
||||
class _SourceFileLoader(_FileLoader, SourceLoader):
|
||||
class SourceFileLoader(FileLoader, SourceLoader):
|
||||
|
||||
"""Concrete implementation of SourceLoader using the file system."""
|
||||
|
||||
|
@ -668,7 +671,7 @@ class _SourceFileLoader(_FileLoader, SourceLoader):
|
|||
pass
|
||||
|
||||
|
||||
class _SourcelessFileLoader(_FileLoader, _LoaderBasics):
|
||||
class _SourcelessFileLoader(FileLoader, _LoaderBasics):
|
||||
|
||||
"""Loader which handles sourceless file imports."""
|
||||
|
||||
|
@ -692,7 +695,7 @@ class _SourcelessFileLoader(_FileLoader, _LoaderBasics):
|
|||
return None
|
||||
|
||||
|
||||
class _ExtensionFileLoader:
|
||||
class ExtensionFileLoader:
|
||||
|
||||
"""Loader for extension modules.
|
||||
|
||||
|
@ -701,8 +704,8 @@ class _ExtensionFileLoader:
|
|||
"""
|
||||
|
||||
def __init__(self, name, path):
|
||||
self._name = name
|
||||
self._path = path
|
||||
self.name = name
|
||||
self.path = path
|
||||
|
||||
@_check_name
|
||||
@set_package
|
||||
|
@ -711,8 +714,8 @@ class _ExtensionFileLoader:
|
|||
"""Load an extension module."""
|
||||
is_reload = fullname in sys.modules
|
||||
try:
|
||||
module = _imp.load_dynamic(fullname, self._path)
|
||||
verbose_message('extension module loaded from {!r}', self._path)
|
||||
module = _imp.load_dynamic(fullname, self.path)
|
||||
verbose_message('extension module loaded from {!r}', self.path)
|
||||
return module
|
||||
except:
|
||||
if not is_reload and fullname in sys.modules:
|
||||
|
@ -805,24 +808,25 @@ class PathFinder:
|
|||
return None
|
||||
|
||||
|
||||
class _FileFinder:
|
||||
class FileFinder:
|
||||
|
||||
"""File-based finder.
|
||||
|
||||
Constructor takes a list of objects detailing what file extensions their
|
||||
loader supports along with whether it can be used for a package.
|
||||
Interactions with the file system are cached for performance, being
|
||||
refreshed when the directory the finder is handling has been modified.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, path, *details):
|
||||
"""Initialize with finder details."""
|
||||
"""Initialize with the path to search on and a variable number of
|
||||
3-tuples containing the loader, file suffixes the loader recognizes, and
|
||||
a boolean of whether the loader handles packages."""
|
||||
packages = []
|
||||
modules = []
|
||||
for detail in details:
|
||||
modules.extend((suffix, detail.loader) for suffix in detail.suffixes)
|
||||
if detail.supports_packages:
|
||||
packages.extend((suffix, detail.loader)
|
||||
for suffix in detail.suffixes)
|
||||
for loader, suffixes, supports_packages in details:
|
||||
modules.extend((suffix, loader) for suffix in suffixes)
|
||||
if supports_packages:
|
||||
packages.extend((suffix, loader) for suffix in suffixes)
|
||||
self.packages = packages
|
||||
self.modules = modules
|
||||
# Base (directory) path
|
||||
|
@ -898,46 +902,29 @@ class _FileFinder:
|
|||
if sys.platform.startswith(CASE_INSENSITIVE_PLATFORMS):
|
||||
self._relaxed_path_cache = set(fn.lower() for fn in contents)
|
||||
|
||||
@classmethod
|
||||
def path_hook(cls, *loader_details):
|
||||
"""A class method which returns a closure to use on sys.path_hook
|
||||
which will return an instance using the specified loaders and the path
|
||||
called on the closure.
|
||||
|
||||
class _SourceFinderDetails:
|
||||
If the path called on the closure is not a directory, ImportError is
|
||||
raised.
|
||||
|
||||
loader = _SourceFileLoader
|
||||
supports_packages = True
|
||||
"""
|
||||
def path_hook_for_FileFinder(path):
|
||||
"""Path hook for importlib.machinery.FileFinder."""
|
||||
if not _path_isdir(path):
|
||||
raise ImportError("only directories are supported", path=path)
|
||||
return cls(path, *loader_details)
|
||||
|
||||
def __init__(self):
|
||||
self.suffixes = _suffix_list(_imp.PY_SOURCE)
|
||||
return path_hook_for_FileFinder
|
||||
|
||||
class _SourcelessFinderDetails:
|
||||
|
||||
loader = _SourcelessFileLoader
|
||||
supports_packages = True
|
||||
|
||||
def __init__(self):
|
||||
self.suffixes = _suffix_list(_imp.PY_COMPILED)
|
||||
|
||||
|
||||
class _ExtensionFinderDetails:
|
||||
|
||||
loader = _ExtensionFileLoader
|
||||
supports_packages = False
|
||||
|
||||
def __init__(self):
|
||||
self.suffixes = _suffix_list(_imp.C_EXTENSION)
|
||||
|
||||
|
||||
# Import itself ###############################################################
|
||||
|
||||
def _file_path_hook(path):
|
||||
"""If the path is a directory, return a file-based finder."""
|
||||
if _path_isdir(path):
|
||||
return _FileFinder(path, _ExtensionFinderDetails(),
|
||||
_SourceFinderDetails(),
|
||||
_SourcelessFinderDetails())
|
||||
else:
|
||||
raise ImportError("only directories are supported", path=path)
|
||||
|
||||
|
||||
_DEFAULT_PATH_HOOK = _file_path_hook
|
||||
_DEFAULT_PATH_HOOK = None # Set in _setup()
|
||||
|
||||
class _DefaultPathFinder(PathFinder):
|
||||
|
||||
|
@ -1209,6 +1196,12 @@ def _setup(sys_module, _imp_module):
|
|||
if builtin_os == 'nt':
|
||||
SOURCE_SUFFIXES.append('.pyw')
|
||||
|
||||
supported_loaders = [(ExtensionFileLoader, _suffix_list(3), False),
|
||||
(SourceFileLoader, _suffix_list(1), True),
|
||||
(_SourcelessFileLoader, _suffix_list(2), True)]
|
||||
setattr(self_module, '_DEFAULT_PATH_HOOK',
|
||||
FileFinder.path_hook(*supported_loaders))
|
||||
|
||||
|
||||
def _install(sys_module, _imp_module):
|
||||
"""Install importlib as the implementation of import.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue