blob: 08c0fb648053babe5bb8c6939bbd585355060c54 [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:
Greg Steind4f1d202000-02-18 12:03:40 +000013# http://www.lyra.org/greg/python/
Greg Steind4c64ba1999-11-07 13:14:58 +000014#
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
Greg Steinf23aa1e2000-01-03 02:38:29 +000030_StringType = type('')
Greg Steind4f1d202000-02-18 12:03:40 +000031_ModuleType = type(sys) ### doesn't work in JPython...
Greg Steinf23aa1e2000-01-03 02:38:29 +000032
33class ImportManager:
34 "Manage the import process."
Greg Stein281b8d81999-11-07 12:54:45 +000035
Greg Steind4f1d202000-02-18 12:03:40 +000036 def install(self, namespace=vars(__builtin__)):
37 "Install this ImportManager into the specified namespace."
38
39 if isinstance(namespace, _ModuleType):
40 namespace = vars(namespace)
41
42 ### Note that we have no notion of "uninstall" or "chaining"
43 namespace['__import__'] = self._import_hook
Greg Steinf23aa1e2000-01-03 02:38:29 +000044 ### fix this
Greg Steind4f1d202000-02-18 12:03:40 +000045 #namespace['reload'] = self._reload_hook
Greg Steinf23aa1e2000-01-03 02:38:29 +000046
47 def add_suffix(self, suffix, importer):
48 assert isinstance(importer, SuffixImporter)
49 self.suffixes.append((suffix, importer))
Greg Stein281b8d81999-11-07 12:54:45 +000050
51 ######################################################################
52 #
53 # PRIVATE METHODS
54 #
Greg Steinf23aa1e2000-01-03 02:38:29 +000055 def __init__(self):
56 # we're definitely going to be importing something in the future,
57 # so let's just load the OS-related facilities.
58 if not _os_stat:
59 _os_bootstrap()
Greg Stein281b8d81999-11-07 12:54:45 +000060
Greg Steinf23aa1e2000-01-03 02:38:29 +000061 # Initialize the set of suffixes that we recognize and import.
62 # The default will import dynamic-load modules first, followed by
63 # .py files (or a .py file's cached bytecode)
64 self.suffixes = [ ]
65 for desc in imp.get_suffixes():
66 if desc[2] == imp.C_EXTENSION:
67 self.suffixes.append((desc[0], DynLoadSuffixImporter(desc)))
68 self.suffixes.append(('.py', PySuffixImporter()))
Greg Stein281b8d81999-11-07 12:54:45 +000069
Greg Steinf23aa1e2000-01-03 02:38:29 +000070 # This is the importer that we use for grabbing stuff from the
71 # filesystem. It defines one more method (import_from_dir) for our use.
72 self.fs_imp = _FilesystemImporter(self.suffixes)
Greg Stein281b8d81999-11-07 12:54:45 +000073
Greg Steinf23aa1e2000-01-03 02:38:29 +000074 def _import_hook(self, fqname, globals=None, locals=None, fromlist=None):
75 """Python calls this hook to locate and import a module."""
76
77 parts = strop.split(fqname, '.')
Greg Stein63faa011999-11-20 11:22:37 +000078
Greg Stein281b8d81999-11-07 12:54:45 +000079 # determine the context of this import
80 parent = self._determine_import_context(globals)
81
Greg Steinf23aa1e2000-01-03 02:38:29 +000082 # if there is a parent, then its importer should manage this import
83 if parent:
84 module = parent.__importer__._do_import(parent, parts, fromlist)
85 if module:
86 return module
Greg Stein281b8d81999-11-07 12:54:45 +000087
Greg Steinf23aa1e2000-01-03 02:38:29 +000088 # has the top module already been imported?
89 try:
90 top_module = sys.modules[parts[0]]
91 except KeyError:
Greg Stein281b8d81999-11-07 12:54:45 +000092
Greg Steinf23aa1e2000-01-03 02:38:29 +000093 # look for the topmost module
94 top_module = self._import_top_module(parts[0])
95 if not top_module:
96 # the topmost module wasn't found at all.
97 raise ImportError, 'No module named ' + fqname
Greg Steinf23aa1e2000-01-03 02:38:29 +000098
99 # fast-path simple imports
100 if len(parts) == 1:
101 if not fromlist:
102 return top_module
103
104 if not top_module.__dict__.get('__ispkg__'):
105 # __ispkg__ isn't defined (the module was not imported by us), or
106 # it is zero.
107 #
108 # In the former case, there is no way that we could import
109 # sub-modules that occur in the fromlist (but we can't raise an
110 # error because it may just be names) because we don't know how
111 # to deal with packages that were imported by other systems.
112 #
113 # In the latter case (__ispkg__ == 0), there can't be any sub-
114 # modules present, so we can just return.
115 #
116 # In both cases, since len(parts) == 1, the top_module is also
117 # the "bottom" which is the defined return when a fromlist exists.
118 return top_module
119
120 importer = top_module.__dict__.get('__importer__')
121 if importer:
122 return importer._finish_import(top_module, parts[1:], fromlist)
123
124 # If the importer does not exist, then we have to bail. A missing importer
125 # means that something else imported the module, and we have no knowledge
126 # of how to get sub-modules out of the thing.
127 raise ImportError, 'No module named ' + fqname
Greg Steinf23aa1e2000-01-03 02:38:29 +0000128
129 def _determine_import_context(self, globals):
130 """Returns the context in which a module should be imported.
131
132 The context could be a loaded (package) module and the imported module
133 will be looked for within that package. The context could also be None,
134 meaning there is no context -- the module should be looked for as a
135 "top-level" module.
136 """
137
138 if not globals or not globals.get('__importer__'):
139 # globals does not refer to one of our modules or packages. That
140 # implies there is no relative import context (as far as we are
141 # concerned), and it should just pick it off the standard path.
142 return None
143
144 # The globals refer to a module or package of ours. It will define
145 # the context of the new import. Get the module/package fqname.
146 parent_fqname = globals['__name__']
147
148 # if a package is performing the import, then return itself (imports
149 # refer to pkg contents)
150 if globals['__ispkg__']:
151 parent = sys.modules[parent_fqname]
152 assert globals is parent.__dict__
153 return parent
154
155 i = strop.rfind(parent_fqname, '.')
156
157 # a module outside of a package has no particular import context
158 if i == -1:
159 return None
160
161 # if a module in a package is performing the import, then return the
162 # 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, name):
169 # scan sys.path looking for a location in the filesystem that contains
170 # the module, or an Importer object that can import the module.
171 for item in sys.path:
Greg Steind4f1d202000-02-18 12:03:40 +0000172 if isinstance(item, _StringType):
Greg Steinf23aa1e2000-01-03 02:38:29 +0000173 module = self.fs_imp.import_from_dir(item, name)
174 else:
175 module = item.import_top(name)
176 if module:
177 return module
178 return None
179
180 def _reload_hook(self, module):
181 "Python calls this hook to reload a module."
182
183 # reloading of a module may or may not be possible (depending on the
184 # importer), but at least we can validate that it's ours to reload
185 importer = module.__dict__.get('__importer__')
186 if not importer:
Greg Steind4f1d202000-02-18 12:03:40 +0000187 ### oops. now what...
188 pass
Greg Steinf23aa1e2000-01-03 02:38:29 +0000189
190 # okay. it is using the imputil system, and we must delegate it, but
191 # we don't know what to do (yet)
192 ### we should blast the module dict and do another get_code(). need to
193 ### flesh this out and add proper docco...
194 raise SystemError, "reload not yet implemented"
195
196
197class Importer:
198 "Base class for replacing standard import functions."
199
200 def install(self):
201 sys.path.insert(0, self)
202
203 def import_top(self, name):
204 "Import a top-level module."
205 return self._import_one(None, name, name)
206
207 ######################################################################
208 #
209 # PRIVATE METHODS
210 #
211 def _finish_import(self, top, parts, fromlist):
Greg Stein281b8d81999-11-07 12:54:45 +0000212 # if "a.b.c" was provided, then load the ".b.c" portion down from
213 # below the top-level module.
Greg Steinf23aa1e2000-01-03 02:38:29 +0000214 bottom = self._load_tail(top, parts)
Greg Stein281b8d81999-11-07 12:54:45 +0000215
216 # if the form is "import a.b.c", then return "a"
217 if not fromlist:
218 # no fromlist: return the top of the import tree
219 return top
220
Greg Steinf23aa1e2000-01-03 02:38:29 +0000221 # the top module was imported by self.
Greg Stein281b8d81999-11-07 12:54:45 +0000222 #
Greg Steinf23aa1e2000-01-03 02:38:29 +0000223 # this means that the bottom module was also imported by self (just
224 # now, or in the past and we fetched it from sys.modules).
Greg Stein281b8d81999-11-07 12:54:45 +0000225 #
226 # since we imported/handled the bottom module, this means that we can
Greg Steinf23aa1e2000-01-03 02:38:29 +0000227 # also handle its fromlist (and reliably use __ispkg__).
Greg Stein281b8d81999-11-07 12:54:45 +0000228
229 # if the bottom node is a package, then (potentially) import some modules.
230 #
231 # note: if it is not a package, then "fromlist" refers to names in
232 # the bottom module rather than modules.
233 # note: for a mix of names and modules in the fromlist, we will
234 # import all modules and insert those into the namespace of
235 # the package module. Python will pick up all fromlist names
236 # from the bottom (package) module; some will be modules that
237 # we imported and stored in the namespace, others are expected
238 # to be present already.
Greg Steinf23aa1e2000-01-03 02:38:29 +0000239 if bottom.__ispkg__:
Greg Stein281b8d81999-11-07 12:54:45 +0000240 self._import_fromlist(bottom, fromlist)
241
242 # if the form is "from a.b import c, d" then return "b"
243 return bottom
244
Greg Stein281b8d81999-11-07 12:54:45 +0000245 def _import_one(self, parent, modname, fqname):
246 "Import a single module."
247
248 # has the module already been imported?
249 try:
250 return sys.modules[fqname]
251 except KeyError:
252 pass
253
254 # load the module's code, or fetch the module itself
255 result = self.get_code(parent, modname, fqname)
256 if result is None:
257 return None
258
Greg Steinf23aa1e2000-01-03 02:38:29 +0000259 ### backwards-compat
260 if len(result) == 2:
261 result = result + ({},)
Greg Stein281b8d81999-11-07 12:54:45 +0000262
Greg Steinf23aa1e2000-01-03 02:38:29 +0000263 module = self._process_result(result, fqname)
Greg Stein281b8d81999-11-07 12:54:45 +0000264
265 # insert the module into its parent
266 if parent:
267 setattr(parent, modname, module)
268 return module
269
Greg Steinf23aa1e2000-01-03 02:38:29 +0000270 def _process_result(self, (ispkg, code, values), fqname):
271 # did get_code() return an actual module? (rather than a code object)
Greg Steind4f1d202000-02-18 12:03:40 +0000272 is_module = isinstance(code, _ModuleType)
Greg Steinf23aa1e2000-01-03 02:38:29 +0000273
274 # use the returned module, or create a new one to exec code into
275 if is_module:
276 module = code
277 else:
278 module = imp.new_module(fqname)
279
280 ### record packages a bit differently??
281 module.__importer__ = self
282 module.__ispkg__ = ispkg
283
284 # insert additional values into the module (before executing the code)
285 module.__dict__.update(values)
286
287 # the module is almost ready... make it visible
288 sys.modules[fqname] = module
289
290 # execute the code within the module's namespace
291 if not is_module:
292 exec code in module.__dict__
293
294 return module
295
296 def _load_tail(self, m, parts):
Greg Stein281b8d81999-11-07 12:54:45 +0000297 """Import the rest of the modules, down from the top-level module.
298
299 Returns the last module in the dotted list of modules.
300 """
Greg Steinf23aa1e2000-01-03 02:38:29 +0000301 for part in parts:
302 fqname = "%s.%s" % (m.__name__, part)
303 m = self._import_one(m, part, fqname)
304 if not m:
305 raise ImportError, "No module named " + fqname
Greg Stein281b8d81999-11-07 12:54:45 +0000306 return m
307
308 def _import_fromlist(self, package, fromlist):
309 'Import any sub-modules in the "from" list.'
310
311 # if '*' is present in the fromlist, then look for the '__all__' variable
312 # to find additional items (modules) to import.
313 if '*' in fromlist:
314 fromlist = list(fromlist) + list(package.__dict__.get('__all__', []))
315
316 for sub in fromlist:
317 # if the name is already present, then don't try to import it (it
318 # might not be a module!).
319 if sub != '*' and not hasattr(package, sub):
320 subname = "%s.%s" % (package.__name__, sub)
321 submod = self._import_one(package, sub, subname)
322 if not submod:
323 raise ImportError, "cannot import name " + subname
324
Greg Steinf23aa1e2000-01-03 02:38:29 +0000325 def _do_import(self, parent, parts, fromlist):
326 """Attempt to import the module relative to parent.
Greg Stein281b8d81999-11-07 12:54:45 +0000327
Greg Steinf23aa1e2000-01-03 02:38:29 +0000328 This method is used when the import context specifies that <self>
329 imported the parent module.
Greg Stein281b8d81999-11-07 12:54:45 +0000330 """
Greg Steinf23aa1e2000-01-03 02:38:29 +0000331 top_name = parts[0]
332 top_fqname = parent.__name__ + '.' + top_name
333 top_module = self._import_one(parent, top_name, top_fqname)
334 if not top_module:
335 # this importer and parent could not find the module (relatively)
336 return None
337
338 return self._finish_import(top_module, parts[1:], fromlist)
Greg Stein281b8d81999-11-07 12:54:45 +0000339
340 ######################################################################
341 #
342 # METHODS TO OVERRIDE
343 #
344 def get_code(self, parent, modname, fqname):
345 """Find and retrieve the code for the given module.
346
347 parent specifies a parent module to define a context for importing. It
348 may be None, indicating no particular context for the search.
349
350 modname specifies a single module (not dotted) within the parent.
351
352 fqname specifies the fully-qualified module name. This is a (potentially)
353 dotted name from the "root" of the module namespace down to the modname.
354 If there is no parent, then modname==fqname.
355
Greg Steinf23aa1e2000-01-03 02:38:29 +0000356 This method should return None, or a 3-tuple.
Greg Stein281b8d81999-11-07 12:54:45 +0000357
358 * If the module was not found, then None should be returned.
359
360 * The first item of the 2- or 3-tuple should be the integer 0 or 1,
361 specifying whether the module that was found is a package or not.
362
363 * The second item is the code object for the module (it will be
364 executed within the new module's namespace). This item can also
365 be a fully-loaded module object (e.g. loaded from a shared lib).
366
Greg Steinf23aa1e2000-01-03 02:38:29 +0000367 * The third item is a dictionary of name/value pairs that will be
368 inserted into new module before the code object is executed. This
369 is provided in case the module's code expects certain values (such
Greg Stein281b8d81999-11-07 12:54:45 +0000370 as where the module was found). When the second item is a module
371 object, then these names/values will be inserted *after* the module
372 has been loaded/initialized.
373 """
374 raise RuntimeError, "get_code not implemented"
375
376
377######################################################################
378#
Greg Stein63faa011999-11-20 11:22:37 +0000379# Some handy stuff for the Importers
380#
381
Greg Steind4f1d202000-02-18 12:03:40 +0000382# byte-compiled file suffix character
Greg Stein63faa011999-11-20 11:22:37 +0000383_suffix_char = __debug__ and 'c' or 'o'
384
385# byte-compiled file suffix
386_suffix = '.py' + _suffix_char
387
Greg Stein63faa011999-11-20 11:22:37 +0000388def _compile(pathname, timestamp):
389 """Compile (and cache) a Python source file.
390
391 The file specified by <pathname> is compiled to a code object and
392 returned.
393
394 Presuming the appropriate privileges exist, the bytecodes will be
395 saved back to the filesystem for future imports. The source file's
396 modification timestamp must be provided as a Long value.
397 """
398 codestring = open(pathname, 'r').read()
399 if codestring and codestring[-1] != '\n':
400 codestring = codestring + '\n'
401 code = __builtin__.compile(codestring, pathname, 'exec')
402
403 # try to cache the compiled code
404 try:
405 f = open(pathname + _suffix_char, 'wb')
406 except IOError:
407 pass
408 else:
409 f.write('\0\0\0\0')
410 f.write(struct.pack('<I', timestamp))
411 marshal.dump(code, f)
412 f.flush()
413 f.seek(0, 0)
414 f.write(imp.get_magic())
415 f.close()
416
417 return code
418
419_os_stat = _os_path_join = None
420def _os_bootstrap():
421 "Set up 'os' module replacement functions for use during import bootstrap."
422
423 names = sys.builtin_module_names
424
425 join = None
426 if 'posix' in names:
427 sep = '/'
428 from posix import stat
429 elif 'nt' in names:
430 sep = '\\'
431 from nt import stat
432 elif 'dos' in names:
433 sep = '\\'
434 from dos import stat
435 elif 'os2' in names:
436 sep = '\\'
437 from os2 import stat
438 elif 'mac' in names:
439 from mac import stat
440 def join(a, b):
441 if a == '':
442 return b
443 path = s
444 if ':' not in a:
445 a = ':' + a
446 if a[-1:] <> ':':
447 a = a + ':'
448 return a + b
449 else:
450 raise ImportError, 'no os specific module found'
451
452 if join is None:
453 def join(a, b, sep=sep):
454 if a == '':
455 return b
456 lastchar = a[-1:]
457 if lastchar == '/' or lastchar == sep:
458 return a + b
459 return a + sep + b
460
461 global _os_stat
462 _os_stat = stat
463
464 global _os_path_join
465 _os_path_join = join
466
467def _os_path_isdir(pathname):
468 "Local replacement for os.path.isdir()."
469 try:
470 s = _os_stat(pathname)
471 except OSError:
472 return None
473 return (s[0] & 0170000) == 0040000
474
475def _timestamp(pathname):
476 "Return the file modification time as a Long."
477 try:
478 s = _os_stat(pathname)
479 except OSError:
480 return None
481 return long(s[8])
482
Greg Stein63faa011999-11-20 11:22:37 +0000483
484######################################################################
485#
486# Emulate the import mechanism for builtin and frozen modules
487#
488class BuiltinImporter(Importer):
489 def get_code(self, parent, modname, fqname):
490 if parent:
491 # these modules definitely do not occur within a package context
492 return None
493
494 # look for the module
495 if imp.is_builtin(modname):
496 type = imp.C_BUILTIN
497 elif imp.is_frozen(modname):
498 type = imp.PY_FROZEN
499 else:
500 # not found
501 return None
502
503 # got it. now load and return it.
504 module = imp.load_module(modname, None, modname, ('', '', type))
505 return 0, module, { }
506
507
508######################################################################
Greg Steinf23aa1e2000-01-03 02:38:29 +0000509#
510# Internal importer used for importing from the filesystem
511#
512class _FilesystemImporter(Importer):
513 def __init__(self, suffixes):
514 # this list is shared with the ImportManager.
515 self.suffixes = suffixes
516
517 def import_from_dir(self, dir, fqname):
518 result = self._import_pathname(_os_path_join(dir, fqname), fqname)
519 if result:
520 return self._process_result(result, fqname)
521 return None
522
523 def get_code(self, parent, modname, fqname):
524 # This importer is never used with an empty parent. Its existence is
525 # private to the ImportManager. The ImportManager uses the
526 # import_from_dir() method to import top-level modules/packages.
527 # This method is only used when we look for a module within a package.
528 assert parent
529
530 return self._import_pathname(_os_path_join(parent.__pkgdir__, modname),
531 fqname)
532
533 def _import_pathname(self, pathname, fqname):
534 if _os_path_isdir(pathname):
535 result = self._import_pathname(_os_path_join(pathname, '__init__'),
536 fqname)
537 if result:
538 values = result[2]
539 values['__pkgdir__'] = pathname
540 values['__path__'] = [ pathname ]
541 return 1, result[1], values
542 return None
543
544 for suffix, importer in self.suffixes:
545 filename = pathname + suffix
546 try:
547 finfo = _os_stat(filename)
548 except OSError:
549 pass
550 else:
551 return importer.import_file(filename, finfo, fqname)
552 return None
553
554######################################################################
555#
556# SUFFIX-BASED IMPORTERS
557#
558
559class SuffixImporter:
560 def import_file(self, filename, finfo, fqname):
561 raise RuntimeError
562
563class PySuffixImporter(SuffixImporter):
564 def import_file(self, filename, finfo, fqname):
565 file = filename[:-3] + _suffix
566 t_py = long(finfo[8])
567 t_pyc = _timestamp(file)
568
569 code = None
570 if t_pyc is not None and t_pyc >= t_py:
571 f = open(file, 'rb')
572 if f.read(4) == imp.get_magic():
573 t = struct.unpack('<I', f.read(4))[0]
574 if t == t_py:
575 code = marshal.load(f)
576 f.close()
577 if code is None:
578 file = filename
579 code = _compile(file, t_py)
580
581 return 0, code, { '__file__' : file }
582
583class DynLoadSuffixImporter(SuffixImporter):
584 def __init__(self, desc):
585 self.desc = desc
586
587 def import_file(self, filename, finfo, fqname):
588 fp = open(filename, self.desc[1])
589 module = imp.load_module(fqname, fp, filename, self.desc)
590 module.__file__ = filename
591 return 0, module, { }
592
593
594######################################################################
Greg Stein63faa011999-11-20 11:22:37 +0000595
Greg Stein63faa011999-11-20 11:22:37 +0000596def _print_importers():
597 items = sys.modules.items()
598 items.sort()
599 for name, module in items:
600 if module:
601 print name, module.__dict__.get('__importer__', '-- no importer')
602 else:
603 print name, '-- non-existent module'
604
Greg Steinf23aa1e2000-01-03 02:38:29 +0000605def _test_revamp():
606 ImportManager().install()
607 sys.path.insert(0, BuiltinImporter())
608
Greg Stein281b8d81999-11-07 12:54:45 +0000609######################################################################