blob: 4c184678a29128f03c9edbcdf0dd47f35dcbe9bd [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
Vinay Sajip1ed61612020-02-14 22:02:13 +000010import re
Łukasz Langa6f692512013-06-05 12:20:24 +020011import sys
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000012from types import ModuleType
Brett Cannone4f41de2013-06-16 13:13:40 -040013import warnings
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000014
15__all__ = [
16 'get_importer', 'iter_importers', 'get_loader', 'find_loader',
Éric Araujoa4e2d4f2011-05-02 22:59:15 +020017 'walk_packages', 'iter_modules', 'get_data',
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000018 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path',
Eric Snowd5f92232016-09-07 18:37:17 -070019 'ModuleInfo',
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000020]
21
Eric Snow37148b22014-01-04 15:09:53 -070022
Eric Snowd5f92232016-09-07 18:37:17 -070023ModuleInfo = namedtuple('ModuleInfo', 'module_finder name ispkg')
24ModuleInfo.__doc__ = 'A namedtuple with minimal info about a module.'
25
26
Eric Snow37148b22014-01-04 15:09:53 -070027def _get_spec(finder, name):
28 """Return the finder-specific module spec."""
29 # Works with legacy finders.
30 try:
31 find_spec = finder.find_spec
32 except AttributeError:
33 loader = finder.find_module(name)
34 if loader is None:
35 return None
36 return importlib.util.spec_from_loader(name, loader)
37 else:
38 return find_spec(name)
39
40
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000041def read_code(stream):
42 # This helper is needed in order for the PEP 302 emulation to
43 # correctly handle compiled files
44 import marshal
45
46 magic = stream.read(4)
Brett Cannone4f41de2013-06-16 13:13:40 -040047 if magic != importlib.util.MAGIC_NUMBER:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000048 return None
49
Benjamin Peterson42aa93b2017-12-09 10:26:52 -080050 stream.read(12) # Skip rest of the header
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000051 return marshal.load(stream)
52
53
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000054def walk_packages(path=None, prefix='', onerror=None):
Eric Snowd5f92232016-09-07 18:37:17 -070055 """Yields ModuleInfo for all modules recursively
Thomas Wouters0e3f5912006-08-11 14:57:12 +000056 on path, or, if path is None, all accessible modules.
57
58 'path' should be either None or a list of paths to look for
59 modules in.
60
61 'prefix' is a string to output on the front of every module name
62 on output.
63
64 Note that this function must import all *packages* (NOT all
65 modules!) on the given path, in order to access the __path__
66 attribute to find submodules.
67
68 'onerror' is a function which gets called with one argument (the
69 name of the package which was being imported) if any exception
70 occurs while trying to import a package. If no onerror function is
71 supplied, ImportErrors are caught and ignored, while all other
72 exceptions are propagated, terminating the search.
73
74 Examples:
75
76 # list all modules python can access
77 walk_packages()
78
79 # list all submodules of ctypes
80 walk_packages(ctypes.__path__, ctypes.__name__+'.')
81 """
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000082
Thomas Wouters477c8d52006-05-27 19:21:47 +000083 def seen(p, m={}):
84 if p in m:
85 return True
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000086 m[p] = True
87
Eric Snowd5f92232016-09-07 18:37:17 -070088 for info in iter_modules(path, prefix):
89 yield info
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000090
Eric Snowd5f92232016-09-07 18:37:17 -070091 if info.ispkg:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000092 try:
Eric Snowd5f92232016-09-07 18:37:17 -070093 __import__(info.name)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000094 except ImportError:
95 if onerror is not None:
Eric Snowd5f92232016-09-07 18:37:17 -070096 onerror(info.name)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000097 except Exception:
98 if onerror is not None:
Eric Snowd5f92232016-09-07 18:37:17 -070099 onerror(info.name)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000100 else:
101 raise
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000102 else:
Eric Snowd5f92232016-09-07 18:37:17 -0700103 path = getattr(sys.modules[info.name], '__path__', None) or []
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000104
105 # don't traverse path items we've seen before
106 path = [p for p in path if not seen(p)]
107
Eric Snowd5f92232016-09-07 18:37:17 -0700108 yield from walk_packages(path, info.name+'.', onerror)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000109
110
111def iter_modules(path=None, prefix=''):
Eric Snowd5f92232016-09-07 18:37:17 -0700112 """Yields ModuleInfo for all submodules on path,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000113 or, if path is None, all top-level modules on sys.path.
114
115 'path' should be either None or a list of paths to look for
116 modules in.
117
118 'prefix' is a string to output on the front of every module name
119 on output.
120 """
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000121 if path is None:
122 importers = iter_importers()
Sanyam Khuranab9c3da52017-06-13 22:41:14 +0530123 elif isinstance(path, str):
124 raise ValueError("path must be None or list of paths to look for "
125 "modules in")
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000126 else:
127 importers = map(get_importer, path)
128
129 yielded = {}
130 for i in importers:
131 for name, ispkg in iter_importer_modules(i, prefix):
132 if name not in yielded:
133 yielded[name] = 1
Eric Snowd5f92232016-09-07 18:37:17 -0700134 yield ModuleInfo(i, name, ispkg)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000135
136
Łukasz Langa6f692512013-06-05 12:20:24 +0200137@simplegeneric
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000138def iter_importer_modules(importer, prefix=''):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000139 if not hasattr(importer, 'iter_modules'):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000140 return []
141 return importer.iter_modules(prefix)
142
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000143
Nick Coghlan8ecf5042012-07-15 21:19:18 +1000144# Implement a file walker for the normal importlib path hook
145def _iter_file_finder_modules(importer, prefix=''):
146 if importer.path is None or not os.path.isdir(importer.path):
147 return
148
149 yielded = {}
150 import inspect
151 try:
152 filenames = os.listdir(importer.path)
153 except OSError:
154 # ignore unreadable directories like import does
155 filenames = []
156 filenames.sort() # handle packages before same-named modules
157
158 for fn in filenames:
159 modname = inspect.getmodulename(fn)
160 if modname=='__init__' or modname in yielded:
161 continue
162
163 path = os.path.join(importer.path, fn)
164 ispkg = False
165
166 if not modname and os.path.isdir(path) and '.' not in fn:
167 modname = fn
168 try:
169 dircontents = os.listdir(path)
170 except OSError:
171 # ignore unreadable directories like import does
172 dircontents = []
173 for fn in dircontents:
174 subname = inspect.getmodulename(fn)
175 if subname=='__init__':
176 ispkg = True
177 break
178 else:
179 continue # not a package
180
181 if modname and '.' not in modname:
182 yielded[modname] = 1
183 yield prefix + modname, ispkg
184
185iter_importer_modules.register(
186 importlib.machinery.FileFinder, _iter_file_finder_modules)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000187
Brett Cannone4f41de2013-06-16 13:13:40 -0400188
189def _import_imp():
190 global imp
191 with warnings.catch_warnings():
Brett Cannonc0d91af2015-10-16 12:21:37 -0700192 warnings.simplefilter('ignore', DeprecationWarning)
Brett Cannone4f41de2013-06-16 13:13:40 -0400193 imp = importlib.import_module('imp')
194
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000195class ImpImporter:
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700196 """PEP 302 Finder that wraps Python's "classic" import algorithm
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000197
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700198 ImpImporter(dirname) produces a PEP 302 finder that searches that
199 directory. ImpImporter(None) produces a PEP 302 finder that searches
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000200 the current sys.path, plus any modules that are frozen or built-in.
201
202 Note that ImpImporter does not currently support being used by placement
203 on sys.meta_path.
204 """
205
206 def __init__(self, path=None):
Brett Cannone4f41de2013-06-16 13:13:40 -0400207 global imp
208 warnings.warn("This emulation is deprecated, 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 Cannone4f41de2013-06-16 13:13:40 -0400275 warnings.warn("This emulation is deprecated, use 'importlib' instead",
276 DeprecationWarning)
277 _import_imp()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000278 self.file = file
279 self.filename = filename
280 self.fullname = fullname
281 self.etc = etc
282
283 def load_module(self, fullname):
284 self._reopen()
285 try:
286 mod = imp.load_module(fullname, self.file, self.filename, self.etc)
287 finally:
288 if self.file:
289 self.file.close()
290 # Note: we don't set __loader__ because we want the module to look
291 # normal; i.e. this is just a wrapper for standard import machinery
292 return mod
293
294 def get_data(self, pathname):
Brett Cannon1ab58df2010-10-29 22:36:53 +0000295 with open(pathname, "rb") as file:
296 return file.read()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000297
298 def _reopen(self):
299 if self.file and self.file.closed:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000300 mod_type = self.etc[2]
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000301 if mod_type==imp.PY_SOURCE:
Victor Stinner4e86d5b2011-05-04 13:55:36 +0200302 self.file = open(self.filename, 'r')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000303 elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION):
304 self.file = open(self.filename, 'rb')
305
306 def _fix_name(self, fullname):
307 if fullname is None:
308 fullname = self.fullname
309 elif fullname != self.fullname:
310 raise ImportError("Loader for module %s cannot handle "
311 "module %s" % (self.fullname, fullname))
312 return fullname
313
314 def is_package(self, fullname):
315 fullname = self._fix_name(fullname)
316 return self.etc[2]==imp.PKG_DIRECTORY
317
318 def get_code(self, fullname=None):
319 fullname = self._fix_name(fullname)
320 if self.code is None:
321 mod_type = self.etc[2]
322 if mod_type==imp.PY_SOURCE:
323 source = self.get_source(fullname)
324 self.code = compile(source, self.filename, 'exec')
325 elif mod_type==imp.PY_COMPILED:
326 self._reopen()
327 try:
328 self.code = read_code(self.file)
329 finally:
330 self.file.close()
331 elif mod_type==imp.PKG_DIRECTORY:
332 self.code = self._get_delegate().get_code()
333 return self.code
334
335 def get_source(self, fullname=None):
336 fullname = self._fix_name(fullname)
337 if self.source is None:
338 mod_type = self.etc[2]
339 if mod_type==imp.PY_SOURCE:
340 self._reopen()
341 try:
342 self.source = self.file.read()
343 finally:
344 self.file.close()
345 elif mod_type==imp.PY_COMPILED:
346 if os.path.exists(self.filename[:-1]):
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +0100347 with open(self.filename[:-1], 'r') as f:
348 self.source = f.read()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000349 elif mod_type==imp.PKG_DIRECTORY:
350 self.source = self._get_delegate().get_source()
351 return self.source
352
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000353 def _get_delegate(self):
Eric Snow37148b22014-01-04 15:09:53 -0700354 finder = ImpImporter(self.filename)
355 spec = _get_spec(finder, '__init__')
356 return spec.loader
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000357
358 def get_filename(self, fullname=None):
359 fullname = self._fix_name(fullname)
360 mod_type = self.etc[2]
Éric Araujo0cfb81d2011-09-17 03:35:57 +0200361 if mod_type==imp.PKG_DIRECTORY:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000362 return self._get_delegate().get_filename()
Éric Araujo0cfb81d2011-09-17 03:35:57 +0200363 elif mod_type in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000364 return self.filename
365 return None
366
367
368try:
369 import zipimport
370 from zipimport import zipimporter
371
372 def iter_zipimport_modules(importer, prefix=''):
Alexandre Vassalotti515a74f2009-07-05 06:42:44 +0000373 dirlist = sorted(zipimport._zip_directory_cache[importer.archive])
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000374 _prefix = importer.prefix
375 plen = len(_prefix)
376 yielded = {}
377 import inspect
378 for fn in dirlist:
379 if not fn.startswith(_prefix):
380 continue
381
382 fn = fn[plen:].split(os.sep)
383
384 if len(fn)==2 and fn[1].startswith('__init__.py'):
385 if fn[0] not in yielded:
386 yielded[fn[0]] = 1
Łukasz Langa0d18c152016-06-11 18:02:46 -0700387 yield prefix + fn[0], True
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000388
389 if len(fn)!=1:
390 continue
391
392 modname = inspect.getmodulename(fn[0])
393 if modname=='__init__':
394 continue
395
396 if modname and '.' not in modname and modname not in yielded:
397 yielded[modname] = 1
398 yield prefix + modname, False
399
400 iter_importer_modules.register(zipimporter, iter_zipimport_modules)
401
402except ImportError:
403 pass
404
405
406def get_importer(path_item):
Senthil Kumaran46720602016-09-05 17:11:51 -0700407 """Retrieve a finder for the given path item
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000408
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700409 The returned finder is cached in sys.path_importer_cache
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000410 if it was newly created by a path hook.
411
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000412 The cache (or part of it) can be cleared manually if a
413 rescan of sys.path_hooks is necessary.
414 """
415 try:
416 importer = sys.path_importer_cache[path_item]
417 except KeyError:
418 for path_hook in sys.path_hooks:
419 try:
420 importer = path_hook(path_item)
Brett Cannone0d88a12012-04-25 20:54:04 -0400421 sys.path_importer_cache.setdefault(path_item, importer)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000422 break
423 except ImportError:
424 pass
425 else:
Nick Coghlan85e729e2012-07-15 18:09:52 +1000426 importer = None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000427 return importer
428
429
430def iter_importers(fullname=""):
Senthil Kumaran46720602016-09-05 17:11:51 -0700431 """Yield finders for the given module name
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000432
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700433 If fullname contains a '.', the finders will be for the package
Nick Coghlan85e729e2012-07-15 18:09:52 +1000434 containing fullname, otherwise they will be all registered top level
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700435 finders (i.e. those on both sys.meta_path and sys.path_hooks).
Nick Coghlan85e729e2012-07-15 18:09:52 +1000436
437 If the named module is in a package, that package is imported as a side
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000438 effect of invoking this function.
439
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700440 If no module name is specified, all top level finders are produced.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000441 """
442 if fullname.startswith('.'):
Nick Coghlan85e729e2012-07-15 18:09:52 +1000443 msg = "Relative module name {!r} not supported".format(fullname)
444 raise ImportError(msg)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000445 if '.' in fullname:
446 # Get the containing package's __path__
Nick Coghlan85e729e2012-07-15 18:09:52 +1000447 pkg_name = fullname.rpartition(".")[0]
Nick Coghlanc4e0d982013-04-14 22:30:42 +1000448 pkg = importlib.import_module(pkg_name)
449 path = getattr(pkg, '__path__', None)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000450 if path is None:
451 return
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000452 else:
Andrew Svetlov2aa5f3c2012-10-07 23:21:15 +0300453 yield from sys.meta_path
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000454 path = sys.path
455 for item in path:
456 yield get_importer(item)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000457
Eric Snowb523f842013-11-22 09:05:39 -0700458
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000459def get_loader(module_or_name):
Senthil Kumaran46720602016-09-05 17:11:51 -0700460 """Get a "loader" object for module_or_name
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000461
Nick Coghlan85e729e2012-07-15 18:09:52 +1000462 Returns None if the module cannot be found or imported.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000463 If the named module is not already imported, its containing package
464 (if any) is imported, in order to establish the package __path__.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000465 """
466 if module_or_name in sys.modules:
467 module_or_name = sys.modules[module_or_name]
Brett Cannon8447c702014-05-23 12:30:37 -0400468 if module_or_name is None:
469 return None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000470 if isinstance(module_or_name, ModuleType):
471 module = module_or_name
Thomas Wouters477c8d52006-05-27 19:21:47 +0000472 loader = getattr(module, '__loader__', None)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000473 if loader is not None:
474 return loader
Eric Snow658af312014-04-19 00:13:23 -0600475 if getattr(module, '__spec__', None) is None:
476 return None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000477 fullname = module.__name__
478 else:
479 fullname = module_or_name
480 return find_loader(fullname)
481
Nick Coghlan85e729e2012-07-15 18:09:52 +1000482
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000483def find_loader(fullname):
Senthil Kumaran46720602016-09-05 17:11:51 -0700484 """Find a "loader" object for fullname
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000485
Nick Coghlan62b4b9e2014-03-04 20:39:42 +1000486 This is a backwards compatibility wrapper around
487 importlib.util.find_spec that converts most failures to ImportError
488 and only returns the loader rather than the full spec
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000489 """
Nick Coghlan85e729e2012-07-15 18:09:52 +1000490 if fullname.startswith('.'):
491 msg = "Relative module name {!r} not supported".format(fullname)
492 raise ImportError(msg)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000493 try:
Nick Coghlan62b4b9e2014-03-04 20:39:42 +1000494 spec = importlib.util.find_spec(fullname)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000495 except (ImportError, AttributeError, TypeError, ValueError) as ex:
496 # This hack fixes an impedance mismatch between pkgutil and
Andrew Svetlov5b898402012-12-18 21:26:36 +0200497 # importlib, where the latter raises other errors for cases where
Andrew Svetlov1f415cf2012-12-19 22:54:47 +0200498 # pkgutil previously raised ImportError
Nick Coghlan85e729e2012-07-15 18:09:52 +1000499 msg = "Error while finding loader for {!r} ({}: {})"
500 raise ImportError(msg.format(fullname, type(ex), ex)) from ex
Brett Cannon8447c702014-05-23 12:30:37 -0400501 return spec.loader if spec is not None else None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000502
Guido van Rossuma4deda02002-12-23 16:30:00 +0000503
504def extend_path(path, name):
505 """Extend a package's path.
506
507 Intended use is to place the following code in a package's __init__.py:
508
509 from pkgutil import extend_path
510 __path__ = extend_path(__path__, __name__)
511
512 This will add to the package's __path__ all subdirectories of
513 directories on sys.path named after the package. This is useful
514 if one wants to distribute different parts of a single logical
515 package as multiple directories.
516
517 It also looks for *.pkg files beginning where * matches the name
518 argument. This feature is similar to *.pth files (see site.py),
519 except that it doesn't special-case lines starting with 'import'.
520 A *.pkg file is trusted at face value: apart from checking for
521 duplicates, all entries found in a *.pkg file are added to the
522 path, regardless of whether they are exist the filesystem. (This
523 is a feature.)
524
525 If the input path is not a list (as is the case for frozen
526 packages) it is returned unchanged. The input path is not
527 modified; an extended copy is returned. Items are only appended
528 to the copy at the end.
529
530 It is assumed that sys.path is a sequence. Items of sys.path that
531 are not (unicode or 8-bit) strings referring to existing
532 directories are ignored. Unicode items of sys.path that cause
533 errors when used as filenames may cause this function to raise an
534 exception (in line with os.path.isdir() behavior).
535 """
536
537 if not isinstance(path, list):
538 # This could happen e.g. when this is called from inside a
539 # frozen package. Return the path unchanged in that case.
540 return path
541
Skip Montanaro7a98be22007-08-16 14:35:24 +0000542 sname_pkg = name + ".pkg"
Guido van Rossuma4deda02002-12-23 16:30:00 +0000543
544 path = path[:] # Start with a copy of the existing path
545
Antoine Pitroub2dd8802012-07-09 21:23:58 +0200546 parent_package, _, final_name = name.rpartition('.')
547 if parent_package:
548 try:
549 search_path = sys.modules[parent_package].__path__
550 except (KeyError, AttributeError):
551 # We can't do anything: find_loader() returns None when
552 # passed a dotted name.
553 return path
554 else:
555 search_path = sys.path
556
557 for dir in search_path:
Eric V. Smith984b11f2012-05-24 20:21:04 -0400558 if not isinstance(dir, str):
Guido van Rossuma4deda02002-12-23 16:30:00 +0000559 continue
Eric V. Smith984b11f2012-05-24 20:21:04 -0400560
561 finder = get_importer(dir)
562 if finder is not None:
Eric Snow02b9f9d2014-01-06 20:42:59 -0700563 portions = []
564 if hasattr(finder, 'find_spec'):
565 spec = finder.find_spec(final_name)
566 if spec is not None:
567 portions = spec.submodule_search_locations or []
Eric V. Smith984b11f2012-05-24 20:21:04 -0400568 # Is this finder PEP 420 compliant?
Eric Snow02b9f9d2014-01-06 20:42:59 -0700569 elif hasattr(finder, 'find_loader'):
570 _, portions = finder.find_loader(final_name)
Eric V. Smith984b11f2012-05-24 20:21:04 -0400571
572 for portion in portions:
573 # XXX This may still add duplicate entries to path on
574 # case-insensitive filesystems
575 if portion not in path:
576 path.append(portion)
577
Guido van Rossuma4deda02002-12-23 16:30:00 +0000578 # XXX Is this the right thing for subpackages like zope.app?
579 # It looks for a file named "zope.app.pkg"
580 pkgfile = os.path.join(dir, sname_pkg)
581 if os.path.isfile(pkgfile):
582 try:
583 f = open(pkgfile)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200584 except OSError as msg:
Guido van Rossuma4deda02002-12-23 16:30:00 +0000585 sys.stderr.write("Can't open %s: %s\n" %
586 (pkgfile, msg))
587 else:
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +0100588 with f:
589 for line in f:
590 line = line.rstrip('\n')
591 if not line or line.startswith('#'):
592 continue
593 path.append(line) # Don't check for existence!
Guido van Rossuma4deda02002-12-23 16:30:00 +0000594
595 return path
Christian Heimesdae2a892008-04-19 00:55:37 +0000596
Eric Snowb523f842013-11-22 09:05:39 -0700597
Christian Heimesdae2a892008-04-19 00:55:37 +0000598def get_data(package, resource):
599 """Get a resource from a package.
600
601 This is a wrapper round the PEP 302 loader get_data API. The package
602 argument should be the name of a package, in standard module format
603 (foo.bar). The resource argument should be in the form of a relative
604 filename, using '/' as the path separator. The parent directory name '..'
605 is not allowed, and nor is a rooted name (starting with a '/').
606
607 The function returns a binary string, which is the contents of the
608 specified resource.
609
610 For packages located in the filesystem, which have already been imported,
611 this is the rough equivalent of
612
613 d = os.path.dirname(sys.modules[package].__file__)
614 data = open(os.path.join(d, resource), 'rb').read()
615
616 If the package cannot be located or loaded, or it uses a PEP 302 loader
617 which does not support get_data(), then None is returned.
618 """
619
Eric Snow6029e082014-01-25 15:32:46 -0700620 spec = importlib.util.find_spec(package)
Eric Snowb523f842013-11-22 09:05:39 -0700621 if spec is None:
622 return None
623 loader = spec.loader
Christian Heimesdae2a892008-04-19 00:55:37 +0000624 if loader is None or not hasattr(loader, 'get_data'):
625 return None
Eric Snowb523f842013-11-22 09:05:39 -0700626 # XXX needs test
627 mod = (sys.modules.get(package) or
Brett Cannon2a17bde2014-05-30 14:55:29 -0400628 importlib._bootstrap._load(spec))
Christian Heimesdae2a892008-04-19 00:55:37 +0000629 if mod is None or not hasattr(mod, '__file__'):
630 return None
631
632 # Modify the resource name to be compatible with the loader.get_data
633 # signature - an os.path format "filename" starting with the dirname of
634 # the package's __file__
635 parts = resource.split('/')
636 parts.insert(0, os.path.dirname(mod.__file__))
637 resource_name = os.path.join(*parts)
638 return loader.get_data(resource_name)
Vinay Sajip1ed61612020-02-14 22:02:13 +0000639
640
Vinay Sajip4f17c5c2020-02-28 14:26:27 +0000641_DOTTED_WORDS = r'(?!\d)(\w+)(\.(?!\d)(\w+))*'
642_NAME_PATTERN = re.compile(f'^(?P<pkg>{_DOTTED_WORDS})(?P<cln>:(?P<obj>{_DOTTED_WORDS})?)?$', re.U)
Vinay Sajip1ed61612020-02-14 22:02:13 +0000643del _DOTTED_WORDS
644
645def resolve_name(name):
646 """
647 Resolve a name to an object.
648
649 It is expected that `name` will be a string in one of the following
650 formats, where W is shorthand for a valid Python identifier and dot stands
651 for a literal period in these pseudo-regexes:
652
653 W(.W)*
654 W(.W)*:(W(.W)*)?
655
656 The first form is intended for backward compatibility only. It assumes that
657 some part of the dotted name is a package, and the rest is an object
658 somewhere within that package, possibly nested inside other objects.
659 Because the place where the package stops and the object hierarchy starts
660 can't be inferred by inspection, repeated attempts to import must be done
661 with this form.
662
663 In the second form, the caller makes the division point clear through the
664 provision of a single colon: the dotted name to the left of the colon is a
665 package to be imported, and the dotted name to the right is the object
666 hierarchy within that package. Only one import is needed in this form. If
667 it ends with the colon, then a module object is returned.
668
669 The function will return an object (which might be a module), or raise one
670 of the following exceptions:
671
672 ValueError - if `name` isn't in a recognised format
673 ImportError - if an import failed when it shouldn't have
674 AttributeError - if a failure occurred when traversing the object hierarchy
675 within the imported package to get to the desired object)
676 """
677 m = _NAME_PATTERN.match(name)
678 if not m:
679 raise ValueError(f'invalid format: {name!r}')
Vinay Sajip4f17c5c2020-02-28 14:26:27 +0000680 gd = m.groupdict()
681 if gd.get('cln'):
Vinay Sajip1ed61612020-02-14 22:02:13 +0000682 # there is a colon - a one-step import is all that's needed
Vinay Sajip4f17c5c2020-02-28 14:26:27 +0000683 mod = importlib.import_module(gd['pkg'])
684 parts = gd.get('obj')
685 parts = parts.split('.') if parts else []
Vinay Sajip1ed61612020-02-14 22:02:13 +0000686 else:
687 # no colon - have to iterate to find the package boundary
688 parts = name.split('.')
689 modname = parts.pop(0)
690 # first part *must* be a module/package.
691 mod = importlib.import_module(modname)
692 while parts:
693 p = parts[0]
694 s = f'{modname}.{p}'
695 try:
696 mod = importlib.import_module(s)
697 parts.pop(0)
698 modname = s
699 except ImportError:
700 break
701 # if we reach this point, mod is the module, already imported, and
702 # parts is the list of parts in the object hierarchy to be traversed, or
703 # an empty list if just the module is wanted.
704 result = mod
705 for p in parts:
706 result = getattr(result, p)
707 return result