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.py164
1 files changed, 132 insertions, 32 deletions
diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py
index fa343f8..387567a 100644
--- a/Lib/importlib/abc.py
+++ b/Lib/importlib/abc.py
@@ -1,44 +1,109 @@
"""Abstract base classes related to import."""
from . import _bootstrap
from . import machinery
-from . import util
+try:
+ import _frozen_importlib
+except ImportError as exc:
+ if exc.name != '_frozen_importlib':
+ raise
+ _frozen_importlib = None
import abc
import imp
-import io
import marshal
-import os.path
import sys
import tokenize
-import types
import warnings
-class Loader(metaclass=abc.ABCMeta):
+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)
- """Abstract base class for import loaders."""
+
+class Finder(metaclass=abc.ABCMeta):
+
+ """Legacy abstract base class for import finders.
+
+ It may be subclassed for compatibility with legacy third party
+ reimplementations of the import system. Otherwise, finder
+ implementations should derive from the more specific MetaPathFinder
+ or PathEntryFinder ABCs.
+ """
@abc.abstractmethod
- def load_module(self, fullname):
- """Abstract method which when implemented should load a module.
- The fullname is a str."""
+ def find_module(self, fullname, path=None):
+ """An abstract method that should find a module.
+ The fullname is a str and the optional path is a str or None.
+ Returns a Loader object.
+ """
raise NotImplementedError
-class Finder(metaclass=abc.ABCMeta):
+class MetaPathFinder(Finder):
- """Abstract base class for import finders."""
+ """Abstract base class for import finders on sys.meta_path."""
@abc.abstractmethod
- 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.
+ def find_module(self, fullname, path):
+ """Abstract method which, when implemented, should find a module.
+ The fullname is a str and the path is a str or None.
Returns a Loader object.
"""
raise NotImplementedError
-Finder.register(machinery.BuiltinImporter)
-Finder.register(machinery.FrozenImporter)
-Finder.register(machinery.PathFinder)
+ def invalidate_caches(self):
+ """An optional method for clearing the finder's cache, if any.
+ This method is used by importlib.invalidate_caches().
+ """
+ return NotImplemented
+
+_register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter,
+ machinery.PathFinder, machinery.WindowsRegistryFinder)
+
+
+class PathEntryFinder(Finder):
+
+ """Abstract base class for path entry finders used by PathFinder."""
+
+ @abc.abstractmethod
+ def find_loader(self, fullname):
+ """Abstract method which, when implemented, returns a module loader.
+ The fullname is a str. Returns a 2-tuple of (Loader, portion) where
+ portion is a sequence of file system locations contributing to part of
+ a namespace package. The sequence may be empty and the loader may be
+ None.
+ """
+ raise NotImplementedError
+
+ find_module = _bootstrap._find_module_shim
+
+ def invalidate_caches(self):
+ """An optional method for clearing the finder's cache, if any.
+ This method is used by PathFinder.invalidate_caches().
+ """
+ return NotImplemented
+
+_register(PathEntryFinder, machinery.FileFinder)
+
+
+class Loader(metaclass=abc.ABCMeta):
+
+ """Abstract base class for import loaders."""
+
+ @abc.abstractmethod
+ def load_module(self, fullname):
+ """Abstract method which when implemented should load a module.
+ The fullname is a str."""
+ raise NotImplementedError
+
+ @abc.abstractmethod
+ def module_repr(self, module):
+ """Abstract method which when implemented calculates and returns the
+ given module's repr."""
+ raise NotImplementedError
class ResourceLoader(Loader):
@@ -84,8 +149,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):
@@ -104,6 +169,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
@@ -123,7 +197,20 @@ class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
def path_mtime(self, path):
"""Return the (int) modification time for the path (str)."""
- raise NotImplementedError
+ if self.path_stats.__func__ is SourceLoader.path_stats:
+ raise NotImplementedError
+ return int(self.path_stats(path)['mtime'])
+
+ def path_stats(self, path):
+ """Return a metadata dict for the source pointed to by the path (str).
+ Possible keys:
+ - 'mtime' (mandatory) is the numeric timestamp of last source
+ code modification;
+ - 'size' (optional) is the size in bytes of the source code.
+ """
+ if self.path_mtime.__func__ is SourceLoader.path_mtime:
+ raise NotImplementedError
+ return {'mtime': self.path_mtime(path)}
def set_data(self, path, data):
"""Write the bytes to the path (if possible).
@@ -137,6 +224,7 @@ class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
"""
raise NotImplementedError
+_register(SourceLoader, machinery.SourceFileLoader)
class PyLoader(SourceLoader):
@@ -195,10 +283,10 @@ class PyLoader(SourceLoader):
"use SourceLoader instead. "
"See the importlib documentation on how to be "
"compatible with Python 3.1 onwards.",
- PendingDeprecationWarning)
+ DeprecationWarning)
path = self.source_path(fullname)
if path is None:
- raise ImportError
+ raise ImportError(name=fullname)
else:
return path
@@ -226,7 +314,7 @@ class PyPycLoader(PyLoader):
if path is not None:
return path
raise ImportError("no source or bytecode path available for "
- "{0!r}".format(fullname))
+ "{0!r}".format(fullname), name=fullname)
def get_code(self, fullname):
"""Get a code object from source or bytecode."""
@@ -234,7 +322,7 @@ class PyPycLoader(PyLoader):
"removal in Python 3.4; use SourceLoader instead. "
"If Python 3.1 compatibility is required, see the "
"latest documentation for PyLoader.",
- PendingDeprecationWarning)
+ DeprecationWarning)
source_timestamp = self.source_mtime(fullname)
# Try to use bytecode if it is available.
bytecode_path = self.bytecode_path(fullname)
@@ -243,20 +331,30 @@ class PyPycLoader(PyLoader):
try:
magic = data[:4]
if len(magic) < 4:
- raise ImportError("bad magic number in {}".format(fullname))
+ 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 = marshal._r_long(raw_timestamp)
- bytecode = data[8:]
+ 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))
+ 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")
+ 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:
@@ -268,18 +366,20 @@ class PyPycLoader(PyLoader):
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))
+ "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)
+ 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(marshal._w_long(source_timestamp))
+ 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