1"""Abstract base classes related to import.""" 2from . import _bootstrap 3from . import _bootstrap_external 4from . import machinery 5try: 6 import _frozen_importlib 7except ImportError as exc: 8 if exc.name != '_frozen_importlib': 9 raise 10 _frozen_importlib = None 11try: 12 import _frozen_importlib_external 13except ImportError: 14 _frozen_importlib_external = _bootstrap_external 15import abc 16import warnings 17from typing import Protocol, runtime_checkable 18 19 20def _register(abstract_cls, *classes): 21 for cls in classes: 22 abstract_cls.register(cls) 23 if _frozen_importlib is not None: 24 try: 25 frozen_cls = getattr(_frozen_importlib, cls.__name__) 26 except AttributeError: 27 frozen_cls = getattr(_frozen_importlib_external, cls.__name__) 28 abstract_cls.register(frozen_cls) 29 30 31class Finder(metaclass=abc.ABCMeta): 32 33 """Legacy abstract base class for import finders. 34 35 It may be subclassed for compatibility with legacy third party 36 reimplementations of the import system. Otherwise, finder 37 implementations should derive from the more specific MetaPathFinder 38 or PathEntryFinder ABCs. 39 40 Deprecated since Python 3.3 41 """ 42 43 @abc.abstractmethod 44 def find_module(self, fullname, path=None): 45 """An abstract method that should find a module. 46 The fullname is a str and the optional path is a str or None. 47 Returns a Loader object or None. 48 """ 49 50 51class MetaPathFinder(Finder): 52 53 """Abstract base class for import finders on sys.meta_path.""" 54 55 # We don't define find_spec() here since that would break 56 # hasattr checks we do to support backward compatibility. 57 58 def find_module(self, fullname, path): 59 """Return a loader for the module. 60 61 If no module is found, return None. The fullname is a str and 62 the path is a list of strings or None. 63 64 This method is deprecated since Python 3.4 in favor of 65 finder.find_spec(). If find_spec() exists then backwards-compatible 66 functionality is provided for this method. 67 68 """ 69 warnings.warn("MetaPathFinder.find_module() is deprecated since Python " 70 "3.4 in favor of MetaPathFinder.find_spec() " 71 "(available since 3.4)", 72 DeprecationWarning, 73 stacklevel=2) 74 if not hasattr(self, 'find_spec'): 75 return None 76 found = self.find_spec(fullname, path) 77 return found.loader if found is not None else None 78 79 def invalidate_caches(self): 80 """An optional method for clearing the finder's cache, if any. 81 This method is used by importlib.invalidate_caches(). 82 """ 83 84_register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter, 85 machinery.PathFinder, machinery.WindowsRegistryFinder) 86 87 88class PathEntryFinder(Finder): 89 90 """Abstract base class for path entry finders used by PathFinder.""" 91 92 # We don't define find_spec() here since that would break 93 # hasattr checks we do to support backward compatibility. 94 95 def find_loader(self, fullname): 96 """Return (loader, namespace portion) for the path entry. 97 98 The fullname is a str. The namespace portion is a sequence of 99 path entries contributing to part of a namespace package. The 100 sequence may be empty. If loader is not None, the portion will 101 be ignored. 102 103 The portion will be discarded if another path entry finder 104 locates the module as a normal module or package. 105 106 This method is deprecated since Python 3.4 in favor of 107 finder.find_spec(). If find_spec() is provided than backwards-compatible 108 functionality is provided. 109 """ 110 warnings.warn("PathEntryFinder.find_loader() is deprecated since Python " 111 "3.4 in favor of PathEntryFinder.find_spec() " 112 "(available since 3.4)", 113 DeprecationWarning, 114 stacklevel=2) 115 if not hasattr(self, 'find_spec'): 116 return None, [] 117 found = self.find_spec(fullname) 118 if found is not None: 119 if not found.submodule_search_locations: 120 portions = [] 121 else: 122 portions = found.submodule_search_locations 123 return found.loader, portions 124 else: 125 return None, [] 126 127 find_module = _bootstrap_external._find_module_shim 128 129 def invalidate_caches(self): 130 """An optional method for clearing the finder's cache, if any. 131 This method is used by PathFinder.invalidate_caches(). 132 """ 133 134_register(PathEntryFinder, machinery.FileFinder) 135 136 137class Loader(metaclass=abc.ABCMeta): 138 139 """Abstract base class for import loaders.""" 140 141 def create_module(self, spec): 142 """Return a module to initialize and into which to load. 143 144 This method should raise ImportError if anything prevents it 145 from creating a new module. It may return None to indicate 146 that the spec should create the new module. 147 """ 148 # By default, defer to default semantics for the new module. 149 return None 150 151 # We don't define exec_module() here since that would break 152 # hasattr checks we do to support backward compatibility. 153 154 def load_module(self, fullname): 155 """Return the loaded module. 156 157 The module must be added to sys.modules and have import-related 158 attributes set properly. The fullname is a str. 159 160 ImportError is raised on failure. 161 162 This method is deprecated in favor of loader.exec_module(). If 163 exec_module() exists then it is used to provide a backwards-compatible 164 functionality for this method. 165 166 """ 167 if not hasattr(self, 'exec_module'): 168 raise ImportError 169 return _bootstrap._load_module_shim(self, fullname) 170 171 def module_repr(self, module): 172 """Return a module's repr. 173 174 Used by the module type when the method does not raise 175 NotImplementedError. 176 177 This method is deprecated. 178 179 """ 180 # The exception will cause ModuleType.__repr__ to ignore this method. 181 raise NotImplementedError 182 183 184class ResourceLoader(Loader): 185 186 """Abstract base class for loaders which can return data from their 187 back-end storage. 188 189 This ABC represents one of the optional protocols specified by PEP 302. 190 191 """ 192 193 @abc.abstractmethod 194 def get_data(self, path): 195 """Abstract method which when implemented should return the bytes for 196 the specified path. The path must be a str.""" 197 raise OSError 198 199 200class InspectLoader(Loader): 201 202 """Abstract base class for loaders which support inspection about the 203 modules they can load. 204 205 This ABC represents one of the optional protocols specified by PEP 302. 206 207 """ 208 209 def is_package(self, fullname): 210 """Optional method which when implemented should return whether the 211 module is a package. The fullname is a str. Returns a bool. 212 213 Raises ImportError if the module cannot be found. 214 """ 215 raise ImportError 216 217 def get_code(self, fullname): 218 """Method which returns the code object for the module. 219 220 The fullname is a str. Returns a types.CodeType if possible, else 221 returns None if a code object does not make sense 222 (e.g. built-in module). Raises ImportError if the module cannot be 223 found. 224 """ 225 source = self.get_source(fullname) 226 if source is None: 227 return None 228 return self.source_to_code(source) 229 230 @abc.abstractmethod 231 def get_source(self, fullname): 232 """Abstract method which should return the source code for the 233 module. The fullname is a str. Returns a str. 234 235 Raises ImportError if the module cannot be found. 236 """ 237 raise ImportError 238 239 @staticmethod 240 def source_to_code(data, path='<string>'): 241 """Compile 'data' into a code object. 242 243 The 'data' argument can be anything that compile() can handle. The'path' 244 argument should be where the data was retrieved (when applicable).""" 245 return compile(data, path, 'exec', dont_inherit=True) 246 247 exec_module = _bootstrap_external._LoaderBasics.exec_module 248 load_module = _bootstrap_external._LoaderBasics.load_module 249 250_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter) 251 252 253class ExecutionLoader(InspectLoader): 254 255 """Abstract base class for loaders that wish to support the execution of 256 modules as scripts. 257 258 This ABC represents one of the optional protocols specified in PEP 302. 259 260 """ 261 262 @abc.abstractmethod 263 def get_filename(self, fullname): 264 """Abstract method which should return the value that __file__ is to be 265 set to. 266 267 Raises ImportError if the module cannot be found. 268 """ 269 raise ImportError 270 271 def get_code(self, fullname): 272 """Method to return the code object for fullname. 273 274 Should return None if not applicable (e.g. built-in module). 275 Raise ImportError if the module cannot be found. 276 """ 277 source = self.get_source(fullname) 278 if source is None: 279 return None 280 try: 281 path = self.get_filename(fullname) 282 except ImportError: 283 return self.source_to_code(source) 284 else: 285 return self.source_to_code(source, path) 286 287_register(ExecutionLoader, machinery.ExtensionFileLoader) 288 289 290class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader): 291 292 """Abstract base class partially implementing the ResourceLoader and 293 ExecutionLoader ABCs.""" 294 295_register(FileLoader, machinery.SourceFileLoader, 296 machinery.SourcelessFileLoader) 297 298 299class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader): 300 301 """Abstract base class for loading source code (and optionally any 302 corresponding bytecode). 303 304 To support loading from source code, the abstractmethods inherited from 305 ResourceLoader and ExecutionLoader need to be implemented. To also support 306 loading from bytecode, the optional methods specified directly by this ABC 307 is required. 308 309 Inherited abstractmethods not implemented in this ABC: 310 311 * ResourceLoader.get_data 312 * ExecutionLoader.get_filename 313 314 """ 315 316 def path_mtime(self, path): 317 """Return the (int) modification time for the path (str).""" 318 if self.path_stats.__func__ is SourceLoader.path_stats: 319 raise OSError 320 return int(self.path_stats(path)['mtime']) 321 322 def path_stats(self, path): 323 """Return a metadata dict for the source pointed to by the path (str). 324 Possible keys: 325 - 'mtime' (mandatory) is the numeric timestamp of last source 326 code modification; 327 - 'size' (optional) is the size in bytes of the source code. 328 """ 329 if self.path_mtime.__func__ is SourceLoader.path_mtime: 330 raise OSError 331 return {'mtime': self.path_mtime(path)} 332 333 def set_data(self, path, data): 334 """Write the bytes to the path (if possible). 335 336 Accepts a str path and data as bytes. 337 338 Any needed intermediary directories are to be created. If for some 339 reason the file cannot be written because of permissions, fail 340 silently. 341 """ 342 343_register(SourceLoader, machinery.SourceFileLoader) 344 345 346class ResourceReader(metaclass=abc.ABCMeta): 347 348 """Abstract base class to provide resource-reading support. 349 350 Loaders that support resource reading are expected to implement 351 the ``get_resource_reader(fullname)`` method and have it either return None 352 or an object compatible with this ABC. 353 """ 354 355 @abc.abstractmethod 356 def open_resource(self, resource): 357 """Return an opened, file-like object for binary reading. 358 359 The 'resource' argument is expected to represent only a file name 360 and thus not contain any subdirectory components. 361 362 If the resource cannot be found, FileNotFoundError is raised. 363 """ 364 raise FileNotFoundError 365 366 @abc.abstractmethod 367 def resource_path(self, resource): 368 """Return the file system path to the specified resource. 369 370 The 'resource' argument is expected to represent only a file name 371 and thus not contain any subdirectory components. 372 373 If the resource does not exist on the file system, raise 374 FileNotFoundError. 375 """ 376 raise FileNotFoundError 377 378 @abc.abstractmethod 379 def is_resource(self, name): 380 """Return True if the named 'name' is consider a resource.""" 381 raise FileNotFoundError 382 383 @abc.abstractmethod 384 def contents(self): 385 """Return an iterable of strings over the contents of the package.""" 386 return [] 387 388 389_register(ResourceReader, machinery.SourceFileLoader) 390 391 392@runtime_checkable 393class Traversable(Protocol): 394 """ 395 An object with a subset of pathlib.Path methods suitable for 396 traversing directories and opening files. 397 """ 398 399 @abc.abstractmethod 400 def iterdir(self): 401 """ 402 Yield Traversable objects in self 403 """ 404 405 @abc.abstractmethod 406 def read_bytes(self): 407 """ 408 Read contents of self as bytes 409 """ 410 411 @abc.abstractmethod 412 def read_text(self, encoding=None): 413 """ 414 Read contents of self as bytes 415 """ 416 417 @abc.abstractmethod 418 def is_dir(self): 419 """ 420 Return True if self is a dir 421 """ 422 423 @abc.abstractmethod 424 def is_file(self): 425 """ 426 Return True if self is a file 427 """ 428 429 @abc.abstractmethod 430 def joinpath(self, child): 431 """ 432 Return Traversable child in self 433 """ 434 435 @abc.abstractmethod 436 def __truediv__(self, child): 437 """ 438 Return Traversable child in self 439 """ 440 441 @abc.abstractmethod 442 def open(self, mode='r', *args, **kwargs): 443 """ 444 mode may be 'r' or 'rb' to open as text or binary. Return a handle 445 suitable for reading (same as pathlib.Path.open). 446 447 When opening as text, accepts encoding parameters such as those 448 accepted by io.TextIOWrapper. 449 """ 450 451 @abc.abstractproperty 452 def name(self): 453 # type: () -> str 454 """ 455 The base name of this object without any parent references. 456 """ 457 458 459class TraversableResources(ResourceReader): 460 @abc.abstractmethod 461 def files(self): 462 """Return a Traversable object for the loaded package.""" 463 464 def open_resource(self, resource): 465 return self.files().joinpath(resource).open('rb') 466 467 def resource_path(self, resource): 468 raise FileNotFoundError(resource) 469 470 def is_resource(self, path): 471 return self.files().joinpath(path).isfile() 472 473 def contents(self): 474 return (item.name for item in self.files().iterdir()) 475