1"""Abstract base classes related to import.""" 2from . import _bootstrap_external 3from . import machinery 4try: 5 import _frozen_importlib 6except ImportError as exc: 7 if exc.name != '_frozen_importlib': 8 raise 9 _frozen_importlib = None 10try: 11 import _frozen_importlib_external 12except ImportError: 13 _frozen_importlib_external = _bootstrap_external 14from ._abc import Loader 15import abc 16import warnings 17 18# for compatibility with Python 3.10 19from .resources.abc import ResourceReader, Traversable, TraversableResources 20 21 22__all__ = [ 23 'Loader', 'Finder', 'MetaPathFinder', 'PathEntryFinder', 24 'ResourceLoader', 'InspectLoader', 'ExecutionLoader', 25 'FileLoader', 'SourceLoader', 26 27 # for compatibility with Python 3.10 28 'ResourceReader', 'Traversable', 'TraversableResources', 29] 30 31 32def _register(abstract_cls, *classes): 33 for cls in classes: 34 abstract_cls.register(cls) 35 if _frozen_importlib is not None: 36 try: 37 frozen_cls = getattr(_frozen_importlib, cls.__name__) 38 except AttributeError: 39 frozen_cls = getattr(_frozen_importlib_external, cls.__name__) 40 abstract_cls.register(frozen_cls) 41 42 43class Finder(metaclass=abc.ABCMeta): 44 45 """Legacy abstract base class for import finders. 46 47 It may be subclassed for compatibility with legacy third party 48 reimplementations of the import system. Otherwise, finder 49 implementations should derive from the more specific MetaPathFinder 50 or PathEntryFinder ABCs. 51 52 Deprecated since Python 3.3 53 """ 54 55 def __init__(self): 56 warnings.warn("the Finder ABC is deprecated and " 57 "slated for removal in Python 3.12; use MetaPathFinder " 58 "or PathEntryFinder instead", 59 DeprecationWarning) 60 61 @abc.abstractmethod 62 def find_module(self, fullname, path=None): 63 """An abstract method that should find a module. 64 The fullname is a str and the optional path is a str or None. 65 Returns a Loader object or None. 66 """ 67 warnings.warn("importlib.abc.Finder along with its find_module() " 68 "method are deprecated and " 69 "slated for removal in Python 3.12; use " 70 "MetaPathFinder.find_spec() or " 71 "PathEntryFinder.find_spec() instead", 72 DeprecationWarning) 73 74 75class MetaPathFinder(metaclass=abc.ABCMeta): 76 77 """Abstract base class for import finders on sys.meta_path.""" 78 79 # We don't define find_spec() here since that would break 80 # hasattr checks we do to support backward compatibility. 81 82 def find_module(self, fullname, path): 83 """Return a loader for the module. 84 85 If no module is found, return None. The fullname is a str and 86 the path is a list of strings or None. 87 88 This method is deprecated since Python 3.4 in favor of 89 finder.find_spec(). If find_spec() exists then backwards-compatible 90 functionality is provided for this method. 91 92 """ 93 warnings.warn("MetaPathFinder.find_module() is deprecated since Python " 94 "3.4 in favor of MetaPathFinder.find_spec() and is " 95 "slated for removal in Python 3.12", 96 DeprecationWarning, 97 stacklevel=2) 98 if not hasattr(self, 'find_spec'): 99 return None 100 found = self.find_spec(fullname, path) 101 return found.loader if found is not None else None 102 103 def invalidate_caches(self): 104 """An optional method for clearing the finder's cache, if any. 105 This method is used by importlib.invalidate_caches(). 106 """ 107 108_register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter, 109 machinery.PathFinder, machinery.WindowsRegistryFinder) 110 111 112class PathEntryFinder(metaclass=abc.ABCMeta): 113 114 """Abstract base class for path entry finders used by PathFinder.""" 115 116 # We don't define find_spec() here since that would break 117 # hasattr checks we do to support backward compatibility. 118 119 def find_loader(self, fullname): 120 """Return (loader, namespace portion) for the path entry. 121 122 The fullname is a str. The namespace portion is a sequence of 123 path entries contributing to part of a namespace package. The 124 sequence may be empty. If loader is not None, the portion will 125 be ignored. 126 127 The portion will be discarded if another path entry finder 128 locates the module as a normal module or package. 129 130 This method is deprecated since Python 3.4 in favor of 131 finder.find_spec(). If find_spec() is provided than backwards-compatible 132 functionality is provided. 133 """ 134 warnings.warn("PathEntryFinder.find_loader() is deprecated since Python " 135 "3.4 in favor of PathEntryFinder.find_spec() " 136 "(available since 3.4)", 137 DeprecationWarning, 138 stacklevel=2) 139 if not hasattr(self, 'find_spec'): 140 return None, [] 141 found = self.find_spec(fullname) 142 if found is not None: 143 if not found.submodule_search_locations: 144 portions = [] 145 else: 146 portions = found.submodule_search_locations 147 return found.loader, portions 148 else: 149 return None, [] 150 151 find_module = _bootstrap_external._find_module_shim 152 153 def invalidate_caches(self): 154 """An optional method for clearing the finder's cache, if any. 155 This method is used by PathFinder.invalidate_caches(). 156 """ 157 158_register(PathEntryFinder, machinery.FileFinder) 159 160 161class ResourceLoader(Loader): 162 163 """Abstract base class for loaders which can return data from their 164 back-end storage. 165 166 This ABC represents one of the optional protocols specified by PEP 302. 167 168 """ 169 170 @abc.abstractmethod 171 def get_data(self, path): 172 """Abstract method which when implemented should return the bytes for 173 the specified path. The path must be a str.""" 174 raise OSError 175 176 177class InspectLoader(Loader): 178 179 """Abstract base class for loaders which support inspection about the 180 modules they can load. 181 182 This ABC represents one of the optional protocols specified by PEP 302. 183 184 """ 185 186 def is_package(self, fullname): 187 """Optional method which when implemented should return whether the 188 module is a package. The fullname is a str. Returns a bool. 189 190 Raises ImportError if the module cannot be found. 191 """ 192 raise ImportError 193 194 def get_code(self, fullname): 195 """Method which returns the code object for the module. 196 197 The fullname is a str. Returns a types.CodeType if possible, else 198 returns None if a code object does not make sense 199 (e.g. built-in module). Raises ImportError if the module cannot be 200 found. 201 """ 202 source = self.get_source(fullname) 203 if source is None: 204 return None 205 return self.source_to_code(source) 206 207 @abc.abstractmethod 208 def get_source(self, fullname): 209 """Abstract method which should return the source code for the 210 module. The fullname is a str. Returns a str. 211 212 Raises ImportError if the module cannot be found. 213 """ 214 raise ImportError 215 216 @staticmethod 217 def source_to_code(data, path='<string>'): 218 """Compile 'data' into a code object. 219 220 The 'data' argument can be anything that compile() can handle. The'path' 221 argument should be where the data was retrieved (when applicable).""" 222 return compile(data, path, 'exec', dont_inherit=True) 223 224 exec_module = _bootstrap_external._LoaderBasics.exec_module 225 load_module = _bootstrap_external._LoaderBasics.load_module 226 227_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter, machinery.NamespaceLoader) 228 229 230class ExecutionLoader(InspectLoader): 231 232 """Abstract base class for loaders that wish to support the execution of 233 modules as scripts. 234 235 This ABC represents one of the optional protocols specified in PEP 302. 236 237 """ 238 239 @abc.abstractmethod 240 def get_filename(self, fullname): 241 """Abstract method which should return the value that __file__ is to be 242 set to. 243 244 Raises ImportError if the module cannot be found. 245 """ 246 raise ImportError 247 248 def get_code(self, fullname): 249 """Method to return the code object for fullname. 250 251 Should return None if not applicable (e.g. built-in module). 252 Raise ImportError if the module cannot be found. 253 """ 254 source = self.get_source(fullname) 255 if source is None: 256 return None 257 try: 258 path = self.get_filename(fullname) 259 except ImportError: 260 return self.source_to_code(source) 261 else: 262 return self.source_to_code(source, path) 263 264_register(ExecutionLoader, machinery.ExtensionFileLoader) 265 266 267class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader): 268 269 """Abstract base class partially implementing the ResourceLoader and 270 ExecutionLoader ABCs.""" 271 272_register(FileLoader, machinery.SourceFileLoader, 273 machinery.SourcelessFileLoader) 274 275 276class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader): 277 278 """Abstract base class for loading source code (and optionally any 279 corresponding bytecode). 280 281 To support loading from source code, the abstractmethods inherited from 282 ResourceLoader and ExecutionLoader need to be implemented. To also support 283 loading from bytecode, the optional methods specified directly by this ABC 284 is required. 285 286 Inherited abstractmethods not implemented in this ABC: 287 288 * ResourceLoader.get_data 289 * ExecutionLoader.get_filename 290 291 """ 292 293 def path_mtime(self, path): 294 """Return the (int) modification time for the path (str).""" 295 if self.path_stats.__func__ is SourceLoader.path_stats: 296 raise OSError 297 return int(self.path_stats(path)['mtime']) 298 299 def path_stats(self, path): 300 """Return a metadata dict for the source pointed to by the path (str). 301 Possible keys: 302 - 'mtime' (mandatory) is the numeric timestamp of last source 303 code modification; 304 - 'size' (optional) is the size in bytes of the source code. 305 """ 306 if self.path_mtime.__func__ is SourceLoader.path_mtime: 307 raise OSError 308 return {'mtime': self.path_mtime(path)} 309 310 def set_data(self, path, data): 311 """Write the bytes to the path (if possible). 312 313 Accepts a str path and data as bytes. 314 315 Any needed intermediary directories are to be created. If for some 316 reason the file cannot be written because of permissions, fail 317 silently. 318 """ 319 320_register(SourceLoader, machinery.SourceFileLoader) 321