jpayne@68: """Abstract base classes related to import.""" jpayne@68: from . import _bootstrap jpayne@68: from . import _bootstrap_external jpayne@68: from . import machinery jpayne@68: try: jpayne@68: import _frozen_importlib jpayne@68: except ImportError as exc: jpayne@68: if exc.name != '_frozen_importlib': jpayne@68: raise jpayne@68: _frozen_importlib = None jpayne@68: try: jpayne@68: import _frozen_importlib_external jpayne@68: except ImportError as exc: jpayne@68: _frozen_importlib_external = _bootstrap_external jpayne@68: import abc jpayne@68: import warnings jpayne@68: jpayne@68: jpayne@68: def _register(abstract_cls, *classes): jpayne@68: for cls in classes: jpayne@68: abstract_cls.register(cls) jpayne@68: if _frozen_importlib is not None: jpayne@68: try: jpayne@68: frozen_cls = getattr(_frozen_importlib, cls.__name__) jpayne@68: except AttributeError: jpayne@68: frozen_cls = getattr(_frozen_importlib_external, cls.__name__) jpayne@68: abstract_cls.register(frozen_cls) jpayne@68: jpayne@68: jpayne@68: class Finder(metaclass=abc.ABCMeta): jpayne@68: jpayne@68: """Legacy abstract base class for import finders. jpayne@68: jpayne@68: It may be subclassed for compatibility with legacy third party jpayne@68: reimplementations of the import system. Otherwise, finder jpayne@68: implementations should derive from the more specific MetaPathFinder jpayne@68: or PathEntryFinder ABCs. jpayne@68: jpayne@68: Deprecated since Python 3.3 jpayne@68: """ jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def find_module(self, fullname, path=None): jpayne@68: """An abstract method that should find a module. jpayne@68: The fullname is a str and the optional path is a str or None. jpayne@68: Returns a Loader object or None. jpayne@68: """ jpayne@68: jpayne@68: jpayne@68: class MetaPathFinder(Finder): jpayne@68: jpayne@68: """Abstract base class for import finders on sys.meta_path.""" jpayne@68: jpayne@68: # We don't define find_spec() here since that would break jpayne@68: # hasattr checks we do to support backward compatibility. jpayne@68: jpayne@68: def find_module(self, fullname, path): jpayne@68: """Return a loader for the module. jpayne@68: jpayne@68: If no module is found, return None. The fullname is a str and jpayne@68: the path is a list of strings or None. jpayne@68: jpayne@68: This method is deprecated since Python 3.4 in favor of jpayne@68: finder.find_spec(). If find_spec() exists then backwards-compatible jpayne@68: functionality is provided for this method. jpayne@68: jpayne@68: """ jpayne@68: warnings.warn("MetaPathFinder.find_module() is deprecated since Python " jpayne@68: "3.4 in favor of MetaPathFinder.find_spec() " jpayne@68: "(available since 3.4)", jpayne@68: DeprecationWarning, jpayne@68: stacklevel=2) jpayne@68: if not hasattr(self, 'find_spec'): jpayne@68: return None jpayne@68: found = self.find_spec(fullname, path) jpayne@68: return found.loader if found is not None else None jpayne@68: jpayne@68: def invalidate_caches(self): jpayne@68: """An optional method for clearing the finder's cache, if any. jpayne@68: This method is used by importlib.invalidate_caches(). jpayne@68: """ jpayne@68: jpayne@68: _register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter, jpayne@68: machinery.PathFinder, machinery.WindowsRegistryFinder) jpayne@68: jpayne@68: jpayne@68: class PathEntryFinder(Finder): jpayne@68: jpayne@68: """Abstract base class for path entry finders used by PathFinder.""" jpayne@68: jpayne@68: # We don't define find_spec() here since that would break jpayne@68: # hasattr checks we do to support backward compatibility. jpayne@68: jpayne@68: def find_loader(self, fullname): jpayne@68: """Return (loader, namespace portion) for the path entry. jpayne@68: jpayne@68: The fullname is a str. The namespace portion is a sequence of jpayne@68: path entries contributing to part of a namespace package. The jpayne@68: sequence may be empty. If loader is not None, the portion will jpayne@68: be ignored. jpayne@68: jpayne@68: The portion will be discarded if another path entry finder jpayne@68: locates the module as a normal module or package. jpayne@68: jpayne@68: This method is deprecated since Python 3.4 in favor of jpayne@68: finder.find_spec(). If find_spec() is provided than backwards-compatible jpayne@68: functionality is provided. jpayne@68: """ jpayne@68: warnings.warn("PathEntryFinder.find_loader() is deprecated since Python " jpayne@68: "3.4 in favor of PathEntryFinder.find_spec() " jpayne@68: "(available since 3.4)", jpayne@68: DeprecationWarning, jpayne@68: stacklevel=2) jpayne@68: if not hasattr(self, 'find_spec'): jpayne@68: return None, [] jpayne@68: found = self.find_spec(fullname) jpayne@68: if found is not None: jpayne@68: if not found.submodule_search_locations: jpayne@68: portions = [] jpayne@68: else: jpayne@68: portions = found.submodule_search_locations jpayne@68: return found.loader, portions jpayne@68: else: jpayne@68: return None, [] jpayne@68: jpayne@68: find_module = _bootstrap_external._find_module_shim jpayne@68: jpayne@68: def invalidate_caches(self): jpayne@68: """An optional method for clearing the finder's cache, if any. jpayne@68: This method is used by PathFinder.invalidate_caches(). jpayne@68: """ jpayne@68: jpayne@68: _register(PathEntryFinder, machinery.FileFinder) jpayne@68: jpayne@68: jpayne@68: class Loader(metaclass=abc.ABCMeta): jpayne@68: jpayne@68: """Abstract base class for import loaders.""" jpayne@68: jpayne@68: def create_module(self, spec): jpayne@68: """Return a module to initialize and into which to load. jpayne@68: jpayne@68: This method should raise ImportError if anything prevents it jpayne@68: from creating a new module. It may return None to indicate jpayne@68: that the spec should create the new module. jpayne@68: """ jpayne@68: # By default, defer to default semantics for the new module. jpayne@68: return None jpayne@68: jpayne@68: # We don't define exec_module() here since that would break jpayne@68: # hasattr checks we do to support backward compatibility. jpayne@68: jpayne@68: def load_module(self, fullname): jpayne@68: """Return the loaded module. jpayne@68: jpayne@68: The module must be added to sys.modules and have import-related jpayne@68: attributes set properly. The fullname is a str. jpayne@68: jpayne@68: ImportError is raised on failure. jpayne@68: jpayne@68: This method is deprecated in favor of loader.exec_module(). If jpayne@68: exec_module() exists then it is used to provide a backwards-compatible jpayne@68: functionality for this method. jpayne@68: jpayne@68: """ jpayne@68: if not hasattr(self, 'exec_module'): jpayne@68: raise ImportError jpayne@68: return _bootstrap._load_module_shim(self, fullname) jpayne@68: jpayne@68: def module_repr(self, module): jpayne@68: """Return a module's repr. jpayne@68: jpayne@68: Used by the module type when the method does not raise jpayne@68: NotImplementedError. jpayne@68: jpayne@68: This method is deprecated. jpayne@68: jpayne@68: """ jpayne@68: # The exception will cause ModuleType.__repr__ to ignore this method. jpayne@68: raise NotImplementedError jpayne@68: jpayne@68: jpayne@68: class ResourceLoader(Loader): jpayne@68: jpayne@68: """Abstract base class for loaders which can return data from their jpayne@68: back-end storage. jpayne@68: jpayne@68: This ABC represents one of the optional protocols specified by PEP 302. jpayne@68: jpayne@68: """ jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def get_data(self, path): jpayne@68: """Abstract method which when implemented should return the bytes for jpayne@68: the specified path. The path must be a str.""" jpayne@68: raise OSError jpayne@68: jpayne@68: jpayne@68: class InspectLoader(Loader): jpayne@68: jpayne@68: """Abstract base class for loaders which support inspection about the jpayne@68: modules they can load. jpayne@68: jpayne@68: This ABC represents one of the optional protocols specified by PEP 302. jpayne@68: jpayne@68: """ jpayne@68: jpayne@68: def is_package(self, fullname): jpayne@68: """Optional method which when implemented should return whether the jpayne@68: module is a package. The fullname is a str. Returns a bool. jpayne@68: jpayne@68: Raises ImportError if the module cannot be found. jpayne@68: """ jpayne@68: raise ImportError jpayne@68: jpayne@68: def get_code(self, fullname): jpayne@68: """Method which returns the code object for the module. jpayne@68: jpayne@68: The fullname is a str. Returns a types.CodeType if possible, else jpayne@68: returns None if a code object does not make sense jpayne@68: (e.g. built-in module). Raises ImportError if the module cannot be jpayne@68: found. jpayne@68: """ jpayne@68: source = self.get_source(fullname) jpayne@68: if source is None: jpayne@68: return None jpayne@68: return self.source_to_code(source) jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def get_source(self, fullname): jpayne@68: """Abstract method which should return the source code for the jpayne@68: module. The fullname is a str. Returns a str. jpayne@68: jpayne@68: Raises ImportError if the module cannot be found. jpayne@68: """ jpayne@68: raise ImportError jpayne@68: jpayne@68: @staticmethod jpayne@68: def source_to_code(data, path=''): jpayne@68: """Compile 'data' into a code object. jpayne@68: jpayne@68: The 'data' argument can be anything that compile() can handle. The'path' jpayne@68: argument should be where the data was retrieved (when applicable).""" jpayne@68: return compile(data, path, 'exec', dont_inherit=True) jpayne@68: jpayne@68: exec_module = _bootstrap_external._LoaderBasics.exec_module jpayne@68: load_module = _bootstrap_external._LoaderBasics.load_module jpayne@68: jpayne@68: _register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter) jpayne@68: jpayne@68: jpayne@68: class ExecutionLoader(InspectLoader): jpayne@68: jpayne@68: """Abstract base class for loaders that wish to support the execution of jpayne@68: modules as scripts. jpayne@68: jpayne@68: This ABC represents one of the optional protocols specified in PEP 302. jpayne@68: jpayne@68: """ jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def get_filename(self, fullname): jpayne@68: """Abstract method which should return the value that __file__ is to be jpayne@68: set to. jpayne@68: jpayne@68: Raises ImportError if the module cannot be found. jpayne@68: """ jpayne@68: raise ImportError jpayne@68: jpayne@68: def get_code(self, fullname): jpayne@68: """Method to return the code object for fullname. jpayne@68: jpayne@68: Should return None if not applicable (e.g. built-in module). jpayne@68: Raise ImportError if the module cannot be found. jpayne@68: """ jpayne@68: source = self.get_source(fullname) jpayne@68: if source is None: jpayne@68: return None jpayne@68: try: jpayne@68: path = self.get_filename(fullname) jpayne@68: except ImportError: jpayne@68: return self.source_to_code(source) jpayne@68: else: jpayne@68: return self.source_to_code(source, path) jpayne@68: jpayne@68: _register(ExecutionLoader, machinery.ExtensionFileLoader) jpayne@68: jpayne@68: jpayne@68: class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader): jpayne@68: jpayne@68: """Abstract base class partially implementing the ResourceLoader and jpayne@68: ExecutionLoader ABCs.""" jpayne@68: jpayne@68: _register(FileLoader, machinery.SourceFileLoader, jpayne@68: machinery.SourcelessFileLoader) jpayne@68: jpayne@68: jpayne@68: class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader): jpayne@68: jpayne@68: """Abstract base class for loading source code (and optionally any jpayne@68: corresponding bytecode). jpayne@68: jpayne@68: To support loading from source code, the abstractmethods inherited from jpayne@68: ResourceLoader and ExecutionLoader need to be implemented. To also support jpayne@68: loading from bytecode, the optional methods specified directly by this ABC jpayne@68: is required. jpayne@68: jpayne@68: Inherited abstractmethods not implemented in this ABC: jpayne@68: jpayne@68: * ResourceLoader.get_data jpayne@68: * ExecutionLoader.get_filename jpayne@68: jpayne@68: """ jpayne@68: jpayne@68: def path_mtime(self, path): jpayne@68: """Return the (int) modification time for the path (str).""" jpayne@68: if self.path_stats.__func__ is SourceLoader.path_stats: jpayne@68: raise OSError jpayne@68: return int(self.path_stats(path)['mtime']) jpayne@68: jpayne@68: def path_stats(self, path): jpayne@68: """Return a metadata dict for the source pointed to by the path (str). jpayne@68: Possible keys: jpayne@68: - 'mtime' (mandatory) is the numeric timestamp of last source jpayne@68: code modification; jpayne@68: - 'size' (optional) is the size in bytes of the source code. jpayne@68: """ jpayne@68: if self.path_mtime.__func__ is SourceLoader.path_mtime: jpayne@68: raise OSError jpayne@68: return {'mtime': self.path_mtime(path)} jpayne@68: jpayne@68: def set_data(self, path, data): jpayne@68: """Write the bytes to the path (if possible). jpayne@68: jpayne@68: Accepts a str path and data as bytes. jpayne@68: jpayne@68: Any needed intermediary directories are to be created. If for some jpayne@68: reason the file cannot be written because of permissions, fail jpayne@68: silently. jpayne@68: """ jpayne@68: jpayne@68: _register(SourceLoader, machinery.SourceFileLoader) jpayne@68: jpayne@68: jpayne@68: class ResourceReader(metaclass=abc.ABCMeta): jpayne@68: jpayne@68: """Abstract base class to provide resource-reading support. jpayne@68: jpayne@68: Loaders that support resource reading are expected to implement jpayne@68: the ``get_resource_reader(fullname)`` method and have it either return None jpayne@68: or an object compatible with this ABC. jpayne@68: """ jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def open_resource(self, resource): jpayne@68: """Return an opened, file-like object for binary reading. jpayne@68: jpayne@68: The 'resource' argument is expected to represent only a file name jpayne@68: and thus not contain any subdirectory components. jpayne@68: jpayne@68: If the resource cannot be found, FileNotFoundError is raised. jpayne@68: """ jpayne@68: raise FileNotFoundError jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def resource_path(self, resource): jpayne@68: """Return the file system path to the specified resource. jpayne@68: jpayne@68: The 'resource' argument is expected to represent only a file name jpayne@68: and thus not contain any subdirectory components. jpayne@68: jpayne@68: If the resource does not exist on the file system, raise jpayne@68: FileNotFoundError. jpayne@68: """ jpayne@68: raise FileNotFoundError jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def is_resource(self, name): jpayne@68: """Return True if the named 'name' is consider a resource.""" jpayne@68: raise FileNotFoundError jpayne@68: jpayne@68: @abc.abstractmethod jpayne@68: def contents(self): jpayne@68: """Return an iterable of strings over the contents of the package.""" jpayne@68: return [] jpayne@68: jpayne@68: jpayne@68: _register(ResourceReader, machinery.SourceFileLoader)