Issue #15641: Clean up deprecated classes from importlib

Patch by Taras Lyapun.
This commit is contained in:
Andrew Svetlov 2012-11-05 09:34:46 +02:00
parent bcbf4036c9
commit 90a654b1dd
5 changed files with 7 additions and 834 deletions

View File

@ -133,8 +133,6 @@ ABC hierarchy::
+-- ExecutionLoader --+ +-- ExecutionLoader --+
+-- FileLoader +-- FileLoader
+-- SourceLoader +-- SourceLoader
+-- PyLoader (deprecated)
+-- PyPycLoader (deprecated)
.. class:: Finder .. class:: Finder
@ -431,142 +429,6 @@ ABC hierarchy::
itself does not end in ``__init__``. itself does not end in ``__init__``.
.. class:: PyLoader
An abstract base class inheriting from
:class:`ExecutionLoader` and
:class:`ResourceLoader` designed to ease the loading of
Python source modules (bytecode is not handled; see
:class:`SourceLoader` for a source/bytecode ABC). A subclass
implementing this ABC will only need to worry about exposing how the source
code is stored; all other details for loading Python source code will be
handled by the concrete implementations of key methods.
.. deprecated:: 3.2
This class has been deprecated in favor of :class:`SourceLoader` and is
slated for removal in Python 3.4. See below for how to create a
subclass that is compatible with Python 3.1 onwards.
If compatibility with Python 3.1 is required, then use the following idiom
to implement a subclass that will work with Python 3.1 onwards (make sure
to implement :meth:`ExecutionLoader.get_filename`)::
try:
from importlib.abc import SourceLoader
except ImportError:
from importlib.abc import PyLoader as SourceLoader
class CustomLoader(SourceLoader):
def get_filename(self, fullname):
"""Return the path to the source file."""
# Implement ...
def source_path(self, fullname):
"""Implement source_path in terms of get_filename."""
try:
return self.get_filename(fullname)
except ImportError:
return None
def is_package(self, fullname):
"""Implement is_package by looking for an __init__ file
name as returned by get_filename."""
filename = os.path.basename(self.get_filename(fullname))
return os.path.splitext(filename)[0] == '__init__'
.. method:: source_path(fullname)
An abstract method that returns the path to the source code for a
module. Should return ``None`` if there is no source code.
Raises :exc:`ImportError` if the loader knows it cannot handle the
module.
.. method:: get_filename(fullname)
A concrete implementation of
:meth:`importlib.abc.ExecutionLoader.get_filename` that
relies on :meth:`source_path`. If :meth:`source_path` returns
``None``, then :exc:`ImportError` is raised.
.. method:: load_module(fullname)
A concrete implementation of :meth:`importlib.abc.Loader.load_module`
that loads Python source code. All needed information comes from the
abstract methods required by this ABC. The only pertinent assumption
made by this method is that when loading a package
:attr:`__path__` is set to ``[os.path.dirname(__file__)]``.
.. method:: get_code(fullname)
A concrete implementation of
:meth:`importlib.abc.InspectLoader.get_code` that creates code objects
from Python source code, by requesting the source code (using
:meth:`source_path` and :meth:`get_data`) and compiling it with the
built-in :func:`compile` function.
.. method:: get_source(fullname)
A concrete implementation of
:meth:`importlib.abc.InspectLoader.get_source`. Uses
:meth:`importlib.abc.ResourceLoader.get_data` and :meth:`source_path`
to get the source code. It tries to guess the source encoding using
:func:`tokenize.detect_encoding`.
.. class:: PyPycLoader
An abstract base class inheriting from :class:`PyLoader`.
This ABC is meant to help in creating loaders that support both Python
source and bytecode.
.. deprecated:: 3.2
This class has been deprecated in favor of :class:`SourceLoader` and to
properly support :pep:`3147`. If compatibility is required with
Python 3.1, implement both :class:`SourceLoader` and :class:`PyLoader`;
instructions on how to do so are included in the documentation for
:class:`PyLoader`. Do note that this solution will not support
sourceless/bytecode-only loading; only source *and* bytecode loading.
.. versionchanged:: 3.3
Updated to parse (but not use) the new source size field in bytecode
files when reading and to write out the field properly when writing.
.. method:: source_mtime(fullname)
An abstract method which returns the modification time for the source
code of the specified module. The modification time should be an
integer. If there is no source code, return ``None``. If the
module cannot be found then :exc:`ImportError` is raised.
.. method:: bytecode_path(fullname)
An abstract method which returns the path to the bytecode for the
specified module, if it exists. It returns ``None``
if no bytecode exists (yet).
Raises :exc:`ImportError` if the loader knows it cannot handle the
module.
.. method:: get_filename(fullname)
A concrete implementation of
:meth:`ExecutionLoader.get_filename` that relies on
:meth:`PyLoader.source_path` and :meth:`bytecode_path`.
If :meth:`source_path` returns a path, then that value is returned.
Else if :meth:`bytecode_path` returns a path, that path will be
returned. If a path is not available from both methods,
:exc:`ImportError` is raised.
.. method:: write_bytecode(fullname, bytecode)
An abstract method which has the loader write *bytecode* for future
use. If the bytecode is written, return ``True``. Return
``False`` if the bytecode could not be written. This method
should not be called if :data:`sys.dont_write_bytecode` is true.
The *bytecode* argument should be a bytes string or bytes array.
:mod:`importlib.machinery` -- Importers and path hooks :mod:`importlib.machinery` -- Importers and path hooks
------------------------------------------------------ ------------------------------------------------------

View File

@ -225,180 +225,3 @@ class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
raise NotImplementedError raise NotImplementedError
_register(SourceLoader, machinery.SourceFileLoader) _register(SourceLoader, machinery.SourceFileLoader)
class PyLoader(SourceLoader):
"""Implement the deprecated PyLoader ABC in terms of SourceLoader.
This class has been deprecated! It is slated for removal in Python 3.4.
If compatibility with Python 3.1 is not needed then implement the
SourceLoader ABC instead of this class. If Python 3.1 compatibility is
needed, then use the following idiom to have a single class that is
compatible with Python 3.1 onwards::
try:
from importlib.abc import SourceLoader
except ImportError:
from importlib.abc import PyLoader as SourceLoader
class CustomLoader(SourceLoader):
def get_filename(self, fullname):
# Implement ...
def source_path(self, fullname):
'''Implement source_path in terms of get_filename.'''
try:
return self.get_filename(fullname)
except ImportError:
return None
def is_package(self, fullname):
filename = os.path.basename(self.get_filename(fullname))
return os.path.splitext(filename)[0] == '__init__'
"""
@abc.abstractmethod
def is_package(self, fullname):
raise NotImplementedError
@abc.abstractmethod
def source_path(self, fullname):
"""Abstract method. Accepts a str module name and returns the path to
the source code for the module."""
raise NotImplementedError
def get_filename(self, fullname):
"""Implement get_filename in terms of source_path.
As get_filename should only return a source file path there is no
chance of the path not existing but loading still being possible, so
ImportError should propagate instead of being turned into returning
None.
"""
warnings.warn("importlib.abc.PyLoader is deprecated and is "
"slated for removal in Python 3.4; "
"use SourceLoader instead. "
"See the importlib documentation on how to be "
"compatible with Python 3.1 onwards.",
DeprecationWarning)
path = self.source_path(fullname)
if path is None:
raise ImportError(name=fullname)
else:
return path
class PyPycLoader(PyLoader):
"""Abstract base class to assist in loading source and bytecode by
requiring only back-end storage methods to be implemented.
This class has been deprecated! Removal is slated for Python 3.4. Implement
the SourceLoader ABC instead. If Python 3.1 compatibility is needed, see
PyLoader.
The methods get_code, get_source, and load_module are implemented for the
user.
"""
def get_filename(self, fullname):
"""Return the source or bytecode file path."""
path = self.source_path(fullname)
if path is not None:
return path
path = self.bytecode_path(fullname)
if path is not None:
return path
raise ImportError("no source or bytecode path available for "
"{0!r}".format(fullname), name=fullname)
def get_code(self, fullname):
"""Get a code object from source or bytecode."""
warnings.warn("importlib.abc.PyPycLoader is deprecated and slated for "
"removal in Python 3.4; use SourceLoader instead. "
"If Python 3.1 compatibility is required, see the "
"latest documentation for PyLoader.",
DeprecationWarning)
source_timestamp = self.source_mtime(fullname)
# Try to use bytecode if it is available.
bytecode_path = self.bytecode_path(fullname)
if bytecode_path:
data = self.get_data(bytecode_path)
try:
magic = data[:4]
if len(magic) < 4:
raise ImportError(
"bad magic number in {}".format(fullname),
name=fullname, path=bytecode_path)
raw_timestamp = data[4:8]
if len(raw_timestamp) < 4:
raise EOFError("bad timestamp in {}".format(fullname))
pyc_timestamp = _bootstrap._r_long(raw_timestamp)
raw_source_size = data[8:12]
if len(raw_source_size) != 4:
raise EOFError("bad file size in {}".format(fullname))
# Source size is unused as the ABC does not provide a way to
# get the size of the source ahead of reading it.
bytecode = data[12:]
# Verify that the magic number is valid.
if imp.get_magic() != magic:
raise ImportError(
"bad magic number in {}".format(fullname),
name=fullname, path=bytecode_path)
# Verify that the bytecode is not stale (only matters when
# there is source to fall back on.
if source_timestamp:
if pyc_timestamp < source_timestamp:
raise ImportError("bytecode is stale", name=fullname,
path=bytecode_path)
except (ImportError, EOFError):
# If source is available give it a shot.
if source_timestamp is not None:
pass
else:
raise
else:
# Bytecode seems fine, so try to use it.
return marshal.loads(bytecode)
elif source_timestamp is None:
raise ImportError("no source or bytecode available to create code "
"object for {0!r}".format(fullname),
name=fullname)
# Use the source.
source_path = self.source_path(fullname)
if source_path is None:
message = "a source path must exist to load {0}".format(fullname)
raise ImportError(message, name=fullname)
source = self.get_data(source_path)
code_object = compile(source, source_path, 'exec', dont_inherit=True)
# Generate bytecode and write it out.
if not sys.dont_write_bytecode:
data = bytearray(imp.get_magic())
data.extend(_bootstrap._w_long(source_timestamp))
data.extend(_bootstrap._w_long(len(source) & 0xFFFFFFFF))
data.extend(marshal.dumps(code_object))
self.write_bytecode(fullname, data)
return code_object
@abc.abstractmethod
def source_mtime(self, fullname):
"""Abstract method. Accepts a str filename and returns an int
modification time for the source of the module."""
raise NotImplementedError
@abc.abstractmethod
def bytecode_path(self, fullname):
"""Abstract method. Accepts a str filename and returns the str pathname
to the bytecode for the module."""
raise NotImplementedError
@abc.abstractmethod
def write_bytecode(self, fullname, bytecode):
"""Abstract method. Accepts a str filename and bytes object
representing the bytecode for the module. Returns a boolean
representing whether the bytecode was written or not."""
raise NotImplementedError

View File

@ -70,483 +70,9 @@ class SourceLoaderMock(SourceOnlyLoaderMock):
return path == self.bytecode_path return path == self.bytecode_path
class PyLoaderMock(abc.PyLoader):
# Globals that should be defined for all modules.
source = (b"_ = '::'.join([__name__, __file__, __package__, "
b"repr(__loader__)])")
def __init__(self, data):
"""Take a dict of 'module_name: path' pairings.
Paths should have no file extension, allowing packages to be denoted by
ending in '__init__'.
"""
self.module_paths = data
self.path_to_module = {val:key for key,val in data.items()}
def get_data(self, path):
if path not in self.path_to_module:
raise IOError
return self.source
def is_package(self, name):
filename = os.path.basename(self.get_filename(name))
return os.path.splitext(filename)[0] == '__init__'
def source_path(self, name):
try:
return self.module_paths[name]
except KeyError:
raise ImportError
def get_filename(self, name):
"""Silence deprecation warning."""
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
path = super().get_filename(name)
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
return path
def module_repr(self):
return '<module>'
class PyLoaderCompatMock(PyLoaderMock):
"""Mock that matches what is suggested to have a loader that is compatible
from Python 3.1 onwards."""
def get_filename(self, fullname):
try:
return self.module_paths[fullname]
except KeyError:
raise ImportError
def source_path(self, fullname):
try:
return self.get_filename(fullname)
except ImportError:
return None
class PyPycLoaderMock(abc.PyPycLoader, PyLoaderMock):
default_mtime = 1
def __init__(self, source, bc={}):
"""Initialize mock.
'bc' is a dict keyed on a module's name. The value is dict with
possible keys of 'path', 'mtime', 'magic', and 'bc'. Except for 'path',
each of those keys control if any part of created bytecode is to
deviate from default values.
"""
super().__init__(source)
self.module_bytecode = {}
self.path_to_bytecode = {}
self.bytecode_to_path = {}
for name, data in bc.items():
self.path_to_bytecode[data['path']] = name
self.bytecode_to_path[name] = data['path']
magic = data.get('magic', imp.get_magic())
mtime = importlib._w_long(data.get('mtime', self.default_mtime))
source_size = importlib._w_long(len(self.source) & 0xFFFFFFFF)
if 'bc' in data:
bc = data['bc']
else:
bc = self.compile_bc(name)
self.module_bytecode[name] = magic + mtime + source_size + bc
def compile_bc(self, name):
source_path = self.module_paths.get(name, '<test>') or '<test>'
code = compile(self.source, source_path, 'exec')
return marshal.dumps(code)
def source_mtime(self, name):
if name in self.module_paths:
return self.default_mtime
elif name in self.module_bytecode:
return None
else:
raise ImportError
def bytecode_path(self, name):
try:
return self.bytecode_to_path[name]
except KeyError:
if name in self.module_paths:
return None
else:
raise ImportError
def write_bytecode(self, name, bytecode):
self.module_bytecode[name] = bytecode
return True
def get_data(self, path):
if path in self.path_to_module:
return super().get_data(path)
elif path in self.path_to_bytecode:
name = self.path_to_bytecode[path]
return self.module_bytecode[name]
else:
raise IOError
def is_package(self, name):
try:
return super().is_package(name)
except TypeError:
return '__init__' in self.bytecode_to_path[name]
def get_code(self, name):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
code_object = super().get_code(name)
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
return code_object
class PyLoaderTests(testing_abc.LoaderTests):
"""Tests for importlib.abc.PyLoader."""
mocker = PyLoaderMock
def eq_attrs(self, ob, **kwargs):
for attr, val in kwargs.items():
found = getattr(ob, attr)
self.assertEqual(found, val,
"{} attribute: {} != {}".format(attr, found, val))
def test_module(self):
name = '<module>'
path = os.path.join('', 'path', 'to', 'module')
mock = self.mocker({name: path})
with util.uncache(name):
module = mock.load_module(name)
self.assertIn(name, sys.modules)
self.eq_attrs(module, __name__=name, __file__=path, __package__='',
__loader__=mock)
self.assertTrue(not hasattr(module, '__path__'))
return mock, name
def test_package(self):
name = '<pkg>'
path = os.path.join('path', 'to', name, '__init__')
mock = self.mocker({name: path})
with util.uncache(name):
module = mock.load_module(name)
self.assertIn(name, sys.modules)
self.eq_attrs(module, __name__=name, __file__=path,
__path__=[os.path.dirname(path)], __package__=name,
__loader__=mock)
return mock, name
def test_lacking_parent(self):
name = 'pkg.mod'
path = os.path.join('path', 'to', 'pkg', 'mod')
mock = self.mocker({name: path})
with util.uncache(name):
module = mock.load_module(name)
self.assertIn(name, sys.modules)
self.eq_attrs(module, __name__=name, __file__=path, __package__='pkg',
__loader__=mock)
self.assertFalse(hasattr(module, '__path__'))
return mock, name
def test_module_reuse(self):
name = 'mod'
path = os.path.join('path', 'to', 'mod')
module = imp.new_module(name)
mock = self.mocker({name: path})
with util.uncache(name):
sys.modules[name] = module
loaded_module = mock.load_module(name)
self.assertIs(loaded_module, module)
self.assertIs(sys.modules[name], module)
return mock, name
def test_state_after_failure(self):
name = "mod"
module = imp.new_module(name)
module.blah = None
mock = self.mocker({name: os.path.join('path', 'to', 'mod')})
mock.source = b"1/0"
with util.uncache(name):
sys.modules[name] = module
with self.assertRaises(ZeroDivisionError):
mock.load_module(name)
self.assertIs(sys.modules[name], module)
self.assertTrue(hasattr(module, 'blah'))
return mock
def test_unloadable(self):
name = "mod"
mock = self.mocker({name: os.path.join('path', 'to', 'mod')})
mock.source = b"1/0"
with util.uncache(name):
with self.assertRaises(ZeroDivisionError):
mock.load_module(name)
self.assertNotIn(name, sys.modules)
return mock
class PyLoaderCompatTests(PyLoaderTests):
"""Test that the suggested code to make a loader that is compatible from
Python 3.1 forward works."""
mocker = PyLoaderCompatMock
class PyLoaderInterfaceTests(unittest.TestCase):
"""Tests for importlib.abc.PyLoader to make sure that when source_path()
doesn't return a path everything works as expected."""
def test_no_source_path(self):
# No source path should lead to ImportError.
name = 'mod'
mock = PyLoaderMock({})
with util.uncache(name), self.assertRaises(ImportError):
mock.load_module(name)
def test_source_path_is_None(self):
name = 'mod'
mock = PyLoaderMock({name: None})
with util.uncache(name), self.assertRaises(ImportError):
mock.load_module(name)
def test_get_filename_with_source_path(self):
# get_filename() should return what source_path() returns.
name = 'mod'
path = os.path.join('path', 'to', 'source')
mock = PyLoaderMock({name: path})
with util.uncache(name):
self.assertEqual(mock.get_filename(name), path)
def test_get_filename_no_source_path(self):
# get_filename() should raise ImportError if source_path returns None.
name = 'mod'
mock = PyLoaderMock({name: None})
with util.uncache(name), self.assertRaises(ImportError):
mock.get_filename(name)
class PyPycLoaderTests(PyLoaderTests):
"""Tests for importlib.abc.PyPycLoader."""
mocker = PyPycLoaderMock
@source_util.writes_bytecode_files
def verify_bytecode(self, mock, name):
assert name in mock.module_paths
self.assertIn(name, mock.module_bytecode)
magic = mock.module_bytecode[name][:4]
self.assertEqual(magic, imp.get_magic())
mtime = importlib._r_long(mock.module_bytecode[name][4:8])
self.assertEqual(mtime, 1)
source_size = mock.module_bytecode[name][8:12]
self.assertEqual(len(mock.source) & 0xFFFFFFFF,
importlib._r_long(source_size))
bc = mock.module_bytecode[name][12:]
self.assertEqual(bc, mock.compile_bc(name))
def test_module(self):
mock, name = super().test_module()
self.verify_bytecode(mock, name)
def test_package(self):
mock, name = super().test_package()
self.verify_bytecode(mock, name)
def test_lacking_parent(self):
mock, name = super().test_lacking_parent()
self.verify_bytecode(mock, name)
def test_module_reuse(self):
mock, name = super().test_module_reuse()
self.verify_bytecode(mock, name)
def test_state_after_failure(self):
super().test_state_after_failure()
def test_unloadable(self):
super().test_unloadable()
class PyPycLoaderInterfaceTests(unittest.TestCase):
"""Test for the interface of importlib.abc.PyPycLoader."""
def get_filename_check(self, src_path, bc_path, expect):
name = 'mod'
mock = PyPycLoaderMock({name: src_path}, {name: {'path': bc_path}})
with util.uncache(name):
assert mock.source_path(name) == src_path
assert mock.bytecode_path(name) == bc_path
self.assertEqual(mock.get_filename(name), expect)
def test_filename_with_source_bc(self):
# When source and bytecode paths present, return the source path.
self.get_filename_check('source_path', 'bc_path', 'source_path')
def test_filename_with_source_no_bc(self):
# With source but no bc, return source path.
self.get_filename_check('source_path', None, 'source_path')
def test_filename_with_no_source_bc(self):
# With not source but bc, return the bc path.
self.get_filename_check(None, 'bc_path', 'bc_path')
def test_filename_with_no_source_or_bc(self):
# With no source or bc, raise ImportError.
name = 'mod'
mock = PyPycLoaderMock({name: None}, {name: {'path': None}})
with util.uncache(name), self.assertRaises(ImportError):
mock.get_filename(name)
class SkipWritingBytecodeTests(unittest.TestCase):
"""Test that bytecode is properly handled based on
sys.dont_write_bytecode."""
@source_util.writes_bytecode_files
def run_test(self, dont_write_bytecode):
name = 'mod'
mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')})
sys.dont_write_bytecode = dont_write_bytecode
with util.uncache(name):
mock.load_module(name)
self.assertIsNot(name in mock.module_bytecode, dont_write_bytecode)
def test_no_bytecode_written(self):
self.run_test(True)
def test_bytecode_written(self):
self.run_test(False)
class RegeneratedBytecodeTests(unittest.TestCase):
"""Test that bytecode is regenerated as expected."""
@source_util.writes_bytecode_files
def test_different_magic(self):
# A different magic number should lead to new bytecode.
name = 'mod'
bad_magic = b'\x00\x00\x00\x00'
assert bad_magic != imp.get_magic()
mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')},
{name: {'path': os.path.join('path', 'to',
'mod.bytecode'),
'magic': bad_magic}})
with util.uncache(name):
mock.load_module(name)
self.assertIn(name, mock.module_bytecode)
magic = mock.module_bytecode[name][:4]
self.assertEqual(magic, imp.get_magic())
@source_util.writes_bytecode_files
def test_old_mtime(self):
# Bytecode with an older mtime should be regenerated.
name = 'mod'
old_mtime = PyPycLoaderMock.default_mtime - 1
mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')},
{name: {'path': 'path/to/mod.bytecode', 'mtime': old_mtime}})
with util.uncache(name):
mock.load_module(name)
self.assertIn(name, mock.module_bytecode)
mtime = importlib._r_long(mock.module_bytecode[name][4:8])
self.assertEqual(mtime, PyPycLoaderMock.default_mtime)
class BadBytecodeFailureTests(unittest.TestCase):
"""Test import failures when there is no source and parts of the bytecode
is bad."""
def test_bad_magic(self):
# A bad magic number should lead to an ImportError.
name = 'mod'
bad_magic = b'\x00\x00\x00\x00'
bc = {name:
{'path': os.path.join('path', 'to', 'mod'),
'magic': bad_magic}}
mock = PyPycLoaderMock({name: None}, bc)
with util.uncache(name), self.assertRaises(ImportError) as cm:
mock.load_module(name)
self.assertEqual(cm.exception.name, name)
def test_no_bytecode(self):
# Missing code object bytecode should lead to an EOFError.
name = 'mod'
bc = {name: {'path': os.path.join('path', 'to', 'mod'), 'bc': b''}}
mock = PyPycLoaderMock({name: None}, bc)
with util.uncache(name), self.assertRaises(EOFError):
mock.load_module(name)
def test_bad_bytecode(self):
# Malformed code object bytecode should lead to a ValueError.
name = 'mod'
bc = {name: {'path': os.path.join('path', 'to', 'mod'), 'bc': b'1234'}}
mock = PyPycLoaderMock({name: None}, bc)
with util.uncache(name), self.assertRaises(ValueError):
mock.load_module(name)
def raise_ImportError(*args, **kwargs): def raise_ImportError(*args, **kwargs):
raise ImportError raise ImportError
class MissingPathsTests(unittest.TestCase):
"""Test what happens when a source or bytecode path does not exist (either
from *_path returning None or raising ImportError)."""
def test_source_path_None(self):
# Bytecode should be used when source_path returns None, along with
# __file__ being set to the bytecode path.
name = 'mod'
bytecode_path = 'path/to/mod'
mock = PyPycLoaderMock({name: None}, {name: {'path': bytecode_path}})
with util.uncache(name):
module = mock.load_module(name)
self.assertEqual(module.__file__, bytecode_path)
# Testing for bytecode_path returning None handled by all tests where no
# bytecode initially exists.
def test_all_paths_None(self):
# If all *_path methods return None, raise ImportError.
name = 'mod'
mock = PyPycLoaderMock({name: None})
with util.uncache(name), self.assertRaises(ImportError) as cm:
mock.load_module(name)
self.assertEqual(cm.exception.name, name)
def test_source_path_ImportError(self):
# An ImportError from source_path should trigger an ImportError.
name = 'mod'
mock = PyPycLoaderMock({}, {name: {'path': os.path.join('path', 'to',
'mod')}})
with util.uncache(name), self.assertRaises(ImportError):
mock.load_module(name)
def test_bytecode_path_ImportError(self):
# An ImportError from bytecode_path should trigger an ImportError.
name = 'mod'
mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')})
bad_meth = types.MethodType(raise_ImportError, mock)
mock.bytecode_path = bad_meth
with util.uncache(name), self.assertRaises(ImportError) as cm:
mock.load_module(name)
class SourceLoaderTestHarness(unittest.TestCase): class SourceLoaderTestHarness(unittest.TestCase):
@ -801,6 +327,7 @@ class AbstractMethodImplTests(unittest.TestCase):
class Loader(abc.Loader): class Loader(abc.Loader):
def load_module(self, fullname): def load_module(self, fullname):
super().load_module(fullname) super().load_module(fullname)
def module_repr(self, module): def module_repr(self, module):
super().module_repr(module) super().module_repr(module)
@ -825,20 +352,6 @@ class AbstractMethodImplTests(unittest.TestCase):
class SourceLoader(ResourceLoader, ExecutionLoader, abc.SourceLoader): class SourceLoader(ResourceLoader, ExecutionLoader, abc.SourceLoader):
pass pass
class PyLoader(ResourceLoader, InspectLoader, abc.PyLoader):
def source_path(self, _):
super().source_path(_)
class PyPycLoader(PyLoader, abc.PyPycLoader):
def bytecode_path(self, _):
super().bytecode_path(_)
def source_mtime(self, _):
super().source_mtime(_)
def write_bytecode(self, _, _2):
super().write_bytecode(_, _2)
def raises_NotImplementedError(self, ins, *args): def raises_NotImplementedError(self, ins, *args):
for method_name in args: for method_name in args:
method = getattr(ins, method_name) method = getattr(ins, method_name)
@ -877,24 +390,12 @@ class AbstractMethodImplTests(unittest.TestCase):
# Required abstractmethods. # Required abstractmethods.
self.raises_NotImplementedError(ins, 'get_filename', 'get_data') self.raises_NotImplementedError(ins, 'get_filename', 'get_data')
# Optional abstractmethods. # Optional abstractmethods.
self.raises_NotImplementedError(ins,'path_stats', 'set_data') self.raises_NotImplementedError(ins, 'path_stats', 'set_data')
def test_PyLoader(self):
self.raises_NotImplementedError(self.PyLoader(), 'source_path',
'get_data', 'is_package')
def test_PyPycLoader(self):
self.raises_NotImplementedError(self.PyPycLoader(), 'source_path',
'source_mtime', 'bytecode_path',
'write_bytecode')
def test_main(): def test_main():
from test.support import run_unittest from test.support import run_unittest
run_unittest(PyLoaderTests, PyLoaderCompatTests, run_unittest(SkipWritingBytecodeTests, RegeneratedBytecodeTests,
PyLoaderInterfaceTests,
PyPycLoaderTests, PyPycLoaderInterfaceTests,
SkipWritingBytecodeTests, RegeneratedBytecodeTests,
BadBytecodeFailureTests, MissingPathsTests, BadBytecodeFailureTests, MissingPathsTests,
SourceOnlyLoaderTests, SourceOnlyLoaderTests,
SourceLoaderBytecodeTests, SourceLoaderBytecodeTests,

View File

@ -43,11 +43,6 @@ class PathEntryFinder(InheritanceTests, unittest.TestCase):
subclasses = [machinery.FileFinder] subclasses = [machinery.FileFinder]
class Loader(InheritanceTests, unittest.TestCase):
subclasses = [abc.PyLoader]
class ResourceLoader(InheritanceTests, unittest.TestCase): class ResourceLoader(InheritanceTests, unittest.TestCase):
superclasses = [abc.Loader] superclasses = [abc.Loader]
@ -56,14 +51,13 @@ class ResourceLoader(InheritanceTests, unittest.TestCase):
class InspectLoader(InheritanceTests, unittest.TestCase): class InspectLoader(InheritanceTests, unittest.TestCase):
superclasses = [abc.Loader] superclasses = [abc.Loader]
subclasses = [abc.PyLoader, machinery.BuiltinImporter, subclasses = [machinery.BuiltinImporter,
machinery.FrozenImporter, machinery.ExtensionFileLoader] machinery.FrozenImporter, machinery.ExtensionFileLoader]
class ExecutionLoader(InheritanceTests, unittest.TestCase): class ExecutionLoader(InheritanceTests, unittest.TestCase):
superclasses = [abc.InspectLoader] superclasses = [abc.InspectLoader]
subclasses = [abc.PyLoader]
class FileLoader(InheritanceTests, unittest.TestCase): class FileLoader(InheritanceTests, unittest.TestCase):
@ -78,16 +72,6 @@ class SourceLoader(InheritanceTests, unittest.TestCase):
subclasses = [machinery.SourceFileLoader] subclasses = [machinery.SourceFileLoader]
class PyLoader(InheritanceTests, unittest.TestCase):
superclasses = [abc.Loader, abc.ResourceLoader, abc.ExecutionLoader]
class PyPycLoader(InheritanceTests, unittest.TestCase):
superclasses = [abc.PyLoader]
def test_main(): def test_main():
from test.support import run_unittest from test.support import run_unittest
classes = [] classes = []

View File

@ -107,6 +107,9 @@ Core and Builtins
Library Library
------- -------
- Issue #15641: Clean up deprecated classes from importlib
Patch by Taras Lyapun.
- Issue #16350: zlib.Decompress.decompress() now accumulates data from - Issue #16350: zlib.Decompress.decompress() now accumulates data from
successive calls after EOF in unused_data, instead of only saving the argument successive calls after EOF in unused_data, instead of only saving the argument
to the last call. Patch by Serhiy Storchaka. to the last call. Patch by Serhiy Storchaka.