mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 11:49:12 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			304 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""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):
 | 
						|
 | 
						|
    """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
 | 
						|
 | 
						|
 | 
						|
class Finder(metaclass=abc.ABCMeta):
 | 
						|
 | 
						|
    """Abstract base class for import finders."""
 | 
						|
 | 
						|
    @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.
 | 
						|
        Returns a Loader object.
 | 
						|
        """
 | 
						|
        raise NotImplementedError
 | 
						|
 | 
						|
Finder.register(machinery.BuiltinImporter)
 | 
						|
Finder.register(machinery.FrozenImporter)
 | 
						|
Finder.register(machinery.PathFinder)
 | 
						|
 | 
						|
 | 
						|
class ResourceLoader(Loader):
 | 
						|
 | 
						|
    """Abstract base class for loaders which can return data from their
 | 
						|
    back-end storage.
 | 
						|
 | 
						|
    This ABC represents one of the optional protocols specified by PEP 302.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    @abc.abstractmethod
 | 
						|
    def get_data(self, path):
 | 
						|
        """Abstract method which when implemented should return the bytes for
 | 
						|
        the specified path.  The path must be a str."""
 | 
						|
        raise NotImplementedError
 | 
						|
 | 
						|
 | 
						|
class InspectLoader(Loader):
 | 
						|
 | 
						|
    """Abstract base class for loaders which support inspection about the
 | 
						|
    modules they can load.
 | 
						|
 | 
						|
    This ABC represents one of the optional protocols specified by PEP 302.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    @abc.abstractmethod
 | 
						|
    def is_package(self, fullname):
 | 
						|
        """Abstract method which when implemented should return whether the
 | 
						|
        module is a package.  The fullname is a str.  Returns a bool."""
 | 
						|
        raise NotImplementedError
 | 
						|
 | 
						|
    @abc.abstractmethod
 | 
						|
    def get_code(self, fullname):
 | 
						|
        """Abstract method which when implemented should return the code object
 | 
						|
        for the module.  The fullname is a str.  Returns a types.CodeType."""
 | 
						|
        raise NotImplementedError
 | 
						|
 | 
						|
    @abc.abstractmethod
 | 
						|
    def get_source(self, fullname):
 | 
						|
        """Abstract method which should return the source code for the
 | 
						|
        module.  The fullname is a str.  Returns a str."""
 | 
						|
        raise NotImplementedError
 | 
						|
 | 
						|
InspectLoader.register(machinery.BuiltinImporter)
 | 
						|
InspectLoader.register(machinery.FrozenImporter)
 | 
						|
 | 
						|
 | 
						|
class ExecutionLoader(InspectLoader):
 | 
						|
 | 
						|
    """Abstract base class for loaders that wish to support the execution of
 | 
						|
    modules as scripts.
 | 
						|
 | 
						|
    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 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.",
 | 
						|
                        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):
 | 
						|
        """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
 |