blob: 55e70889f22f70c751aeb725727487ff72918a75 [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. Coombs7f7e7062020-05-08 19:20:26 -040017from typing import Protocol, runtime_checkable
Brett Cannon2a922ed2009-03-09 03:35:50 +000018
19
Brett Cannon938d44d2012-04-22 19:58:33 -040020def _register(abstract_cls, *classes):
21 for cls in classes:
22 abstract_cls.register(cls)
23 if _frozen_importlib is not None:
Eric Snow32439d62015-05-02 19:15:18 -060024 try:
25 frozen_cls = getattr(_frozen_importlib, cls.__name__)
26 except AttributeError:
27 frozen_cls = getattr(_frozen_importlib_external, cls.__name__)
Brett Cannon938d44d2012-04-22 19:58:33 -040028 abstract_cls.register(frozen_cls)
29
30
Nick Coghlan8a9080f2012-08-02 21:26:03 +100031class Finder(metaclass=abc.ABCMeta):
32
Brett Cannonf4dc9202012-08-10 12:21:12 -040033 """Legacy abstract base class for import finders.
Nick Coghlan8a9080f2012-08-02 21:26:03 +100034
Brett Cannonf4dc9202012-08-10 12:21:12 -040035 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.
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -080039
40 Deprecated since Python 3.3
Nick Coghlan8a9080f2012-08-02 21:26:03 +100041 """
42
Brett Cannonf4dc9202012-08-10 12:21:12 -040043 @abc.abstractmethod
Nick Coghlan8a9080f2012-08-02 21:26:03 +100044 def find_module(self, fullname, path=None):
Brett Cannonf4dc9202012-08-10 12:21:12 -040045 """An abstract method that should find a module.
Nick Coghlan8a9080f2012-08-02 21:26:03 +100046 The fullname is a str and the optional path is a str or None.
Brett Cannon100883f2013-04-09 16:59:39 -040047 Returns a Loader object or None.
Nick Coghlan8a9080f2012-08-02 21:26:03 +100048 """
Nick Coghlan8a9080f2012-08-02 21:26:03 +100049
Nick Coghlan8a9080f2012-08-02 21:26:03 +100050
51class MetaPathFinder(Finder):
52
53 """Abstract base class for import finders on sys.meta_path."""
54
Eric Snowb523f842013-11-22 09:05:39 -070055 # We don't define find_spec() here since that would break
56 # hasattr checks we do to support backward compatibility.
57
Nick Coghlan8a9080f2012-08-02 21:26:03 +100058 def find_module(self, fullname, path):
Eric Snowb523f842013-11-22 09:05:39 -070059 """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
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -080064 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.
Eric Snow1500d492014-01-06 20:49:04 -070067
Nick Coghlan8a9080f2012-08-02 21:26:03 +100068 """
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -080069 warnings.warn("MetaPathFinder.find_module() is deprecated since Python "
Serhiy Storchaka34fd4c22018-11-05 16:20:25 +020070 "3.4 in favor of MetaPathFinder.find_spec() "
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -080071 "(available since 3.4)",
72 DeprecationWarning,
73 stacklevel=2)
Brett Cannon8d942292014-01-07 15:52:42 -050074 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
Nick Coghlan8a9080f2012-08-02 21:26:03 +100078
Brett Cannonf4dc9202012-08-10 12:21:12 -040079 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 """
Brett Cannonf4dc9202012-08-10 12:21:12 -040083
Nick Coghlan8a9080f2012-08-02 21:26:03 +100084_register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter,
Nick Coghlanff794862012-08-02 21:45:24 +100085 machinery.PathFinder, machinery.WindowsRegistryFinder)
Nick Coghlan8a9080f2012-08-02 21:26:03 +100086
87
88class PathEntryFinder(Finder):
89
90 """Abstract base class for path entry finders used by PathFinder."""
91
Eric Snowb523f842013-11-22 09:05:39 -070092 # We don't define find_spec() here since that would break
93 # hasattr checks we do to support backward compatibility.
94
Nick Coghlan8a9080f2012-08-02 21:26:03 +100095 def find_loader(self, fullname):
Eric Snowb523f842013-11-22 09:05:39 -070096 """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
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -0800106 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.
Nick Coghlan8a9080f2012-08-02 21:26:03 +1000109 """
Matthias Bussonnier1d4601c2017-02-15 18:00:32 -0800110 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)
Brett Cannon8d942292014-01-07 15:52:42 -0500115 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, []
Nick Coghlan8a9080f2012-08-02 21:26:03 +1000126
Eric Snow32439d62015-05-02 19:15:18 -0600127 find_module = _bootstrap_external._find_module_shim
Brett Cannonf4dc9202012-08-10 12:21:12 -0400128
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 """
Brett Cannonf4dc9202012-08-10 12:21:12 -0400133
Nick Coghlan8a9080f2012-08-02 21:26:03 +1000134_register(PathEntryFinder, machinery.FileFinder)
135
136
Brett Cannon2a922ed2009-03-09 03:35:50 +0000137class ResourceLoader(Loader):
138
Brett Cannon7aa21f72009-03-15 00:53:05 +0000139 """Abstract base class for loaders which can return data from their
140 back-end storage.
Brett Cannon2a922ed2009-03-09 03:35:50 +0000141
142 This ABC represents one of the optional protocols specified by PEP 302.
143
144 """
145
146 @abc.abstractmethod
Raymond Hettingercd92f372011-01-13 02:31:25 +0000147 def get_data(self, path):
Brett Cannon7aa21f72009-03-15 00:53:05 +0000148 """Abstract method which when implemented should return the bytes for
Raymond Hettingerd958ea72011-01-13 19:08:04 +0000149 the specified path. The path must be a str."""
Serhiy Storchaka55fe1ae2017-04-16 10:46:38 +0300150 raise OSError
Brett Cannon2a922ed2009-03-09 03:35:50 +0000151
152
153class InspectLoader(Loader):
154
Brett Cannon7aa21f72009-03-15 00:53:05 +0000155 """Abstract base class for loaders which support inspection about the
156 modules they can load.
Brett Cannon2a922ed2009-03-09 03:35:50 +0000157
158 This ABC represents one of the optional protocols specified by PEP 302.
159
160 """
161
Raymond Hettingercd92f372011-01-13 02:31:25 +0000162 def is_package(self, fullname):
Eric Snowb523f842013-11-22 09:05:39 -0700163 """Optional method which when implemented should return whether the
Brett Cannon100883f2013-04-09 16:59:39 -0400164 module is a package. The fullname is a str. Returns a bool.
165
Eric Snowb523f842013-11-22 09:05:39 -0700166 Raises ImportError if the module cannot be found.
Brett Cannon100883f2013-04-09 16:59:39 -0400167 """
168 raise ImportError
Brett Cannon2a922ed2009-03-09 03:35:50 +0000169
Raymond Hettingercd92f372011-01-13 02:31:25 +0000170 def get_code(self, fullname):
Brett Cannon3b62ca82013-05-27 21:11:04 -0400171 """Method which returns the code object for the module.
Brett Cannon100883f2013-04-09 16:59:39 -0400172
Brett Cannon3b62ca82013-05-27 21:11:04 -0400173 The fullname is a str. Returns a types.CodeType if possible, else
174 returns None if a code object does not make sense
175 (e.g. built-in module). Raises ImportError if the module cannot be
176 found.
Brett Cannon100883f2013-04-09 16:59:39 -0400177 """
Brett Cannon3b62ca82013-05-27 21:11:04 -0400178 source = self.get_source(fullname)
179 if source is None:
180 return None
181 return self.source_to_code(source)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000182
183 @abc.abstractmethod
Raymond Hettingercd92f372011-01-13 02:31:25 +0000184 def get_source(self, fullname):
Brett Cannon7aa21f72009-03-15 00:53:05 +0000185 """Abstract method which should return the source code for the
Brett Cannon100883f2013-04-09 16:59:39 -0400186 module. The fullname is a str. Returns a str.
187
188 Raises ImportError if the module cannot be found.
189 """
190 raise ImportError
Brett Cannon2a922ed2009-03-09 03:35:50 +0000191
Brett Cannon6eaac132014-05-09 12:28:22 -0400192 @staticmethod
193 def source_to_code(data, path='<string>'):
Brett Cannon9ffe85e2013-05-26 16:45:10 -0400194 """Compile 'data' into a code object.
195
196 The 'data' argument can be anything that compile() can handle. The'path'
197 argument should be where the data was retrieved (when applicable)."""
198 return compile(data, path, 'exec', dont_inherit=True)
199
Eric Snow32439d62015-05-02 19:15:18 -0600200 exec_module = _bootstrap_external._LoaderBasics.exec_module
201 load_module = _bootstrap_external._LoaderBasics.load_module
Brett Cannon0dbb4c72013-05-31 18:56:47 -0400202
Eric Snowb523f842013-11-22 09:05:39 -0700203_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter)
Brett Cannona113ac52009-03-15 01:41:33 +0000204
Brett Cannon2a922ed2009-03-09 03:35:50 +0000205
Brett Cannon69194272009-07-20 04:23:48 +0000206class ExecutionLoader(InspectLoader):
207
208 """Abstract base class for loaders that wish to support the execution of
209 modules as scripts.
210
211 This ABC represents one of the optional protocols specified in PEP 302.
212
213 """
214
215 @abc.abstractmethod
Raymond Hettingercd92f372011-01-13 02:31:25 +0000216 def get_filename(self, fullname):
Brett Cannon69194272009-07-20 04:23:48 +0000217 """Abstract method which should return the value that __file__ is to be
Brett Cannon100883f2013-04-09 16:59:39 -0400218 set to.
219
220 Raises ImportError if the module cannot be found.
221 """
222 raise ImportError
Brett Cannon69194272009-07-20 04:23:48 +0000223
Brett Cannon3b62ca82013-05-27 21:11:04 -0400224 def get_code(self, fullname):
225 """Method to return the code object for fullname.
226
227 Should return None if not applicable (e.g. built-in module).
228 Raise ImportError if the module cannot be found.
229 """
230 source = self.get_source(fullname)
231 if source is None:
232 return None
233 try:
234 path = self.get_filename(fullname)
235 except ImportError:
236 return self.source_to_code(source)
237 else:
238 return self.source_to_code(source, path)
239
Eric Snow7e70fa52013-10-04 20:28:52 -0600240_register(ExecutionLoader, machinery.ExtensionFileLoader)
Eric Snow51794452013-10-03 12:08:55 -0600241
Brett Cannon69194272009-07-20 04:23:48 +0000242
Eric Snow32439d62015-05-02 19:15:18 -0600243class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader):
Brett Cannon938d44d2012-04-22 19:58:33 -0400244
245 """Abstract base class partially implementing the ResourceLoader and
246 ExecutionLoader ABCs."""
247
248_register(FileLoader, machinery.SourceFileLoader,
Marc-Andre Lemburg4fe29c92012-04-25 02:31:37 +0200249 machinery.SourcelessFileLoader)
Brett Cannon938d44d2012-04-22 19:58:33 -0400250
251
Eric Snow32439d62015-05-02 19:15:18 -0600252class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader):
Brett Cannon2a922ed2009-03-09 03:35:50 +0000253
Brett Cannonf23e3742010-06-27 23:57:46 +0000254 """Abstract base class for loading source code (and optionally any
255 corresponding bytecode).
Brett Cannon2a922ed2009-03-09 03:35:50 +0000256
Brett Cannonf23e3742010-06-27 23:57:46 +0000257 To support loading from source code, the abstractmethods inherited from
258 ResourceLoader and ExecutionLoader need to be implemented. To also support
259 loading from bytecode, the optional methods specified directly by this ABC
260 is required.
261
262 Inherited abstractmethods not implemented in this ABC:
263
264 * ResourceLoader.get_data
265 * ExecutionLoader.get_filename
266
267 """
268
Raymond Hettingercd92f372011-01-13 02:31:25 +0000269 def path_mtime(self, path):
Raymond Hettingerd958ea72011-01-13 19:08:04 +0000270 """Return the (int) modification time for the path (str)."""
Antoine Pitrou5136ac02012-01-13 18:52:16 +0100271 if self.path_stats.__func__ is SourceLoader.path_stats:
Serhiy Storchaka55fe1ae2017-04-16 10:46:38 +0300272 raise OSError
Antoine Pitrou5136ac02012-01-13 18:52:16 +0100273 return int(self.path_stats(path)['mtime'])
274
275 def path_stats(self, path):
276 """Return a metadata dict for the source pointed to by the path (str).
277 Possible keys:
278 - 'mtime' (mandatory) is the numeric timestamp of last source
279 code modification;
280 - 'size' (optional) is the size in bytes of the source code.
281 """
282 if self.path_mtime.__func__ is SourceLoader.path_mtime:
Serhiy Storchaka55fe1ae2017-04-16 10:46:38 +0300283 raise OSError
Antoine Pitrou5136ac02012-01-13 18:52:16 +0100284 return {'mtime': self.path_mtime(path)}
Brett Cannon8d189072010-08-22 20:38:47 +0000285
Raymond Hettingercd92f372011-01-13 02:31:25 +0000286 def set_data(self, path, data):
Brett Cannon8d189072010-08-22 20:38:47 +0000287 """Write the bytes to the path (if possible).
288
Raymond Hettingerd958ea72011-01-13 19:08:04 +0000289 Accepts a str path and data as bytes.
290
Brett Cannon8d189072010-08-22 20:38:47 +0000291 Any needed intermediary directories are to be created. If for some
292 reason the file cannot be written because of permissions, fail
293 silently.
Brett Cannon8d189072010-08-22 20:38:47 +0000294 """
Brett Cannon8d189072010-08-22 20:38:47 +0000295
Brett Cannon938d44d2012-04-22 19:58:33 -0400296_register(SourceLoader, machinery.SourceFileLoader)
Brett Cannon4ac51502017-12-15 16:29:35 -0800297
298
Barry Warsaw5ec0fee2018-01-15 15:07:11 -0800299class ResourceReader(metaclass=abc.ABCMeta):
Brett Cannon4ac51502017-12-15 16:29:35 -0800300
Brett Cannonbca42182018-01-12 15:08:59 -0800301 """Abstract base class to provide resource-reading support.
302
303 Loaders that support resource reading are expected to implement
304 the ``get_resource_reader(fullname)`` method and have it either return None
305 or an object compatible with this ABC.
306 """
Brett Cannon4ac51502017-12-15 16:29:35 -0800307
308 @abc.abstractmethod
309 def open_resource(self, resource):
310 """Return an opened, file-like object for binary reading.
311
312 The 'resource' argument is expected to represent only a file name
313 and thus not contain any subdirectory components.
314
315 If the resource cannot be found, FileNotFoundError is raised.
316 """
317 raise FileNotFoundError
318
319 @abc.abstractmethod
320 def resource_path(self, resource):
321 """Return the file system path to the specified resource.
322
323 The 'resource' argument is expected to represent only a file name
324 and thus not contain any subdirectory components.
325
326 If the resource does not exist on the file system, raise
327 FileNotFoundError.
328 """
329 raise FileNotFoundError
330
331 @abc.abstractmethod
332 def is_resource(self, name):
333 """Return True if the named 'name' is consider a resource."""
334 raise FileNotFoundError
335
336 @abc.abstractmethod
337 def contents(self):
Brett Cannon3ab93652018-04-30 11:31:45 -0700338 """Return an iterable of strings over the contents of the package."""
339 return []
Barry Warsaw5ec0fee2018-01-15 15:07:11 -0800340
341
342_register(ResourceReader, machinery.SourceFileLoader)
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400343
344
345@runtime_checkable
346class Traversable(Protocol):
347 """
348 An object with a subset of pathlib.Path methods suitable for
349 traversing directories and opening files.
350 """
351
352 @abc.abstractmethod
353 def iterdir(self):
354 """
355 Yield Traversable objects in self
356 """
357
358 @abc.abstractmethod
359 def read_bytes(self):
360 """
361 Read contents of self as bytes
362 """
363
364 @abc.abstractmethod
365 def read_text(self, encoding=None):
366 """
367 Read contents of self as bytes
368 """
369
370 @abc.abstractmethod
371 def is_dir(self):
372 """
373 Return True if self is a dir
374 """
375
376 @abc.abstractmethod
377 def is_file(self):
378 """
379 Return True if self is a file
380 """
381
382 @abc.abstractmethod
383 def joinpath(self, child):
384 """
385 Return Traversable child in self
386 """
387
388 @abc.abstractmethod
389 def __truediv__(self, child):
390 """
391 Return Traversable child in self
392 """
393
394 @abc.abstractmethod
395 def open(self, mode='r', *args, **kwargs):
396 """
397 mode may be 'r' or 'rb' to open as text or binary. Return a handle
398 suitable for reading (same as pathlib.Path.open).
399
400 When opening as text, accepts encoding parameters such as those
401 accepted by io.TextIOWrapper.
402 """
403
404 @abc.abstractproperty
405 def name(self):
406 # type: () -> str
407 """
408 The base name of this object without any parent references.
409 """
410
411
412class TraversableResources(ResourceReader):
413 @abc.abstractmethod
414 def files(self):
415 """Return a Traversable object for the loaded package."""
416
417 def open_resource(self, resource):
418 return self.files().joinpath(resource).open('rb')
419
420 def resource_path(self, resource):
421 raise FileNotFoundError(resource)
422
423 def is_resource(self, path):
Jason R. Coombs843c2772020-06-07 21:00:51 -0400424 return self.files().joinpath(path).is_file()
Jason R. Coombs7f7e7062020-05-08 19:20:26 -0400425
426 def contents(self):
427 return (item.name for item in self.files().iterdir())