blob: 4be51e2346282f25817de2c3e83df146b5b0cd4b [file] [log] [blame]
Brett Cannon2a922ed2009-03-09 03:35:50 +00001"""Abstract base classes related to import."""
Eric Snow32439d62015-05-02 19:15:18 -06002from . import _bootstrap_external
Brett Cannon2a922ed2009-03-09 03:35:50 +00003from . import machinery
Brett Cannon938d44d2012-04-22 19:58:33 -04004try:
5 import _frozen_importlib
Brett Cannoncd171c82013-07-04 17:43:24 -04006except ImportError as exc:
Brett Cannon938d44d2012-04-22 19:58:33 -04007 if exc.name != '_frozen_importlib':
8 raise
9 _frozen_importlib = None
Eric Snow32439d62015-05-02 19:15:18 -060010try:
11 import _frozen_importlib_external
Pablo Galindo293dd232019-11-19 21:34:03 +000012except ImportError:
Eric Snow32439d62015-05-02 19:15:18 -060013 _frozen_importlib_external = _bootstrap_external
Victor Stinner9e098492020-06-17 23:15:59 +020014from ._abc import Loader
Brett Cannon2a922ed2009-03-09 03:35:50 +000015import abc
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -080016import warnings
Jason R. Coombs67148252021-03-04 13:43:00 -050017from typing import BinaryIO, Iterable, Text
Jason R. Coombs7f7e7062020-05-08 19:20:26 -040018from typing import Protocol, runtime_checkable
Brett Cannon2a922ed2009-03-09 03:35:50 +000019
20
Brett Cannon938d44d2012-04-22 19:58:33 -040021def _register(abstract_cls, *classes):
22 for cls in classes:
23 abstract_cls.register(cls)
24 if _frozen_importlib is not None:
Eric Snow32439d62015-05-02 19:15:18 -060025 try:
26 frozen_cls = getattr(_frozen_importlib, cls.__name__)
27 except AttributeError:
28 frozen_cls = getattr(_frozen_importlib_external, cls.__name__)
Brett Cannon938d44d2012-04-22 19:58:33 -040029 abstract_cls.register(frozen_cls)
30
31
Nick Coghlan8a9080f2012-08-02 21:26:03 +100032class Finder(metaclass=abc.ABCMeta):
33
Brett Cannonf4dc9202012-08-10 12:21:12 -040034 """Legacy abstract base class for import finders.
Nick Coghlan8a9080f2012-08-02 21:26:03 +100035
Brett Cannonf4dc9202012-08-10 12:21:12 -040036 It may be subclassed for compatibility with legacy third party
37 reimplementations of the import system. Otherwise, finder
38 implementations should derive from the more specific MetaPathFinder
39 or PathEntryFinder ABCs.
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -080040
41 Deprecated since Python 3.3
Nick Coghlan8a9080f2012-08-02 21:26:03 +100042 """
43
Brett Cannonf4dc9202012-08-10 12:21:12 -040044 @abc.abstractmethod
Nick Coghlan8a9080f2012-08-02 21:26:03 +100045 def find_module(self, fullname, path=None):
Brett Cannonf4dc9202012-08-10 12:21:12 -040046 """An abstract method that should find a module.
Nick Coghlan8a9080f2012-08-02 21:26:03 +100047 The fullname is a str and the optional path is a str or None.
Brett Cannon100883f2013-04-09 16:59:39 -040048 Returns a Loader object or None.
Nick Coghlan8a9080f2012-08-02 21:26:03 +100049 """
Nick Coghlan8a9080f2012-08-02 21:26:03 +100050
Nick Coghlan8a9080f2012-08-02 21:26:03 +100051
52class MetaPathFinder(Finder):
53
54 """Abstract base class for import finders on sys.meta_path."""
55
Eric Snowb523f842013-11-22 09:05:39 -070056 # We don't define find_spec() here since that would break
57 # hasattr checks we do to support backward compatibility.
58
Nick Coghlan8a9080f2012-08-02 21:26:03 +100059 def find_module(self, fullname, path):
Eric Snowb523f842013-11-22 09:05:39 -070060 """Return a loader for the module.
61
62 If no module is found, return None. The fullname is a str and
63 the path is a list of strings or None.
64
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -080065 This method is deprecated since Python 3.4 in favor of
66 finder.find_spec(). If find_spec() exists then backwards-compatible
67 functionality is provided for this method.
Eric Snow1500d492014-01-06 20:49:04 -070068
Nick Coghlan8a9080f2012-08-02 21:26:03 +100069 """
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -080070 warnings.warn("MetaPathFinder.find_module() is deprecated since Python "
Serhiy Storchaka34fd4c22018-11-05 16:20:25 +020071 "3.4 in favor of MetaPathFinder.find_spec() "
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -080072 "(available since 3.4)",
73 DeprecationWarning,
74 stacklevel=2)
Brett Cannon8d942292014-01-07 15:52:42 -050075 if not hasattr(self, 'find_spec'):
76 return None
77 found = self.find_spec(fullname, path)
78 return found.loader if found is not None else None
Nick Coghlan8a9080f2012-08-02 21:26:03 +100079
Brett Cannonf4dc9202012-08-10 12:21:12 -040080 def invalidate_caches(self):
81 """An optional method for clearing the finder's cache, if any.
82 This method is used by importlib.invalidate_caches().
83 """
Brett Cannonf4dc9202012-08-10 12:21:12 -040084
Nick Coghlan8a9080f2012-08-02 21:26:03 +100085_register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter,
Nick Coghlanff794862012-08-02 21:45:24 +100086 machinery.PathFinder, machinery.WindowsRegistryFinder)
Nick Coghlan8a9080f2012-08-02 21:26:03 +100087
88
89class PathEntryFinder(Finder):
90
91 """Abstract base class for path entry finders used by PathFinder."""
92
Eric Snowb523f842013-11-22 09:05:39 -070093 # We don't define find_spec() here since that would break
94 # hasattr checks we do to support backward compatibility.
95
Nick Coghlan8a9080f2012-08-02 21:26:03 +100096 def find_loader(self, fullname):
Eric Snowb523f842013-11-22 09:05:39 -070097 """Return (loader, namespace portion) for the path entry.
98
99 The fullname is a str. The namespace portion is a sequence of
100 path entries contributing to part of a namespace package. The
101 sequence may be empty. If loader is not None, the portion will
102 be ignored.
103
104 The portion will be discarded if another path entry finder
105 locates the module as a normal module or package.
106
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -0800107 This method is deprecated since Python 3.4 in favor of
108 finder.find_spec(). If find_spec() is provided than backwards-compatible
109 functionality is provided.
Nick Coghlan8a9080f2012-08-02 21:26:03 +1000110 """
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -0800111 warnings.warn("PathEntryFinder.find_loader() is deprecated since Python "
112 "3.4 in favor of PathEntryFinder.find_spec() "
113 "(available since 3.4)",
114 DeprecationWarning,
115 stacklevel=2)
Brett Cannon8d942292014-01-07 15:52:42 -0500116 if not hasattr(self, 'find_spec'):
117 return None, []
118 found = self.find_spec(fullname)
119 if found is not None:
120 if not found.submodule_search_locations:
121 portions = []
122 else:
123 portions = found.submodule_search_locations
124 return found.loader, portions
125 else:
126 return None, []
Nick Coghlan8a9080f2012-08-02 21:26:03 +1000127
Eric Snow32439d62015-05-02 19:15:18 -0600128 find_module = _bootstrap_external._find_module_shim
Brett Cannonf4dc9202012-08-10 12:21:12 -0400129
130 def invalidate_caches(self):
131 """An optional method for clearing the finder's cache, if any.
132 This method is used by PathFinder.invalidate_caches().
133 """
Brett Cannonf4dc9202012-08-10 12:21:12 -0400134
Nick Coghlan8a9080f2012-08-02 21:26:03 +1000135_register(PathEntryFinder, machinery.FileFinder)
136
137
Brett Cannon2a922ed2009-03-09 03:35:50 +0000138class ResourceLoader(Loader):
139
Brett Cannon7aa21f72009-03-15 00:53:05 +0000140 """Abstract base class for loaders which can return data from their
141 back-end storage.
Brett Cannon2a922ed2009-03-09 03:35:50 +0000142
143 This ABC represents one of the optional protocols specified by PEP 302.
144
145 """
146
147 @abc.abstractmethod
Raymond Hettingercd92f372011-01-13 02:31:25 +0000148 def get_data(self, path):
Brett Cannon7aa21f72009-03-15 00:53:05 +0000149 """Abstract method which when implemented should return the bytes for
Raymond Hettingerd958ea72011-01-13 19:08:04 +0000150 the specified path. The path must be a str."""
Serhiy Storchaka55fe1ae2017-04-16 10:46:38 +0300151 raise OSError
Brett Cannon2a922ed2009-03-09 03:35:50 +0000152
153
154class InspectLoader(Loader):
155
Brett Cannon7aa21f72009-03-15 00:53:05 +0000156 """Abstract base class for loaders which support inspection about the
157 modules they can load.
Brett Cannon2a922ed2009-03-09 03:35:50 +0000158
159 This ABC represents one of the optional protocols specified by PEP 302.
160
161 """
162
Raymond Hettingercd92f372011-01-13 02:31:25 +0000163 def is_package(self, fullname):
Eric Snowb523f842013-11-22 09:05:39 -0700164 """Optional method which when implemented should return whether the
Brett Cannon100883f2013-04-09 16:59:39 -0400165 module is a package. The fullname is a str. Returns a bool.
166
Eric Snowb523f842013-11-22 09:05:39 -0700167 Raises ImportError if the module cannot be found.
Brett Cannon100883f2013-04-09 16:59:39 -0400168 """
169 raise ImportError
Brett Cannon2a922ed2009-03-09 03:35:50 +0000170
Raymond Hettingercd92f372011-01-13 02:31:25 +0000171 def get_code(self, fullname):
Brett Cannon3b62ca82013-05-27 21:11:04 -0400172 """Method which returns the code object for the module.
Brett Cannon100883f2013-04-09 16:59:39 -0400173
Brett Cannon3b62ca82013-05-27 21:11:04 -0400174 The fullname is a str. Returns a types.CodeType if possible, else
175 returns None if a code object does not make sense
176 (e.g. built-in module). Raises ImportError if the module cannot be
177 found.
Brett Cannon100883f2013-04-09 16:59:39 -0400178 """
Brett Cannon3b62ca82013-05-27 21:11:04 -0400179 source = self.get_source(fullname)
180 if source is None:
181 return None
182 return self.source_to_code(source)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000183
184 @abc.abstractmethod
Raymond Hettingercd92f372011-01-13 02:31:25 +0000185 def get_source(self, fullname):
Brett Cannon7aa21f72009-03-15 00:53:05 +0000186 """Abstract method which should return the source code for the
Brett Cannon100883f2013-04-09 16:59:39 -0400187 module. The fullname is a str. Returns a str.
188
189 Raises ImportError if the module cannot be found.
190 """
191 raise ImportError
Brett Cannon2a922ed2009-03-09 03:35:50 +0000192
Brett Cannon6eaac132014-05-09 12:28:22 -0400193 @staticmethod
194 def source_to_code(data, path='<string>'):
Brett Cannon9ffe85e2013-05-26 16:45:10 -0400195 """Compile 'data' into a code object.
196
197 The 'data' argument can be anything that compile() can handle. The'path'
198 argument should be where the data was retrieved (when applicable)."""
199 return compile(data, path, 'exec', dont_inherit=True)
200
Eric Snow32439d62015-05-02 19:15:18 -0600201 exec_module = _bootstrap_external._LoaderBasics.exec_module
202 load_module = _bootstrap_external._LoaderBasics.load_module
Brett Cannon0dbb4c72013-05-31 18:56:47 -0400203
Eric Snowb523f842013-11-22 09:05:39 -0700204_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter)
Brett Cannona113ac52009-03-15 01:41:33 +0000205
Brett Cannon2a922ed2009-03-09 03:35:50 +0000206
Brett Cannon69194272009-07-20 04:23:48 +0000207class ExecutionLoader(InspectLoader):
208
209 """Abstract base class for loaders that wish to support the execution of
210 modules as scripts.
211
212 This ABC represents one of the optional protocols specified in PEP 302.
213
214 """
215
216 @abc.abstractmethod
Raymond Hettingercd92f372011-01-13 02:31:25 +0000217 def get_filename(self, fullname):
Brett Cannon69194272009-07-20 04:23:48 +0000218 """Abstract method which should return the value that __file__ is to be
Brett Cannon100883f2013-04-09 16:59:39 -0400219 set to.
220
221 Raises ImportError if the module cannot be found.
222 """
223 raise ImportError
Brett Cannon69194272009-07-20 04:23:48 +0000224
Brett Cannon3b62ca82013-05-27 21:11:04 -0400225 def get_code(self, fullname):
226 """Method to return the code object for fullname.
227
228 Should return None if not applicable (e.g. built-in module).
229 Raise ImportError if the module cannot be found.
230 """
231 source = self.get_source(fullname)
232 if source is None:
233 return None
234 try:
235 path = self.get_filename(fullname)
236 except ImportError:
237 return self.source_to_code(source)
238 else:
239 return self.source_to_code(source, path)
240
Eric Snow7e70fa52013-10-04 20:28:52 -0600241_register(ExecutionLoader, machinery.ExtensionFileLoader)
Eric Snow51794452013-10-03 12:08:55 -0600242
Brett Cannon69194272009-07-20 04:23:48 +0000243
Eric Snow32439d62015-05-02 19:15:18 -0600244class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader):
Brett Cannon938d44d2012-04-22 19:58:33 -0400245
246 """Abstract base class partially implementing the ResourceLoader and
247 ExecutionLoader ABCs."""
248
249_register(FileLoader, machinery.SourceFileLoader,
Marc-Andre Lemburg4fe29c92012-04-25 02:31:37 +0200250 machinery.SourcelessFileLoader)
Brett Cannon938d44d2012-04-22 19:58:33 -0400251
252
Eric Snow32439d62015-05-02 19:15:18 -0600253class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader):
Brett Cannon2a922ed2009-03-09 03:35:50 +0000254
Brett Cannonf23e3742010-06-27 23:57:46 +0000255 """Abstract base class for loading source code (and optionally any
256 corresponding bytecode).
Brett Cannon2a922ed2009-03-09 03:35:50 +0000257
Brett Cannonf23e3742010-06-27 23:57:46 +0000258 To support loading from source code, the abstractmethods inherited from
259 ResourceLoader and ExecutionLoader need to be implemented. To also support
260 loading from bytecode, the optional methods specified directly by this ABC
261 is required.
262
263 Inherited abstractmethods not implemented in this ABC:
264
265 * ResourceLoader.get_data
266 * ExecutionLoader.get_filename
267
268 """
269
Raymond Hettingercd92f372011-01-13 02:31:25 +0000270 def path_mtime(self, path):
Raymond Hettingerd958ea72011-01-13 19:08:04 +0000271 """Return the (int) modification time for the path (str)."""
Antoine Pitrou5136ac02012-01-13 18:52:16 +0100272 if self.path_stats.__func__ is SourceLoader.path_stats:
Serhiy Storchaka55fe1ae2017-04-16 10:46:38 +0300273 raise OSError
Antoine Pitrou5136ac02012-01-13 18:52:16 +0100274 return int(self.path_stats(path)['mtime'])
275
276 def path_stats(self, path):
277 """Return a metadata dict for the source pointed to by the path (str).
278 Possible keys:
279 - 'mtime' (mandatory) is the numeric timestamp of last source
280 code modification;
281 - 'size' (optional) is the size in bytes of the source code.
282 """
283 if self.path_mtime.__func__ is SourceLoader.path_mtime:
Serhiy Storchaka55fe1ae2017-04-16 10:46:38 +0300284 raise OSError
Antoine Pitrou5136ac02012-01-13 18:52:16 +0100285 return {'mtime': self.path_mtime(path)}
Brett Cannon8d189072010-08-22 20:38:47 +0000286
Raymond Hettingercd92f372011-01-13 02:31:25 +0000287 def set_data(self, path, data):
Brett Cannon8d189072010-08-22 20:38:47 +0000288 """Write the bytes to the path (if possible).
289
Raymond Hettingerd958ea72011-01-13 19:08:04 +0000290 Accepts a str path and data as bytes.
291
Brett Cannon8d189072010-08-22 20:38:47 +0000292 Any needed intermediary directories are to be created. If for some
293 reason the file cannot be written because of permissions, fail
294 silently.
Brett Cannon8d189072010-08-22 20:38:47 +0000295 """
Brett Cannon8d189072010-08-22 20:38:47 +0000296
Brett Cannon938d44d2012-04-22 19:58:33 -0400297_register(SourceLoader, machinery.SourceFileLoader)
Brett Cannon4ac51502017-12-15 16:29:35 -0800298
299
Barry Warsaw5ec0fee2018-01-15 15:07:11 -0800300class ResourceReader(metaclass=abc.ABCMeta):
Jason R. Coombs67148252021-03-04 13:43:00 -0500301 """Abstract base class for loaders to provide resource reading support."""
Brett Cannon4ac51502017-12-15 16:29:35 -0800302
303 @abc.abstractmethod
Jason R. Coombs67148252021-03-04 13:43:00 -0500304 def open_resource(self, resource: Text) -> BinaryIO:
Brett Cannon4ac51502017-12-15 16:29:35 -0800305 """Return an opened, file-like object for binary reading.
306
Jason R. Coombs67148252021-03-04 13:43:00 -0500307 The 'resource' argument is expected to represent only a file name.
Brett Cannon4ac51502017-12-15 16:29:35 -0800308 If the resource cannot be found, FileNotFoundError is raised.
309 """
Jason R. Coombs67148252021-03-04 13:43:00 -0500310 # This deliberately raises FileNotFoundError instead of
311 # NotImplementedError so that if this method is accidentally called,
312 # it'll still do the right thing.
Brett Cannon4ac51502017-12-15 16:29:35 -0800313 raise FileNotFoundError
314
315 @abc.abstractmethod
Jason R. Coombs67148252021-03-04 13:43:00 -0500316 def resource_path(self, resource: Text) -> Text:
Brett Cannon4ac51502017-12-15 16:29:35 -0800317 """Return the file system path to the specified resource.
318
Jason R. Coombs67148252021-03-04 13:43:00 -0500319 The 'resource' argument is expected to represent only a file name.
Brett Cannon4ac51502017-12-15 16:29:35 -0800320 If the resource does not exist on the file system, raise
321 FileNotFoundError.
322 """
Jason R. Coombs67148252021-03-04 13:43:00 -0500323 # This deliberately raises FileNotFoundError instead of
324 # NotImplementedError so that if this method is accidentally called,
325 # it'll still do the right thing.
Brett Cannon4ac51502017-12-15 16:29:35 -0800326 raise FileNotFoundError
327
328 @abc.abstractmethod
Jason R. Coombs67148252021-03-04 13:43:00 -0500329 def is_resource(self, path: Text) -> bool:
330 """Return True if the named 'path' is a resource.
331
332 Files are resources, directories are not.
333 """
Brett Cannon4ac51502017-12-15 16:29:35 -0800334 raise FileNotFoundError
335
336 @abc.abstractmethod
Jason R. Coombs67148252021-03-04 13:43:00 -0500337 def contents(self) -> Iterable[str]:
338 """Return an iterable of entries in `package`."""
339 raise FileNotFoundError
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400340
341
342@runtime_checkable
343class Traversable(Protocol):
344 """
345 An object with a subset of pathlib.Path methods suitable for
346 traversing directories and opening files.
347 """
348
349 @abc.abstractmethod
350 def iterdir(self):
351 """
352 Yield Traversable objects in self
353 """
354
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400355 def read_bytes(self):
356 """
357 Read contents of self as bytes
358 """
Jason R. Coombs67148252021-03-04 13:43:00 -0500359 with self.open('rb') as strm:
360 return strm.read()
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400361
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400362 def read_text(self, encoding=None):
363 """
Jason R. Coombs67148252021-03-04 13:43:00 -0500364 Read contents of self as text
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400365 """
Jason R. Coombs67148252021-03-04 13:43:00 -0500366 with self.open(encoding=encoding) as strm:
367 return strm.read()
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400368
369 @abc.abstractmethod
Jason R. Coombs67148252021-03-04 13:43:00 -0500370 def is_dir(self) -> bool:
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400371 """
372 Return True if self is a dir
373 """
374
375 @abc.abstractmethod
Jason R. Coombs67148252021-03-04 13:43:00 -0500376 def is_file(self) -> bool:
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400377 """
378 Return True if self is a file
379 """
380
381 @abc.abstractmethod
382 def joinpath(self, child):
383 """
384 Return Traversable child in self
385 """
386
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400387 def __truediv__(self, child):
388 """
389 Return Traversable child in self
390 """
Jason R. Coombs67148252021-03-04 13:43:00 -0500391 return self.joinpath(child)
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400392
393 @abc.abstractmethod
394 def open(self, mode='r', *args, **kwargs):
395 """
396 mode may be 'r' or 'rb' to open as text or binary. Return a handle
397 suitable for reading (same as pathlib.Path.open).
398
399 When opening as text, accepts encoding parameters such as those
400 accepted by io.TextIOWrapper.
401 """
402
403 @abc.abstractproperty
Jason R. Coombs67148252021-03-04 13:43:00 -0500404 def name(self) -> str:
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400405 """
406 The base name of this object without any parent references.
407 """
408
409
410class TraversableResources(ResourceReader):
Jason R. Coombs67148252021-03-04 13:43:00 -0500411 """
412 The required interface for providing traversable
413 resources.
414 """
415
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400416 @abc.abstractmethod
417 def files(self):
418 """Return a Traversable object for the loaded package."""
419
420 def open_resource(self, resource):
421 return self.files().joinpath(resource).open('rb')
422
423 def resource_path(self, resource):
424 raise FileNotFoundError(resource)
425
426 def is_resource(self, path):
Jason R. Coombs843c2772020-06-07 21:00:51 -0400427 return self.files().joinpath(path).is_file()
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400428
429 def contents(self):
430 return (item.name for item in self.files().iterdir())