summaryrefslogtreecommitdiffstats
path: root/Lib/importlib/abc.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/importlib/abc.py')
-rw-r--r--Lib/importlib/abc.py246
1 files changed, 214 insertions, 32 deletions
diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py
index 7b89d0b..fa343f8 100644
--- a/Lib/importlib/abc.py
+++ b/Lib/importlib/abc.py
@@ -1,8 +1,16 @@
"""Abstract base classes related to import."""
from . import _bootstrap
from . import machinery
+from . import util
import abc
+import imp
+import io
+import marshal
+import os.path
+import sys
+import tokenize
import types
+import warnings
class Loader(metaclass=abc.ABCMeta):
@@ -10,8 +18,9 @@ class Loader(metaclass=abc.ABCMeta):
"""Abstract base class for import loaders."""
@abc.abstractmethod
- def load_module(self, fullname:str) -> types.ModuleType:
- """Abstract method which when implemented should load a module."""
+ def load_module(self, fullname):
+ """Abstract method which when implemented should load a module.
+ The fullname is a str."""
raise NotImplementedError
@@ -20,8 +29,11 @@ class Finder(metaclass=abc.ABCMeta):
"""Abstract base class for import finders."""
@abc.abstractmethod
- def find_module(self, fullname:str, path:[str]=None) -> Loader:
- """Abstract method which when implemented should find a module."""
+ def find_module(self, fullname, path=None):
+ """Abstract method which when implemented should find a module.
+ The fullname is a str and the optional path is a str or None.
+ Returns a Loader object.
+ """
raise NotImplementedError
Finder.register(machinery.BuiltinImporter)
@@ -39,9 +51,9 @@ class ResourceLoader(Loader):
"""
@abc.abstractmethod
- def get_data(self, path:str) -> bytes:
+ def get_data(self, path):
"""Abstract method which when implemented should return the bytes for
- the specified path."""
+ the specified path. The path must be a str."""
raise NotImplementedError
@@ -55,68 +67,238 @@ class InspectLoader(Loader):
"""
@abc.abstractmethod
- def is_package(self, fullname:str) -> bool:
+ def is_package(self, fullname):
"""Abstract method which when implemented should return whether the
- module is a package."""
- return NotImplementedError
+ module is a package. The fullname is a str. Returns a bool."""
+ raise NotImplementedError
@abc.abstractmethod
- def get_code(self, fullname:str) -> types.CodeType:
+ def get_code(self, fullname):
"""Abstract method which when implemented should return the code object
- for the module"""
- return NotImplementedError
+ for the module. The fullname is a str. Returns a types.CodeType."""
+ raise NotImplementedError
@abc.abstractmethod
- def get_source(self, fullname:str) -> str:
+ def get_source(self, fullname):
"""Abstract method which should return the source code for the
- module."""
- return NotImplementedError
+ module. The fullname is a str. Returns a str."""
+ raise NotImplementedError
InspectLoader.register(machinery.BuiltinImporter)
InspectLoader.register(machinery.FrozenImporter)
-class PyLoader(_bootstrap.PyLoader, ResourceLoader, InspectLoader):
+class ExecutionLoader(InspectLoader):
- """Abstract base class to assist in loading source code by requiring only
- back-end storage methods to be implemented.
+ """Abstract base class for loaders that wish to support the execution of
+ modules as scripts.
- The methods get_code, get_source, and load_module are implemented for the
- user.
+ This ABC represents one of the optional protocols specified in PEP 302.
+
+ """
+
+ @abc.abstractmethod
+ def get_filename(self, fullname):
+ """Abstract method which should return the value that __file__ is to be
+ set to."""
+ raise NotImplementedError
+
+
+class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
+
+ """Abstract base class for loading source code (and optionally any
+ corresponding bytecode).
+
+ To support loading from source code, the abstractmethods inherited from
+ ResourceLoader and ExecutionLoader need to be implemented. To also support
+ loading from bytecode, the optional methods specified directly by this ABC
+ is required.
+
+ Inherited abstractmethods not implemented in this ABC:
+
+ * ResourceLoader.get_data
+ * ExecutionLoader.get_filename
+
+ """
+
+ def path_mtime(self, path):
+ """Return the (int) modification time for the path (str)."""
+ raise NotImplementedError
+
+ def set_data(self, path, data):
+ """Write the bytes to the path (if possible).
+
+ Accepts a str path and data as bytes.
+
+ Any needed intermediary directories are to be created. If for some
+ reason the file cannot be written because of permissions, fail
+ silently.
+
+ """
+ raise NotImplementedError
+
+
+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 source_path(self, fullname:str) -> object:
- """Abstract method which when implemented should return the path to the
- sourced code for the module."""
+ 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.
-class PyPycLoader(_bootstrap.PyPycLoader, PyLoader):
+ 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.",
+ PendingDeprecationWarning)
+ path = self.source_path(fullname)
+ if path is None:
+ raise ImportError
+ 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))
+
+ 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.",
+ PendingDeprecationWarning)
+ 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))
+ raw_timestamp = data[4:8]
+ if len(raw_timestamp) < 4:
+ raise EOFError("bad timestamp in {}".format(fullname))
+ pyc_timestamp = marshal._r_long(raw_timestamp)
+ bytecode = data[8:]
+ # Verify that the magic number is valid.
+ if imp.get_magic() != magic:
+ raise ImportError("bad magic number in {}".format(fullname))
+ # 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")
+ 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))
+ # 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)
+ 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(marshal._w_long(source_timestamp))
+ data.extend(marshal.dumps(code_object))
+ self.write_bytecode(fullname, data)
+ return code_object
+
@abc.abstractmethod
- def source_mtime(self, fullname:str) -> int:
- """Abstract method which when implemented should return the
+ 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:str) -> object:
- """Abstract method which when implemented should return the path to the
- bytecode for the module."""
+ 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:str, bytecode:bytes):
- """Abstract method which when implemented should attempt to write the
- bytecode for the module."""
+ 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