blob: a54e9474d5bbb2bcd67a81a4dd7ad937f4b98fbd [file] [log] [blame]
Guido van Rossuma4deda02002-12-23 16:30:00 +00001"""Utilities to support packages."""
2
Łukasz Langa6f692512013-06-05 12:20:24 +02003from functools import singledispatch as simplegeneric
Łukasz Langa6f692512013-06-05 12:20:24 +02004import importlib
Brett Cannone4f41de2013-06-16 13:13:40 -04005import importlib.util
Nick Coghlan862542e2013-10-27 00:27:39 +10006import importlib.machinery
Łukasz Langa6f692512013-06-05 12:20:24 +02007import os
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00008import os.path
Łukasz Langa6f692512013-06-05 12:20:24 +02009import sys
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000010from types import ModuleType
Brett Cannone4f41de2013-06-16 13:13:40 -040011import warnings
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000012
13__all__ = [
14 'get_importer', 'iter_importers', 'get_loader', 'find_loader',
Éric Araujoa4e2d4f2011-05-02 22:59:15 +020015 'walk_packages', 'iter_modules', 'get_data',
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000016 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path',
17]
18
Eric Snow37148b22014-01-04 15:09:53 -070019
20def _get_spec(finder, name):
21 """Return the finder-specific module spec."""
22 # Works with legacy finders.
23 try:
24 find_spec = finder.find_spec
25 except AttributeError:
26 loader = finder.find_module(name)
27 if loader is None:
28 return None
29 return importlib.util.spec_from_loader(name, loader)
30 else:
31 return find_spec(name)
32
33
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000034def read_code(stream):
35 # This helper is needed in order for the PEP 302 emulation to
36 # correctly handle compiled files
37 import marshal
38
39 magic = stream.read(4)
Brett Cannone4f41de2013-06-16 13:13:40 -040040 if magic != importlib.util.MAGIC_NUMBER:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000041 return None
42
Antoine Pitrou5136ac02012-01-13 18:52:16 +010043 stream.read(8) # Skip timestamp and size
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000044 return marshal.load(stream)
45
46
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000047def walk_packages(path=None, prefix='', onerror=None):
Thomas Wouters0e3f5912006-08-11 14:57:12 +000048 """Yields (module_loader, name, ispkg) for all modules recursively
49 on path, or, if path is None, all accessible modules.
50
51 'path' should be either None or a list of paths to look for
52 modules in.
53
54 'prefix' is a string to output on the front of every module name
55 on output.
56
57 Note that this function must import all *packages* (NOT all
58 modules!) on the given path, in order to access the __path__
59 attribute to find submodules.
60
61 'onerror' is a function which gets called with one argument (the
62 name of the package which was being imported) if any exception
63 occurs while trying to import a package. If no onerror function is
64 supplied, ImportErrors are caught and ignored, while all other
65 exceptions are propagated, terminating the search.
66
67 Examples:
68
69 # list all modules python can access
70 walk_packages()
71
72 # list all submodules of ctypes
73 walk_packages(ctypes.__path__, ctypes.__name__+'.')
74 """
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000075
Thomas Wouters477c8d52006-05-27 19:21:47 +000076 def seen(p, m={}):
77 if p in m:
78 return True
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000079 m[p] = True
80
81 for importer, name, ispkg in iter_modules(path, prefix):
82 yield importer, name, ispkg
83
84 if ispkg:
85 try:
86 __import__(name)
87 except ImportError:
88 if onerror is not None:
Thomas Wouters0e3f5912006-08-11 14:57:12 +000089 onerror(name)
90 except Exception:
91 if onerror is not None:
92 onerror(name)
93 else:
94 raise
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000095 else:
96 path = getattr(sys.modules[name], '__path__', None) or []
97
98 # don't traverse path items we've seen before
99 path = [p for p in path if not seen(p)]
100
Philip Jenvey4993cc02012-10-01 12:53:43 -0700101 yield from walk_packages(path, name+'.', onerror)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000102
103
104def iter_modules(path=None, prefix=''):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000105 """Yields (module_loader, name, ispkg) for all submodules on path,
106 or, if path is None, all top-level modules on sys.path.
107
108 'path' should be either None or a list of paths to look for
109 modules in.
110
111 'prefix' is a string to output on the front of every module name
112 on output.
113 """
114
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000115 if path is None:
116 importers = iter_importers()
117 else:
118 importers = map(get_importer, path)
119
120 yielded = {}
121 for i in importers:
122 for name, ispkg in iter_importer_modules(i, prefix):
123 if name not in yielded:
124 yielded[name] = 1
125 yield i, name, ispkg
126
127
Łukasz Langa6f692512013-06-05 12:20:24 +0200128@simplegeneric
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000129def iter_importer_modules(importer, prefix=''):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000130 if not hasattr(importer, 'iter_modules'):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000131 return []
132 return importer.iter_modules(prefix)
133
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000134
Nick Coghlan8ecf5042012-07-15 21:19:18 +1000135# Implement a file walker for the normal importlib path hook
136def _iter_file_finder_modules(importer, prefix=''):
137 if importer.path is None or not os.path.isdir(importer.path):
138 return
139
140 yielded = {}
141 import inspect
142 try:
143 filenames = os.listdir(importer.path)
144 except OSError:
145 # ignore unreadable directories like import does
146 filenames = []
147 filenames.sort() # handle packages before same-named modules
148
149 for fn in filenames:
150 modname = inspect.getmodulename(fn)
151 if modname=='__init__' or modname in yielded:
152 continue
153
154 path = os.path.join(importer.path, fn)
155 ispkg = False
156
157 if not modname and os.path.isdir(path) and '.' not in fn:
158 modname = fn
159 try:
160 dircontents = os.listdir(path)
161 except OSError:
162 # ignore unreadable directories like import does
163 dircontents = []
164 for fn in dircontents:
165 subname = inspect.getmodulename(fn)
166 if subname=='__init__':
167 ispkg = True
168 break
169 else:
170 continue # not a package
171
172 if modname and '.' not in modname:
173 yielded[modname] = 1
174 yield prefix + modname, ispkg
175
176iter_importer_modules.register(
177 importlib.machinery.FileFinder, _iter_file_finder_modules)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000178
Brett Cannone4f41de2013-06-16 13:13:40 -0400179
180def _import_imp():
181 global imp
182 with warnings.catch_warnings():
183 warnings.simplefilter('ignore', PendingDeprecationWarning)
184 imp = importlib.import_module('imp')
185
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000186class ImpImporter:
187 """PEP 302 Importer that wraps Python's "classic" import algorithm
188
189 ImpImporter(dirname) produces a PEP 302 importer that searches that
190 directory. ImpImporter(None) produces a PEP 302 importer that searches
191 the current sys.path, plus any modules that are frozen or built-in.
192
193 Note that ImpImporter does not currently support being used by placement
194 on sys.meta_path.
195 """
196
197 def __init__(self, path=None):
Brett Cannone4f41de2013-06-16 13:13:40 -0400198 global imp
199 warnings.warn("This emulation is deprecated, use 'importlib' instead",
Nick Coghlan85e729e2012-07-15 18:09:52 +1000200 DeprecationWarning)
Brett Cannone4f41de2013-06-16 13:13:40 -0400201 _import_imp()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000202 self.path = path
203
204 def find_module(self, fullname, path=None):
205 # Note: we ignore 'path' argument since it is only used via meta_path
206 subname = fullname.split(".")[-1]
207 if subname != fullname and self.path is None:
208 return None
209 if self.path is None:
210 path = None
211 else:
212 path = [os.path.realpath(self.path)]
213 try:
214 file, filename, etc = imp.find_module(subname, path)
215 except ImportError:
216 return None
217 return ImpLoader(fullname, file, filename, etc)
218
219 def iter_modules(self, prefix=''):
220 if self.path is None or not os.path.isdir(self.path):
221 return
222
223 yielded = {}
224 import inspect
Ned Deilyed27df72011-10-06 14:19:08 -0700225 try:
226 filenames = os.listdir(self.path)
227 except OSError:
228 # ignore unreadable directories like import does
229 filenames = []
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000230 filenames.sort() # handle packages before same-named modules
231
232 for fn in filenames:
233 modname = inspect.getmodulename(fn)
234 if modname=='__init__' or modname in yielded:
235 continue
236
237 path = os.path.join(self.path, fn)
238 ispkg = False
239
240 if not modname and os.path.isdir(path) and '.' not in fn:
241 modname = fn
Ned Deilyed27df72011-10-06 14:19:08 -0700242 try:
243 dircontents = os.listdir(path)
244 except OSError:
245 # ignore unreadable directories like import does
246 dircontents = []
247 for fn in dircontents:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000248 subname = inspect.getmodulename(fn)
249 if subname=='__init__':
250 ispkg = True
251 break
252 else:
253 continue # not a package
254
255 if modname and '.' not in modname:
256 yielded[modname] = 1
257 yield prefix + modname, ispkg
258
259
260class ImpLoader:
261 """PEP 302 Loader that wraps Python's "classic" import algorithm
262 """
263 code = source = None
264
265 def __init__(self, fullname, file, filename, etc):
Brett Cannone4f41de2013-06-16 13:13:40 -0400266 warnings.warn("This emulation is deprecated, use 'importlib' instead",
267 DeprecationWarning)
268 _import_imp()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000269 self.file = file
270 self.filename = filename
271 self.fullname = fullname
272 self.etc = etc
273
274 def load_module(self, fullname):
275 self._reopen()
276 try:
277 mod = imp.load_module(fullname, self.file, self.filename, self.etc)
278 finally:
279 if self.file:
280 self.file.close()
281 # Note: we don't set __loader__ because we want the module to look
282 # normal; i.e. this is just a wrapper for standard import machinery
283 return mod
284
285 def get_data(self, pathname):
Brett Cannon1ab58df2010-10-29 22:36:53 +0000286 with open(pathname, "rb") as file:
287 return file.read()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000288
289 def _reopen(self):
290 if self.file and self.file.closed:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000291 mod_type = self.etc[2]
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000292 if mod_type==imp.PY_SOURCE:
Victor Stinner4e86d5b2011-05-04 13:55:36 +0200293 self.file = open(self.filename, 'r')
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000294 elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION):
295 self.file = open(self.filename, 'rb')
296
297 def _fix_name(self, fullname):
298 if fullname is None:
299 fullname = self.fullname
300 elif fullname != self.fullname:
301 raise ImportError("Loader for module %s cannot handle "
302 "module %s" % (self.fullname, fullname))
303 return fullname
304
305 def is_package(self, fullname):
306 fullname = self._fix_name(fullname)
307 return self.etc[2]==imp.PKG_DIRECTORY
308
309 def get_code(self, fullname=None):
310 fullname = self._fix_name(fullname)
311 if self.code is None:
312 mod_type = self.etc[2]
313 if mod_type==imp.PY_SOURCE:
314 source = self.get_source(fullname)
315 self.code = compile(source, self.filename, 'exec')
316 elif mod_type==imp.PY_COMPILED:
317 self._reopen()
318 try:
319 self.code = read_code(self.file)
320 finally:
321 self.file.close()
322 elif mod_type==imp.PKG_DIRECTORY:
323 self.code = self._get_delegate().get_code()
324 return self.code
325
326 def get_source(self, fullname=None):
327 fullname = self._fix_name(fullname)
328 if self.source is None:
329 mod_type = self.etc[2]
330 if mod_type==imp.PY_SOURCE:
331 self._reopen()
332 try:
333 self.source = self.file.read()
334 finally:
335 self.file.close()
336 elif mod_type==imp.PY_COMPILED:
337 if os.path.exists(self.filename[:-1]):
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +0100338 with open(self.filename[:-1], 'r') as f:
339 self.source = f.read()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000340 elif mod_type==imp.PKG_DIRECTORY:
341 self.source = self._get_delegate().get_source()
342 return self.source
343
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000344 def _get_delegate(self):
Eric Snow37148b22014-01-04 15:09:53 -0700345 finder = ImpImporter(self.filename)
346 spec = _get_spec(finder, '__init__')
347 return spec.loader
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000348
349 def get_filename(self, fullname=None):
350 fullname = self._fix_name(fullname)
351 mod_type = self.etc[2]
Éric Araujo0cfb81d2011-09-17 03:35:57 +0200352 if mod_type==imp.PKG_DIRECTORY:
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000353 return self._get_delegate().get_filename()
Éric Araujo0cfb81d2011-09-17 03:35:57 +0200354 elif mod_type in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000355 return self.filename
356 return None
357
358
359try:
360 import zipimport
361 from zipimport import zipimporter
362
363 def iter_zipimport_modules(importer, prefix=''):
Alexandre Vassalotti515a74f2009-07-05 06:42:44 +0000364 dirlist = sorted(zipimport._zip_directory_cache[importer.archive])
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000365 _prefix = importer.prefix
366 plen = len(_prefix)
367 yielded = {}
368 import inspect
369 for fn in dirlist:
370 if not fn.startswith(_prefix):
371 continue
372
373 fn = fn[plen:].split(os.sep)
374
375 if len(fn)==2 and fn[1].startswith('__init__.py'):
376 if fn[0] not in yielded:
377 yielded[fn[0]] = 1
378 yield fn[0], True
379
380 if len(fn)!=1:
381 continue
382
383 modname = inspect.getmodulename(fn[0])
384 if modname=='__init__':
385 continue
386
387 if modname and '.' not in modname and modname not in yielded:
388 yielded[modname] = 1
389 yield prefix + modname, False
390
391 iter_importer_modules.register(zipimporter, iter_zipimport_modules)
392
393except ImportError:
394 pass
395
396
397def get_importer(path_item):
398 """Retrieve a PEP 302 importer for the given path item
399
400 The returned importer is cached in sys.path_importer_cache
401 if it was newly created by a path hook.
402
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000403 The cache (or part of it) can be cleared manually if a
404 rescan of sys.path_hooks is necessary.
405 """
406 try:
407 importer = sys.path_importer_cache[path_item]
408 except KeyError:
409 for path_hook in sys.path_hooks:
410 try:
411 importer = path_hook(path_item)
Brett Cannone0d88a12012-04-25 20:54:04 -0400412 sys.path_importer_cache.setdefault(path_item, importer)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000413 break
414 except ImportError:
415 pass
416 else:
Nick Coghlan85e729e2012-07-15 18:09:52 +1000417 importer = None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000418 return importer
419
420
421def iter_importers(fullname=""):
422 """Yield PEP 302 importers for the given module name
423
424 If fullname contains a '.', the importers will be for the package
Nick Coghlan85e729e2012-07-15 18:09:52 +1000425 containing fullname, otherwise they will be all registered top level
426 importers (i.e. those on both sys.meta_path and sys.path_hooks).
427
428 If the named module is in a package, that package is imported as a side
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000429 effect of invoking this function.
430
Nick Coghlan85e729e2012-07-15 18:09:52 +1000431 If no module name is specified, all top level importers are produced.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000432 """
433 if fullname.startswith('.'):
Nick Coghlan85e729e2012-07-15 18:09:52 +1000434 msg = "Relative module name {!r} not supported".format(fullname)
435 raise ImportError(msg)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000436 if '.' in fullname:
437 # Get the containing package's __path__
Nick Coghlan85e729e2012-07-15 18:09:52 +1000438 pkg_name = fullname.rpartition(".")[0]
Nick Coghlanc4e0d982013-04-14 22:30:42 +1000439 pkg = importlib.import_module(pkg_name)
440 path = getattr(pkg, '__path__', None)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000441 if path is None:
442 return
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000443 else:
Andrew Svetlov2aa5f3c2012-10-07 23:21:15 +0300444 yield from sys.meta_path
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000445 path = sys.path
446 for item in path:
447 yield get_importer(item)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000448
Eric Snowb523f842013-11-22 09:05:39 -0700449
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000450def get_loader(module_or_name):
451 """Get a PEP 302 "loader" object for module_or_name
452
Nick Coghlan85e729e2012-07-15 18:09:52 +1000453 Returns None if the module cannot be found or imported.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000454 If the named module is not already imported, its containing package
455 (if any) is imported, in order to establish the package __path__.
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000456 """
457 if module_or_name in sys.modules:
458 module_or_name = sys.modules[module_or_name]
Brett Cannon8447c702014-05-23 12:30:37 -0400459 if module_or_name is None:
460 return None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000461 if isinstance(module_or_name, ModuleType):
462 module = module_or_name
Thomas Wouters477c8d52006-05-27 19:21:47 +0000463 loader = getattr(module, '__loader__', None)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000464 if loader is not None:
465 return loader
Eric Snow658af312014-04-19 00:13:23 -0600466 if getattr(module, '__spec__', None) is None:
467 return None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000468 fullname = module.__name__
469 else:
470 fullname = module_or_name
471 return find_loader(fullname)
472
Nick Coghlan85e729e2012-07-15 18:09:52 +1000473
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000474def find_loader(fullname):
475 """Find a PEP 302 "loader" object for fullname
476
Nick Coghlan62b4b9e2014-03-04 20:39:42 +1000477 This is a backwards compatibility wrapper around
478 importlib.util.find_spec that converts most failures to ImportError
479 and only returns the loader rather than the full spec
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000480 """
Nick Coghlan85e729e2012-07-15 18:09:52 +1000481 if fullname.startswith('.'):
482 msg = "Relative module name {!r} not supported".format(fullname)
483 raise ImportError(msg)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000484 try:
Nick Coghlan62b4b9e2014-03-04 20:39:42 +1000485 spec = importlib.util.find_spec(fullname)
Nick Coghlan85e729e2012-07-15 18:09:52 +1000486 except (ImportError, AttributeError, TypeError, ValueError) as ex:
487 # This hack fixes an impedance mismatch between pkgutil and
Andrew Svetlov5b898402012-12-18 21:26:36 +0200488 # importlib, where the latter raises other errors for cases where
Andrew Svetlov1f415cf2012-12-19 22:54:47 +0200489 # pkgutil previously raised ImportError
Nick Coghlan85e729e2012-07-15 18:09:52 +1000490 msg = "Error while finding loader for {!r} ({}: {})"
491 raise ImportError(msg.format(fullname, type(ex), ex)) from ex
Brett Cannon8447c702014-05-23 12:30:37 -0400492 return spec.loader if spec is not None else None
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000493
Guido van Rossuma4deda02002-12-23 16:30:00 +0000494
495def extend_path(path, name):
496 """Extend a package's path.
497
498 Intended use is to place the following code in a package's __init__.py:
499
500 from pkgutil import extend_path
501 __path__ = extend_path(__path__, __name__)
502
503 This will add to the package's __path__ all subdirectories of
504 directories on sys.path named after the package. This is useful
505 if one wants to distribute different parts of a single logical
506 package as multiple directories.
507
508 It also looks for *.pkg files beginning where * matches the name
509 argument. This feature is similar to *.pth files (see site.py),
510 except that it doesn't special-case lines starting with 'import'.
511 A *.pkg file is trusted at face value: apart from checking for
512 duplicates, all entries found in a *.pkg file are added to the
513 path, regardless of whether they are exist the filesystem. (This
514 is a feature.)
515
516 If the input path is not a list (as is the case for frozen
517 packages) it is returned unchanged. The input path is not
518 modified; an extended copy is returned. Items are only appended
519 to the copy at the end.
520
521 It is assumed that sys.path is a sequence. Items of sys.path that
522 are not (unicode or 8-bit) strings referring to existing
523 directories are ignored. Unicode items of sys.path that cause
524 errors when used as filenames may cause this function to raise an
525 exception (in line with os.path.isdir() behavior).
526 """
527
528 if not isinstance(path, list):
529 # This could happen e.g. when this is called from inside a
530 # frozen package. Return the path unchanged in that case.
531 return path
532
Skip Montanaro7a98be22007-08-16 14:35:24 +0000533 sname_pkg = name + ".pkg"
Guido van Rossuma4deda02002-12-23 16:30:00 +0000534
535 path = path[:] # Start with a copy of the existing path
536
Antoine Pitroub2dd8802012-07-09 21:23:58 +0200537 parent_package, _, final_name = name.rpartition('.')
538 if parent_package:
539 try:
540 search_path = sys.modules[parent_package].__path__
541 except (KeyError, AttributeError):
542 # We can't do anything: find_loader() returns None when
543 # passed a dotted name.
544 return path
545 else:
546 search_path = sys.path
547
548 for dir in search_path:
Eric V. Smith984b11f2012-05-24 20:21:04 -0400549 if not isinstance(dir, str):
Guido van Rossuma4deda02002-12-23 16:30:00 +0000550 continue
Eric V. Smith984b11f2012-05-24 20:21:04 -0400551
552 finder = get_importer(dir)
553 if finder is not None:
Eric Snow02b9f9d2014-01-06 20:42:59 -0700554 portions = []
555 if hasattr(finder, 'find_spec'):
556 spec = finder.find_spec(final_name)
557 if spec is not None:
558 portions = spec.submodule_search_locations or []
Eric V. Smith984b11f2012-05-24 20:21:04 -0400559 # Is this finder PEP 420 compliant?
Eric Snow02b9f9d2014-01-06 20:42:59 -0700560 elif hasattr(finder, 'find_loader'):
561 _, portions = finder.find_loader(final_name)
Eric V. Smith984b11f2012-05-24 20:21:04 -0400562
563 for portion in portions:
564 # XXX This may still add duplicate entries to path on
565 # case-insensitive filesystems
566 if portion not in path:
567 path.append(portion)
568
Guido van Rossuma4deda02002-12-23 16:30:00 +0000569 # XXX Is this the right thing for subpackages like zope.app?
570 # It looks for a file named "zope.app.pkg"
571 pkgfile = os.path.join(dir, sname_pkg)
572 if os.path.isfile(pkgfile):
573 try:
574 f = open(pkgfile)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200575 except OSError as msg:
Guido van Rossuma4deda02002-12-23 16:30:00 +0000576 sys.stderr.write("Can't open %s: %s\n" %
577 (pkgfile, msg))
578 else:
Giampaolo Rodola'2f50aaf2013-02-12 02:04:27 +0100579 with f:
580 for line in f:
581 line = line.rstrip('\n')
582 if not line or line.startswith('#'):
583 continue
584 path.append(line) # Don't check for existence!
Guido van Rossuma4deda02002-12-23 16:30:00 +0000585
586 return path
Christian Heimesdae2a892008-04-19 00:55:37 +0000587
Eric Snowb523f842013-11-22 09:05:39 -0700588
Christian Heimesdae2a892008-04-19 00:55:37 +0000589def get_data(package, resource):
590 """Get a resource from a package.
591
592 This is a wrapper round the PEP 302 loader get_data API. The package
593 argument should be the name of a package, in standard module format
594 (foo.bar). The resource argument should be in the form of a relative
595 filename, using '/' as the path separator. The parent directory name '..'
596 is not allowed, and nor is a rooted name (starting with a '/').
597
598 The function returns a binary string, which is the contents of the
599 specified resource.
600
601 For packages located in the filesystem, which have already been imported,
602 this is the rough equivalent of
603
604 d = os.path.dirname(sys.modules[package].__file__)
605 data = open(os.path.join(d, resource), 'rb').read()
606
607 If the package cannot be located or loaded, or it uses a PEP 302 loader
608 which does not support get_data(), then None is returned.
609 """
610
Eric Snow6029e082014-01-25 15:32:46 -0700611 spec = importlib.util.find_spec(package)
Eric Snowb523f842013-11-22 09:05:39 -0700612 if spec is None:
613 return None
614 loader = spec.loader
Christian Heimesdae2a892008-04-19 00:55:37 +0000615 if loader is None or not hasattr(loader, 'get_data'):
616 return None
Eric Snowb523f842013-11-22 09:05:39 -0700617 # XXX needs test
618 mod = (sys.modules.get(package) or
619 importlib._bootstrap._SpecMethods(spec).load())
Christian Heimesdae2a892008-04-19 00:55:37 +0000620 if mod is None or not hasattr(mod, '__file__'):
621 return None
622
623 # Modify the resource name to be compatible with the loader.get_data
624 # signature - an os.path format "filename" starting with the dirname of
625 # the package's __file__
626 parts = resource.split('/')
627 parts.insert(0, os.path.dirname(mod.__file__))
628 resource_name = os.path.join(*parts)
629 return loader.get_data(resource_name)