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
|
@ -237,6 +237,34 @@ are also provided to help in implementing the core ABCs.
|
||||||
module.
|
module.
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: FileLoader(fullname, path)
|
||||||
|
|
||||||
|
An abstract base class which inherits from :class:`ResourceLoader` and
|
||||||
|
:class:`ExecutionLoader`, providing concreate implementations of
|
||||||
|
:meth:`ResourceLoader.get_data` and :meth:`ExecutionLoader.get_filename`.
|
||||||
|
|
||||||
|
The *fullname* argument is a fully resolved name of the module the loader is
|
||||||
|
to handle. The *path* argument is the path to the file for the module.
|
||||||
|
|
||||||
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. attribute:: name
|
||||||
|
|
||||||
|
The name of the module the loader can handle.
|
||||||
|
|
||||||
|
.. attribute:: path
|
||||||
|
|
||||||
|
Path to the file of the module.
|
||||||
|
|
||||||
|
.. method:: get_filename(fullname)
|
||||||
|
|
||||||
|
Returns :attr:`path`.
|
||||||
|
|
||||||
|
.. method:: get_data(path)
|
||||||
|
|
||||||
|
Returns the open, binary file for *path*.
|
||||||
|
|
||||||
|
|
||||||
.. class:: SourceLoader
|
.. class:: SourceLoader
|
||||||
|
|
||||||
An abstract base class for implementing source (and optionally bytecode)
|
An abstract base class for implementing source (and optionally bytecode)
|
||||||
|
@ -498,6 +526,163 @@ find and load modules.
|
||||||
module. If no finder is ever found then ``None`` is returned.
|
module. If no finder is ever found then ``None`` is returned.
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: FileFinder(path, \*loader_details)
|
||||||
|
|
||||||
|
A concrete implementation of :class:`importlib.abc.Finder` which caches
|
||||||
|
results from the file system.
|
||||||
|
|
||||||
|
The *path* argument is the directory for which the finder is in charge of
|
||||||
|
searching.
|
||||||
|
|
||||||
|
The *loader_details* argument is a variable number of 3-item tuples each
|
||||||
|
containing a loader, file suffixes the loader recognizes, and a boolean
|
||||||
|
representing whether the loader handles packages.
|
||||||
|
|
||||||
|
The finder will cache the directory contents as necessary, making stat calls
|
||||||
|
for each module search to verify the cache is not outdated. Because cache
|
||||||
|
staleness relies upon the granularity of the operating system's state
|
||||||
|
information of the file system, there is a potential race condition of
|
||||||
|
searching for a module, creating a new file, and then searching for the
|
||||||
|
module the new file represents. If the operations happen fast enough to fit
|
||||||
|
within the granularity of stat calls, then the module search will fail. To
|
||||||
|
prevent this from happening, when you create a module dynamically, make sure
|
||||||
|
to call :func:`importlib.invalidate_caches`.
|
||||||
|
|
||||||
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. attribute:: path
|
||||||
|
|
||||||
|
The path the finder will search in.
|
||||||
|
|
||||||
|
.. method:: find_module(fullname)
|
||||||
|
|
||||||
|
Attempt to find the loader to handle *fullname* within :attr:`path`.
|
||||||
|
|
||||||
|
.. method:: invalidate_caches()
|
||||||
|
|
||||||
|
Clear out the internal cache.
|
||||||
|
|
||||||
|
.. classmethod:: path_hook(\*loader_details)
|
||||||
|
|
||||||
|
A class method which returns a closure for use on :attr:`sys.path_hooks`.
|
||||||
|
An instance of :class:`FileFinder` is returned by the closure using the
|
||||||
|
path argument given to the closure directly and *loader_details*
|
||||||
|
indirectly.
|
||||||
|
|
||||||
|
If the argument to the closure is not an existing directory,
|
||||||
|
:exc:`ImportError` is raised.
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: SourceFileLoader(fullname, path)
|
||||||
|
|
||||||
|
A concrete implementation of :class:`importlib.abc.SourceLoader` by
|
||||||
|
subclassing :class:`importlib.abc.FileLoader` and providing some concrete
|
||||||
|
implementations of other methods.
|
||||||
|
|
||||||
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. attribute:: name
|
||||||
|
|
||||||
|
The name of the module that this loader will handle.
|
||||||
|
|
||||||
|
.. attribute:: path
|
||||||
|
|
||||||
|
The path to the source file.
|
||||||
|
|
||||||
|
.. method:: is_package(fullname)
|
||||||
|
|
||||||
|
Return true if :attr:`path` appears to be for a package.
|
||||||
|
|
||||||
|
.. method:: path_stats(path)
|
||||||
|
|
||||||
|
Concrete implementation of :meth:`importlib.abc.SourceLoader.path_stats`.
|
||||||
|
|
||||||
|
.. method:: set_data(path, data)
|
||||||
|
|
||||||
|
Concrete implementation of :meth:`importlib.abc.SourceLoader.set_data`.
|
||||||
|
|
||||||
|
.. method:: load_module(fullname)
|
||||||
|
|
||||||
|
Load the specified module if it is the same as :attr:`name`.
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: _SourcelessFileLoader(fullname, path)
|
||||||
|
|
||||||
|
A concrete implementation of :class:`importlib.abc.FileLoader` which can
|
||||||
|
import bytecode files (i.e. no source code files exist).
|
||||||
|
|
||||||
|
It is **strongly** suggested you do not rely on this loader (hence the
|
||||||
|
leading underscore of the class). Direct use of bytecode files (and thus not
|
||||||
|
source code files) inhibits your modules from being usable by all Python
|
||||||
|
implementations. It also runs the risk of your bytecode files not being
|
||||||
|
usable by new versions of Python which change the bytecode format. This
|
||||||
|
class is only documented as it is directly used by import and thus can
|
||||||
|
potentially have instances show up as a module's ``__loader__`` attribute.
|
||||||
|
|
||||||
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. attribute:: name
|
||||||
|
|
||||||
|
The name of the module the loader will handle.
|
||||||
|
|
||||||
|
.. attribute:: path
|
||||||
|
|
||||||
|
The path to the bytecode file.
|
||||||
|
|
||||||
|
.. method:: is_package(fullname)
|
||||||
|
|
||||||
|
Determines if the module is a package based on :attr:`path`.
|
||||||
|
|
||||||
|
.. method:: get_code(fullname)
|
||||||
|
|
||||||
|
Returns the code object for :attr:`name` created from :attr:`path`.
|
||||||
|
|
||||||
|
.. method:: get_source(fullname)
|
||||||
|
|
||||||
|
Returns ``None`` as bytecode files have no source when this loader is
|
||||||
|
used.
|
||||||
|
|
||||||
|
.. method:: load_module(fullname)
|
||||||
|
|
||||||
|
Loads the specified module if it is the same as :attr:`name`.
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: ExtensionFileLoader(fullname, path)
|
||||||
|
|
||||||
|
A concrete implementation of :class:`importlib.abc.InspectLoader` for
|
||||||
|
extension modules.
|
||||||
|
|
||||||
|
The *fullname* argument specifies the name of the module the loader is to
|
||||||
|
support. The *path* argument is the path to the extension module's file.
|
||||||
|
|
||||||
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. attribute:: name
|
||||||
|
|
||||||
|
Name of the module the loader supports.
|
||||||
|
|
||||||
|
.. attribute:: path
|
||||||
|
|
||||||
|
Path to the extension module.
|
||||||
|
|
||||||
|
.. method:: load_module(fullname)
|
||||||
|
|
||||||
|
Loads the extension module if and only if *fullname** is the same as
|
||||||
|
:attr:`name`.
|
||||||
|
|
||||||
|
.. method:: is_package(fullname)
|
||||||
|
|
||||||
|
Returns ``False`` as extension modules can never be packages.
|
||||||
|
|
||||||
|
.. method:: get_code(fullname)
|
||||||
|
|
||||||
|
Returns ``None`` as extension modules lack a code object.
|
||||||
|
|
||||||
|
.. method:: get_source(fullname)
|
||||||
|
|
||||||
|
Returns ``None`` as extension modules do not have source code.
|
||||||
|
|
||||||
|
|
||||||
:mod:`importlib.util` -- Utility code for importers
|
:mod:`importlib.util` -- Utility code for importers
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ class _HackedGetData:
|
||||||
|
|
||||||
def get_data(self, path):
|
def get_data(self, path):
|
||||||
"""Gross hack to contort loader to deal w/ load_*()'s bad API."""
|
"""Gross hack to contort loader to deal w/ load_*()'s bad API."""
|
||||||
if self.file and path == self._path:
|
if self.file and path == self.path:
|
||||||
with self.file:
|
with self.file:
|
||||||
# Technically should be returning bytes, but
|
# Technically should be returning bytes, but
|
||||||
# SourceLoader.get_code() just passed what is returned to
|
# SourceLoader.get_code() just passed what is returned to
|
||||||
|
@ -83,7 +83,7 @@ class _HackedGetData:
|
||||||
return super().get_data(path)
|
return super().get_data(path)
|
||||||
|
|
||||||
|
|
||||||
class _LoadSourceCompatibility(_HackedGetData, _bootstrap._SourceFileLoader):
|
class _LoadSourceCompatibility(_HackedGetData, _bootstrap.SourceFileLoader):
|
||||||
|
|
||||||
"""Compatibility support for implementing load_source()."""
|
"""Compatibility support for implementing load_source()."""
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ def load_package(name, path):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise ValueError('{!r} is not a package'.format(path))
|
raise ValueError('{!r} is not a package'.format(path))
|
||||||
return _bootstrap._SourceFileLoader(name, path).load_module(name)
|
return _bootstrap.SourceFileLoader(name, path).load_module(name)
|
||||||
|
|
||||||
|
|
||||||
# XXX deprecate
|
# XXX deprecate
|
||||||
|
|
|
@ -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
|
# reference any injected objects! This includes not only global code but also
|
||||||
# anything specified at the class level.
|
# anything specified at the class level.
|
||||||
|
|
||||||
|
# XXX Make sure all public names have no single leading underscore and all
|
||||||
|
# others do.
|
||||||
|
|
||||||
|
|
||||||
# Bootstrap-related code ######################################################
|
# Bootstrap-related code ######################################################
|
||||||
|
|
||||||
|
@ -283,7 +286,7 @@ def _check_name(method):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def _check_name_wrapper(self, name, *args, **kwargs):
|
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)
|
raise ImportError("loader cannot handle %s" % name, name=name)
|
||||||
return method(self, name, *args, **kwargs)
|
return method(self, name, *args, **kwargs)
|
||||||
_wrap(_check_name_wrapper, method)
|
_wrap(_check_name_wrapper, method)
|
||||||
|
@ -423,7 +426,7 @@ class FrozenImporter:
|
||||||
class _LoaderBasics:
|
class _LoaderBasics:
|
||||||
|
|
||||||
"""Base class of common code needed by both SourceLoader and
|
"""Base class of common code needed by both SourceLoader and
|
||||||
_SourcelessFileLoader."""
|
SourcelessFileLoader."""
|
||||||
|
|
||||||
def is_package(self, fullname):
|
def is_package(self, fullname):
|
||||||
"""Concrete implementation of InspectLoader.is_package by checking if
|
"""Concrete implementation of InspectLoader.is_package by checking if
|
||||||
|
@ -608,7 +611,7 @@ class SourceLoader(_LoaderBasics):
|
||||||
return self._load_module(fullname)
|
return self._load_module(fullname)
|
||||||
|
|
||||||
|
|
||||||
class _FileLoader:
|
class FileLoader:
|
||||||
|
|
||||||
"""Base file loader class which implements the loader protocol methods that
|
"""Base file loader class which implements the loader protocol methods that
|
||||||
require file system usage."""
|
require file system usage."""
|
||||||
|
@ -616,13 +619,13 @@ class _FileLoader:
|
||||||
def __init__(self, fullname, path):
|
def __init__(self, fullname, path):
|
||||||
"""Cache the module name and the path to the file found by the
|
"""Cache the module name and the path to the file found by the
|
||||||
finder."""
|
finder."""
|
||||||
self._name = fullname
|
self.name = fullname
|
||||||
self._path = path
|
self.path = path
|
||||||
|
|
||||||
@_check_name
|
@_check_name
|
||||||
def get_filename(self, fullname):
|
def get_filename(self, fullname):
|
||||||
"""Return the path to the source file as found by the finder."""
|
"""Return the path to the source file as found by the finder."""
|
||||||
return self._path
|
return self.path
|
||||||
|
|
||||||
def get_data(self, path):
|
def get_data(self, path):
|
||||||
"""Return the data from path as raw bytes."""
|
"""Return the data from path as raw bytes."""
|
||||||
|
@ -630,7 +633,7 @@ class _FileLoader:
|
||||||
return file.read()
|
return file.read()
|
||||||
|
|
||||||
|
|
||||||
class _SourceFileLoader(_FileLoader, SourceLoader):
|
class SourceFileLoader(FileLoader, SourceLoader):
|
||||||
|
|
||||||
"""Concrete implementation of SourceLoader using the file system."""
|
"""Concrete implementation of SourceLoader using the file system."""
|
||||||
|
|
||||||
|
@ -668,7 +671,7 @@ class _SourceFileLoader(_FileLoader, SourceLoader):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class _SourcelessFileLoader(_FileLoader, _LoaderBasics):
|
class _SourcelessFileLoader(FileLoader, _LoaderBasics):
|
||||||
|
|
||||||
"""Loader which handles sourceless file imports."""
|
"""Loader which handles sourceless file imports."""
|
||||||
|
|
||||||
|
@ -692,7 +695,7 @@ class _SourcelessFileLoader(_FileLoader, _LoaderBasics):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class _ExtensionFileLoader:
|
class ExtensionFileLoader:
|
||||||
|
|
||||||
"""Loader for extension modules.
|
"""Loader for extension modules.
|
||||||
|
|
||||||
|
@ -701,8 +704,8 @@ class _ExtensionFileLoader:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, path):
|
def __init__(self, name, path):
|
||||||
self._name = name
|
self.name = name
|
||||||
self._path = path
|
self.path = path
|
||||||
|
|
||||||
@_check_name
|
@_check_name
|
||||||
@set_package
|
@set_package
|
||||||
|
@ -711,8 +714,8 @@ class _ExtensionFileLoader:
|
||||||
"""Load an extension module."""
|
"""Load an extension module."""
|
||||||
is_reload = fullname in sys.modules
|
is_reload = fullname in sys.modules
|
||||||
try:
|
try:
|
||||||
module = _imp.load_dynamic(fullname, self._path)
|
module = _imp.load_dynamic(fullname, self.path)
|
||||||
verbose_message('extension module loaded from {!r}', self._path)
|
verbose_message('extension module loaded from {!r}', self.path)
|
||||||
return module
|
return module
|
||||||
except:
|
except:
|
||||||
if not is_reload and fullname in sys.modules:
|
if not is_reload and fullname in sys.modules:
|
||||||
|
@ -805,24 +808,25 @@ class PathFinder:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class _FileFinder:
|
class FileFinder:
|
||||||
|
|
||||||
"""File-based finder.
|
"""File-based finder.
|
||||||
|
|
||||||
Constructor takes a list of objects detailing what file extensions their
|
Interactions with the file system are cached for performance, being
|
||||||
loader supports along with whether it can be used for a package.
|
refreshed when the directory the finder is handling has been modified.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, path, *details):
|
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 = []
|
packages = []
|
||||||
modules = []
|
modules = []
|
||||||
for detail in details:
|
for loader, suffixes, supports_packages in details:
|
||||||
modules.extend((suffix, detail.loader) for suffix in detail.suffixes)
|
modules.extend((suffix, loader) for suffix in suffixes)
|
||||||
if detail.supports_packages:
|
if supports_packages:
|
||||||
packages.extend((suffix, detail.loader)
|
packages.extend((suffix, loader) for suffix in suffixes)
|
||||||
for suffix in detail.suffixes)
|
|
||||||
self.packages = packages
|
self.packages = packages
|
||||||
self.modules = modules
|
self.modules = modules
|
||||||
# Base (directory) path
|
# Base (directory) path
|
||||||
|
@ -898,46 +902,29 @@ class _FileFinder:
|
||||||
if sys.platform.startswith(CASE_INSENSITIVE_PLATFORMS):
|
if sys.platform.startswith(CASE_INSENSITIVE_PLATFORMS):
|
||||||
self._relaxed_path_cache = set(fn.lower() for fn in contents)
|
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):
|
return path_hook_for_FileFinder
|
||||||
self.suffixes = _suffix_list(_imp.PY_SOURCE)
|
|
||||||
|
|
||||||
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 ###############################################################
|
# Import itself ###############################################################
|
||||||
|
|
||||||
def _file_path_hook(path):
|
_DEFAULT_PATH_HOOK = None # Set in _setup()
|
||||||
"""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
|
|
||||||
|
|
||||||
class _DefaultPathFinder(PathFinder):
|
class _DefaultPathFinder(PathFinder):
|
||||||
|
|
||||||
|
@ -1209,6 +1196,12 @@ def _setup(sys_module, _imp_module):
|
||||||
if builtin_os == 'nt':
|
if builtin_os == 'nt':
|
||||||
SOURCE_SUFFIXES.append('.pyw')
|
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):
|
def _install(sys_module, _imp_module):
|
||||||
"""Install importlib as the implementation of import.
|
"""Install importlib as the implementation of import.
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
"""Abstract base classes related to import."""
|
"""Abstract base classes related to import."""
|
||||||
from . import _bootstrap
|
from . import _bootstrap
|
||||||
from . import machinery
|
from . import machinery
|
||||||
|
try:
|
||||||
|
import _frozen_importlib
|
||||||
|
except ImportError as exc:
|
||||||
|
if exc.name != '_frozen_importlib':
|
||||||
|
raise
|
||||||
|
_frozen_importlib = None
|
||||||
import abc
|
import abc
|
||||||
import imp
|
import imp
|
||||||
import marshal
|
import marshal
|
||||||
|
@ -9,6 +15,14 @@ import tokenize
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
|
def _register(abstract_cls, *classes):
|
||||||
|
for cls in classes:
|
||||||
|
abstract_cls.register(cls)
|
||||||
|
if _frozen_importlib is not None:
|
||||||
|
frozen_cls = getattr(_frozen_importlib, cls.__name__)
|
||||||
|
abstract_cls.register(frozen_cls)
|
||||||
|
|
||||||
|
|
||||||
class Loader(metaclass=abc.ABCMeta):
|
class Loader(metaclass=abc.ABCMeta):
|
||||||
|
|
||||||
"""Abstract base class for import loaders."""
|
"""Abstract base class for import loaders."""
|
||||||
|
@ -32,9 +46,8 @@ class Finder(metaclass=abc.ABCMeta):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
Finder.register(machinery.BuiltinImporter)
|
_register(Finder, machinery.BuiltinImporter, machinery.FrozenImporter,
|
||||||
Finder.register(machinery.FrozenImporter)
|
machinery.PathFinder, machinery.FileFinder)
|
||||||
Finder.register(machinery.PathFinder)
|
|
||||||
|
|
||||||
|
|
||||||
class ResourceLoader(Loader):
|
class ResourceLoader(Loader):
|
||||||
|
@ -80,8 +93,8 @@ class InspectLoader(Loader):
|
||||||
module. The fullname is a str. Returns a str."""
|
module. The fullname is a str. Returns a str."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
InspectLoader.register(machinery.BuiltinImporter)
|
_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter,
|
||||||
InspectLoader.register(machinery.FrozenImporter)
|
machinery.ExtensionFileLoader)
|
||||||
|
|
||||||
|
|
||||||
class ExecutionLoader(InspectLoader):
|
class ExecutionLoader(InspectLoader):
|
||||||
|
@ -100,6 +113,15 @@ class ExecutionLoader(InspectLoader):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class FileLoader(_bootstrap.FileLoader, ResourceLoader, ExecutionLoader):
|
||||||
|
|
||||||
|
"""Abstract base class partially implementing the ResourceLoader and
|
||||||
|
ExecutionLoader ABCs."""
|
||||||
|
|
||||||
|
_register(FileLoader, machinery.SourceFileLoader,
|
||||||
|
machinery._SourcelessFileLoader)
|
||||||
|
|
||||||
|
|
||||||
class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
|
class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
|
||||||
|
|
||||||
"""Abstract base class for loading source code (and optionally any
|
"""Abstract base class for loading source code (and optionally any
|
||||||
|
@ -146,6 +168,7 @@ class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
_register(SourceLoader, machinery.SourceFileLoader)
|
||||||
|
|
||||||
class PyLoader(SourceLoader):
|
class PyLoader(SourceLoader):
|
||||||
|
|
||||||
|
|
|
@ -3,3 +3,7 @@
|
||||||
from ._bootstrap import BuiltinImporter
|
from ._bootstrap import BuiltinImporter
|
||||||
from ._bootstrap import FrozenImporter
|
from ._bootstrap import FrozenImporter
|
||||||
from ._bootstrap import PathFinder
|
from ._bootstrap import PathFinder
|
||||||
|
from ._bootstrap import FileFinder
|
||||||
|
from ._bootstrap import SourceFileLoader
|
||||||
|
from ._bootstrap import _SourcelessFileLoader
|
||||||
|
from ._bootstrap import ExtensionFileLoader
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import imp
|
||||||
import sys
|
import sys
|
||||||
from test import support
|
from test import support
|
||||||
import unittest
|
import unittest
|
||||||
|
@ -13,8 +14,10 @@ class ExtensionModuleCaseSensitivityTest(unittest.TestCase):
|
||||||
good_name = ext_util.NAME
|
good_name = ext_util.NAME
|
||||||
bad_name = good_name.upper()
|
bad_name = good_name.upper()
|
||||||
assert good_name != bad_name
|
assert good_name != bad_name
|
||||||
finder = _bootstrap._FileFinder(ext_util.PATH,
|
finder = _bootstrap.FileFinder(ext_util.PATH,
|
||||||
_bootstrap._ExtensionFinderDetails())
|
(_bootstrap.ExtensionFileLoader,
|
||||||
|
_bootstrap._suffix_list(imp.C_EXTENSION),
|
||||||
|
False))
|
||||||
return finder.find_module(bad_name)
|
return finder.find_module(bad_name)
|
||||||
|
|
||||||
def test_case_sensitive(self):
|
def test_case_sensitive(self):
|
||||||
|
|
|
@ -2,6 +2,7 @@ from importlib import _bootstrap
|
||||||
from .. import abc
|
from .. import abc
|
||||||
from . import util
|
from . import util
|
||||||
|
|
||||||
|
import imp
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
class FinderTests(abc.FinderTests):
|
class FinderTests(abc.FinderTests):
|
||||||
|
@ -9,8 +10,10 @@ class FinderTests(abc.FinderTests):
|
||||||
"""Test the finder for extension modules."""
|
"""Test the finder for extension modules."""
|
||||||
|
|
||||||
def find_module(self, fullname):
|
def find_module(self, fullname):
|
||||||
importer = _bootstrap._FileFinder(util.PATH,
|
importer = _bootstrap.FileFinder(util.PATH,
|
||||||
_bootstrap._ExtensionFinderDetails())
|
(_bootstrap.ExtensionFileLoader,
|
||||||
|
_bootstrap._suffix_list(imp.C_EXTENSION),
|
||||||
|
False))
|
||||||
return importer.find_module(fullname)
|
return importer.find_module(fullname)
|
||||||
|
|
||||||
def test_module(self):
|
def test_module(self):
|
||||||
|
|
|
@ -12,7 +12,7 @@ class LoaderTests(abc.LoaderTests):
|
||||||
"""Test load_module() for extension modules."""
|
"""Test load_module() for extension modules."""
|
||||||
|
|
||||||
def load_module(self, fullname):
|
def load_module(self, fullname):
|
||||||
loader = _bootstrap._ExtensionFileLoader(ext_util.NAME,
|
loader = _bootstrap.ExtensionFileLoader(ext_util.NAME,
|
||||||
ext_util.FILEPATH)
|
ext_util.FILEPATH)
|
||||||
return loader.load_module(fullname)
|
return loader.load_module(fullname)
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class LoaderTests(abc.LoaderTests):
|
||||||
self.assertEqual(getattr(module, attr), value)
|
self.assertEqual(getattr(module, attr), value)
|
||||||
self.assertTrue(ext_util.NAME in sys.modules)
|
self.assertTrue(ext_util.NAME in sys.modules)
|
||||||
self.assertTrue(isinstance(module.__loader__,
|
self.assertTrue(isinstance(module.__loader__,
|
||||||
_bootstrap._ExtensionFileLoader))
|
_bootstrap.ExtensionFileLoader))
|
||||||
|
|
||||||
def test_package(self):
|
def test_package(self):
|
||||||
# Extensions are not found in packages.
|
# Extensions are not found in packages.
|
||||||
|
|
|
@ -14,7 +14,8 @@ class PathHookTests(unittest.TestCase):
|
||||||
# XXX Should it only work for directories containing an extension module?
|
# XXX Should it only work for directories containing an extension module?
|
||||||
|
|
||||||
def hook(self, entry):
|
def hook(self, entry):
|
||||||
return _bootstrap._file_path_hook(entry)
|
return _bootstrap.FileFinder.path_hook((_bootstrap.ExtensionFileLoader,
|
||||||
|
_bootstrap._suffix_list(imp.C_EXTENSION), False))(entry)
|
||||||
|
|
||||||
def test_success(self):
|
def test_success(self):
|
||||||
# Path hook should handle a directory where a known extension module
|
# Path hook should handle a directory where a known extension module
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from importlib import _bootstrap
|
from importlib import _bootstrap
|
||||||
from .. import util
|
from .. import util
|
||||||
from . import util as source_util
|
from . import util as source_util
|
||||||
|
import imp
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from test import support as test_support
|
from test import support as test_support
|
||||||
|
@ -19,9 +20,13 @@ class CaseSensitivityTest(unittest.TestCase):
|
||||||
assert name != name.lower()
|
assert name != name.lower()
|
||||||
|
|
||||||
def find(self, path):
|
def find(self, path):
|
||||||
finder = _bootstrap._FileFinder(path,
|
finder = _bootstrap.FileFinder(path,
|
||||||
_bootstrap._SourceFinderDetails(),
|
(_bootstrap.SourceFileLoader,
|
||||||
_bootstrap._SourcelessFinderDetails())
|
_bootstrap._suffix_list(imp.PY_SOURCE),
|
||||||
|
True),
|
||||||
|
(_bootstrap._SourcelessFileLoader,
|
||||||
|
_bootstrap._suffix_list(imp.PY_COMPILED),
|
||||||
|
True))
|
||||||
return finder.find_module(self.name)
|
return finder.find_module(self.name)
|
||||||
|
|
||||||
def sensitivity_test(self):
|
def sensitivity_test(self):
|
||||||
|
|
|
@ -27,7 +27,7 @@ class SimpleTest(unittest.TestCase):
|
||||||
# [basic]
|
# [basic]
|
||||||
def test_module(self):
|
def test_module(self):
|
||||||
with source_util.create_modules('_temp') as mapping:
|
with source_util.create_modules('_temp') as mapping:
|
||||||
loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
|
loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
|
||||||
module = loader.load_module('_temp')
|
module = loader.load_module('_temp')
|
||||||
self.assertTrue('_temp' in sys.modules)
|
self.assertTrue('_temp' in sys.modules)
|
||||||
check = {'__name__': '_temp', '__file__': mapping['_temp'],
|
check = {'__name__': '_temp', '__file__': mapping['_temp'],
|
||||||
|
@ -37,7 +37,7 @@ class SimpleTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_package(self):
|
def test_package(self):
|
||||||
with source_util.create_modules('_pkg.__init__') as mapping:
|
with source_util.create_modules('_pkg.__init__') as mapping:
|
||||||
loader = _bootstrap._SourceFileLoader('_pkg',
|
loader = _bootstrap.SourceFileLoader('_pkg',
|
||||||
mapping['_pkg.__init__'])
|
mapping['_pkg.__init__'])
|
||||||
module = loader.load_module('_pkg')
|
module = loader.load_module('_pkg')
|
||||||
self.assertTrue('_pkg' in sys.modules)
|
self.assertTrue('_pkg' in sys.modules)
|
||||||
|
@ -50,7 +50,7 @@ class SimpleTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_lacking_parent(self):
|
def test_lacking_parent(self):
|
||||||
with source_util.create_modules('_pkg.__init__', '_pkg.mod')as mapping:
|
with source_util.create_modules('_pkg.__init__', '_pkg.mod')as mapping:
|
||||||
loader = _bootstrap._SourceFileLoader('_pkg.mod',
|
loader = _bootstrap.SourceFileLoader('_pkg.mod',
|
||||||
mapping['_pkg.mod'])
|
mapping['_pkg.mod'])
|
||||||
module = loader.load_module('_pkg.mod')
|
module = loader.load_module('_pkg.mod')
|
||||||
self.assertTrue('_pkg.mod' in sys.modules)
|
self.assertTrue('_pkg.mod' in sys.modules)
|
||||||
|
@ -65,7 +65,7 @@ class SimpleTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_module_reuse(self):
|
def test_module_reuse(self):
|
||||||
with source_util.create_modules('_temp') as mapping:
|
with source_util.create_modules('_temp') as mapping:
|
||||||
loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
|
loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
|
||||||
module = loader.load_module('_temp')
|
module = loader.load_module('_temp')
|
||||||
module_id = id(module)
|
module_id = id(module)
|
||||||
module_dict_id = id(module.__dict__)
|
module_dict_id = id(module.__dict__)
|
||||||
|
@ -90,7 +90,7 @@ class SimpleTest(unittest.TestCase):
|
||||||
setattr(orig_module, attr, value)
|
setattr(orig_module, attr, value)
|
||||||
with open(mapping[name], 'w') as file:
|
with open(mapping[name], 'w') as file:
|
||||||
file.write('+++ bad syntax +++')
|
file.write('+++ bad syntax +++')
|
||||||
loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
|
loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
|
||||||
with self.assertRaises(SyntaxError):
|
with self.assertRaises(SyntaxError):
|
||||||
loader.load_module(name)
|
loader.load_module(name)
|
||||||
for attr in attributes:
|
for attr in attributes:
|
||||||
|
@ -101,7 +101,7 @@ class SimpleTest(unittest.TestCase):
|
||||||
with source_util.create_modules('_temp') as mapping:
|
with source_util.create_modules('_temp') as mapping:
|
||||||
with open(mapping['_temp'], 'w') as file:
|
with open(mapping['_temp'], 'w') as file:
|
||||||
file.write('=')
|
file.write('=')
|
||||||
loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
|
loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
|
||||||
with self.assertRaises(SyntaxError):
|
with self.assertRaises(SyntaxError):
|
||||||
loader.load_module('_temp')
|
loader.load_module('_temp')
|
||||||
self.assertTrue('_temp' not in sys.modules)
|
self.assertTrue('_temp' not in sys.modules)
|
||||||
|
@ -114,7 +114,7 @@ class SimpleTest(unittest.TestCase):
|
||||||
file.write("# test file for importlib")
|
file.write("# test file for importlib")
|
||||||
try:
|
try:
|
||||||
with util.uncache('_temp'):
|
with util.uncache('_temp'):
|
||||||
loader = _bootstrap._SourceFileLoader('_temp', file_path)
|
loader = _bootstrap.SourceFileLoader('_temp', file_path)
|
||||||
mod = loader.load_module('_temp')
|
mod = loader.load_module('_temp')
|
||||||
self.assertEqual(file_path, mod.__file__)
|
self.assertEqual(file_path, mod.__file__)
|
||||||
self.assertEqual(imp.cache_from_source(file_path),
|
self.assertEqual(imp.cache_from_source(file_path),
|
||||||
|
@ -140,7 +140,7 @@ class SimpleTest(unittest.TestCase):
|
||||||
if e.errno != getattr(errno, 'EOVERFLOW', None):
|
if e.errno != getattr(errno, 'EOVERFLOW', None):
|
||||||
raise
|
raise
|
||||||
self.skipTest("cannot set modification time to large integer ({})".format(e))
|
self.skipTest("cannot set modification time to large integer ({})".format(e))
|
||||||
loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
|
loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
|
||||||
mod = loader.load_module('_temp')
|
mod = loader.load_module('_temp')
|
||||||
# Sanity checks.
|
# Sanity checks.
|
||||||
self.assertEqual(mod.__cached__, compiled)
|
self.assertEqual(mod.__cached__, compiled)
|
||||||
|
@ -255,7 +255,7 @@ class BadBytecodeTest(unittest.TestCase):
|
||||||
|
|
||||||
class SourceLoaderBadBytecodeTest(BadBytecodeTest):
|
class SourceLoaderBadBytecodeTest(BadBytecodeTest):
|
||||||
|
|
||||||
loader = _bootstrap._SourceFileLoader
|
loader = _bootstrap.SourceFileLoader
|
||||||
|
|
||||||
@source_util.writes_bytecode_files
|
@source_util.writes_bytecode_files
|
||||||
def test_empty_file(self):
|
def test_empty_file(self):
|
||||||
|
|
|
@ -3,6 +3,7 @@ from . import util as source_util
|
||||||
|
|
||||||
from importlib import _bootstrap
|
from importlib import _bootstrap
|
||||||
import errno
|
import errno
|
||||||
|
import imp
|
||||||
import os
|
import os
|
||||||
import py_compile
|
import py_compile
|
||||||
from test.support import make_legacy_pyc
|
from test.support import make_legacy_pyc
|
||||||
|
@ -35,9 +36,11 @@ class FinderTests(abc.FinderTests):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def import_(self, root, module):
|
def import_(self, root, module):
|
||||||
finder = _bootstrap._FileFinder(root,
|
loader_details = [(_bootstrap.SourceFileLoader,
|
||||||
_bootstrap._SourceFinderDetails(),
|
_bootstrap._suffix_list(imp.PY_SOURCE), True),
|
||||||
_bootstrap._SourcelessFinderDetails())
|
(_bootstrap._SourcelessFileLoader,
|
||||||
|
_bootstrap._suffix_list(imp.PY_COMPILED), True)]
|
||||||
|
finder = _bootstrap.FileFinder(root, *loader_details)
|
||||||
return finder.find_module(module)
|
return finder.find_module(module)
|
||||||
|
|
||||||
def run_test(self, test, create=None, *, compile_=None, unlink=None):
|
def run_test(self, test, create=None, *, compile_=None, unlink=None):
|
||||||
|
@ -135,7 +138,8 @@ class FinderTests(abc.FinderTests):
|
||||||
|
|
||||||
def test_empty_string_for_dir(self):
|
def test_empty_string_for_dir(self):
|
||||||
# The empty string from sys.path means to search in the cwd.
|
# The empty string from sys.path means to search in the cwd.
|
||||||
finder = _bootstrap._FileFinder('', _bootstrap._SourceFinderDetails())
|
finder = _bootstrap.FileFinder('', (_bootstrap.SourceFileLoader,
|
||||||
|
_bootstrap._suffix_list(imp.PY_SOURCE), True))
|
||||||
with open('mod.py', 'w') as file:
|
with open('mod.py', 'w') as file:
|
||||||
file.write("# test file for importlib")
|
file.write("# test file for importlib")
|
||||||
try:
|
try:
|
||||||
|
@ -146,7 +150,8 @@ class FinderTests(abc.FinderTests):
|
||||||
|
|
||||||
def test_invalidate_caches(self):
|
def test_invalidate_caches(self):
|
||||||
# invalidate_caches() should reset the mtime.
|
# invalidate_caches() should reset the mtime.
|
||||||
finder = _bootstrap._FileFinder('', _bootstrap._SourceFinderDetails())
|
finder = _bootstrap.FileFinder('', (_bootstrap.SourceFileLoader,
|
||||||
|
_bootstrap._suffix_list(imp.PY_SOURCE), True))
|
||||||
finder._path_mtime = 42
|
finder._path_mtime = 42
|
||||||
finder.invalidate_caches()
|
finder.invalidate_caches()
|
||||||
self.assertEqual(finder._path_mtime, -1)
|
self.assertEqual(finder._path_mtime, -1)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from . import util as source_util
|
from . import util as source_util
|
||||||
|
|
||||||
from importlib import _bootstrap
|
from importlib import _bootstrap
|
||||||
|
import imp
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,14 +9,18 @@ class PathHookTest(unittest.TestCase):
|
||||||
|
|
||||||
"""Test the path hook for source."""
|
"""Test the path hook for source."""
|
||||||
|
|
||||||
|
def path_hook(self):
|
||||||
|
return _bootstrap.FileFinder.path_hook((_bootstrap.SourceFileLoader,
|
||||||
|
_bootstrap._suffix_list(imp.PY_SOURCE), True))
|
||||||
|
|
||||||
def test_success(self):
|
def test_success(self):
|
||||||
with source_util.create_modules('dummy') as mapping:
|
with source_util.create_modules('dummy') as mapping:
|
||||||
self.assertTrue(hasattr(_bootstrap._file_path_hook(mapping['.root']),
|
self.assertTrue(hasattr(self.path_hook()(mapping['.root']),
|
||||||
'find_module'))
|
'find_module'))
|
||||||
|
|
||||||
def test_empty_string(self):
|
def test_empty_string(self):
|
||||||
# The empty string represents the cwd.
|
# The empty string represents the cwd.
|
||||||
self.assertTrue(hasattr(_bootstrap._file_path_hook(''), 'find_module'))
|
self.assertTrue(hasattr(self.path_hook()(''), 'find_module'))
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
|
|
|
@ -35,7 +35,7 @@ class EncodingTest(unittest.TestCase):
|
||||||
with source_util.create_modules(self.module_name) as mapping:
|
with source_util.create_modules(self.module_name) as mapping:
|
||||||
with open(mapping[self.module_name], 'wb') as file:
|
with open(mapping[self.module_name], 'wb') as file:
|
||||||
file.write(source)
|
file.write(source)
|
||||||
loader = _bootstrap._SourceFileLoader(self.module_name,
|
loader = _bootstrap.SourceFileLoader(self.module_name,
|
||||||
mapping[self.module_name])
|
mapping[self.module_name])
|
||||||
return loader.load_module(self.module_name)
|
return loader.load_module(self.module_name)
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ class LineEndingTest(unittest.TestCase):
|
||||||
with source_util.create_modules(module_name) as mapping:
|
with source_util.create_modules(module_name) as mapping:
|
||||||
with open(mapping[module_name], 'wb') as file:
|
with open(mapping[module_name], 'wb') as file:
|
||||||
file.write(source)
|
file.write(source)
|
||||||
loader = _bootstrap._SourceFileLoader(module_name,
|
loader = _bootstrap.SourceFileLoader(module_name,
|
||||||
mapping[module_name])
|
mapping[module_name])
|
||||||
return loader.load_module(module_name)
|
return loader.load_module(module_name)
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ class InspectLoader(InheritanceTests, unittest.TestCase):
|
||||||
|
|
||||||
superclasses = [abc.Loader]
|
superclasses = [abc.Loader]
|
||||||
subclasses = [abc.PyLoader, machinery.BuiltinImporter,
|
subclasses = [abc.PyLoader, machinery.BuiltinImporter,
|
||||||
machinery.FrozenImporter]
|
machinery.FrozenImporter, machinery.ExtensionFileLoader]
|
||||||
|
|
||||||
|
|
||||||
class ExecutionLoader(InheritanceTests, unittest.TestCase):
|
class ExecutionLoader(InheritanceTests, unittest.TestCase):
|
||||||
|
@ -59,9 +59,16 @@ class ExecutionLoader(InheritanceTests, unittest.TestCase):
|
||||||
subclasses = [abc.PyLoader]
|
subclasses = [abc.PyLoader]
|
||||||
|
|
||||||
|
|
||||||
|
class FileLoader(InheritanceTests, unittest.TestCase):
|
||||||
|
|
||||||
|
superclasses = [abc.ResourceLoader, abc.ExecutionLoader]
|
||||||
|
subclasses = [machinery.SourceFileLoader, machinery._SourcelessFileLoader]
|
||||||
|
|
||||||
|
|
||||||
class SourceLoader(InheritanceTests, unittest.TestCase):
|
class SourceLoader(InheritanceTests, unittest.TestCase):
|
||||||
|
|
||||||
superclasses = [abc.ResourceLoader, abc.ExecutionLoader]
|
superclasses = [abc.ResourceLoader, abc.ExecutionLoader]
|
||||||
|
subclasses = [machinery.SourceFileLoader]
|
||||||
|
|
||||||
|
|
||||||
class PyLoader(InheritanceTests, unittest.TestCase):
|
class PyLoader(InheritanceTests, unittest.TestCase):
|
||||||
|
|
|
@ -61,6 +61,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #14605: Add importlib.abc.FileLoader, importlib.machinery.(FileFinder,
|
||||||
|
SourceFileLoader, _SourcelessFileLoader, ExtensionFileLoader).
|
||||||
|
|
||||||
- Issue #13959: imp.cache_from_source()/source_from_cache() now follow
|
- Issue #13959: imp.cache_from_source()/source_from_cache() now follow
|
||||||
os.path.join()/split() semantics for path manipulation instead of its prior,
|
os.path.join()/split() semantics for path manipulation instead of its prior,
|
||||||
custom semantics of caring the right-most path separator forward in path
|
custom semantics of caring the right-most path separator forward in path
|
||||||
|
|
5903
Python/importlib.h
5903
Python/importlib.h
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue