Commits

Andrew Svetlov committed 48228fb

Issue #15641: Clean up deprecated classes from importlib

Patch by Taras Lyapun.

Comments (0)

Files changed (5)

Doc/library/importlib.rst

                +-- ExecutionLoader --+
                                      +-- FileLoader
                                      +-- SourceLoader
-                                          +-- PyLoader (deprecated)
-                                          +-- PyPycLoader (deprecated)
 
 
 .. class:: Finder
         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
 ------------------------------------------------------
 

Lib/importlib/abc.py

         raise NotImplementedError
 
 _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

Lib/test/test_importlib/source/test_abc_loader.py

         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):
     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 Loader(abc.Loader):
         def load_module(self, fullname):
             super().load_module(fullname)
+
         def module_repr(self, module):
             super().module_repr(module)
 
     class SourceLoader(ResourceLoader, ExecutionLoader, abc.SourceLoader):
         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):
         for method_name in args:
             method = getattr(ins, method_name)
         # Required abstractmethods.
         self.raises_NotImplementedError(ins, 'get_filename', 'get_data')
         # Optional abstractmethods.
-        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')
+        self.raises_NotImplementedError(ins, 'path_stats', 'set_data')
 
 
 def test_main():
     from test.support import run_unittest
-    run_unittest(PyLoaderTests, PyLoaderCompatTests,
-                    PyLoaderInterfaceTests,
-                    PyPycLoaderTests, PyPycLoaderInterfaceTests,
-                    SkipWritingBytecodeTests, RegeneratedBytecodeTests,
+    run_unittest(SkipWritingBytecodeTests, RegeneratedBytecodeTests,
                     BadBytecodeFailureTests, MissingPathsTests,
                     SourceOnlyLoaderTests,
                     SourceLoaderBytecodeTests,

Lib/test/test_importlib/test_abc.py

     subclasses = [machinery.FileFinder]
 
 
-class Loader(InheritanceTests, unittest.TestCase):
-
-    subclasses = [abc.PyLoader]
-
-
 class ResourceLoader(InheritanceTests, unittest.TestCase):
 
     superclasses = [abc.Loader]
 class InspectLoader(InheritanceTests, unittest.TestCase):
 
     superclasses = [abc.Loader]
-    subclasses = [abc.PyLoader, machinery.BuiltinImporter,
+    subclasses = [machinery.BuiltinImporter,
                     machinery.FrozenImporter, machinery.ExtensionFileLoader]
 
 
 class ExecutionLoader(InheritanceTests, unittest.TestCase):
 
     superclasses = [abc.InspectLoader]
-    subclasses = [abc.PyLoader]
 
 
 class FileLoader(InheritanceTests, unittest.TestCase):
     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():
     from test.support import run_unittest
     classes = []
 Library
 -------
 
+- Issue #15641: Clean up deprecated classes from importlib
+  Patch by Taras Lyapun.
+
 - Issue #16350: zlib.Decompress.decompress() now accumulates data from
   successive calls after EOF in unused_data, instead of only saving the argument
   to the last call. Patch by Serhiy Storchaka.