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:
Brett Cannon 2012-04-22 19:58:33 -04:00
parent 8c5e920ae3
commit 938d44d59c
17 changed files with 3246 additions and 3076 deletions

View File

@ -237,6 +237,34 @@ are also provided to help in implementing the core ABCs.
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
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.
.. 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
---------------------------------------------------

View File

@ -71,7 +71,7 @@ class _HackedGetData:
def get_data(self, path):
"""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:
# Technically should be returning bytes, but
# SourceLoader.get_code() just passed what is returned to
@ -83,7 +83,7 @@ class _HackedGetData:
return super().get_data(path)
class _LoadSourceCompatibility(_HackedGetData, _bootstrap._SourceFileLoader):
class _LoadSourceCompatibility(_HackedGetData, _bootstrap.SourceFileLoader):
"""Compatibility support for implementing load_source()."""
@ -115,7 +115,7 @@ def load_package(name, path):
break
else:
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

View File

@ -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.

View File

@ -1,6 +1,12 @@
"""Abstract base classes related to import."""
from . import _bootstrap
from . import machinery
try:
import _frozen_importlib
except ImportError as exc:
if exc.name != '_frozen_importlib':
raise
_frozen_importlib = None
import abc
import imp
import marshal
@ -9,6 +15,14 @@ import tokenize
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):
"""Abstract base class for import loaders."""
@ -32,9 +46,8 @@ class Finder(metaclass=abc.ABCMeta):
"""
raise NotImplementedError
Finder.register(machinery.BuiltinImporter)
Finder.register(machinery.FrozenImporter)
Finder.register(machinery.PathFinder)
_register(Finder, machinery.BuiltinImporter, machinery.FrozenImporter,
machinery.PathFinder, machinery.FileFinder)
class ResourceLoader(Loader):
@ -80,8 +93,8 @@ class InspectLoader(Loader):
module. The fullname is a str. Returns a str."""
raise NotImplementedError
InspectLoader.register(machinery.BuiltinImporter)
InspectLoader.register(machinery.FrozenImporter)
_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter,
machinery.ExtensionFileLoader)
class ExecutionLoader(InspectLoader):
@ -100,6 +113,15 @@ class ExecutionLoader(InspectLoader):
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):
"""Abstract base class for loading source code (and optionally any
@ -146,6 +168,7 @@ class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
"""
raise NotImplementedError
_register(SourceLoader, machinery.SourceFileLoader)
class PyLoader(SourceLoader):

View File

@ -3,3 +3,7 @@
from ._bootstrap import BuiltinImporter
from ._bootstrap import FrozenImporter
from ._bootstrap import PathFinder
from ._bootstrap import FileFinder
from ._bootstrap import SourceFileLoader
from ._bootstrap import _SourcelessFileLoader
from ._bootstrap import ExtensionFileLoader

View File

@ -1,3 +1,4 @@
import imp
import sys
from test import support
import unittest
@ -13,8 +14,10 @@ class ExtensionModuleCaseSensitivityTest(unittest.TestCase):
good_name = ext_util.NAME
bad_name = good_name.upper()
assert good_name != bad_name
finder = _bootstrap._FileFinder(ext_util.PATH,
_bootstrap._ExtensionFinderDetails())
finder = _bootstrap.FileFinder(ext_util.PATH,
(_bootstrap.ExtensionFileLoader,
_bootstrap._suffix_list(imp.C_EXTENSION),
False))
return finder.find_module(bad_name)
def test_case_sensitive(self):

View File

@ -2,6 +2,7 @@ from importlib import _bootstrap
from .. import abc
from . import util
import imp
import unittest
class FinderTests(abc.FinderTests):
@ -9,8 +10,10 @@ class FinderTests(abc.FinderTests):
"""Test the finder for extension modules."""
def find_module(self, fullname):
importer = _bootstrap._FileFinder(util.PATH,
_bootstrap._ExtensionFinderDetails())
importer = _bootstrap.FileFinder(util.PATH,
(_bootstrap.ExtensionFileLoader,
_bootstrap._suffix_list(imp.C_EXTENSION),
False))
return importer.find_module(fullname)
def test_module(self):

View File

@ -12,7 +12,7 @@ class LoaderTests(abc.LoaderTests):
"""Test load_module() for extension modules."""
def load_module(self, fullname):
loader = _bootstrap._ExtensionFileLoader(ext_util.NAME,
loader = _bootstrap.ExtensionFileLoader(ext_util.NAME,
ext_util.FILEPATH)
return loader.load_module(fullname)
@ -25,7 +25,7 @@ class LoaderTests(abc.LoaderTests):
self.assertEqual(getattr(module, attr), value)
self.assertTrue(ext_util.NAME in sys.modules)
self.assertTrue(isinstance(module.__loader__,
_bootstrap._ExtensionFileLoader))
_bootstrap.ExtensionFileLoader))
def test_package(self):
# Extensions are not found in packages.

View File

@ -14,7 +14,8 @@ class PathHookTests(unittest.TestCase):
# XXX Should it only work for directories containing an extension module?
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):
# Path hook should handle a directory where a known extension module

View File

@ -2,6 +2,7 @@
from importlib import _bootstrap
from .. import util
from . import util as source_util
import imp
import os
import sys
from test import support as test_support
@ -19,9 +20,13 @@ class CaseSensitivityTest(unittest.TestCase):
assert name != name.lower()
def find(self, path):
finder = _bootstrap._FileFinder(path,
_bootstrap._SourceFinderDetails(),
_bootstrap._SourcelessFinderDetails())
finder = _bootstrap.FileFinder(path,
(_bootstrap.SourceFileLoader,
_bootstrap._suffix_list(imp.PY_SOURCE),
True),
(_bootstrap._SourcelessFileLoader,
_bootstrap._suffix_list(imp.PY_COMPILED),
True))
return finder.find_module(self.name)
def sensitivity_test(self):

View File

@ -27,7 +27,7 @@ class SimpleTest(unittest.TestCase):
# [basic]
def test_module(self):
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')
self.assertTrue('_temp' in sys.modules)
check = {'__name__': '_temp', '__file__': mapping['_temp'],
@ -37,7 +37,7 @@ class SimpleTest(unittest.TestCase):
def test_package(self):
with source_util.create_modules('_pkg.__init__') as mapping:
loader = _bootstrap._SourceFileLoader('_pkg',
loader = _bootstrap.SourceFileLoader('_pkg',
mapping['_pkg.__init__'])
module = loader.load_module('_pkg')
self.assertTrue('_pkg' in sys.modules)
@ -50,7 +50,7 @@ class SimpleTest(unittest.TestCase):
def test_lacking_parent(self):
with source_util.create_modules('_pkg.__init__', '_pkg.mod')as mapping:
loader = _bootstrap._SourceFileLoader('_pkg.mod',
loader = _bootstrap.SourceFileLoader('_pkg.mod',
mapping['_pkg.mod'])
module = loader.load_module('_pkg.mod')
self.assertTrue('_pkg.mod' in sys.modules)
@ -65,7 +65,7 @@ class SimpleTest(unittest.TestCase):
def test_module_reuse(self):
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_id = id(module)
module_dict_id = id(module.__dict__)
@ -90,7 +90,7 @@ class SimpleTest(unittest.TestCase):
setattr(orig_module, attr, value)
with open(mapping[name], 'w') as file:
file.write('+++ bad syntax +++')
loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
with self.assertRaises(SyntaxError):
loader.load_module(name)
for attr in attributes:
@ -101,7 +101,7 @@ class SimpleTest(unittest.TestCase):
with source_util.create_modules('_temp') as mapping:
with open(mapping['_temp'], 'w') as file:
file.write('=')
loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
loader = _bootstrap.SourceFileLoader('_temp', mapping['_temp'])
with self.assertRaises(SyntaxError):
loader.load_module('_temp')
self.assertTrue('_temp' not in sys.modules)
@ -114,7 +114,7 @@ class SimpleTest(unittest.TestCase):
file.write("# test file for importlib")
try:
with util.uncache('_temp'):
loader = _bootstrap._SourceFileLoader('_temp', file_path)
loader = _bootstrap.SourceFileLoader('_temp', file_path)
mod = loader.load_module('_temp')
self.assertEqual(file_path, mod.__file__)
self.assertEqual(imp.cache_from_source(file_path),
@ -140,7 +140,7 @@ class SimpleTest(unittest.TestCase):
if e.errno != getattr(errno, 'EOVERFLOW', None):
raise
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')
# Sanity checks.
self.assertEqual(mod.__cached__, compiled)
@ -255,7 +255,7 @@ class BadBytecodeTest(unittest.TestCase):
class SourceLoaderBadBytecodeTest(BadBytecodeTest):
loader = _bootstrap._SourceFileLoader
loader = _bootstrap.SourceFileLoader
@source_util.writes_bytecode_files
def test_empty_file(self):

View File

@ -3,6 +3,7 @@ from . import util as source_util
from importlib import _bootstrap
import errno
import imp
import os
import py_compile
from test.support import make_legacy_pyc
@ -35,9 +36,11 @@ class FinderTests(abc.FinderTests):
"""
def import_(self, root, module):
finder = _bootstrap._FileFinder(root,
_bootstrap._SourceFinderDetails(),
_bootstrap._SourcelessFinderDetails())
loader_details = [(_bootstrap.SourceFileLoader,
_bootstrap._suffix_list(imp.PY_SOURCE), True),
(_bootstrap._SourcelessFileLoader,
_bootstrap._suffix_list(imp.PY_COMPILED), True)]
finder = _bootstrap.FileFinder(root, *loader_details)
return finder.find_module(module)
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):
# 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:
file.write("# test file for importlib")
try:
@ -146,7 +150,8 @@ class FinderTests(abc.FinderTests):
def test_invalidate_caches(self):
# 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.invalidate_caches()
self.assertEqual(finder._path_mtime, -1)

View File

@ -1,6 +1,7 @@
from . import util as source_util
from importlib import _bootstrap
import imp
import unittest
@ -8,14 +9,18 @@ class PathHookTest(unittest.TestCase):
"""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):
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'))
def test_empty_string(self):
# 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():

View File

@ -35,7 +35,7 @@ class EncodingTest(unittest.TestCase):
with source_util.create_modules(self.module_name) as mapping:
with open(mapping[self.module_name], 'wb') as file:
file.write(source)
loader = _bootstrap._SourceFileLoader(self.module_name,
loader = _bootstrap.SourceFileLoader(self.module_name,
mapping[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 open(mapping[module_name], 'wb') as file:
file.write(source)
loader = _bootstrap._SourceFileLoader(module_name,
loader = _bootstrap.SourceFileLoader(module_name,
mapping[module_name])
return loader.load_module(module_name)

View File

@ -50,7 +50,7 @@ class InspectLoader(InheritanceTests, unittest.TestCase):
superclasses = [abc.Loader]
subclasses = [abc.PyLoader, machinery.BuiltinImporter,
machinery.FrozenImporter]
machinery.FrozenImporter, machinery.ExtensionFileLoader]
class ExecutionLoader(InheritanceTests, unittest.TestCase):
@ -59,9 +59,16 @@ class ExecutionLoader(InheritanceTests, unittest.TestCase):
subclasses = [abc.PyLoader]
class FileLoader(InheritanceTests, unittest.TestCase):
superclasses = [abc.ResourceLoader, abc.ExecutionLoader]
subclasses = [machinery.SourceFileLoader, machinery._SourcelessFileLoader]
class SourceLoader(InheritanceTests, unittest.TestCase):
superclasses = [abc.ResourceLoader, abc.ExecutionLoader]
subclasses = [machinery.SourceFileLoader]
class PyLoader(InheritanceTests, unittest.TestCase):

View File

@ -61,6 +61,9 @@ Core and Builtins
Library
-------
- Issue #14605: Add importlib.abc.FileLoader, importlib.machinery.(FileFinder,
SourceFileLoader, _SourcelessFileLoader, ExtensionFileLoader).
- Issue #13959: imp.cache_from_source()/source_from_cache() now follow
os.path.join()/split() semantics for path manipulation instead of its prior,
custom semantics of caring the right-most path separator forward in path

File diff suppressed because it is too large Load Diff