diff options
Diffstat (limited to 'Lib/importlib/abc.py')
| -rw-r--r-- | Lib/importlib/abc.py | 164 | 
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 | 
