blob: dd8c95cef9687d78912b80d847024f20edbd8a5d [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
20__version__ = '0.3'
21
22# note: avoid importing non-builtin modules
23import imp
24import sys
25import strop
26import __builtin__ ### why this instead of just using __builtins__ ??
27
28# for the DirectoryImporter
29import struct
30import marshal
31
32class Importer:
33 "Base class for replacing standard import functions."
34
35 def install(self):
36 self.__chain_import = __builtin__.__import__
37 self.__chain_reload = __builtin__.reload
38 __builtin__.__import__ = self._import_hook
39 __builtin__.reload = self._reload_hook
40
41 ######################################################################
42 #
43 # PRIVATE METHODS
44 #
45 def _import_hook(self, name, globals=None, locals=None, fromlist=None):
46 """Python calls this hook to locate and import a module.
47
48 This method attempts to load the (dotted) module name. If it cannot
49 find it, then it delegates the import to the next import hook in the
50 chain (where "next" is defined as the import hook that was in place
51 at the time this Importer instance was installed).
52 """
53
54 ### insert a fast-path check for whether the module is already
55 ### loaded? use a variant of _determine_import_context() which
56 ### returns a context regardless of Importer used. generate an
57 ### fqname and look in sys.modules for it.
58
Greg Stein63faa011999-11-20 11:22:37 +000059 ### note that given module a.b which imports c, if c is already
60 ### loaded, python still wants to look for a.c
61
Greg Stein281b8d81999-11-07 12:54:45 +000062 # determine the context of this import
63 parent = self._determine_import_context(globals)
64
65 # import the module within the context, or from the default context
66 top, tail = self._import_top_module(parent, name)
67 if top is None:
68 # the module was not found; delegate to the next import hook
69 return self.__chain_import(name, globals, locals, fromlist)
70
71 # the top module may be under the control of a different importer.
72 # if so, then defer to that importer for completion of the import.
73 # note it may be self, or is undefined so we (self) may as well
74 # finish the import.
75 importer = top.__dict__.get('__importer__', self)
76 return importer._finish_import(top, tail, fromlist)
77
78 def _finish_import(self, top, tail, fromlist):
79 # if "a.b.c" was provided, then load the ".b.c" portion down from
80 # below the top-level module.
81 bottom = self._load_tail(top, tail)
82
83 # if the form is "import a.b.c", then return "a"
84 if not fromlist:
85 # no fromlist: return the top of the import tree
86 return top
87
88 # the top module was imported by self, or it was not imported through
89 # the Importer mechanism and self is simply handling the import of
90 # the sub-modules and fromlist.
91 #
92 # this means that the bottom module was also imported by self, or we
93 # are handling things in the absence of a prior Importer
94 #
95 # ### why the heck are we handling it? what is the example scenario
96 # ### where this happens? note that we can't determine is_package()
97 # ### for non-Importer modules.
98 #
99 # since we imported/handled the bottom module, this means that we can
100 # also handle its fromlist (and reliably determine is_package()).
101
102 # if the bottom node is a package, then (potentially) import some modules.
103 #
104 # note: if it is not a package, then "fromlist" refers to names in
105 # the bottom module rather than modules.
106 # note: for a mix of names and modules in the fromlist, we will
107 # import all modules and insert those into the namespace of
108 # the package module. Python will pick up all fromlist names
109 # from the bottom (package) module; some will be modules that
110 # we imported and stored in the namespace, others are expected
111 # to be present already.
112 if self._is_package(bottom.__dict__):
113 self._import_fromlist(bottom, fromlist)
114
115 # if the form is "from a.b import c, d" then return "b"
116 return bottom
117
118 def _reload_hook(self, module):
119 "Python calls this hook to reload a module."
120
121 # reloading of a module may or may not be possible (depending on the
122 # importer), but at least we can validate that it's ours to reload
123 importer = module.__dict__.get('__importer__', None)
124 if importer is not self:
125 return self.__chain_reload(module)
126
127 # okay. it is ours, but we don't know what to do (yet)
128 ### we should blast the module dict and do another get_code(). need to
129 ### flesh this out and add proper docco...
130 raise SystemError, "reload not yet implemented"
131
132 def _determine_import_context(self, globals):
133 """Returns the context in which a module should be imported.
134
135 The context could be a loaded (package) module and the imported module
136 will be looked for within that package. The context could also be None,
137 meaning there is no context -- the module should be looked for as a
138 "top-level" module.
139 """
140
141 if not globals or \
142 globals.get('__importer__', None) is not self:
143 # globals does not refer to one of our modules or packages.
144 # That implies there is no relative import context, and it
145 # should just pick it off the standard path.
146 return None
147
148 # The globals refer to a module or package of ours. It will define
149 # the context of the new import. Get the module/package fqname.
150 parent_fqname = globals['__name__']
151
152 # for a package, return itself (imports refer to pkg contents)
153 if self._is_package(globals):
154 parent = sys.modules[parent_fqname]
155 assert globals is parent.__dict__
156 return parent
157
158 i = strop.rfind(parent_fqname, '.')
159
160 # a module outside of a package has no particular import context
161 if i == -1:
162 return None
163
164 # for a module in a package, return the package (imports refer to siblings)
165 parent_fqname = parent_fqname[:i]
166 parent = sys.modules[parent_fqname]
167 assert parent.__name__ == parent_fqname
168 return parent
169
170 def _import_top_module(self, parent, name):
171 """Locate the top of the import tree (relative or absolute).
172
173 parent defines the context in which the import should occur. See
174 _determine_import_context() for details.
175
176 Returns a tuple (module, tail). module is the loaded (top-level) module,
177 or None if the module is not found. tail is the remaining portion of
178 the dotted name.
179 """
180 i = strop.find(name, '.')
181 if i == -1:
182 head = name
183 tail = ""
184 else:
185 head = name[:i]
186 tail = name[i+1:]
187 if parent:
188 fqname = "%s.%s" % (parent.__name__, head)
189 else:
190 fqname = head
191 module = self._import_one(parent, head, fqname)
192 if module:
193 # the module was relative, or no context existed (the module was
194 # simply found on the path).
195 return module, tail
196 if parent:
197 # we tried relative, now try an absolute import (from the path)
198 module = self._import_one(None, head, head)
199 if module:
200 return module, tail
201
202 # the module wasn't found
203 return None, None
204
205 def _import_one(self, parent, modname, fqname):
206 "Import a single module."
207
208 # has the module already been imported?
209 try:
210 return sys.modules[fqname]
211 except KeyError:
212 pass
213
214 # load the module's code, or fetch the module itself
215 result = self.get_code(parent, modname, fqname)
216 if result is None:
217 return None
218
219 # did get_code() return an actual module? (rather than a code object)
220 is_module = type(result[1]) is type(sys)
221
222 # use the returned module, or create a new one to exec code into
223 if is_module:
224 module = result[1]
225 else:
226 module = imp.new_module(fqname)
227
228 ### record packages a bit differently??
229 module.__importer__ = self
230 module.__ispkg__ = result[0]
231
232 # if present, the third item is a set of values to insert into the module
233 if len(result) > 2:
234 module.__dict__.update(result[2])
235
236 # the module is almost ready... make it visible
237 sys.modules[fqname] = module
238
239 # execute the code within the module's namespace
240 if not is_module:
241 exec result[1] in module.__dict__
242
243 # insert the module into its parent
244 if parent:
245 setattr(parent, modname, module)
246 return module
247
248 def _load_tail(self, m, tail):
249 """Import the rest of the modules, down from the top-level module.
250
251 Returns the last module in the dotted list of modules.
252 """
253 if tail:
254 for part in strop.splitfields(tail, '.'):
255 fqname = "%s.%s" % (m.__name__, part)
256 m = self._import_one(m, part, fqname)
257 if not m:
258 raise ImportError, "No module named " + fqname
259 return m
260
261 def _import_fromlist(self, package, fromlist):
262 'Import any sub-modules in the "from" list.'
263
264 # if '*' is present in the fromlist, then look for the '__all__' variable
265 # to find additional items (modules) to import.
266 if '*' in fromlist:
267 fromlist = list(fromlist) + list(package.__dict__.get('__all__', []))
268
269 for sub in fromlist:
270 # if the name is already present, then don't try to import it (it
271 # might not be a module!).
272 if sub != '*' and not hasattr(package, sub):
273 subname = "%s.%s" % (package.__name__, sub)
274 submod = self._import_one(package, sub, subname)
275 if not submod:
276 raise ImportError, "cannot import name " + subname
277
278 def _is_package(self, module_dict):
279 """Determine if a given module (dictionary) specifies a package.
280
281 The package status is in the module-level name __ispkg__. The module
282 must also have been imported by self, so that we can reliably apply
283 semantic meaning to __ispkg__.
284
285 ### weaken the test to issubclass(Importer)?
286 """
287 return module_dict.get('__importer__', None) is self and \
288 module_dict['__ispkg__']
289
290 ######################################################################
291 #
292 # METHODS TO OVERRIDE
293 #
294 def get_code(self, parent, modname, fqname):
295 """Find and retrieve the code for the given module.
296
297 parent specifies a parent module to define a context for importing. It
298 may be None, indicating no particular context for the search.
299
300 modname specifies a single module (not dotted) within the parent.
301
302 fqname specifies the fully-qualified module name. This is a (potentially)
303 dotted name from the "root" of the module namespace down to the modname.
304 If there is no parent, then modname==fqname.
305
306 This method should return None, a 2-tuple, or a 3-tuple.
307
308 * If the module was not found, then None should be returned.
309
310 * The first item of the 2- or 3-tuple should be the integer 0 or 1,
311 specifying whether the module that was found is a package or not.
312
313 * The second item is the code object for the module (it will be
314 executed within the new module's namespace). This item can also
315 be a fully-loaded module object (e.g. loaded from a shared lib).
316
317 * If present, the third item is a dictionary of name/value pairs that
318 will be inserted into new module before the code object is executed.
319 This provided in case the module's code expects certain values (such
320 as where the module was found). When the second item is a module
321 object, then these names/values will be inserted *after* the module
322 has been loaded/initialized.
323 """
324 raise RuntimeError, "get_code not implemented"
325
326
327######################################################################
328#
Greg Stein63faa011999-11-20 11:22:37 +0000329# Some handy stuff for the Importers
330#
331
332# byte-compiled file suffic character
333_suffix_char = __debug__ and 'c' or 'o'
334
335# byte-compiled file suffix
336_suffix = '.py' + _suffix_char
337
338
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
434def _fs_import(dir, modname):
435 "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
446 t_py = _timestamp(pathname + '.py')
447 t_pyc = _timestamp(pathname + _suffix)
448 if t_py is None and t_pyc is None:
449 return None
450 code = None
451 if t_py is None or (t_pyc is not None and t_pyc >= t_py):
452 file = pathname + _suffix
453 f = open(file, 'rb')
454 if f.read(4) == imp.get_magic():
455 t = struct.unpack('<I', f.read(4))[0]
456 if t == t_py:
457 code = marshal.load(f)
458 f.close()
459 if code is None:
460 file = pathname + '.py'
461 code = _compile(file, t_py)
462
463 values['__file__'] = file
464 return ispkg, code, values
465
466
467######################################################################
468#
Greg Stein281b8d81999-11-07 12:54:45 +0000469# Simple function-based importer
470#
471class FuncImporter(Importer):
472 "Importer subclass to use a supplied function rather than method overrides."
473 def __init__(self, func):
474 self.func = func
475 def get_code(self, parent, modname, fqname):
476 return self.func(parent, modname, fqname)
477
478def install_with(func):
479 FuncImporter(func).install()
480
481
482######################################################################
483#
484# Base class for archive-based importing
485#
486class PackageArchiveImporter(Importer):
Greg Stein63faa011999-11-20 11:22:37 +0000487 """Importer subclass to import from (file) archives.
488
489 This Importer handles imports of the style <archive>.<subfile>, where
490 <archive> can be located using a subclass-specific mechanism and the
491 <subfile> is found in the archive using a subclass-specific mechanism.
492
493 This class defines two hooks for subclasses: one to locate an archive
494 (and possibly return some context for future subfile lookups), and one
495 to locate subfiles.
496 """
Greg Stein281b8d81999-11-07 12:54:45 +0000497
498 def get_code(self, parent, modname, fqname):
499 if parent:
Greg Stein63faa011999-11-20 11:22:37 +0000500 # the Importer._finish_import logic ensures that we handle imports
501 # under the top level module (package / archive).
502 assert parent.__importer__ == self
503
Greg Stein281b8d81999-11-07 12:54:45 +0000504 # if a parent "package" is provided, then we are importing a sub-file
505 # from the archive.
506 result = self.get_subfile(parent.__archive__, modname)
507 if result is None:
508 return None
509 if type(result) == type(()):
510 return (0,) + result
511 return 0, result
512
513 # no parent was provided, so the archive should exist somewhere on the
514 # default "path".
515 archive = self.get_archive(modname)
516 if archive is None:
517 return None
518 return 1, "", {'__archive__':archive}
519
520 def get_archive(self, modname):
521 """Get an archive of modules.
522
523 This method should locate an archive and return a value which can be
524 used by get_subfile to load modules from it. The value may be a simple
525 pathname, an open file, or a complex object that caches information
526 for future imports.
527
528 Return None if the archive was not found.
529 """
530 raise RuntimeError, "get_archive not implemented"
531
532 def get_subfile(self, archive, modname):
533 """Get code from a subfile in the specified archive.
534
535 Given the specified archive (as returned by get_archive()), locate
536 and return a code object for the specified module name.
537
538 A 2-tuple may be returned, consisting of a code object and a dict
539 of name/values to place into the target module.
540
541 Return None if the subfile was not found.
542 """
543 raise RuntimeError, "get_subfile not implemented"
544
545
546class PackageArchive(PackageArchiveImporter):
547 "PackageArchiveImporter subclass that refers to a specific archive."
548
549 def __init__(self, modname, archive_pathname):
550 self.__modname = modname
551 self.__path = archive_pathname
552
553 def get_archive(self, modname):
554 if modname == self.__modname:
555 return self.__path
556 return None
557
558 # get_subfile is passed the full pathname of the archive
559
560
561######################################################################
562#
563# Emulate the standard directory-based import mechanism
564#
Greg Stein281b8d81999-11-07 12:54:45 +0000565class DirectoryImporter(Importer):
566 "Importer subclass to emulate the standard importer."
567
568 def __init__(self, dir):
569 self.dir = dir
Greg Stein281b8d81999-11-07 12:54:45 +0000570
571 def get_code(self, parent, modname, fqname):
572 if parent:
573 dir = parent.__pkgdir__
574 else:
575 dir = self.dir
576
Greg Stein63faa011999-11-20 11:22:37 +0000577 # defer the loading of OS-related facilities
578 if not _os_stat:
579 _os_bootstrap()
Greg Stein281b8d81999-11-07 12:54:45 +0000580
Greg Stein63faa011999-11-20 11:22:37 +0000581 # Return the module (and other info) if found in the specified
582 # directory. Otherwise, return None.
583 return _fs_import(dir, modname)
Greg Stein281b8d81999-11-07 12:54:45 +0000584
585 def __repr__(self):
586 return '<%s.%s for "%s" at 0x%x>' % (self.__class__.__module__,
587 self.__class__.__name__,
588 self.dir,
589 id(self))
590
Greg Stein63faa011999-11-20 11:22:37 +0000591######################################################################
592#
593# Emulate the standard sys.path import mechanism
594#
595class SysPathImporter(Importer):
596 def __init__(self):
597
598 # we're definitely going to be importing something in the future,
599 # so let's just load the OS-related facilities.
600 if not _os_stat:
601 _os_bootstrap()
602
603 def get_code(self, parent, modname, fqname):
604 if parent:
605 # we are looking for a module inside of a specific package
606 return _fs_import(parent.__pkgdir__, modname)
607
608 # scan sys.path, looking for the requested module
609 for dir in sys.path:
610 result = _fs_import(dir, modname)
611 if result:
Greg Stein63faa011999-11-20 11:22:37 +0000612 return result
613
614 # not found
615 return None
616
617
618######################################################################
619#
620# Emulate the import mechanism for builtin and frozen modules
621#
622class BuiltinImporter(Importer):
623 def get_code(self, parent, modname, fqname):
624 if parent:
625 # these modules definitely do not occur within a package context
626 return None
627
628 # look for the module
629 if imp.is_builtin(modname):
630 type = imp.C_BUILTIN
631 elif imp.is_frozen(modname):
632 type = imp.PY_FROZEN
633 else:
634 # not found
635 return None
636
637 # got it. now load and return it.
638 module = imp.load_module(modname, None, modname, ('', '', type))
639 return 0, module, { }
640
641
642######################################################################
643
Greg Stein281b8d81999-11-07 12:54:45 +0000644def _test_dir():
645 "Debug/test function to create DirectoryImporters from sys.path."
646 path = sys.path[:]
647 path.reverse()
648 for d in path:
649 DirectoryImporter(d).install()
650
Greg Stein63faa011999-11-20 11:22:37 +0000651def _test_revamp():
652 "Debug/test function for the revamped import system."
653 BuiltinImporter().install()
654 SysPathImporter().install()
655
656def _print_importers():
657 items = sys.modules.items()
658 items.sort()
659 for name, module in items:
660 if module:
661 print name, module.__dict__.get('__importer__', '-- no importer')
662 else:
663 print name, '-- non-existent module'
664
Greg Stein281b8d81999-11-07 12:54:45 +0000665######################################################################