blob: 9059edaafaa165b3b0794345b6682499b8ab89b9 [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001"""Utilities to support packages."""
2
3# NOTE: This module must remain compatible with Python 2.3, as it is shared
4# by setuptools for distribution with Python 2.3 and up.
5
6import os
7import sys
8import imp
9import os.path
10from types import ModuleType
11from org.python.core import imp as _imp, BytecodeLoader
12
13__all__ = [
14 'get_importer', 'iter_importers', 'get_loader', 'find_loader',
15 'walk_packages', 'iter_modules',
16 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path',
17]
18
19
20# equivalent to CPythonLib's pkgutil.read_code except that we need
21# diff args to pass into our underlying imp implementation, as
22# accessed by _imp here
23
24def read_jython_code(fullname, file, filename):
25 data = _imp.readCode(filename, file, False)
26 return BytecodeLoader.makeCode(fullname + "$py", data, filename)
27
28def simplegeneric(func):
29 """Make a trivial single-dispatch generic function"""
30 registry = {}
31 def wrapper(*args, **kw):
32 ob = args[0]
33 try:
34 cls = ob.__class__
35 except AttributeError:
36 cls = type(ob)
37 try:
38 mro = cls.__mro__
39 except AttributeError:
40 try:
41 class cls(cls, object):
42 pass
43 mro = cls.__mro__[1:]
44 except TypeError:
45 mro = object, # must be an ExtensionClass or some such :(
46 for t in mro:
47 if t in registry:
48 return registry[t](*args, **kw)
49 else:
50 return func(*args, **kw)
51 try:
52 wrapper.__name__ = func.__name__
53 except (TypeError, AttributeError):
54 pass # Python 2.3 doesn't allow functions to be renamed
55
56 def register(typ, func=None):
57 if func is None:
58 return lambda f: register(typ, f)
59 registry[typ] = func
60 return func
61
62 wrapper.__dict__ = func.__dict__
63 wrapper.__doc__ = func.__doc__
64 wrapper.register = register
65 return wrapper
66
67
68def walk_packages(path=None, prefix='', onerror=None):
69 """Yields (module_loader, name, ispkg) for all modules recursively
70 on path, or, if path is None, all accessible modules.
71
72 'path' should be either None or a list of paths to look for
73 modules in.
74
75 'prefix' is a string to output on the front of every module name
76 on output.
77
78 Note that this function must import all *packages* (NOT all
79 modules!) on the given path, in order to access the __path__
80 attribute to find submodules.
81
82 'onerror' is a function which gets called with one argument (the
83 name of the package which was being imported) if any exception
84 occurs while trying to import a package. If no onerror function is
85 supplied, ImportErrors are caught and ignored, while all other
86 exceptions are propagated, terminating the search.
87
88 Examples:
89
90 # list all modules python can access
91 walk_packages()
92
93 # list all submodules of ctypes
94 walk_packages(ctypes.__path__, ctypes.__name__+'.')
95 """
96
97 def seen(p, m={}):
98 if p in m:
99 return True
100 m[p] = True
101
102 for importer, name, ispkg in iter_modules(path, prefix):
103 yield importer, name, ispkg
104
105 if ispkg:
106 try:
107 __import__(name)
108 except ImportError:
109 if onerror is not None:
110 onerror(name)
111 except Exception:
112 if onerror is not None:
113 onerror(name)
114 else:
115 raise
116 else:
117 path = getattr(sys.modules[name], '__path__', None) or []
118
119 # don't traverse path items we've seen before
120 path = [p for p in path if not seen(p)]
121
122 for item in walk_packages(path, name+'.', onerror):
123 yield item
124
125
126def iter_modules(path=None, prefix=''):
127 """Yields (module_loader, name, ispkg) for all submodules on path,
128 or, if path is None, all top-level modules on sys.path.
129
130 'path' should be either None or a list of paths to look for
131 modules in.
132
133 'prefix' is a string to output on the front of every module name
134 on output.
135 """
136
137 if path is None:
138 importers = iter_importers()
139 else:
140 importers = map(get_importer, path)
141
142 yielded = {}
143 for i in importers:
144 for name, ispkg in iter_importer_modules(i, prefix):
145 if name not in yielded:
146 yielded[name] = 1
147 yield i, name, ispkg
148
149
150#@simplegeneric
151def iter_importer_modules(importer, prefix=''):
152 if not hasattr(importer, 'iter_modules'):
153 return []
154 return importer.iter_modules(prefix)
155
156iter_importer_modules = simplegeneric(iter_importer_modules)
157
158
159class ImpImporter:
160 """PEP 302 Importer that wraps Python's "classic" import algorithm
161
162 ImpImporter(dirname) produces a PEP 302 importer that searches that
163 directory. ImpImporter(None) produces a PEP 302 importer that searches
164 the current sys.path, plus any modules that are frozen or built-in.
165
166 Note that ImpImporter does not currently support being used by placement
167 on sys.meta_path.
168 """
169
170 def __init__(self, path=None):
171 self.path = path
172
173 def find_module(self, fullname, path=None):
174 # Note: we ignore 'path' argument since it is only used via meta_path
175 subname = fullname.split(".")[-1]
176 if subname != fullname and self.path is None:
177 return None
178 if self.path is None:
179 path = None
180 else:
181 path = [os.path.realpath(self.path)]
182 try:
183 file, filename, etc = imp.find_module(subname, path)
184 except ImportError:
185 return None
186 return ImpLoader(fullname, file, filename, etc)
187
188 def iter_modules(self, prefix=''):
189 if self.path is None or not os.path.isdir(self.path):
190 return
191
192 yielded = {}
193 import inspect
194
195 filenames = os.listdir(self.path)
196 filenames.sort() # handle packages before same-named modules
197
198 for fn in filenames:
199 modname = inspect.getmodulename(fn)
200 if modname=='__init__' or modname in yielded:
201 continue
202
203 path = os.path.join(self.path, fn)
204 ispkg = False
205
206 if not modname and os.path.isdir(path) and '.' not in fn:
207 modname = fn
208 for fn in os.listdir(path):
209 subname = inspect.getmodulename(fn)
210 if subname=='__init__':
211 ispkg = True
212 break
213 else:
214 continue # not a package
215
216 if modname and '.' not in modname:
217 yielded[modname] = 1
218 yield prefix + modname, ispkg
219
220
221class ImpLoader:
222 """PEP 302 Loader that wraps Python's "classic" import algorithm
223 """
224 code = source = None
225
226 def __init__(self, fullname, file, filename, etc):
227 self.file = file
228 self.filename = filename
229 self.fullname = fullname
230 self.etc = etc
231
232 def load_module(self, fullname):
233 self._reopen()
234 try:
235 mod = imp.load_module(fullname, self.file, self.filename, self.etc)
236 finally:
237 if self.file:
238 self.file.close()
239 # Note: we don't set __loader__ because we want the module to look
240 # normal; i.e. this is just a wrapper for standard import machinery
241 return mod
242
243 def get_data(self, pathname):
244 f = open(pathname, "rb")
245 try:
246 return f.read()
247 finally:
248 f.close()
249
250 def _reopen(self):
251 if self.file and self.file.closed:
252 mod_type = self.etc[2]
253 if mod_type==imp.PY_SOURCE:
254 self.file = open(self.filename, 'rU')
255 elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION):
256 self.file = open(self.filename, 'rb')
257
258 def _fix_name(self, fullname):
259 if fullname is None:
260 fullname = self.fullname
261 elif fullname != self.fullname:
262 raise ImportError("Loader for module %s cannot handle "
263 "module %s" % (self.fullname, fullname))
264 return fullname
265
266 def is_package(self, fullname):
267 fullname = self._fix_name(fullname)
268 return self.etc[2]==imp.PKG_DIRECTORY
269
270 def get_code(self, fullname=None):
271 fullname = self._fix_name(fullname)
272 if self.code is None:
273 mod_type = self.etc[2]
274 if mod_type==imp.PY_SOURCE:
275 source = self.get_source(fullname)
276 self.code = compile(source, self.filename, 'exec')
277 elif mod_type==imp.PY_COMPILED:
278 self._reopen()
279 try:
280 self.code = read_jython_code(fullname, self.file, self.filename)
281 finally:
282 self.file.close()
283 elif mod_type==imp.PKG_DIRECTORY:
284 self.code = self._get_delegate().get_code()
285 return self.code
286
287 def get_source(self, fullname=None):
288 fullname = self._fix_name(fullname)
289 if self.source is None:
290 mod_type = self.etc[2]
291 if mod_type==imp.PY_SOURCE:
292 self._reopen()
293 try:
294 self.source = self.file.read()
295 finally:
296 self.file.close()
297 elif mod_type==imp.PY_COMPILED:
298 if os.path.exists(self.filename[:-1]):
299 f = open(self.filename[:-1], 'rU')
300 try:
301 self.source = f.read()
302 finally:
303 f.close()
304 elif mod_type==imp.PKG_DIRECTORY:
305 self.source = self._get_delegate().get_source()
306 return self.source
307
308
309 def _get_delegate(self):
310 return ImpImporter(self.filename).find_module('__init__')
311
312 def get_filename(self, fullname=None):
313 fullname = self._fix_name(fullname)
314 mod_type = self.etc[2]
315 if self.etc[2]==imp.PKG_DIRECTORY:
316 return self._get_delegate().get_filename()
317 elif self.etc[2] in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION):
318 return self.filename
319 return None
320
321
322try:
323 import zipimport
324 from zipimport import zipimporter
325
326 def iter_zipimport_modules(importer, prefix=''):
327 dirlist = zipimport._zip_directory_cache[importer.archive].keys()
328 dirlist.sort()
329 _prefix = importer.prefix
330 plen = len(_prefix)
331 yielded = {}
332 import inspect
333 for fn in dirlist:
334 if not fn.startswith(_prefix):
335 continue
336
337 fn = fn[plen:].split(os.sep)
338
339 if len(fn)==2 and fn[1].startswith('__init__.py'):
340 if fn[0] not in yielded:
341 yielded[fn[0]] = 1
342 yield fn[0], True
343
344 if len(fn)!=1:
345 continue
346
347 modname = inspect.getmodulename(fn[0])
348 if modname=='__init__':
349 continue
350
351 if modname and '.' not in modname and modname not in yielded:
352 yielded[modname] = 1
353 yield prefix + modname, False
354
355 iter_importer_modules.register(zipimporter, iter_zipimport_modules)
356
357except ImportError:
358 pass
359
360
361def get_importer(path_item):
362 """Retrieve a PEP 302 importer for the given path item
363
364 The returned importer is cached in sys.path_importer_cache
365 if it was newly created by a path hook.
366
367 If there is no importer, a wrapper around the basic import
368 machinery is returned. This wrapper is never inserted into
369 the importer cache (None is inserted instead).
370
371 The cache (or part of it) can be cleared manually if a
372 rescan of sys.path_hooks is necessary.
373 """
374 try:
375 importer = sys.path_importer_cache[path_item]
376 except KeyError:
377 for path_hook in sys.path_hooks:
378 try:
379 importer = path_hook(path_item)
380 break
381 except ImportError:
382 pass
383 else:
384 importer = None
385 sys.path_importer_cache.setdefault(path_item, importer)
386
387 if importer is None:
388 try:
389 importer = ImpImporter(path_item)
390 except ImportError:
391 importer = None
392 return importer
393
394
395def iter_importers(fullname=""):
396 """Yield PEP 302 importers for the given module name
397
398 If fullname contains a '.', the importers will be for the package
399 containing fullname, otherwise they will be importers for sys.meta_path,
400 sys.path, and Python's "classic" import machinery, in that order. If
401 the named module is in a package, that package is imported as a side
402 effect of invoking this function.
403
404 Non PEP 302 mechanisms (e.g. the Windows registry) used by the
405 standard import machinery to find files in alternative locations
406 are partially supported, but are searched AFTER sys.path. Normally,
407 these locations are searched BEFORE sys.path, preventing sys.path
408 entries from shadowing them.
409
410 For this to cause a visible difference in behaviour, there must
411 be a module or package name that is accessible via both sys.path
412 and one of the non PEP 302 file system mechanisms. In this case,
413 the emulation will find the former version, while the builtin
414 import mechanism will find the latter.
415
416 Items of the following types can be affected by this discrepancy:
417 imp.C_EXTENSION, imp.PY_SOURCE, imp.PY_COMPILED, imp.PKG_DIRECTORY
418 """
419 if fullname.startswith('.'):
420 raise ImportError("Relative module names not supported")
421 if '.' in fullname:
422 # Get the containing package's __path__
423 pkg = '.'.join(fullname.split('.')[:-1])
424 if pkg not in sys.modules:
425 __import__(pkg)
426 path = getattr(sys.modules[pkg], '__path__', None) or []
427 else:
428 for importer in sys.meta_path:
429 yield importer
430 path = sys.path
431 for item in path:
432 yield get_importer(item)
433 if '.' not in fullname:
434 yield ImpImporter()
435
436def get_loader(module_or_name):
437 """Get a PEP 302 "loader" object for module_or_name
438
439 If the module or package is accessible via the normal import
440 mechanism, a wrapper around the relevant part of that machinery
441 is returned. Returns None if the module cannot be found or imported.
442 If the named module is not already imported, its containing package
443 (if any) is imported, in order to establish the package __path__.
444
445 This function uses iter_importers(), and is thus subject to the same
446 limitations regarding platform-specific special import locations such
447 as the Windows registry.
448 """
449 if module_or_name in sys.modules:
450 module_or_name = sys.modules[module_or_name]
451 if isinstance(module_or_name, ModuleType):
452 module = module_or_name
453 loader = getattr(module, '__loader__', None)
454 if loader is not None:
455 return loader
456 fullname = module.__name__
457 elif module_or_name == sys:
458 # Jython sys is not a real module; fake it here for now since
459 # making it a module requires a fair amount of decoupling from
460 # PySystemState
461 fullname = "sys"
462 else:
463 fullname = module_or_name
464 return find_loader(fullname)
465
466def find_loader(fullname):
467 """Find a PEP 302 "loader" object for fullname
468
469 If fullname contains dots, path must be the containing package's __path__.
470 Returns None if the module cannot be found or imported. This function uses
471 iter_importers(), and is thus subject to the same limitations regarding
472 platform-specific special import locations such as the Windows registry.
473 """
474 for importer in iter_importers(fullname):
475 loader = importer.find_module(fullname)
476 if loader is not None:
477 return loader
478
479 return None
480
481
482def extend_path(path, name):
483 """Extend a package's path.
484
485 Intended use is to place the following code in a package's __init__.py:
486
487 from pkgutil import extend_path
488 __path__ = extend_path(__path__, __name__)
489
490 This will add to the package's __path__ all subdirectories of
491 directories on sys.path named after the package. This is useful
492 if one wants to distribute different parts of a single logical
493 package as multiple directories.
494
495 It also looks for *.pkg files beginning where * matches the name
496 argument. This feature is similar to *.pth files (see site.py),
497 except that it doesn't special-case lines starting with 'import'.
498 A *.pkg file is trusted at face value: apart from checking for
499 duplicates, all entries found in a *.pkg file are added to the
500 path, regardless of whether they are exist the filesystem. (This
501 is a feature.)
502
503 If the input path is not a list (as is the case for frozen
504 packages) it is returned unchanged. The input path is not
505 modified; an extended copy is returned. Items are only appended
506 to the copy at the end.
507
508 It is assumed that sys.path is a sequence. Items of sys.path that
509 are not (unicode or 8-bit) strings referring to existing
510 directories are ignored. Unicode items of sys.path that cause
511 errors when used as filenames may cause this function to raise an
512 exception (in line with os.path.isdir() behavior).
513 """
514
515 if not isinstance(path, list):
516 # This could happen e.g. when this is called from inside a
517 # frozen package. Return the path unchanged in that case.
518 return path
519
520 pname = os.path.join(*name.split('.')) # Reconstitute as relative path
521 # Just in case os.extsep != '.'
522 sname = os.extsep.join(name.split('.'))
523 sname_pkg = sname + os.extsep + "pkg"
524 init_py = "__init__" + os.extsep + "py"
525
526 path = path[:] # Start with a copy of the existing path
527
528 for dir in sys.path:
529 if not isinstance(dir, basestring) or not os.path.isdir(dir):
530 continue
531 subdir = os.path.join(dir, pname)
532 # XXX This may still add duplicate entries to path on
533 # case-insensitive filesystems
534 initfile = os.path.join(subdir, init_py)
535 if subdir not in path and os.path.isfile(initfile):
536 path.append(subdir)
537 # XXX Is this the right thing for subpackages like zope.app?
538 # It looks for a file named "zope.app.pkg"
539 pkgfile = os.path.join(dir, sname_pkg)
540 if os.path.isfile(pkgfile):
541 try:
542 f = open(pkgfile)
543 except IOError, msg:
544 sys.stderr.write("Can't open %s: %s\n" %
545 (pkgfile, msg))
546 else:
547 try:
548 for line in f:
549 line = line.rstrip('\n')
550 if not line or line.startswith('#'):
551 continue
552 path.append(line) # Don't check for existence!
553 finally:
554 f.close()
555
556 return path