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