Kaydet (Commit) 90a654b1 authored tarafından Andrew Svetlov's avatar Andrew Svetlov

Issue #15641: Clean up deprecated classes from importlib

Patch by Taras Lyapun.
üst bcbf4036
......@@ -133,8 +133,6 @@ ABC hierarchy::
+-- ExecutionLoader --+
+-- FileLoader
+-- SourceLoader
+-- PyLoader (deprecated)
+-- PyPycLoader (deprecated)
.. class:: Finder
......@@ -431,142 +429,6 @@ ABC hierarchy::
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
------------------------------------------------------
......
......@@ -225,180 +225,3 @@ class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
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
......@@ -43,11 +43,6 @@ class PathEntryFinder(InheritanceTests, unittest.TestCase):
subclasses = [machinery.FileFinder]
class Loader(InheritanceTests, unittest.TestCase):
subclasses = [abc.PyLoader]
class ResourceLoader(InheritanceTests, unittest.TestCase):
superclasses = [abc.Loader]
......@@ -56,14 +51,13 @@ class ResourceLoader(InheritanceTests, unittest.TestCase):
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):
......@@ -78,16 +72,6 @@ class SourceLoader(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 = []
......
......@@ -107,6 +107,9 @@ Core and Builtins
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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment