blob: e37ad4519666c16da750c39e5dda72a39145bc51 [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
Antoine Pitrou5136ac02012-01-13 18:52:16 +010049 stream.read(8) # Skip timestamp and size
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()
122 else:
123 importers = map(get_importer, path)
124
125 yielded = {}
126 for i in importers:
127 for name, ispkg in iter_importer_modules(i, prefix):
128 if name not in yielded:
129 yielded[name] = 1
Eric Snowd5f92232016-09-07 18:37:17 -0700130 yield ModuleInfo(i, name, ispkg)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000131
132
Łukasz Langa6f692512013-06-05 12:20:24 +0200133@simplegeneric
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000134def iter_importer_modules(importer, prefix=''):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000135 if not hasattr(importer, 'iter_modules'):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000136 return []
137 return importer.iter_modules(prefix)
138
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000139
Nick Coghlan8ecf5042012-07-15 21:19:18 +1000140# Implement a file walker for the normal importlib path hook
141def _iter_file_finder_modules(importer, prefix=''):
142 if importer.path is None or not os.path.isdir(importer.path):
143 return
144
145 yielded = {}
146 import inspect
147 try:
148 filenames = os.listdir(importer.path)
149 except OSError:
150 # ignore unreadable directories like import does
151 filenames = []
152 filenames.sort() # handle packages before same-named modules
153
154 for fn in filenames:
155 modname = inspect.getmodulename(fn)
156 if modname=='__init__' or modname in yielded:
157 continue
158
159 path = os.path.join(importer.path, fn)
160 ispkg = False
161
162 if not modname and os.path.isdir(path) and '.' not in fn:
163 modname = fn
164 try:
165 dircontents = os.listdir(path)
166 except OSError:
167 # ignore unreadable directories like import does
168 dircontents = []
169 for fn in dircontents:
170 subname = inspect.getmodulename(fn)
171 if subname=='__init__':
172 ispkg = True
173 break
174 else:
175 continue # not a package
176
177 if modname and '.' not in modname:
178 yielded[modname] = 1
179 yield prefix + modname, ispkg
180
181iter_importer_modules.register(
182 importlib.machinery.FileFinder, _iter_file_finder_modules)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000183
Brett Cannone4f41de2013-06-16 13:13:40 -0400184
185def _import_imp():
186 global imp
187 with warnings.catch_warnings():
Brett Cannonc0d91af2015-10-16 12:21:37 -0700188 warnings.simplefilter('ignore', DeprecationWarning)
Brett Cannone4f41de2013-06-16 13:13:40 -0400189 imp = importlib.import_module('imp')
190
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000191class ImpImporter:
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700192 """PEP 302 Finder that wraps Python's "classic" import algorithm
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000193
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700194 ImpImporter(dirname) produces a PEP 302 finder that searches that
195 directory. ImpImporter(None) produces a PEP 302 finder that searches
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000196 the current sys.path, plus any modules that are frozen or built-in.
197
198 Note that ImpImporter does not currently support being used by placement
199 on sys.meta_path.
200 """
201
202 def __init__(self, path=None):
Brett Cannone4f41de2013-06-16 13:13:40 -0400203 global imp
204 warnings.warn("This emulation is deprecated, use 'importlib' instead",
Nick Coghlan85e729e2012-07-15 18:09:52 +1000205 DeprecationWarning)
Brett Cannone4f41de2013-06-16 13:13:40 -0400206 _import_imp()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000207 self.path = path
208
209 def find_module(self, fullname, path=None):
210 # Note: we ignore 'path' argument since it is only used via meta_path
211 subname = fullname.split(".")[-1]
212 if subname != fullname and self.path is None:
213 return None
214 if self.path is None:
215 path = None
216 else:
217 path = [os.path.realpath(self.path)]
218 try:
219 file, filename, etc = imp.find_module(subname, path)
220 except ImportError:
221 return None
222 return ImpLoader(fullname, file, filename, etc)
223
224 def iter_modules(self, prefix=''):
225 if self.path is None or not os.path.isdir(self.path):
226 return
227
228 yielded = {}
229 import inspect
Ned Deilyed27df72011-10-06 14:19:08 -0700230 try:
231 filenames = os.listdir(self.path)
232 except OSError:
233 # ignore unreadable directories like import does
234 filenames = []
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000235 filenames.sort() # handle packages before same-named modules
236
237 for fn in filenames:
238 modname = inspect.getmodulename(fn)
239 if modname=='__init__' or modname in yielded:
240 continue
241
242 path = os.path.join(self.path, fn)
243 ispkg = False
244
245 if not modname and os.path.isdir(path) and '.' not in fn:
246 modname = fn
Ned Deilyed27df72011-10-06 14:19:08 -0700247 try:
248 dircontents = os.listdir(path)
249 except OSError:
250 # ignore unreadable directories like import does
251 dircontents = []
252 for fn in dircontents:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000253 subname = inspect.getmodulename(fn)
254 if subname=='__init__':
255 ispkg = True
256 break
257 else:
258 continue # not a package
259
260 if modname and '.' not in modname:
261 yielded[modname] = 1
262 yield prefix + modname, ispkg
263
264
265class ImpLoader:
266 """PEP 302 Loader that wraps Python's "classic" import algorithm
267 """
268 code = source = None
269
270 def __init__(self, fullname, file, filename, etc):
Brett Cannone4f41de2013-06-16 13:13:40 -0400271 warnings.warn("This emulation is deprecated, use 'importlib' instead",
272 DeprecationWarning)
273 _import_imp()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000274 self.file = file
275 self.filename = filename
276 self.fullname = fullname
277 self.etc = etc
278
279 def load_module(self, fullname):
280 self._reopen()
281 try:
282 mod = imp.load_module(fullname, self.file, self.filename, self.etc)
283 finally:
284 if self.file:
285 self.file.close()
286 # Note: we don't set __loader__ because we want the module to look
287 # normal; i.e. this is just a wrapper for standard import machinery
288 return mod
289
290 def get_data(self, pathname):
Brett Cannon1ab58df2010-10-29 22:36:53 +0000291 with open(pathname, "rb") as file:
292 return file.read()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000293
294 def _reopen(self):
295 if self.file and self.file.closed:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000296 mod_type = self.etc[2]
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000297 if mod_type==imp.PY_SOURCE:
Victor Stinner4e86d5b2011-05-04 13:55:36 +0200298 self.file = open(self.filename, 'r')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000299 elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION):
300 self.file = open(self.filename, 'rb')
301
302 def _fix_name(self, fullname):
303 if fullname is None:
304 fullname = self.fullname
305 elif fullname != self.fullname:
306 raise ImportError("Loader for module %s cannot handle "
307 "module %s" % (self.fullname, fullname))
308 return fullname
309
310 def is_package(self, fullname):
311 fullname = self._fix_name(fullname)
312 return self.etc[2]==imp.PKG_DIRECTORY
313
314 def get_code(self, fullname=None):
315 fullname = self._fix_name(fullname)
316 if self.code is None:
317 mod_type = self.etc[2]
318 if mod_type==imp.PY_SOURCE:
319 source = self.get_source(fullname)
320 self.code = compile(source, self.filename, 'exec')
321 elif mod_type==imp.PY_COMPILED:
322 self._reopen()
323 try:
324 self.code = read_code(self.file)
325 finally:
326 self.file.close()
327 elif mod_type==imp.PKG_DIRECTORY:
328 self.code = self._get_delegate().get_code()
329 return self.code
330
331 def get_source(self, fullname=None):
332 fullname = self._fix_name(fullname)
333 if self.source is None:
334 mod_type = self.etc[2]
335 if mod_type==imp.PY_SOURCE:
336 self._reopen()
337 try:
338 self.source = self.file.read()
339 finally:
340 self.file.close()
341 elif mod_type==imp.PY_COMPILED:
342 if os.path.exists(self.filename[:-1]):
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +0100343 with open(self.filename[:-1], 'r') as f:
344 self.source = f.read()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000345 elif mod_type==imp.PKG_DIRECTORY:
346 self.source = self._get_delegate().get_source()
347 return self.source
348
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000349 def _get_delegate(self):
Eric Snow37148b22014-01-04 15:09:53 -0700350 finder = ImpImporter(self.filename)
351 spec = _get_spec(finder, '__init__')
352 return spec.loader
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000353
354 def get_filename(self, fullname=None):
355 fullname = self._fix_name(fullname)
356 mod_type = self.etc[2]
Éric Araujo0cfb81d2011-09-17 03:35:57 +0200357 if mod_type==imp.PKG_DIRECTORY:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000358 return self._get_delegate().get_filename()
Éric Araujo0cfb81d2011-09-17 03:35:57 +0200359 elif mod_type in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000360 return self.filename
361 return None
362
363
364try:
365 import zipimport
366 from zipimport import zipimporter
367
368 def iter_zipimport_modules(importer, prefix=''):
Alexandre Vassalotti515a74f2009-07-05 06:42:44 +0000369 dirlist = sorted(zipimport._zip_directory_cache[importer.archive])
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000370 _prefix = importer.prefix
371 plen = len(_prefix)
372 yielded = {}
373 import inspect
374 for fn in dirlist:
375 if not fn.startswith(_prefix):
376 continue
377
378 fn = fn[plen:].split(os.sep)
379
380 if len(fn)==2 and fn[1].startswith('__init__.py'):
381 if fn[0] not in yielded:
382 yielded[fn[0]] = 1
Łukasz Langa0d18c152016-06-11 18:02:46 -0700383 yield prefix + fn[0], True
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000384
385 if len(fn)!=1:
386 continue
387
388 modname = inspect.getmodulename(fn[0])
389 if modname=='__init__':
390 continue
391
392 if modname and '.' not in modname and modname not in yielded:
393 yielded[modname] = 1
394 yield prefix + modname, False
395
396 iter_importer_modules.register(zipimporter, iter_zipimport_modules)
397
398except ImportError:
399 pass
400
401
402def get_importer(path_item):
Senthil Kumaran46720602016-09-05 17:11:51 -0700403 """Retrieve a finder for the given path item
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000404
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700405 The returned finder is cached in sys.path_importer_cache
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000406 if it was newly created by a path hook.
407
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000408 The cache (or part of it) can be cleared manually if a
409 rescan of sys.path_hooks is necessary.
410 """
411 try:
412 importer = sys.path_importer_cache[path_item]
413 except KeyError:
414 for path_hook in sys.path_hooks:
415 try:
416 importer = path_hook(path_item)
Brett Cannone0d88a12012-04-25 20:54:04 -0400417 sys.path_importer_cache.setdefault(path_item, importer)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000418 break
419 except ImportError:
420 pass
421 else:
Nick Coghlan85e729e2012-07-15 18:09:52 +1000422 importer = None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000423 return importer
424
425
426def iter_importers(fullname=""):
Senthil Kumaran46720602016-09-05 17:11:51 -0700427 """Yield finders for the given module name
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000428
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700429 If fullname contains a '.', the finders will be for the package
Nick Coghlan85e729e2012-07-15 18:09:52 +1000430 containing fullname, otherwise they will be all registered top level
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700431 finders (i.e. those on both sys.meta_path and sys.path_hooks).
Nick Coghlan85e729e2012-07-15 18:09:52 +1000432
433 If the named module is in a package, that package is imported as a side
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000434 effect of invoking this function.
435
Brett Cannonfdcdd9e2016-07-08 11:00:00 -0700436 If no module name is specified, all top level finders are produced.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000437 """
438 if fullname.startswith('.'):
Nick Coghlan85e729e2012-07-15 18:09:52 +1000439 msg = "Relative module name {!r} not supported".format(fullname)
440 raise ImportError(msg)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000441 if '.' in fullname:
442 # Get the containing package's __path__
Nick Coghlan85e729e2012-07-15 18:09:52 +1000443 pkg_name = fullname.rpartition(".")[0]
Nick Coghlanc4e0d982013-04-14 22:30:42 +1000444 pkg = importlib.import_module(pkg_name)
445 path = getattr(pkg, '__path__', None)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000446 if path is None:
447 return
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000448 else:
Andrew Svetlov2aa5f3c2012-10-07 23:21:15 +0300449 yield from sys.meta_path
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000450 path = sys.path
451 for item in path:
452 yield get_importer(item)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000453
Eric Snowb523f842013-11-22 09:05:39 -0700454
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000455def get_loader(module_or_name):
Senthil Kumaran46720602016-09-05 17:11:51 -0700456 """Get a "loader" object for module_or_name
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000457
Nick Coghlan85e729e2012-07-15 18:09:52 +1000458 Returns None if the module cannot be found or imported.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000459 If the named module is not already imported, its containing package
460 (if any) is imported, in order to establish the package __path__.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000461 """
462 if module_or_name in sys.modules:
463 module_or_name = sys.modules[module_or_name]
Brett Cannon8447c702014-05-23 12:30:37 -0400464 if module_or_name is None:
465 return None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000466 if isinstance(module_or_name, ModuleType):
467 module = module_or_name
Thomas Wouters477c8d52006-05-27 19:21:47 +0000468 loader = getattr(module, '__loader__', None)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000469 if loader is not None:
470 return loader
Eric Snow658af312014-04-19 00:13:23 -0600471 if getattr(module, '__spec__', None) is None:
472 return None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000473 fullname = module.__name__
474 else:
475 fullname = module_or_name
476 return find_loader(fullname)
477
Nick Coghlan85e729e2012-07-15 18:09:52 +1000478
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000479def find_loader(fullname):
Senthil Kumaran46720602016-09-05 17:11:51 -0700480 """Find a "loader" object for fullname
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000481
Nick Coghlan62b4b9e2014-03-04 20:39:42 +1000482 This is a backwards compatibility wrapper around
483 importlib.util.find_spec that converts most failures to ImportError
484 and only returns the loader rather than the full spec
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000485 """
Nick Coghlan85e729e2012-07-15 18:09:52 +1000486 if fullname.startswith('.'):
487 msg = "Relative module name {!r} not supported".format(fullname)
488 raise ImportError(msg)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000489 try:
Nick Coghlan62b4b9e2014-03-04 20:39:42 +1000490 spec = importlib.util.find_spec(fullname)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000491 except (ImportError, AttributeError, TypeError, ValueError) as ex:
492 # This hack fixes an impedance mismatch between pkgutil and
Andrew Svetlov5b898402012-12-18 21:26:36 +0200493 # importlib, where the latter raises other errors for cases where
Andrew Svetlov1f415cf2012-12-19 22:54:47 +0200494 # pkgutil previously raised ImportError
Nick Coghlan85e729e2012-07-15 18:09:52 +1000495 msg = "Error while finding loader for {!r} ({}: {})"
496 raise ImportError(msg.format(fullname, type(ex), ex)) from ex
Brett Cannon8447c702014-05-23 12:30:37 -0400497 return spec.loader if spec is not None else None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000498
Guido van Rossuma4deda02002-12-23 16:30:00 +0000499
500def extend_path(path, name):
501 """Extend a package's path.
502
503 Intended use is to place the following code in a package's __init__.py:
504
505 from pkgutil import extend_path
506 __path__ = extend_path(__path__, __name__)
507
508 This will add to the package's __path__ all subdirectories of
509 directories on sys.path named after the package. This is useful
510 if one wants to distribute different parts of a single logical
511 package as multiple directories.
512
513 It also looks for *.pkg files beginning where * matches the name
514 argument. This feature is similar to *.pth files (see site.py),
515 except that it doesn't special-case lines starting with 'import'.
516 A *.pkg file is trusted at face value: apart from checking for
517 duplicates, all entries found in a *.pkg file are added to the
518 path, regardless of whether they are exist the filesystem. (This
519 is a feature.)
520
521 If the input path is not a list (as is the case for frozen
522 packages) it is returned unchanged. The input path is not
523 modified; an extended copy is returned. Items are only appended
524 to the copy at the end.
525
526 It is assumed that sys.path is a sequence. Items of sys.path that
527 are not (unicode or 8-bit) strings referring to existing
528 directories are ignored. Unicode items of sys.path that cause
529 errors when used as filenames may cause this function to raise an
530 exception (in line with os.path.isdir() behavior).
531 """
532
533 if not isinstance(path, list):
534 # This could happen e.g. when this is called from inside a
535 # frozen package. Return the path unchanged in that case.
536 return path
537
Skip Montanaro7a98be22007-08-16 14:35:24 +0000538 sname_pkg = name + ".pkg"
Guido van Rossuma4deda02002-12-23 16:30:00 +0000539
540 path = path[:] # Start with a copy of the existing path
541
Antoine Pitroub2dd8802012-07-09 21:23:58 +0200542 parent_package, _, final_name = name.rpartition('.')
543 if parent_package:
544 try:
545 search_path = sys.modules[parent_package].__path__
546 except (KeyError, AttributeError):
547 # We can't do anything: find_loader() returns None when
548 # passed a dotted name.
549 return path
550 else:
551 search_path = sys.path
552
553 for dir in search_path:
Eric V. Smith984b11f2012-05-24 20:21:04 -0400554 if not isinstance(dir, str):
Guido van Rossuma4deda02002-12-23 16:30:00 +0000555 continue
Eric V. Smith984b11f2012-05-24 20:21:04 -0400556
557 finder = get_importer(dir)
558 if finder is not None:
Eric Snow02b9f9d2014-01-06 20:42:59 -0700559 portions = []
560 if hasattr(finder, 'find_spec'):
561 spec = finder.find_spec(final_name)
562 if spec is not None:
563 portions = spec.submodule_search_locations or []
Eric V. Smith984b11f2012-05-24 20:21:04 -0400564 # Is this finder PEP 420 compliant?
Eric Snow02b9f9d2014-01-06 20:42:59 -0700565 elif hasattr(finder, 'find_loader'):
566 _, portions = finder.find_loader(final_name)
Eric V. Smith984b11f2012-05-24 20:21:04 -0400567
568 for portion in portions:
569 # XXX This may still add duplicate entries to path on
570 # case-insensitive filesystems
571 if portion not in path:
572 path.append(portion)
573
Guido van Rossuma4deda02002-12-23 16:30:00 +0000574 # XXX Is this the right thing for subpackages like zope.app?
575 # It looks for a file named "zope.app.pkg"
576 pkgfile = os.path.join(dir, sname_pkg)
577 if os.path.isfile(pkgfile):
578 try:
579 f = open(pkgfile)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200580 except OSError as msg:
Guido van Rossuma4deda02002-12-23 16:30:00 +0000581 sys.stderr.write("Can't open %s: %s\n" %
582 (pkgfile, msg))
583 else:
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +0100584 with f:
585 for line in f:
586 line = line.rstrip('\n')
587 if not line or line.startswith('#'):
588 continue
589 path.append(line) # Don't check for existence!
Guido van Rossuma4deda02002-12-23 16:30:00 +0000590
591 return path
Christian Heimesdae2a892008-04-19 00:55:37 +0000592
Eric Snowb523f842013-11-22 09:05:39 -0700593
Christian Heimesdae2a892008-04-19 00:55:37 +0000594def get_data(package, resource):
595 """Get a resource from a package.
596
597 This is a wrapper round the PEP 302 loader get_data API. The package
598 argument should be the name of a package, in standard module format
599 (foo.bar). The resource argument should be in the form of a relative
600 filename, using '/' as the path separator. The parent directory name '..'
601 is not allowed, and nor is a rooted name (starting with a '/').
602
603 The function returns a binary string, which is the contents of the
604 specified resource.
605
606 For packages located in the filesystem, which have already been imported,
607 this is the rough equivalent of
608
609 d = os.path.dirname(sys.modules[package].__file__)
610 data = open(os.path.join(d, resource), 'rb').read()
611
612 If the package cannot be located or loaded, or it uses a PEP 302 loader
613 which does not support get_data(), then None is returned.
614 """
615
Eric Snow6029e082014-01-25 15:32:46 -0700616 spec = importlib.util.find_spec(package)
Eric Snowb523f842013-11-22 09:05:39 -0700617 if spec is None:
618 return None
619 loader = spec.loader
Christian Heimesdae2a892008-04-19 00:55:37 +0000620 if loader is None or not hasattr(loader, 'get_data'):
621 return None
Eric Snowb523f842013-11-22 09:05:39 -0700622 # XXX needs test
623 mod = (sys.modules.get(package) or
Brett Cannon2a17bde2014-05-30 14:55:29 -0400624 importlib._bootstrap._load(spec))
Christian Heimesdae2a892008-04-19 00:55:37 +0000625 if mod is None or not hasattr(mod, '__file__'):
626 return None
627
628 # Modify the resource name to be compatible with the loader.get_data
629 # signature - an os.path format "filename" starting with the dirname of
630 # the package's __file__
631 parts = resource.split('/')
632 parts.insert(0, os.path.dirname(mod.__file__))
633 resource_name = os.path.join(*parts)
634 return loader.get_data(resource_name)