blob: 8e010c79c12668c36ca700a6926f0cce54e5eb83 [file] [log] [blame]
Guido van Rossuma4deda02002-12-23 16:30:00 +00001"""Utilities to support packages."""
2
Eric Snowd5f92232016-09-07 18:37:17 -07003from collections import namedtuple
Łukasz Langa6f692512013-06-05 12:20:24 +02004from functools import singledispatch as simplegeneric
Łukasz Langa6f692512013-06-05 12:20:24 +02005import importlib
Brett Cannone4f41de2013-06-16 13:13:40 -04006import importlib.util
Nick Coghlan862542e2013-10-27 00:27:39 +10007import importlib.machinery
Łukasz Langa6f692512013-06-05 12:20:24 +02008import os
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00009import os.path
Łukasz Langa6f692512013-06-05 12:20:24 +020010import sys
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000011from types import ModuleType
Brett Cannone4f41de2013-06-16 13:13:40 -040012import warnings
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000013
14__all__ = [
15 'get_importer', 'iter_importers', 'get_loader', 'find_loader',
Éric Araujoa4e2d4f2011-05-02 22:59:15 +020016 'walk_packages', 'iter_modules', 'get_data',
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000017 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path',
Eric Snowd5f92232016-09-07 18:37:17 -070018 'ModuleInfo',
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000019]
20
Eric Snow37148b22014-01-04 15:09:53 -070021
Eric Snowd5f92232016-09-07 18:37:17 -070022ModuleInfo = namedtuple('ModuleInfo', 'module_finder name ispkg')
23ModuleInfo.__doc__ = 'A namedtuple with minimal info about a module.'
24
25
Eric Snow37148b22014-01-04 15:09:53 -070026def _get_spec(finder, name):
27 """Return the finder-specific module spec."""
28 # Works with legacy finders.
29 try:
30 find_spec = finder.find_spec
31 except AttributeError:
32 loader = finder.find_module(name)
33 if loader is None:
34 return None
35 return importlib.util.spec_from_loader(name, loader)
36 else:
37 return find_spec(name)
38
39
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000040def read_code(stream):
41 # This helper is needed in order for the PEP 302 emulation to
42 # correctly handle compiled files
43 import marshal
44
45 magic = stream.read(4)
Brett Cannone4f41de2013-06-16 13:13:40 -040046 if magic != importlib.util.MAGIC_NUMBER:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000047 return None
48
Benjamin Peterson42aa93b2017-12-09 10:26:52 -080049 stream.read(12) # Skip rest of the header
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000050 return marshal.load(stream)
51
52
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000053def walk_packages(path=None, prefix='', onerror=None):
Eric Snowd5f92232016-09-07 18:37:17 -070054 """Yields ModuleInfo for all modules recursively
Thomas Wouters0e3f5912006-08-11 14:57:12 +000055 on path, or, if path is None, all accessible modules.
56
57 'path' should be either None or a list of paths to look for
58 modules in.
59
60 'prefix' is a string to output on the front of every module name
61 on output.
62
63 Note that this function must import all *packages* (NOT all
64 modules!) on the given path, in order to access the __path__
65 attribute to find submodules.
66
67 'onerror' is a function which gets called with one argument (the
68 name of the package which was being imported) if any exception
69 occurs while trying to import a package. If no onerror function is
70 supplied, ImportErrors are caught and ignored, while all other
71 exceptions are propagated, terminating the search.
72
73 Examples:
74
75 # list all modules python can access
76 walk_packages()
77
78 # list all submodules of ctypes
79 walk_packages(ctypes.__path__, ctypes.__name__+'.')
80 """
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000081
Thomas Wouters477c8d52006-05-27 19:21:47 +000082 def seen(p, m={}):
83 if p in m:
84 return True
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000085 m[p] = True
86
Eric Snowd5f92232016-09-07 18:37:17 -070087 for info in iter_modules(path, prefix):
88 yield info
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000089
Eric Snowd5f92232016-09-07 18:37:17 -070090 if info.ispkg:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000091 try:
Eric Snowd5f92232016-09-07 18:37:17 -070092 __import__(info.name)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000093 except ImportError:
94 if onerror is not None:
Eric Snowd5f92232016-09-07 18:37:17 -070095 onerror(info.name)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000096 except Exception:
97 if onerror is not None:
Eric Snowd5f92232016-09-07 18:37:17 -070098 onerror(info.name)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000099 else:
100 raise
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000101 else:
Eric Snowd5f92232016-09-07 18:37:17 -0700102 path = getattr(sys.modules[info.name], '__path__', None) or []
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000103
104 # don't traverse path items we've seen before
105 path = [p for p in path if not seen(p)]
106
Eric Snowd5f92232016-09-07 18:37:17 -0700107 yield from walk_packages(path, info.name+'.', onerror)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000108
109
110def iter_modules(path=None, prefix=''):
Eric Snowd5f92232016-09-07 18:37:17 -0700111 """Yields ModuleInfo for all submodules on path,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000112 or, if path is None, all top-level modules on sys.path.
113
114 'path' should be either None or a list of paths to look for
115 modules in.
116
117 'prefix' is a string to output on the front of every module name
118 on output.
119 """
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000120 if path is None:
121 importers = iter_importers()
Sanyam Khuranab9c3da52017-06-13 22:41:14 +0530122 elif isinstance(path, str):
123 raise ValueError("path must be None or list of paths to look for "
124 "modules in")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000125 else:
126 importers = map(get_importer, path)
127
128 yielded = {}
129 for i in importers:
130 for name, ispkg in iter_importer_modules(i, prefix):
131 if name not in yielded:
132 yielded[name] = 1
Eric Snowd5f92232016-09-07 18:37:17 -0700133 yield ModuleInfo(i, name, ispkg)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000134
135
Łukasz Langa6f692512013-06-05 12:20:24 +0200136@simplegeneric
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000137def iter_importer_modules(importer, prefix=''):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000138 if not hasattr(importer, 'iter_modules'):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000139 return []
140 return importer.iter_modules(prefix)
141
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000142
Nick Coghlan8ecf5042012-07-15 21:19:18 +1000143# Implement a file walker for the normal importlib path hook
144def _iter_file_finder_modules(importer, prefix=''):
145 if importer.path is None or not os.path.isdir(importer.path):
146 return
147
148 yielded = {}
149 import inspect
150 try:
151 filenames = os.listdir(importer.path)
152 except OSError:
153 # ignore unreadable directories like import does
154 filenames = []
155 filenames.sort() # handle packages before same-named modules
156
157 for fn in filenames:
158 modname = inspect.getmodulename(fn)
159 if modname=='__init__' or modname in yielded:
160 continue
161
162 path = os.path.join(importer.path, fn)
163 ispkg = False
164
165 if not modname and os.path.isdir(path) and '.' not in fn:
166 modname = fn
167 try:
168 dircontents = os.listdir(path)
169 except OSError:
170 # ignore unreadable directories like import does
171 dircontents = []
172 for fn in dircontents:
173 subname = inspect.getmodulename(fn)
174 if subname=='__init__':
175 ispkg = True
176 break
177 else:
178 continue # not a package
179
180 if modname and '.' not in modname:
181 yielded[modname] = 1
182 yield prefix + modname, ispkg
183
184iter_importer_modules.register(
185 importlib.machinery.FileFinder, _iter_file_finder_modules)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000186
Brett Cannone4f41de2013-06-16 13:13:40 -0400187
188def _import_imp():
189 global imp
190 with warnings.catch_warnings():
Brett Cannonc0d91af2015-10-16 12:21:37 -0700191 warnings.simplefilter('ignore', DeprecationWarning)
Brett Cannone4f41de2013-06-16 13:13:40 -0400192 imp = importlib.import_module('imp')
193
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000194class ImpImporter:
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700195 """PEP 302 Finder that wraps Python's "classic" import algorithm
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000196
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700197 ImpImporter(dirname) produces a PEP 302 finder that searches that
198 directory. ImpImporter(None) produces a PEP 302 finder that searches
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000199 the current sys.path, plus any modules that are frozen or built-in.
200
201 Note that ImpImporter does not currently support being used by placement
202 on sys.meta_path.
203 """
204
205 def __init__(self, path=None):
Brett Cannone4f41de2013-06-16 13:13:40 -0400206 global imp
Brett Cannondc6d3e12021-04-03 15:31:15 -0700207 warnings.warn("This emulation is deprecated and slated for removal "
208 "in Python 3.12; use 'importlib' instead",
Nick Coghlan85e729e2012-07-15 18:09:52 +1000209 DeprecationWarning)
Brett Cannone4f41de2013-06-16 13:13:40 -0400210 _import_imp()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000211 self.path = path
212
213 def find_module(self, fullname, path=None):
214 # Note: we ignore 'path' argument since it is only used via meta_path
215 subname = fullname.split(".")[-1]
216 if subname != fullname and self.path is None:
217 return None
218 if self.path is None:
219 path = None
220 else:
221 path = [os.path.realpath(self.path)]
222 try:
223 file, filename, etc = imp.find_module(subname, path)
224 except ImportError:
225 return None
226 return ImpLoader(fullname, file, filename, etc)
227
228 def iter_modules(self, prefix=''):
229 if self.path is None or not os.path.isdir(self.path):
230 return
231
232 yielded = {}
233 import inspect
Ned Deilyed27df72011-10-06 14:19:08 -0700234 try:
235 filenames = os.listdir(self.path)
236 except OSError:
237 # ignore unreadable directories like import does
238 filenames = []
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000239 filenames.sort() # handle packages before same-named modules
240
241 for fn in filenames:
242 modname = inspect.getmodulename(fn)
243 if modname=='__init__' or modname in yielded:
244 continue
245
246 path = os.path.join(self.path, fn)
247 ispkg = False
248
249 if not modname and os.path.isdir(path) and '.' not in fn:
250 modname = fn
Ned Deilyed27df72011-10-06 14:19:08 -0700251 try:
252 dircontents = os.listdir(path)
253 except OSError:
254 # ignore unreadable directories like import does
255 dircontents = []
256 for fn in dircontents:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000257 subname = inspect.getmodulename(fn)
258 if subname=='__init__':
259 ispkg = True
260 break
261 else:
262 continue # not a package
263
264 if modname and '.' not in modname:
265 yielded[modname] = 1
266 yield prefix + modname, ispkg
267
268
269class ImpLoader:
270 """PEP 302 Loader that wraps Python's "classic" import algorithm
271 """
272 code = source = None
273
274 def __init__(self, fullname, file, filename, etc):
Brett Cannondc6d3e12021-04-03 15:31:15 -0700275 warnings.warn("This emulation is deprecated and slated for removal in "
276 "Python 3.12; use 'importlib' instead",
Brett Cannone4f41de2013-06-16 13:13:40 -0400277 DeprecationWarning)
278 _import_imp()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000279 self.file = file
280 self.filename = filename
281 self.fullname = fullname
282 self.etc = etc
283
284 def load_module(self, fullname):
285 self._reopen()
286 try:
287 mod = imp.load_module(fullname, self.file, self.filename, self.etc)
288 finally:
289 if self.file:
290 self.file.close()
291 # Note: we don't set __loader__ because we want the module to look
292 # normal; i.e. this is just a wrapper for standard import machinery
293 return mod
294
295 def get_data(self, pathname):
Brett Cannon1ab58df2010-10-29 22:36:53 +0000296 with open(pathname, "rb") as file:
297 return file.read()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000298
299 def _reopen(self):
300 if self.file and self.file.closed:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000301 mod_type = self.etc[2]
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000302 if mod_type==imp.PY_SOURCE:
Victor Stinner4e86d5b2011-05-04 13:55:36 +0200303 self.file = open(self.filename, 'r')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000304 elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION):
305 self.file = open(self.filename, 'rb')
306
307 def _fix_name(self, fullname):
308 if fullname is None:
309 fullname = self.fullname
310 elif fullname != self.fullname:
311 raise ImportError("Loader for module %s cannot handle "
312 "module %s" % (self.fullname, fullname))
313 return fullname
314
315 def is_package(self, fullname):
316 fullname = self._fix_name(fullname)
317 return self.etc[2]==imp.PKG_DIRECTORY
318
319 def get_code(self, fullname=None):
320 fullname = self._fix_name(fullname)
321 if self.code is None:
322 mod_type = self.etc[2]
323 if mod_type==imp.PY_SOURCE:
324 source = self.get_source(fullname)
325 self.code = compile(source, self.filename, 'exec')
326 elif mod_type==imp.PY_COMPILED:
327 self._reopen()
328 try:
329 self.code = read_code(self.file)
330 finally:
331 self.file.close()
332 elif mod_type==imp.PKG_DIRECTORY:
333 self.code = self._get_delegate().get_code()
334 return self.code
335
336 def get_source(self, fullname=None):
337 fullname = self._fix_name(fullname)
338 if self.source is None:
339 mod_type = self.etc[2]
340 if mod_type==imp.PY_SOURCE:
341 self._reopen()
342 try:
343 self.source = self.file.read()
344 finally:
345 self.file.close()
346 elif mod_type==imp.PY_COMPILED:
347 if os.path.exists(self.filename[:-1]):
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +0100348 with open(self.filename[:-1], 'r') as f:
349 self.source = f.read()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000350 elif mod_type==imp.PKG_DIRECTORY:
351 self.source = self._get_delegate().get_source()
352 return self.source
353
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000354 def _get_delegate(self):
Eric Snow37148b22014-01-04 15:09:53 -0700355 finder = ImpImporter(self.filename)
356 spec = _get_spec(finder, '__init__')
357 return spec.loader
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000358
359 def get_filename(self, fullname=None):
360 fullname = self._fix_name(fullname)
361 mod_type = self.etc[2]
Éric Araujo0cfb81d2011-09-17 03:35:57 +0200362 if mod_type==imp.PKG_DIRECTORY:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000363 return self._get_delegate().get_filename()
Éric Araujo0cfb81d2011-09-17 03:35:57 +0200364 elif mod_type in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000365 return self.filename
366 return None
367
368
369try:
370 import zipimport
371 from zipimport import zipimporter
372
373 def iter_zipimport_modules(importer, prefix=''):
Alexandre Vassalotti515a74f2009-07-05 06:42:44 +0000374 dirlist = sorted(zipimport._zip_directory_cache[importer.archive])
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000375 _prefix = importer.prefix
376 plen = len(_prefix)
377 yielded = {}
378 import inspect
379 for fn in dirlist:
380 if not fn.startswith(_prefix):
381 continue
382
383 fn = fn[plen:].split(os.sep)
384
385 if len(fn)==2 and fn[1].startswith('__init__.py'):
386 if fn[0] not in yielded:
387 yielded[fn[0]] = 1
Łukasz Langa0d18c152016-06-11 18:02:46 -0700388 yield prefix + fn[0], True
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000389
390 if len(fn)!=1:
391 continue
392
393 modname = inspect.getmodulename(fn[0])
394 if modname=='__init__':
395 continue
396
397 if modname and '.' not in modname and modname not in yielded:
398 yielded[modname] = 1
399 yield prefix + modname, False
400
401 iter_importer_modules.register(zipimporter, iter_zipimport_modules)
402
403except ImportError:
404 pass
405
406
407def get_importer(path_item):
Senthil Kumaran46720602016-09-05 17:11:51 -0700408 """Retrieve a finder for the given path item
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000409
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700410 The returned finder is cached in sys.path_importer_cache
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000411 if it was newly created by a path hook.
412
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000413 The cache (or part of it) can be cleared manually if a
414 rescan of sys.path_hooks is necessary.
415 """
416 try:
417 importer = sys.path_importer_cache[path_item]
418 except KeyError:
419 for path_hook in sys.path_hooks:
420 try:
421 importer = path_hook(path_item)
Brett Cannone0d88a12012-04-25 20:54:04 -0400422 sys.path_importer_cache.setdefault(path_item, importer)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000423 break
424 except ImportError:
425 pass
426 else:
Nick Coghlan85e729e2012-07-15 18:09:52 +1000427 importer = None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000428 return importer
429
430
431def iter_importers(fullname=""):
Senthil Kumaran46720602016-09-05 17:11:51 -0700432 """Yield finders for the given module name
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000433
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700434 If fullname contains a '.', the finders will be for the package
Nick Coghlan85e729e2012-07-15 18:09:52 +1000435 containing fullname, otherwise they will be all registered top level
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700436 finders (i.e. those on both sys.meta_path and sys.path_hooks).
Nick Coghlan85e729e2012-07-15 18:09:52 +1000437
438 If the named module is in a package, that package is imported as a side
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000439 effect of invoking this function.
440
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700441 If no module name is specified, all top level finders are produced.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000442 """
443 if fullname.startswith('.'):
Nick Coghlan85e729e2012-07-15 18:09:52 +1000444 msg = "Relative module name {!r} not supported".format(fullname)
445 raise ImportError(msg)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000446 if '.' in fullname:
447 # Get the containing package's __path__
Nick Coghlan85e729e2012-07-15 18:09:52 +1000448 pkg_name = fullname.rpartition(".")[0]
Nick Coghlanc4e0d982013-04-14 22:30:42 +1000449 pkg = importlib.import_module(pkg_name)
450 path = getattr(pkg, '__path__', None)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000451 if path is None:
452 return
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000453 else:
Andrew Svetlov2aa5f3c2012-10-07 23:21:15 +0300454 yield from sys.meta_path
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000455 path = sys.path
456 for item in path:
457 yield get_importer(item)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000458
Eric Snowb523f842013-11-22 09:05:39 -0700459
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000460def get_loader(module_or_name):
Senthil Kumaran46720602016-09-05 17:11:51 -0700461 """Get a "loader" object for module_or_name
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000462
Nick Coghlan85e729e2012-07-15 18:09:52 +1000463 Returns None if the module cannot be found or imported.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000464 If the named module is not already imported, its containing package
465 (if any) is imported, in order to establish the package __path__.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000466 """
467 if module_or_name in sys.modules:
468 module_or_name = sys.modules[module_or_name]
Brett Cannon8447c702014-05-23 12:30:37 -0400469 if module_or_name is None:
470 return None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000471 if isinstance(module_or_name, ModuleType):
472 module = module_or_name
Thomas Wouters477c8d52006-05-27 19:21:47 +0000473 loader = getattr(module, '__loader__', None)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000474 if loader is not None:
475 return loader
Eric Snow658af312014-04-19 00:13:23 -0600476 if getattr(module, '__spec__', None) is None:
477 return None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000478 fullname = module.__name__
479 else:
480 fullname = module_or_name
481 return find_loader(fullname)
482
Nick Coghlan85e729e2012-07-15 18:09:52 +1000483
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000484def find_loader(fullname):
Senthil Kumaran46720602016-09-05 17:11:51 -0700485 """Find a "loader" object for fullname
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000486
Nick Coghlan62b4b9e2014-03-04 20:39:42 +1000487 This is a backwards compatibility wrapper around
488 importlib.util.find_spec that converts most failures to ImportError
489 and only returns the loader rather than the full spec
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000490 """
Nick Coghlan85e729e2012-07-15 18:09:52 +1000491 if fullname.startswith('.'):
492 msg = "Relative module name {!r} not supported".format(fullname)
493 raise ImportError(msg)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000494 try:
Nick Coghlan62b4b9e2014-03-04 20:39:42 +1000495 spec = importlib.util.find_spec(fullname)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000496 except (ImportError, AttributeError, TypeError, ValueError) as ex:
497 # This hack fixes an impedance mismatch between pkgutil and
Andrew Svetlov5b898402012-12-18 21:26:36 +0200498 # importlib, where the latter raises other errors for cases where
Andrew Svetlov1f415cf2012-12-19 22:54:47 +0200499 # pkgutil previously raised ImportError
Nick Coghlan85e729e2012-07-15 18:09:52 +1000500 msg = "Error while finding loader for {!r} ({}: {})"
501 raise ImportError(msg.format(fullname, type(ex), ex)) from ex
Brett Cannon8447c702014-05-23 12:30:37 -0400502 return spec.loader if spec is not None else None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000503
Guido van Rossuma4deda02002-12-23 16:30:00 +0000504
505def extend_path(path, name):
506 """Extend a package's path.
507
508 Intended use is to place the following code in a package's __init__.py:
509
510 from pkgutil import extend_path
511 __path__ = extend_path(__path__, __name__)
512
513 This will add to the package's __path__ all subdirectories of
514 directories on sys.path named after the package. This is useful
515 if one wants to distribute different parts of a single logical
516 package as multiple directories.
517
518 It also looks for *.pkg files beginning where * matches the name
519 argument. This feature is similar to *.pth files (see site.py),
520 except that it doesn't special-case lines starting with 'import'.
521 A *.pkg file is trusted at face value: apart from checking for
522 duplicates, all entries found in a *.pkg file are added to the
523 path, regardless of whether they are exist the filesystem. (This
524 is a feature.)
525
526 If the input path is not a list (as is the case for frozen
527 packages) it is returned unchanged. The input path is not
528 modified; an extended copy is returned. Items are only appended
529 to the copy at the end.
530
531 It is assumed that sys.path is a sequence. Items of sys.path that
532 are not (unicode or 8-bit) strings referring to existing
533 directories are ignored. Unicode items of sys.path that cause
534 errors when used as filenames may cause this function to raise an
535 exception (in line with os.path.isdir() behavior).
536 """
537
538 if not isinstance(path, list):
539 # This could happen e.g. when this is called from inside a
540 # frozen package. Return the path unchanged in that case.
541 return path
542
Skip Montanaro7a98be22007-08-16 14:35:24 +0000543 sname_pkg = name + ".pkg"
Guido van Rossuma4deda02002-12-23 16:30:00 +0000544
545 path = path[:] # Start with a copy of the existing path
546
Antoine Pitroub2dd8802012-07-09 21:23:58 +0200547 parent_package, _, final_name = name.rpartition('.')
548 if parent_package:
549 try:
550 search_path = sys.modules[parent_package].__path__
551 except (KeyError, AttributeError):
552 # We can't do anything: find_loader() returns None when
553 # passed a dotted name.
554 return path
555 else:
556 search_path = sys.path
557
558 for dir in search_path:
Eric V. Smith984b11f2012-05-24 20:21:04 -0400559 if not isinstance(dir, str):
Guido van Rossuma4deda02002-12-23 16:30:00 +0000560 continue
Eric V. Smith984b11f2012-05-24 20:21:04 -0400561
562 finder = get_importer(dir)
563 if finder is not None:
Eric Snow02b9f9d2014-01-06 20:42:59 -0700564 portions = []
565 if hasattr(finder, 'find_spec'):
566 spec = finder.find_spec(final_name)
567 if spec is not None:
568 portions = spec.submodule_search_locations or []
Eric V. Smith984b11f2012-05-24 20:21:04 -0400569 # Is this finder PEP 420 compliant?
Eric Snow02b9f9d2014-01-06 20:42:59 -0700570 elif hasattr(finder, 'find_loader'):
571 _, portions = finder.find_loader(final_name)
Eric V. Smith984b11f2012-05-24 20:21:04 -0400572
573 for portion in portions:
574 # XXX This may still add duplicate entries to path on
575 # case-insensitive filesystems
576 if portion not in path:
577 path.append(portion)
578
Guido van Rossuma4deda02002-12-23 16:30:00 +0000579 # XXX Is this the right thing for subpackages like zope.app?
580 # It looks for a file named "zope.app.pkg"
581 pkgfile = os.path.join(dir, sname_pkg)
582 if os.path.isfile(pkgfile):
583 try:
584 f = open(pkgfile)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200585 except OSError as msg:
Guido van Rossuma4deda02002-12-23 16:30:00 +0000586 sys.stderr.write("Can't open %s: %s\n" %
587 (pkgfile, msg))
588 else:
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +0100589 with f:
590 for line in f:
591 line = line.rstrip('\n')
592 if not line or line.startswith('#'):
593 continue
594 path.append(line) # Don't check for existence!
Guido van Rossuma4deda02002-12-23 16:30:00 +0000595
596 return path
Christian Heimesdae2a892008-04-19 00:55:37 +0000597
Eric Snowb523f842013-11-22 09:05:39 -0700598
Christian Heimesdae2a892008-04-19 00:55:37 +0000599def get_data(package, resource):
600 """Get a resource from a package.
601
602 This is a wrapper round the PEP 302 loader get_data API. The package
603 argument should be the name of a package, in standard module format
604 (foo.bar). The resource argument should be in the form of a relative
605 filename, using '/' as the path separator. The parent directory name '..'
606 is not allowed, and nor is a rooted name (starting with a '/').
607
608 The function returns a binary string, which is the contents of the
609 specified resource.
610
611 For packages located in the filesystem, which have already been imported,
612 this is the rough equivalent of
613
614 d = os.path.dirname(sys.modules[package].__file__)
615 data = open(os.path.join(d, resource), 'rb').read()
616
617 If the package cannot be located or loaded, or it uses a PEP 302 loader
618 which does not support get_data(), then None is returned.
619 """
620
Eric Snow6029e082014-01-25 15:32:46 -0700621 spec = importlib.util.find_spec(package)
Eric Snowb523f842013-11-22 09:05:39 -0700622 if spec is None:
623 return None
624 loader = spec.loader
Christian Heimesdae2a892008-04-19 00:55:37 +0000625 if loader is None or not hasattr(loader, 'get_data'):
626 return None
Eric Snowb523f842013-11-22 09:05:39 -0700627 # XXX needs test
628 mod = (sys.modules.get(package) or
Brett Cannon2a17bde2014-05-30 14:55:29 -0400629 importlib._bootstrap._load(spec))
Christian Heimesdae2a892008-04-19 00:55:37 +0000630 if mod is None or not hasattr(mod, '__file__'):
631 return None
632
633 # Modify the resource name to be compatible with the loader.get_data
634 # signature - an os.path format "filename" starting with the dirname of
635 # the package's __file__
636 parts = resource.split('/')
637 parts.insert(0, os.path.dirname(mod.__file__))
638 resource_name = os.path.join(*parts)
639 return loader.get_data(resource_name)
Vinay Sajip1ed61612020-02-14 22:02:13 +0000640
641
Victor Stinner98ce7b12020-06-17 19:11:50 +0200642_NAME_PATTERN = None
Vinay Sajip1ed61612020-02-14 22:02:13 +0000643
644def resolve_name(name):
645 """
646 Resolve a name to an object.
647
648 It is expected that `name` will be a string in one of the following
649 formats, where W is shorthand for a valid Python identifier and dot stands
650 for a literal period in these pseudo-regexes:
651
652 W(.W)*
653 W(.W)*:(W(.W)*)?
654
655 The first form is intended for backward compatibility only. It assumes that
656 some part of the dotted name is a package, and the rest is an object
657 somewhere within that package, possibly nested inside other objects.
658 Because the place where the package stops and the object hierarchy starts
659 can't be inferred by inspection, repeated attempts to import must be done
660 with this form.
661
662 In the second form, the caller makes the division point clear through the
663 provision of a single colon: the dotted name to the left of the colon is a
664 package to be imported, and the dotted name to the right is the object
665 hierarchy within that package. Only one import is needed in this form. If
666 it ends with the colon, then a module object is returned.
667
668 The function will return an object (which might be a module), or raise one
669 of the following exceptions:
670
671 ValueError - if `name` isn't in a recognised format
672 ImportError - if an import failed when it shouldn't have
673 AttributeError - if a failure occurred when traversing the object hierarchy
Łukasz Langa8c1e1da2021-09-22 01:33:59 +0200674 within the imported package to get to the desired object.
Vinay Sajip1ed61612020-02-14 22:02:13 +0000675 """
Victor Stinner98ce7b12020-06-17 19:11:50 +0200676 global _NAME_PATTERN
677 if _NAME_PATTERN is None:
678 # Lazy import to speedup Python startup time
679 import re
680 dotted_words = r'(?!\d)(\w+)(\.(?!\d)(\w+))*'
681 _NAME_PATTERN = re.compile(f'^(?P<pkg>{dotted_words})'
682 f'(?P<cln>:(?P<obj>{dotted_words})?)?$',
683 re.UNICODE)
684
Vinay Sajip1ed61612020-02-14 22:02:13 +0000685 m = _NAME_PATTERN.match(name)
686 if not m:
687 raise ValueError(f'invalid format: {name!r}')
Vinay Sajip4f17c5c2020-02-28 14:26:27 +0000688 gd = m.groupdict()
689 if gd.get('cln'):
Vinay Sajip1ed61612020-02-14 22:02:13 +0000690 # there is a colon - a one-step import is all that's needed
Vinay Sajip4f17c5c2020-02-28 14:26:27 +0000691 mod = importlib.import_module(gd['pkg'])
692 parts = gd.get('obj')
693 parts = parts.split('.') if parts else []
Vinay Sajip1ed61612020-02-14 22:02:13 +0000694 else:
695 # no colon - have to iterate to find the package boundary
696 parts = name.split('.')
697 modname = parts.pop(0)
698 # first part *must* be a module/package.
699 mod = importlib.import_module(modname)
700 while parts:
701 p = parts[0]
702 s = f'{modname}.{p}'
703 try:
704 mod = importlib.import_module(s)
705 parts.pop(0)
706 modname = s
707 except ImportError:
708 break
709 # if we reach this point, mod is the module, already imported, and
710 # parts is the list of parts in the object hierarchy to be traversed, or
711 # an empty list if just the module is wanted.
712 result = mod
713 for p in parts:
714 result = getattr(result, p)
715 return result