blob: ced38a8a54b976b506ea9dab76f17b909409f1a5 [file] [log] [blame]
Greg Stein281b8d81999-11-07 12:54:45 +00001#
2# imputil.py
3#
4# Written by Greg Stein. Public Domain.
5# No Copyright, no Rights Reserved, and no Warranties.
6#
7# Utilities to help out with custom import mechanisms.
8#
9# Additional modifications were contribed by Marc-Andre Lemburg and
10# Gordon McMillan.
11#
Greg Steind4c64ba1999-11-07 13:14:58 +000012# This module is maintained by Greg and is available at:
13# http://www.lyra.org/greg/python/imputil.py
14#
15# Since this isn't in the Python distribution yet, we'll use the CVS ID
16# for tracking:
17# $Id$
18#
Greg Stein281b8d81999-11-07 12:54:45 +000019
Greg Stein281b8d81999-11-07 12:54:45 +000020# note: avoid importing non-builtin modules
21import imp
22import sys
23import strop
Greg Stein7ec28d21999-11-20 12:31:07 +000024import __builtin__
Greg Stein281b8d81999-11-07 12:54:45 +000025
26# for the DirectoryImporter
27import struct
28import marshal
29
30class Importer:
31 "Base class for replacing standard import functions."
32
33 def install(self):
34 self.__chain_import = __builtin__.__import__
35 self.__chain_reload = __builtin__.reload
36 __builtin__.__import__ = self._import_hook
37 __builtin__.reload = self._reload_hook
38
39 ######################################################################
40 #
41 # PRIVATE METHODS
42 #
43 def _import_hook(self, name, globals=None, locals=None, fromlist=None):
44 """Python calls this hook to locate and import a module.
45
46 This method attempts to load the (dotted) module name. If it cannot
47 find it, then it delegates the import to the next import hook in the
48 chain (where "next" is defined as the import hook that was in place
49 at the time this Importer instance was installed).
50 """
51
52 ### insert a fast-path check for whether the module is already
53 ### loaded? use a variant of _determine_import_context() which
54 ### returns a context regardless of Importer used. generate an
55 ### fqname and look in sys.modules for it.
56
Greg Stein63faa011999-11-20 11:22:37 +000057 ### note that given module a.b which imports c, if c is already
58 ### loaded, python still wants to look for a.c
59
Greg Stein281b8d81999-11-07 12:54:45 +000060 # determine the context of this import
61 parent = self._determine_import_context(globals)
62
63 # import the module within the context, or from the default context
64 top, tail = self._import_top_module(parent, name)
65 if top is None:
66 # the module was not found; delegate to the next import hook
67 return self.__chain_import(name, globals, locals, fromlist)
68
69 # the top module may be under the control of a different importer.
70 # if so, then defer to that importer for completion of the import.
71 # note it may be self, or is undefined so we (self) may as well
72 # finish the import.
73 importer = top.__dict__.get('__importer__', self)
74 return importer._finish_import(top, tail, fromlist)
75
76 def _finish_import(self, top, tail, fromlist):
77 # if "a.b.c" was provided, then load the ".b.c" portion down from
78 # below the top-level module.
79 bottom = self._load_tail(top, tail)
80
81 # if the form is "import a.b.c", then return "a"
82 if not fromlist:
83 # no fromlist: return the top of the import tree
84 return top
85
86 # the top module was imported by self, or it was not imported through
87 # the Importer mechanism and self is simply handling the import of
88 # the sub-modules and fromlist.
89 #
90 # this means that the bottom module was also imported by self, or we
91 # are handling things in the absence of a prior Importer
92 #
93 # ### why the heck are we handling it? what is the example scenario
94 # ### where this happens? note that we can't determine is_package()
95 # ### for non-Importer modules.
96 #
97 # since we imported/handled the bottom module, this means that we can
98 # also handle its fromlist (and reliably determine is_package()).
99
100 # if the bottom node is a package, then (potentially) import some modules.
101 #
102 # note: if it is not a package, then "fromlist" refers to names in
103 # the bottom module rather than modules.
104 # note: for a mix of names and modules in the fromlist, we will
105 # import all modules and insert those into the namespace of
106 # the package module. Python will pick up all fromlist names
107 # from the bottom (package) module; some will be modules that
108 # we imported and stored in the namespace, others are expected
109 # to be present already.
110 if self._is_package(bottom.__dict__):
111 self._import_fromlist(bottom, fromlist)
112
113 # if the form is "from a.b import c, d" then return "b"
114 return bottom
115
116 def _reload_hook(self, module):
117 "Python calls this hook to reload a module."
118
119 # reloading of a module may or may not be possible (depending on the
120 # importer), but at least we can validate that it's ours to reload
121 importer = module.__dict__.get('__importer__', None)
122 if importer is not self:
123 return self.__chain_reload(module)
124
125 # okay. it is ours, but we don't know what to do (yet)
126 ### we should blast the module dict and do another get_code(). need to
127 ### flesh this out and add proper docco...
128 raise SystemError, "reload not yet implemented"
129
130 def _determine_import_context(self, globals):
131 """Returns the context in which a module should be imported.
132
133 The context could be a loaded (package) module and the imported module
134 will be looked for within that package. The context could also be None,
135 meaning there is no context -- the module should be looked for as a
136 "top-level" module.
137 """
138
139 if not globals or \
140 globals.get('__importer__', None) is not self:
141 # globals does not refer to one of our modules or packages.
142 # That implies there is no relative import context, and it
143 # should just pick it off the standard path.
144 return None
145
146 # The globals refer to a module or package of ours. It will define
147 # the context of the new import. Get the module/package fqname.
148 parent_fqname = globals['__name__']
149
150 # for a package, return itself (imports refer to pkg contents)
151 if self._is_package(globals):
152 parent = sys.modules[parent_fqname]
153 assert globals is parent.__dict__
154 return parent
155
156 i = strop.rfind(parent_fqname, '.')
157
158 # a module outside of a package has no particular import context
159 if i == -1:
160 return None
161
162 # for a module in a package, return the package (imports refer to siblings)
163 parent_fqname = parent_fqname[:i]
164 parent = sys.modules[parent_fqname]
165 assert parent.__name__ == parent_fqname
166 return parent
167
168 def _import_top_module(self, parent, name):
169 """Locate the top of the import tree (relative or absolute).
170
171 parent defines the context in which the import should occur. See
172 _determine_import_context() for details.
173
174 Returns a tuple (module, tail). module is the loaded (top-level) module,
175 or None if the module is not found. tail is the remaining portion of
176 the dotted name.
177 """
178 i = strop.find(name, '.')
179 if i == -1:
180 head = name
181 tail = ""
182 else:
183 head = name[:i]
184 tail = name[i+1:]
185 if parent:
186 fqname = "%s.%s" % (parent.__name__, head)
187 else:
188 fqname = head
189 module = self._import_one(parent, head, fqname)
190 if module:
191 # the module was relative, or no context existed (the module was
192 # simply found on the path).
193 return module, tail
194 if parent:
195 # we tried relative, now try an absolute import (from the path)
196 module = self._import_one(None, head, head)
197 if module:
198 return module, tail
199
200 # the module wasn't found
201 return None, None
202
203 def _import_one(self, parent, modname, fqname):
204 "Import a single module."
205
206 # has the module already been imported?
207 try:
208 return sys.modules[fqname]
209 except KeyError:
210 pass
211
212 # load the module's code, or fetch the module itself
213 result = self.get_code(parent, modname, fqname)
214 if result is None:
215 return None
216
217 # did get_code() return an actual module? (rather than a code object)
218 is_module = type(result[1]) is type(sys)
219
220 # use the returned module, or create a new one to exec code into
221 if is_module:
222 module = result[1]
223 else:
224 module = imp.new_module(fqname)
225
226 ### record packages a bit differently??
227 module.__importer__ = self
228 module.__ispkg__ = result[0]
229
230 # if present, the third item is a set of values to insert into the module
231 if len(result) > 2:
232 module.__dict__.update(result[2])
233
234 # the module is almost ready... make it visible
235 sys.modules[fqname] = module
236
237 # execute the code within the module's namespace
238 if not is_module:
239 exec result[1] in module.__dict__
240
241 # insert the module into its parent
242 if parent:
243 setattr(parent, modname, module)
244 return module
245
246 def _load_tail(self, m, tail):
247 """Import the rest of the modules, down from the top-level module.
248
249 Returns the last module in the dotted list of modules.
250 """
251 if tail:
252 for part in strop.splitfields(tail, '.'):
253 fqname = "%s.%s" % (m.__name__, part)
254 m = self._import_one(m, part, fqname)
255 if not m:
256 raise ImportError, "No module named " + fqname
257 return m
258
259 def _import_fromlist(self, package, fromlist):
260 'Import any sub-modules in the "from" list.'
261
262 # if '*' is present in the fromlist, then look for the '__all__' variable
263 # to find additional items (modules) to import.
264 if '*' in fromlist:
265 fromlist = list(fromlist) + list(package.__dict__.get('__all__', []))
266
267 for sub in fromlist:
268 # if the name is already present, then don't try to import it (it
269 # might not be a module!).
270 if sub != '*' and not hasattr(package, sub):
271 subname = "%s.%s" % (package.__name__, sub)
272 submod = self._import_one(package, sub, subname)
273 if not submod:
274 raise ImportError, "cannot import name " + subname
275
276 def _is_package(self, module_dict):
277 """Determine if a given module (dictionary) specifies a package.
278
279 The package status is in the module-level name __ispkg__. The module
280 must also have been imported by self, so that we can reliably apply
281 semantic meaning to __ispkg__.
282
283 ### weaken the test to issubclass(Importer)?
284 """
285 return module_dict.get('__importer__', None) is self and \
286 module_dict['__ispkg__']
287
288 ######################################################################
289 #
290 # METHODS TO OVERRIDE
291 #
292 def get_code(self, parent, modname, fqname):
293 """Find and retrieve the code for the given module.
294
295 parent specifies a parent module to define a context for importing. It
296 may be None, indicating no particular context for the search.
297
298 modname specifies a single module (not dotted) within the parent.
299
300 fqname specifies the fully-qualified module name. This is a (potentially)
301 dotted name from the "root" of the module namespace down to the modname.
302 If there is no parent, then modname==fqname.
303
304 This method should return None, a 2-tuple, or a 3-tuple.
305
306 * If the module was not found, then None should be returned.
307
308 * The first item of the 2- or 3-tuple should be the integer 0 or 1,
309 specifying whether the module that was found is a package or not.
310
311 * The second item is the code object for the module (it will be
312 executed within the new module's namespace). This item can also
313 be a fully-loaded module object (e.g. loaded from a shared lib).
314
315 * If present, the third item is a dictionary of name/value pairs that
316 will be inserted into new module before the code object is executed.
317 This provided in case the module's code expects certain values (such
318 as where the module was found). When the second item is a module
319 object, then these names/values will be inserted *after* the module
320 has been loaded/initialized.
321 """
322 raise RuntimeError, "get_code not implemented"
323
324
325######################################################################
326#
Greg Stein63faa011999-11-20 11:22:37 +0000327# Some handy stuff for the Importers
328#
329
330# byte-compiled file suffic character
331_suffix_char = __debug__ and 'c' or 'o'
332
333# byte-compiled file suffix
334_suffix = '.py' + _suffix_char
335
Greg Stein2b234131999-11-24 02:37:05 +0000336# the C_EXTENSION suffixes
337_c_suffixes = filter(lambda x: x[2] == imp.C_EXTENSION, imp.get_suffixes())
Greg Stein63faa011999-11-20 11:22:37 +0000338
339def _compile(pathname, timestamp):
340 """Compile (and cache) a Python source file.
341
342 The file specified by <pathname> is compiled to a code object and
343 returned.
344
345 Presuming the appropriate privileges exist, the bytecodes will be
346 saved back to the filesystem for future imports. The source file's
347 modification timestamp must be provided as a Long value.
348 """
349 codestring = open(pathname, 'r').read()
350 if codestring and codestring[-1] != '\n':
351 codestring = codestring + '\n'
352 code = __builtin__.compile(codestring, pathname, 'exec')
353
354 # try to cache the compiled code
355 try:
356 f = open(pathname + _suffix_char, 'wb')
357 except IOError:
358 pass
359 else:
360 f.write('\0\0\0\0')
361 f.write(struct.pack('<I', timestamp))
362 marshal.dump(code, f)
363 f.flush()
364 f.seek(0, 0)
365 f.write(imp.get_magic())
366 f.close()
367
368 return code
369
370_os_stat = _os_path_join = None
371def _os_bootstrap():
372 "Set up 'os' module replacement functions for use during import bootstrap."
373
374 names = sys.builtin_module_names
375
376 join = None
377 if 'posix' in names:
378 sep = '/'
379 from posix import stat
380 elif 'nt' in names:
381 sep = '\\'
382 from nt import stat
383 elif 'dos' in names:
384 sep = '\\'
385 from dos import stat
386 elif 'os2' in names:
387 sep = '\\'
388 from os2 import stat
389 elif 'mac' in names:
390 from mac import stat
391 def join(a, b):
392 if a == '':
393 return b
394 path = s
395 if ':' not in a:
396 a = ':' + a
397 if a[-1:] <> ':':
398 a = a + ':'
399 return a + b
400 else:
401 raise ImportError, 'no os specific module found'
402
403 if join is None:
404 def join(a, b, sep=sep):
405 if a == '':
406 return b
407 lastchar = a[-1:]
408 if lastchar == '/' or lastchar == sep:
409 return a + b
410 return a + sep + b
411
412 global _os_stat
413 _os_stat = stat
414
415 global _os_path_join
416 _os_path_join = join
417
418def _os_path_isdir(pathname):
419 "Local replacement for os.path.isdir()."
420 try:
421 s = _os_stat(pathname)
422 except OSError:
423 return None
424 return (s[0] & 0170000) == 0040000
425
426def _timestamp(pathname):
427 "Return the file modification time as a Long."
428 try:
429 s = _os_stat(pathname)
430 except OSError:
431 return None
432 return long(s[8])
433
Greg Stein2b234131999-11-24 02:37:05 +0000434def _fs_import(dir, modname, fqname):
Greg Stein63faa011999-11-20 11:22:37 +0000435 "Fetch a module from the filesystem."
436
437 pathname = _os_path_join(dir, modname)
438 if _os_path_isdir(pathname):
Greg Stein6d3165a1999-11-20 11:39:00 +0000439 values = { '__pkgdir__' : pathname, '__path__' : [ pathname ] }
Greg Stein63faa011999-11-20 11:22:37 +0000440 ispkg = 1
441 pathname = _os_path_join(pathname, '__init__')
442 else:
443 values = { }
444 ispkg = 0
445
Greg Stein2b234131999-11-24 02:37:05 +0000446 # look for dynload modules
447 for desc in _c_suffixes:
448 file = pathname + desc[0]
449 try:
450 fp = open(file, desc[1])
451 except IOError:
452 pass
453 else:
454 module = imp.load_module(fqname, fp, file, desc)
455 values['__file__'] = file
456 return 0, module, values
457
Greg Stein63faa011999-11-20 11:22:37 +0000458 t_py = _timestamp(pathname + '.py')
459 t_pyc = _timestamp(pathname + _suffix)
460 if t_py is None and t_pyc is None:
461 return None
462 code = None
463 if t_py is None or (t_pyc is not None and t_pyc >= t_py):
464 file = pathname + _suffix
465 f = open(file, 'rb')
466 if f.read(4) == imp.get_magic():
467 t = struct.unpack('<I', f.read(4))[0]
468 if t == t_py:
469 code = marshal.load(f)
470 f.close()
471 if code is None:
472 file = pathname + '.py'
473 code = _compile(file, t_py)
474
475 values['__file__'] = file
476 return ispkg, code, values
477
478
479######################################################################
480#
Greg Stein281b8d81999-11-07 12:54:45 +0000481# Simple function-based importer
482#
483class FuncImporter(Importer):
484 "Importer subclass to use a supplied function rather than method overrides."
485 def __init__(self, func):
486 self.func = func
487 def get_code(self, parent, modname, fqname):
488 return self.func(parent, modname, fqname)
489
490def install_with(func):
491 FuncImporter(func).install()
492
493
494######################################################################
495#
496# Base class for archive-based importing
497#
498class PackageArchiveImporter(Importer):
Greg Stein63faa011999-11-20 11:22:37 +0000499 """Importer subclass to import from (file) archives.
500
501 This Importer handles imports of the style <archive>.<subfile>, where
502 <archive> can be located using a subclass-specific mechanism and the
503 <subfile> is found in the archive using a subclass-specific mechanism.
504
505 This class defines two hooks for subclasses: one to locate an archive
506 (and possibly return some context for future subfile lookups), and one
507 to locate subfiles.
508 """
Greg Stein281b8d81999-11-07 12:54:45 +0000509
510 def get_code(self, parent, modname, fqname):
511 if parent:
Greg Stein63faa011999-11-20 11:22:37 +0000512 # the Importer._finish_import logic ensures that we handle imports
513 # under the top level module (package / archive).
514 assert parent.__importer__ == self
515
Greg Stein281b8d81999-11-07 12:54:45 +0000516 # if a parent "package" is provided, then we are importing a sub-file
517 # from the archive.
518 result = self.get_subfile(parent.__archive__, modname)
519 if result is None:
520 return None
521 if type(result) == type(()):
522 return (0,) + result
523 return 0, result
524
525 # no parent was provided, so the archive should exist somewhere on the
526 # default "path".
527 archive = self.get_archive(modname)
528 if archive is None:
529 return None
530 return 1, "", {'__archive__':archive}
531
532 def get_archive(self, modname):
533 """Get an archive of modules.
534
535 This method should locate an archive and return a value which can be
536 used by get_subfile to load modules from it. The value may be a simple
537 pathname, an open file, or a complex object that caches information
538 for future imports.
539
540 Return None if the archive was not found.
541 """
542 raise RuntimeError, "get_archive not implemented"
543
544 def get_subfile(self, archive, modname):
545 """Get code from a subfile in the specified archive.
546
547 Given the specified archive (as returned by get_archive()), locate
548 and return a code object for the specified module name.
549
550 A 2-tuple may be returned, consisting of a code object and a dict
551 of name/values to place into the target module.
552
553 Return None if the subfile was not found.
554 """
555 raise RuntimeError, "get_subfile not implemented"
556
557
558class PackageArchive(PackageArchiveImporter):
559 "PackageArchiveImporter subclass that refers to a specific archive."
560
561 def __init__(self, modname, archive_pathname):
562 self.__modname = modname
563 self.__path = archive_pathname
564
565 def get_archive(self, modname):
566 if modname == self.__modname:
567 return self.__path
568 return None
569
570 # get_subfile is passed the full pathname of the archive
571
572
573######################################################################
574#
575# Emulate the standard directory-based import mechanism
576#
Greg Stein281b8d81999-11-07 12:54:45 +0000577class DirectoryImporter(Importer):
578 "Importer subclass to emulate the standard importer."
579
580 def __init__(self, dir):
581 self.dir = dir
Greg Stein281b8d81999-11-07 12:54:45 +0000582
583 def get_code(self, parent, modname, fqname):
584 if parent:
585 dir = parent.__pkgdir__
586 else:
587 dir = self.dir
588
Greg Stein63faa011999-11-20 11:22:37 +0000589 # defer the loading of OS-related facilities
590 if not _os_stat:
591 _os_bootstrap()
Greg Stein281b8d81999-11-07 12:54:45 +0000592
Greg Stein63faa011999-11-20 11:22:37 +0000593 # Return the module (and other info) if found in the specified
594 # directory. Otherwise, return None.
Greg Stein2b234131999-11-24 02:37:05 +0000595 return _fs_import(dir, modname, fqname)
Greg Stein281b8d81999-11-07 12:54:45 +0000596
597 def __repr__(self):
598 return '<%s.%s for "%s" at 0x%x>' % (self.__class__.__module__,
599 self.__class__.__name__,
600 self.dir,
601 id(self))
602
Greg Stein63faa011999-11-20 11:22:37 +0000603######################################################################
604#
Greg Stein7ec28d21999-11-20 12:31:07 +0000605# Emulate the standard path-style import mechanism
Greg Stein63faa011999-11-20 11:22:37 +0000606#
Greg Stein7ec28d21999-11-20 12:31:07 +0000607class PathImporter(Importer):
608 def __init__(self, path=sys.path):
609 self.path = path
Greg Stein63faa011999-11-20 11:22:37 +0000610
611 # we're definitely going to be importing something in the future,
612 # so let's just load the OS-related facilities.
613 if not _os_stat:
614 _os_bootstrap()
615
616 def get_code(self, parent, modname, fqname):
617 if parent:
618 # we are looking for a module inside of a specific package
Greg Stein2b234131999-11-24 02:37:05 +0000619 return _fs_import(parent.__pkgdir__, modname, fqname)
Greg Stein63faa011999-11-20 11:22:37 +0000620
621 # scan sys.path, looking for the requested module
Greg Stein7ec28d21999-11-20 12:31:07 +0000622 for dir in self.path:
Greg Stein2b234131999-11-24 02:37:05 +0000623 result = _fs_import(dir, modname, fqname)
Greg Stein63faa011999-11-20 11:22:37 +0000624 if result:
Greg Stein63faa011999-11-20 11:22:37 +0000625 return result
626
627 # not found
628 return None
629
630
631######################################################################
632#
633# Emulate the import mechanism for builtin and frozen modules
634#
635class BuiltinImporter(Importer):
636 def get_code(self, parent, modname, fqname):
637 if parent:
638 # these modules definitely do not occur within a package context
639 return None
640
641 # look for the module
642 if imp.is_builtin(modname):
643 type = imp.C_BUILTIN
644 elif imp.is_frozen(modname):
645 type = imp.PY_FROZEN
646 else:
647 # not found
648 return None
649
650 # got it. now load and return it.
651 module = imp.load_module(modname, None, modname, ('', '', type))
652 return 0, module, { }
653
654
655######################################################################
656
Greg Stein281b8d81999-11-07 12:54:45 +0000657def _test_dir():
658 "Debug/test function to create DirectoryImporters from sys.path."
659 path = sys.path[:]
660 path.reverse()
661 for d in path:
662 DirectoryImporter(d).install()
663
Greg Stein63faa011999-11-20 11:22:37 +0000664def _test_revamp():
665 "Debug/test function for the revamped import system."
666 BuiltinImporter().install()
Greg Stein7ec28d21999-11-20 12:31:07 +0000667 PathImporter().install()
Greg Stein63faa011999-11-20 11:22:37 +0000668
669def _print_importers():
670 items = sys.modules.items()
671 items.sort()
672 for name, module in items:
673 if module:
674 print name, module.__dict__.get('__importer__', '-- no importer')
675 else:
676 print name, '-- non-existent module'
677
Greg Stein281b8d81999-11-07 12:54:45 +0000678######################################################################