Clarify portability and main program.
[python/dscho.git] / Lib / newimp.py
blob231a33dcea445f701bee8c60cf7fdfede87eb1ef
1 """Prototype of 'import' functionality enhanced to implement packages.
3 Why packages? Packages enable module nesting and sibling module
4 imports. 'Til now, the python module namespace was flat, which
5 means every module had to have a unique name, in order to not
6 conflict with names of other modules on the load path. Furthermore,
7 suites of modules could not be structurally affiliated with one
8 another.
10 With packages, a suite of, eg, email-oriented modules can include a
11 module named 'mailbox', without conflicting with the, eg, 'mailbox'
12 module of a shared-memory suite - 'email.mailbox' vs
13 'shmem.mailbox'. Packages also enable modules within a suite to
14 load other modules within their package without having the package
15 name hard-coded. Similarly, package suites of modules can be loaded
16 as a unit, by loading the package that contains them.
18 Usage: once installed (newimp.install(); newimp.revert() to revert to
19 the prior __import__ routine), 'import ...' and 'from ... import ...'
20 can be used to:
22 - import modules from the search path, as before.
24 - import modules from within other directory "packages" on the search
25 path using a '.' dot-delimited nesting syntax. The nesting is fully
26 recursive.
28 For example, 'import test.test_types' will import the test_types
29 module within the 'test' package. The calling environment would
30 then access the module as 'test.test_types', which is the name of
31 the fully-loaded 'test_types' module. It is found contained within
32 the stub (ie, only partially loaded) 'test' module, hence accessed as
33 'test.test_types'.
35 - import siblings from modules within a package, using '__.' as a shorthand
36 prefix to refer to the parent package. This enables referential
37 transparency - package modules need not know their package name.
39 The '__' package references are actually names assigned within
40 modules, to refer to their containing package. This means that
41 variable references can be made to imported modules, or to variables
42 defined via 'import ... from', also using the '__.var' shorthand
43 notation. This establishes a proper equivalence between the import
44 reference '__.sibling' and the var reference '__.sibling'.
46 - import an entire package as a unit, by importing the package directory.
47 If there is a module named '__init__.py' in the package, it controls the
48 load. Otherwise, all the modules in the dir, including packages, are
49 inherently loaded into the package module's namespace.
51 For example, 'import test' will load the modules of the entire 'test'
52 package, at least until a test failure is encountered.
54 In a package, a module with the name '__init__' has a special role.
55 If present in a package directory, then it is loaded into the package
56 module, instead of loading the contents of the directory. This
57 enables the __init__ module to control the load, possibly loading
58 the entire directory deliberately (using 'import __', or even
59 'from __ import *', to load all the module contents directly into the
60 package module).
62 - perform any combination of the above - have a package that contains
63 packages, etc.
65 Modules have a few new attributes in support of packages. As mentioned
66 above, '__' is a shorthand attribute denoting the modules' parent package,
67 also denoted in the module by '__package__'. Additionally, modules have
68 associated with them a '__pkgpath__', a path by which sibling modules are
69 found."""
71 __version__ = "$Revision$"
73 # $Id$ First release:
74 # Ken.Manheimer@nist.gov, 5-Apr-1995, for python 1.2
76 # Issues (scattered in code - search for three asterisks)
77 # *** Despite my efforts, 'reload(newimp)' will foul things up.
78 # *** Normalize_pathname will only work for Unix - which we need to detect.
79 # *** when a module with the name of the platform (as indicated by
80 # to-be-created var sys.platform), the package path gets '.' and the
81 # platform dir.
82 # *** use sys.impadmin for things like an import load-hooks var
83 # *** Import-load-hook keying module name versus package path, which dictates
84 # additions to the default ('.' and os-specific dir) path
85 # *** Document that the __init__.py can set __.__pkgpath__, in which case that
86 # will be used for the package-relative loads.
87 # *** Add a 'recursive' option to reload, for reload of package constituent
88 # modules (including subpackages), as well. Or maybe that should be the
89 # default, and eg stub-completion should override that default. ???
91 # Developers Notes:
93 # - 'sys.stub_modules' registers "incidental" (partially loaded) modules.
94 # A stub module is promoted to the fully-loaded 'sys.modules' list when it is
95 # explicitly loaded as a unit.
96 # - One load nuance - the actual load of most module types goes into the
97 # already-generated stub module. HOWEVER, eg dynamically loaded modules
98 # generate a new module object, which must supplant the existing stub. One
99 # consequence is that the import process must use indirection through
100 # sys.stub_modules or sys.modules to track the actual modules across some of
101 # the phases.
102 # - The test routines are cool, including a transient directory
103 # hierarchy facility, and a means of skipping to later tests by giving
104 # the test routine a numeric arg.
105 # - There may still be some loose ends, not to mention bugs. But the full
106 # functionality should be there.
107 # - The ImportStack object is necessary to carry the list of in-process imports
108 # across very open-ended recursions, where the state cannot be passed
109 # explicitly via the import_module calls; for a primary example, via exec of
110 # an 'import' statement within a module.
111 # - Python's (current) handling of extension modules, via imp.load_dynamic,
112 # does too much, some of which needs to be undone. See comments in
113 # load_module. Among other things, we actually change the __name__ of the
114 # module, which conceivably may break something.
116 try:
117 VERBOSE
118 except NameError:
119 VERBOSE = 0 # Will be reset by init(1), also.
121 import sys, string, regex, types, os, marshal, traceback
122 import __main__, __builtin__
124 newimp_globals = vars()
126 try:
127 import imp # Build on this recent addition
128 except ImportError:
129 raise ImportError, 'Pkg import module depends on optional "imp" module'#==X
131 from imp import SEARCH_ERROR, PY_SOURCE, PY_COMPILED, C_EXTENSION
133 def defvar(varNm, envDict, val, override=0):
134 """If VARNAME does not have value in DICT, assign VAL to it. Optional arg
135 OVERRIDE means force the assignment in any case."""
136 if (not envDict.has_key(varNm)) or override:
137 envDict[varNm] = val
139 def init(full_reset=0):
140 """Do environment initialization, including retrofitting sys.modules with
141 module attributes."""
142 # Retrofit all existing modules with package attributes, under auspices of
143 # __root__:
145 locals, globals = vars(), newimp_globals
147 if full_reset:
148 global VERBOSE
149 VERBOSE = 0
151 # sys.stub_modules tracks modules partially loaded modules, ie loaded only
152 # incidental to load of nested components. Together with sys.modules and
153 # the import stack, it serves as part of the module registration mechanism.
154 defvar('stub_modules', sys.__dict__, {}, full_reset)
156 # Environment setup - "root" module, '__root__'
157 # Establish root package '__root__' in __main__ and newimp envs.
159 # Longhand for name of variable identifying module's containing package:
160 defvar('PKG_NM', globals, "__package__", full_reset)
161 # Shorthand for module's container:
162 defvar('PKG_SHORT_NM', globals, "__", full_reset)
163 defvar('PKG_SHORT_NM_LEN', globals, len(PKG_SHORT_NM), full_reset)
165 # Name of controlling module for a package, if any:
166 defvar('INIT_MOD_NM', globals, "__init__", full_reset)
168 # Paths eventually will be extended to accomodate non-filesystem media -
169 # eg, URLs, composite objects, who knows.
171 # Name assigned in sys for general import administration:
172 defvar('IMP_SYS_NM', globals, "imp_admin", full_reset)
173 defvar('MOD_LOAD_HOOKS', globals, "mod_load_hooks", full_reset)
174 if full_reset:
175 defvar(IMP_SYS_NM, sys.__dict__, {MOD_LOAD_HOOKS: {}}, full_reset)
178 # Name assigned in each module to tuple describing module import attrs:
179 defvar('IMP_ADMIN', globals, "__impadmin__", full_reset)
180 # The load-path obtaining for this package. Not defined for non-packages.
181 # If not set, package directory is used. If no package directory
182 # registered, sys.path is used.
183 defvar('PKG_PATH', globals, 0, full_reset)
184 # File from which module was loaded - may be None, eg, for __root__:
185 defvar('MOD_TYPE', globals, 1, full_reset)
186 # Exact path from which the module was loaded:
187 defvar('MOD_PATHNAME', globals, 2, full_reset)
188 # Package within which the module was found:
189 defvar('MOD_PACKAGE', globals, 3, full_reset)
190 defvar('USE_PATH', globals, 'either PKG_PATH or my dir', full_reset)
192 # We're aliasing the top-level __main__ module as '__root__':
193 defvar('__root__', globals, __main__, full_reset)
194 defvar('ROOT_MOD_NM', globals, "__root__", full_reset)
195 if not sys.modules.has_key('__root__') or full_reset:
196 # and register it as an imported module:
197 sys.modules[ROOT_MOD_NM] = __root__
199 # Register package information in all existing top-level modules - they'll
200 # the None's mean, among other things, that their USE_PATH's all defer to
201 # sys.path.
202 for aMod in sys.modules.values():
203 if (not aMod.__dict__.has_key(PKG_NM)) or full_reset:
204 set_mod_attrs(aMod, None, __root__, None, None)
206 try:
207 __builtin__.__import__
208 defvar('origImportFunc', globals, __builtin__.__import__)
209 defvar('origReloadFunc', globals, __builtin__.reload)
210 except AttributeError:
211 pass
213 defvar('PY_PACKAGE', globals, 4, full_reset)
214 defvar('PY_FROZEN', globals, 5, full_reset)
215 defvar('PY_BUILTIN', globals, 6, full_reset)
217 # Establish lookup table from mod-type "constants" to names:
218 defvar('mod_types', globals,
219 {SEARCH_ERROR: 'SEARCH_ERROR',
220 PY_SOURCE: 'PY_SOURCE',
221 PY_COMPILED: 'PY_COMPILED',
222 C_EXTENSION: 'C_EXTENSION',
223 PY_PACKAGE: 'PY_PACKAGE',
224 PY_FROZEN: 'PY_FROZEN',
225 PY_BUILTIN: 'PY_BUILTIN'},
226 full_reset)
228 defvar('stack', globals, ImportStack(), full_reset)
230 def install():
231 """Install newimp import_module() routine, for package support.
233 newimp.revert() reverts to __import__ routine that was superceded."""
234 __builtin__.__import__ = import_module
235 __builtin__.reload = reload
236 __builtin__.unload = unload
237 __builtin__.bypass = bypass
238 return 'Enhanced import functionality in place.'
239 def revert():
240 """Revert to original __builtin__.__import__ func, if newimp.install() has
241 been executed."""
242 if not (origImportFunc and origReloadFunc):
243 raise SystemError, "Can't find original import and reload funcs." # ==X
244 __builtin__.__import__ = origImportFunc
245 __builtin__.reload = origReloadFunc
246 del __builtin__.unload, __builtin__.bypass
247 return 'Original import routines back in place.'
249 def import_module(name,
250 envLocals=None, envGlobals=None,
251 froms=None,
252 inPkg=None):
253 """Primary service routine implementing 'import' with package nesting.
255 NAME: name as specified to 'import NAME' or 'from NAME...'
256 LOCALS, GLOBALS: local and global dicts obtaining for import
257 FROMS: list of strings of "..." in 'import blat from ...'
258 INPKG: package to which the name search is restricted, for use
259 by recursive package loads (from import_module()).
261 A subtle difference from the old import - modules that do fail
262 initialization will not be registered in sys.modules, ie will not, in
263 effect, be registered as being loaded. Note further that packages which
264 fail their overall load, but have successfully loaded constituent modules,
265 will be accessible in the importing namespace as stub modules.
267 A new routine, 'newimp.bypass()', provides the means to circumvent
268 constituent modules that fail their load, in order to enable load of the
269 remainder of a package."""
271 rootMod = sys.modules[ROOT_MOD_NM]
273 note("import_module: seeking '%s'" % name, 1)
275 # We need callers environment dict for local path and resulting module
276 # binding.
277 if not envGlobals:
278 # This should not happen, but does for imports called from within
279 # functions.
280 envLocals, envGlobals = exterior()
282 if inPkg:
283 pkg = inPkg
284 elif envGlobals.has_key(PKG_NM):
285 pkg = envGlobals[PKG_NM]
286 else:
287 # ** KLUDGE - cover for modules that lack package attributes:
288 pkg = rootMod
290 if pkg != rootMod:
291 note(' - relative to package %s' % pkg)
293 modList = theMod = absNm = nesting = None
295 # Normalize
296 # - absNm is absolute w.r.t. __root__
297 # - relNm is relative w.r.t. pkg.
298 if inPkg:
299 absNm, relNm = pkg.__name__ + '.' + name, name
300 else:
301 absNm, relNm, pkg = normalize_import_ref(name, pkg)
302 note("Normalized: %s%s" % (absNm, (((relNm != absNm)
303 and (" ('%s' in %s)" % (relNm, pkg)))
304 or '')), 3)
306 pkgPath = get_mod_attrs(pkg, USE_PATH)
308 try: # try...finally guards import stack integrity.
310 if stack.push(absNm):
311 # We're nested inside a containing import of this module, perhaps
312 # indirectly. Avoid infinite recursion at this point by using the
313 # existing stub module, for now. Load of it will be completed by
314 # the superior import.
315 note('recursion on in-process module %s, punting with stub' %
316 absNm)
317 theMod = stack.mod(absNm)
319 else:
321 # Try to find already-imported:
322 if sys.modules.has_key(absNm):
323 note('found ' + absNm + ' already imported')
324 theMod = sys.modules[absNm]
325 stack.mod(absNm, theMod)
327 else: # Actually do load, of one sort or another:
329 # Seek builtin or frozen first:
330 theMod = imp.init_builtin(absNm)
331 if theMod:
332 set_mod_attrs(theMod, None, pkg, None, PY_BUILTIN)
333 stack.mod(absNm, theMod)
334 note('found builtin ' + absNm)
335 else:
336 theMod = imp.init_frozen(absNm)
337 if theMod:
338 set_mod_attrs(theMod, None, pkg, None, PY_FROZEN)
339 stack.mod(absNm, theMod)
340 note('found frozen ' + absNm)
342 if not theMod:
343 # Not already-loaded, in-process, builtin, or frozen -
344 # we're seeking in the outside world (filesystem):
346 if sys.stub_modules.has_key(absNm):
348 # A package for which we have a stub:
349 theMod = reload(sys.stub_modules[absNm], inPkg)
351 else:
353 # Now we actually search the fs.
355 if type(pkgPath) == types.StringType:
356 pkgPath = [pkgPath]
358 # Find a path leading to the module:
359 modList = find_module(relNm, pkgPath, absNm)
360 if not modList:
361 raise ImportError, ("module '%s' not found" % #==X
362 absNm)
364 # We have a list of successively nested dirs leading
365 # to the module, register with import admin, as stubs:
366 nesting = register_mod_nesting(modList, pkg)
368 # Load from file if necessary and possible:
369 modNm, modf, path, ty = modList[-1]
370 note('found type %s - %s' % (mod_types[ty[2]], absNm))
372 # Establish the module object in question:
373 theMod = procure_module(absNm)
374 stack.mod(absNm, theMod)
376 # Do the load:
377 theMod = load_module(theMod, ty[2], modf, inPkg)
379 commit_mod_containment(absNm)
381 # Successful load - promote to fully-imported status:
382 register_module(theMod, theMod.__name__)
385 # We have a loaded module (perhaps stub): situate specified components,
386 # and return appropriate thing. According to guido:
388 # "Note that for "from spam.ham import bacon" your function should
389 # return the object denoted by 'spam.ham', while for "import
390 # spam.ham" it should return the object denoted by 'spam' -- the
391 # STORE instructions following the import statement expect it this
392 # way."
393 # *** The above rationale should probably be reexamined, since newimp
394 # actually takes care of populating the caller's namespace.
396 if not froms:
398 # Return the outermost container, possibly stub:
399 if nesting:
400 return find_mod_registration(nesting[0][0])
401 else:
402 return find_mod_registration(string.splitfields(absNm,'.')[0])
403 else:
405 return theMod
407 finally: # Decrement stack registration:
408 stack.pop(absNm)
411 def reload(module, inPkg = None):
412 """Re-parse and re-initialize an already (or partially) imported MODULE.
414 The argument can be an already loaded module object or a string name of a
415 loaded module or a "stub" module that was partially loaded package module
416 incidental to the full load of a contained module.
418 This is useful if you have edited the module source file using an external
419 editor and want to try out the new version without leaving the Python
420 interpreter. The return value is the resulting module object.
422 Contrary to the old 'reload', the load is sought from the same location
423 where the module was originally found. If you wish to do a fresh load from
424 a different module on the path, do an 'unload()' and then an import.
426 When a module is reloaded, its dictionary (containing the module's
427 global variables) is retained. Redefinitions of names will
428 override the old definitions, so this is generally not a problem.
429 If the new version of a module does not define a name that was
430 defined by the old version, the old definition remains. This
431 feature can be used to the module's advantage if it maintains a
432 global table or cache of objects -- with a `try' statement it can
433 test for the table's presence and skip its initialization if
434 desired.
436 It is legal though generally not very useful to reload built-in or
437 dynamically loaded modules, except for `sys', `__main__' and
438 `__builtin__'. In certain cases, however, extension modules are
439 not designed to be initialized more than once, and may fail in
440 arbitrary ways when reloaded.
442 If a module imports objects from another module using `from' ...
443 `import' ..., calling `reload()' for the other module does not
444 redefine the objects imported from it -- one way around this is to
445 re-execute the `from' statement, another is to use `import' and
446 qualified names (MODULE.NAME) instead.
448 If a module instantiates instances of a class, reloading the module
449 that defines the class does not affect the method definitions of
450 the instances, unless they are reinstantiated -- they continue to use the
451 old class definition. The same is true for derived classes."""
453 if type(module) == types.StringType:
454 theMod = find_mod_registration(module)
455 elif type(module) == types.ModuleType:
456 theMod = module
457 else:
458 raise ImportError, '%s not already imported' # ==X
460 if theMod in [sys.modules[ROOT_MOD_NM], sys.modules['__builtin__']]:
461 raise ImportError, 'cannot re-init internal module' # ==X
463 try:
464 thePath = get_mod_attrs(theMod, MOD_PATHNAME)
465 except KeyError:
466 thePath = None
468 if not thePath:
469 # If we have no path for the module, we can only reload it from
470 # scratch:
471 note('no pathname registered for %s, doing full reload' % theMod)
472 unload(theMod)
473 envGlobals, envLocals = exterior()
474 return import_module(theMod.__name__,
475 envGlobals, envLocals, None, inPkg)
476 else:
478 stack.mod(theMod.__name__, theMod)
479 ty = get_mod_attrs(theMod, MOD_TYPE)
480 if ty in [PY_SOURCE, PY_COMPILED]:
481 note('reload invoked for %s %s' % (mod_types[ty], theMod))
482 thePath, ty, openFile = prefer_compiled(thePath, ty)
483 else:
484 openFile = open(thePath, get_suffixes(ty)[1])
485 return load_module(theMod, # ==>
487 openFile,
488 inPkg)
489 def unload(module):
490 """Remove registration for a module, so import will do a fresh load.
492 Returns the module registries (sys.modules and/or sys.stub_modules) where
493 it was found."""
494 if type(module) == types.ModuleType:
495 module = module.__name__
496 gotit = []
497 for which in ['sys.modules', 'sys.stub_modules']:
498 m = eval(which)
499 try:
500 del m[module]
501 gotit.append(which)
502 except KeyError:
503 pass
504 if not gotit:
505 raise ValueError, '%s not a module or a stub' % module # ==X
506 else: return gotit
507 def bypass(modNm):
508 """Register MODULE-NAME so module will be skipped, eg in package load."""
509 if sys.modules.has_key(modNm):
510 raise ImportError("'%s' already imported, cannot be bypassed." % modNm)
511 else:
512 sys.modules[modNm] = imp.new_module('bypass()ed module %s' % modNm)
513 commit_mod_containment(modNm)
516 def normalize_import_ref(name, pkg):
517 """Produce absolute and relative nm and relative pkg given MODNM and origin
518 PACKAGE, reducing out all '__'s in the process."""
520 # First reduce out all the '__' container-refs we can:
521 outwards, inwards = 0, []
522 for nm in string.splitfields(name, '.'):
523 if nm == PKG_SHORT_NM:
524 if inwards:
525 # Pop a containing inwards:
526 del inwards[-1]
527 else:
528 # (Effectively) leading '__' - notch outwards:
529 outwards = outwards + 1
530 else:
531 inwards.append(nm)
532 inwards = string.joinfields(inwards, '.')
534 # Now identify the components:
536 if not outwards:
537 pkg = sys.modules[ROOT_MOD_NM]
538 else:
539 while outwards > 1:
540 pkg = pkg.__dict__[PKG_NM] # We'll just loop at top
541 if pkg == __root__:
542 break # ==v
543 outwards = outwards - 1
545 if not inwards: # Entire package:
546 return pkg.__name__, pkg.__name__, pkg # ==>
547 else: # Name relative to package:
548 if pkg == __root__:
549 return inwards, inwards, pkg # ==>
550 else:
551 return pkg.__name__ + '.' + inwards, inwards, pkg # ==>
553 class ImportStack:
554 """Provide judicious support for mutually recursive import loops.
556 Mutually recursive imports, eg a module that imports the package that
557 contains it, which in turn imports the module, are not uncommon, and must
558 be supported judiciously. This class is used to track cycles, so a module
559 already in the process of being imported (via 'stack.push(module)', and
560 concluded via 'stack.release(module)') is not redundantly pursued; *except*
561 when a module master '__init__.py' loads the module, in which case it is
562 'stack.relax(module)'ed, so the full import is pursued."""
564 def __init__(self):
565 self._cycles = {}
566 self._mods = {}
567 self._looped = []
568 def in_process(self, modNm):
569 """1 if modNm load already in process, 0 otherwise."""
570 return self._cycles.has_key(modNm) # ==>
571 def looped(self, modNm):
572 """1 if modNm load has looped once or more, 0 otherwise."""
573 return modNm in self._looped
574 def push(self, modNm):
575 """1 if modNm already in process and not 'relax'ed, 0 otherwise.
576 (Note that the 'looped' status remains even when the cycle count
577 returns to 1. This is so error messages can indicate that it was, at
578 some point, looped during the import process.)"""
579 if self.in_process(modNm):
580 self._looped.append(modNm)
581 self._cycles[modNm] = self._cycles[modNm] + 1
582 return 1 # ==>
583 else:
584 self._cycles[modNm] = 1
585 return 0 # ==>
586 def mod(self, modNm, mod=None):
587 """Associate MOD-NAME with MODULE, for easy reference."""
588 if mod:
589 self._mods[modNm] = mod
590 else:
591 try:
592 return self._mods[modNm] # ==>
593 except KeyError:
594 return None
595 def pop(self, modNm):
596 """Decrement stack count of MODNM"""
597 if self.in_process(modNm):
598 amt = self._cycles[modNm] = self._cycles[modNm] - 1
599 if amt < 1:
600 del self._cycles[modNm]
601 if modNm in self._looped:
602 self._looped.remove(modNm)
603 if self._mods.has_key(modNm):
604 del self._mods[modNm]
605 def relax(self, modNm):
606 """Enable modNm load despite being registered as already in-process."""
607 if self._cycles.has_key(modNm):
608 del self._cycles[modNm]
610 def find_module(name, path, absNm=''):
611 """Locate module NAME on PATH. PATH is pathname string or a list of them.
613 Note that up-to-date compiled versions of a module are preferred to plain
614 source, and compilation is automatically performed when necessary and
615 possible.
617 Returns a list of the tuples returned by 'find_mod_file()', one for
618 each nested level, deepest last."""
620 checked = [] # For avoiding redundant dir lists.
622 if not absNm: absNm = name
624 # Parse name into list of nested components,
625 expNm = string.splitfields(name, '.')
627 for curPath in path:
629 if (type(curPath) != types.StringType) or (curPath in checked):
630 # Disregard bogus or already investigated path elements:
631 continue # ==^
632 else:
633 # Register it for subsequent disregard.
634 checked.append(curPath)
636 if len(expNm) == 1:
638 # Non-nested module name:
640 got = find_mod_file(curPath, absNm)
641 if got:
642 note('using %s' % got[2], 3)
643 return [got] # ==>
645 else:
647 # Composite name specifying nested module:
649 gotList = []; nameAccume = expNm[0]
651 got = find_mod_file(curPath, nameAccume)
652 if not got: # Continue to next prospective path.
653 continue # ==^
654 else:
655 gotList.append(got)
656 nm, file, fullPath, ty = got
658 # Work on successively nested components:
659 for component in expNm[1:]:
660 # 'ty'pe of containing component must be package:
661 if ty[2] != PY_PACKAGE:
662 gotList, got = [], None
663 break # ==v
664 if nameAccume:
665 nameAccume = nameAccume + '.' + component
666 else:
667 nameAccume = component
668 got = find_mod_file(fullPath, nameAccume)
669 if got:
670 gotList.append(got)
671 nm, file, fullPath, ty = got
672 else:
673 # Clear state vars:
674 gotList, got, nameAccume = [], None, ''
675 break # ==v
676 # Found nesting all the way to the specified tip:
677 if got:
678 return gotList # ==>
680 # Failed.
681 return None
683 def find_mod_file(pathNm, modname):
684 """Find right module file given DIR and module NAME, compiling if needed.
686 If successful, returns quadruple consisting of:
687 - mod name,
688 - file object,
689 - full pathname for the found file,
690 - a description triple as contained in the list returned by get_suffixes.
692 Otherwise, returns None.
694 Note that up-to-date compiled versions of a module are preferred to plain
695 source, and compilation is automatically performed, when necessary and
696 possible."""
698 relNm = modname[1 + string.rfind(modname, '.'):]
700 for suff, mode, ty in get_suffixes():
701 fullPath = os.path.join(pathNm, relNm + suff)
702 note('trying ' + fullPath + '...', 4)
703 try:
704 modf = open(fullPath, mode)
705 except IOError:
706 # ** ?? Skip unreadable ones:
707 continue # ==^
709 if ty == PY_PACKAGE:
710 # Enforce directory characteristic:
711 if not os.path.isdir(fullPath):
712 note('Skipping non-dir match ' + fullPath, 3)
713 continue # ==^
714 else:
715 return (modname, modf, fullPath, (suff, mode, ty)) # ==>
718 elif ty in [PY_SOURCE, PY_COMPILED]:
719 usePath, useTy, openFile = prefer_compiled(fullPath, ty)
720 return (modname, # ==>
721 openFile,
722 usePath,
723 get_suffixes(useTy))
725 elif ty == C_EXTENSION:
726 note('found C_EXTENSION ' + fullPath, 3)
727 return (modname, modf, fullPath, (suff, mode, ty)) # ==>
729 else:
730 raise SystemError, 'Unanticipated module type encountered' # ==X
732 return None
734 def prefer_compiled(path, ty, modf=None):
735 """Given a path to a .py or .pyc file, attempt to return a path to a
736 current pyc file, compiling the .py in the process if necessary. Returns
737 the path to the most current version we can get."""
739 if ty == PY_SOURCE:
740 if not modf:
741 try:
742 modf = open(path, 'r')
743 except IOError:
744 pass
745 note('working from PY_SOURCE', 3)
746 # Try for a compiled version:
747 pyc = path + 'c' # Sadly, we're presuming '.py' suff.
748 if (not os.path.exists(pyc) or
749 (os.stat(path)[8] > os.stat(pyc)[8])):
750 # Try to compile:
751 pyc = compile_source(path, modf)
752 if pyc and not (os.stat(path)[8] > os.stat(pyc)[8]):
753 # Either pyc was already newer or we just made it so; in either
754 # case it's what we crave:
755 note('but got newer compiled, ' + pyc, 3)
756 try:
757 return (pyc, PY_COMPILED, open(pyc, 'rb')) # ==>
758 except IOError:
759 if modf:
760 return (path, PY_SOURCE, modf) # ==>
761 else:
762 raise ImportError, 'Failed acces to .py and .pyc' # ==X
763 else:
764 note("couldn't get newer compiled, using PY_SOURCE", 3)
765 if modf:
766 return (path, PY_SOURCE, modf) # ==>
767 else:
768 raise ImportError, 'Failed acces to .py and .pyc' # ==X
770 elif ty == PY_COMPILED:
771 note('working from PY_COMPILED', 3)
772 if not modf:
773 try:
774 modf = open(path, 'rb')
775 except IOError:
776 return prefer_compiled(path[:-1], PY_SOURCE)
777 # Make sure it is current, trying to compile if necessary, and
778 # prefer source failing that:
779 note('found compiled ' + path, 3)
780 py = path[:-1] # ** Presuming '.pyc' suffix
781 if not os.path.exists(py):
782 note('pyc SANS py: ' + path, 3)
783 return (path, PY_COMPILED, open(py, 'r')) # ==>
784 elif (os.stat(py)[8] > os.stat(path)[8]):
785 note('Forced to compile: ' + py, 3)
786 pyc = compile_source(py, open(py, 'r'))
787 if pyc:
788 return (pyc, PY_COMPILED, modf) # ==>
789 else:
790 note('failed compile - must use more recent .py', 3)
791 return (py, PY_SOURCE, open(py, 'r')) # ==>
792 else:
793 return (path, PY_COMPILED, modf) # ==>
795 def load_module(theMod, ty, theFile, fromMod):
796 """Load module NAME, of TYPE, from FILE, within MODULE.
798 Optional arg fromMod indicates the module from which the load is being done
799 - necessary for detecting import of __ from a package's __init__ module.
801 Return the populated module object."""
803 # Note: we mint and register intermediate package directories, as necessary
805 name = theMod.__name__
806 nameTail = name[1 + string.rfind(name, '.'):]
807 thePath = theFile.name
809 if ty == PY_SOURCE:
810 exec_into(theFile, theMod, theFile.name)
812 elif ty == PY_COMPILED:
813 pyc = open(theFile.name, 'rb').read()
814 if pyc[0:4] != imp.get_magic():
815 raise ImportError, 'bad magic number: ' + theFile.name # ==X
816 code = marshal.loads(pyc[8:])
817 exec_into(code, theMod, theFile.name)
819 elif ty == C_EXTENSION:
820 # Dynamically loaded C_EXTENSION modules do too much import admin,
821 # themselves, which we need to *undo* in order to integrate them with
822 # the new import scheme.
823 # 1 They register themselves in sys.modules, registering themselves
824 # under their top-level names. Have to rectify that.
825 # 2 The produce their own module objects, *unless* they find an
826 # existing module already registered a la 1, above. We employ this
827 # quirk to make it use the already generated module.
828 try:
829 # Stash a ref to any module that is already registered under the
830 # dyamic module's simple name (nameTail), so we can reestablish it
831 # after the dynamic takes over its' slot:
832 protMod = None
833 if nameTail != name:
834 if sys.modules.has_key(nameTail):
835 protMod = sys.modules[nameTail]
836 # Trick the dynamic load, by registering the module we generated
837 # under the nameTail of the module we're loading, so the one we're
838 # loading will use that established module, rather than producing a
839 # new one:
840 sys.modules[nameTail] = theMod
841 theMod = imp.load_dynamic(nameTail, thePath, theFile)
842 theMod.__name__ = name
843 # Cleanup dynamic mod's bogus self-registration, if necessary:
844 if nameTail != name:
845 if protMod:
846 # ... reinstating the one that was already there...
847 sys.modules[nameTail] = protMod
848 else:
849 if sys.modules.has_key(nameTail):
850 # Certain, as long os dynamics continue to misbehave.
851 del sys.modules[nameTail]
852 stack.mod(name, theMod)
853 if sys.stub_modules.has_key(name):
854 sys.stub_modules[name] = theMod
855 elif sys.modules.has_key(name):
856 sys.modules[name] = theMod
857 except:
858 # Provide import-nesting info, including signs of circularity:
859 raise sys.exc_type, import_trail_msg(str(sys.exc_value),# ==X
860 sys.exc_traceback,
861 name)
862 elif ty == PY_PACKAGE:
863 # Load package constituents, doing the controlling module *if* it
864 # exists *and* it isn't already in process:
866 init_mod_f = init_mod = None
867 if not stack.in_process(name + '.' + INIT_MOD_NM):
868 # Not already doing __init__ - check for it:
869 init_mod_f = find_mod_file(thePath, INIT_MOD_NM)
870 else:
871 note('skipping already-in-process %s.%s' % (theMod.__name__,
872 INIT_MOD_NM))
873 got = {}
874 if init_mod_f:
875 note("Found package's __init__: " + init_mod_f[2])
876 # Enable full continuance of containing-package-load from __init__:
877 if stack.in_process(theMod.__name__):
878 stack.relax(theMod.__name__)
879 init_mod = import_module(INIT_MOD_NM,
880 theMod.__dict__, theMod.__dict__,
881 None,
882 theMod)
883 else:
884 # ... or else recursively load all constituent modules, except
885 # __init__:
886 for prospect in mod_prospects(thePath):
887 if prospect != INIT_MOD_NM:
888 import_module(prospect,
889 theMod.__dict__, theMod.__dict__,
890 None,
891 theMod)
893 else:
894 raise ImportError, 'Unimplemented import type: %s' % ty # ==X
896 return theMod
898 def exec_into(obj, module, path):
899 """Helper for load_module, execfile/exec path or code OBJ within MODULE."""
901 # This depends on ability of exec and execfile to mutilate, erhm, mutate
902 # the __dict__ of a module. It will not work if/when this becomes
903 # disallowed, as it is for normal assignments.
905 try:
906 if type(obj) == types.FileType:
907 execfile(path, module.__dict__, module.__dict__)
908 elif type(obj) in [types.CodeType, types.StringType]:
909 exec obj in module.__dict__, module.__dict__
910 except:
911 # Make the error message nicer?
912 raise sys.exc_type, import_trail_msg(str(sys.exc_value), # ==X
913 sys.exc_traceback,
914 module.__name__)
917 def mod_prospects(path):
918 """Return a list of prospective modules within directory PATH.
920 We actually return the distinct names resulting from stripping the dir
921 entries (excluding os.curdir and os.pardir) of their suffixes (as
922 represented by 'get_suffixes').
924 (Note that matches for the PY_PACKAGE type with null suffix are
925 implicitly constrained to be directories.)"""
927 # We actually strip the longest matching suffixes, so eg 'dbmmodule.so'
928 # mates with 'module.so' rather than '.so'.
930 dirList = os.listdir(path)
931 excludes = [os.curdir, os.pardir]
932 sortedSuffs = sorted_suffixes()
933 entries = []
934 for item in dirList:
935 if item in excludes: continue # ==^
936 for suff in sortedSuffs:
937 # *** ?? maybe platform-specific:
938 sub = -1 * len(suff)
939 if sub == 0:
940 if os.path.isdir(os.path.join(path, item)):
941 entries.append(item)
942 elif item[sub:] == suff:
943 it = item[:sub]
944 if not it in entries:
945 entries.append(it)
946 break # ==v
947 return entries
951 def procure_module(name):
952 """Return an established or else new module object having NAME.
954 First checks sys.modules, then sys.stub_modules."""
956 if sys.modules.has_key(name):
957 return sys.modules[name] # ==>
958 elif sys.stub_modules.has_key(name):
959 return sys.stub_modules[name] # ==>
960 else:
961 return (stack.mod(name) or imp.new_module(name)) # ==>
963 def commit_mod_containment(name):
964 """Bind a module object and its containers within their respective
965 containers."""
966 cume, pkg = '', find_mod_registration(ROOT_MOD_NM)
967 for next in string.splitfields(name, '.'):
968 if cume:
969 cume = cume + '.' + next
970 else:
971 cume = next
972 cumeMod = find_mod_registration(cume)
973 pkg.__dict__[next] = cumeMod
974 pkg = cumeMod
976 def register_mod_nesting(modList, pkg):
977 """Given find_module()-style NEST-LIST and parent PACKAGE, register new
978 package components as stub modules, and return list of nested
979 module/relative-name pairs.
981 Note that the modules objects are not situated in their containing packages
982 here - that is left 'til after a successful load, and done by
983 commit_mod_nesting()."""
984 nesting = []
986 for modNm, modF, path, ty in modList:
988 relNm = modNm[1 + string.rfind(modNm, '.'):]
990 if sys.modules.has_key(modNm):
991 theMod = sys.modules[modNm] # Nestle in containing package
992 pkg = theMod # Set as parent for next in sequence.
993 elif sys.stub_modules.has_key(modNm):
994 # Similar to above...
995 theMod = sys.stub_modules[modNm]
996 pkg = theMod
997 else:
998 theMod = procure_module(modNm)
999 stack.mod(modNm, theMod)
1000 # *** ??? Should we be using 'path' instead of modF.name? If not,
1001 # should we get rid of the 'path' return val?
1002 set_mod_attrs(theMod, normalize_pathname(modF.name),
1003 pkg, None, ty[2])
1004 if ty[2] == PY_PACKAGE:
1005 # Register as a stub:
1006 register_module(theMod, modNm, 1)
1007 pkg = theMod
1008 nesting.append((theMod.__name__,relNm))
1010 return nesting
1012 def register_module(theMod, name, stub=0):
1013 """Properly register MODULE, NAME, and optional STUB qualification."""
1015 if stub:
1016 sys.stub_modules[name] = theMod
1017 else:
1018 sys.modules[name] = theMod
1019 if sys.stub_modules.has_key(name):
1020 del sys.stub_modules[name]
1022 def find_mod_registration(name):
1023 """Find module named NAME sys.modules, .stub_modules, or on the stack."""
1024 if sys.stub_modules.has_key(name):
1025 return sys.stub_modules[name] # ==>
1026 elif sys.modules.has_key(name):
1027 return sys.modules[name] # ==>
1028 else:
1029 if stack.in_process(name):
1030 it = stack.mod(name)
1031 if it:
1032 return it # ==>
1033 else:
1034 raise ValueError, '%s %s in %s or %s' % (name, # ==X
1035 'not registered',
1036 'sys.modules',
1037 'sys.stub_modules')
1039 def get_mod_attrs(theMod, which = None):
1040 """Get MODULE object's path, containing-package, and designated path.
1042 Virtual attribute USE_PATH is derived from PKG_PATH, MOD_PATHNAME,
1043 and/or sys.path, depending on the module type and settings."""
1044 it = theMod.__dict__[IMP_ADMIN]
1045 if which:
1046 # Load path is either the explicitly designated load path for the
1047 # package, or else the directory in which it resides:
1048 if which == USE_PATH:
1049 if it[PKG_PATH]:
1050 # Return explicitly designated path:
1051 return it[PKG_PATH] # ==>
1052 if it[MOD_PATHNAME]:
1053 if it[MOD_TYPE] == PY_PACKAGE:
1054 # Return the package's directory path:
1055 return [it[MOD_PATHNAME]] # ==>
1056 else:
1057 # Return the directory where the module resides:
1058 return [os.path.split(it[MOD_PATHNAME])[0]] # ==>
1059 # No explicitly designated path - use sys.path, eg for system
1060 # modules, etc:
1061 return sys.path
1062 return it[which] # ==>
1063 else:
1064 return it # ==>
1066 def set_mod_attrs(theMod, path, pkg, pkgPath, ty):
1067 """Register MOD import attrs PATH, PKG container, and PKGPATH, linking
1068 the package container into the module along the way."""
1069 theDict = theMod.__dict__
1070 try:
1071 # Get existing one, if any:
1072 it = theDict[IMP_ADMIN]
1073 except KeyError:
1074 # None existing, gen a new one:
1075 it = [None] * 4
1076 for fld, val in ((MOD_PATHNAME, path), (MOD_PACKAGE, pkg),
1077 (PKG_PATH, pkgPath), (MOD_TYPE, ty)):
1078 if val:
1079 it[fld] = val
1081 theDict[IMP_ADMIN] = it
1082 if pkg:
1083 theDict[PKG_NM] = theDict[PKG_SHORT_NM] = pkg
1084 return it # ==>
1086 def format_tb_msg(tb, recursive):
1087 """This should be in traceback.py, and traceback.print_tb() should use it
1088 and traceback.extract_tb(), instead of print_tb() and extract_tb() having
1089 so much redundant code!"""
1090 tb_lines, formed = traceback.extract_tb(tb), ''
1091 for line in tb_lines:
1092 f, lno, nm, ln = line
1093 if f[-1 * (len(__name__) + 3):] == __name__ + '.py':
1094 # Skip newimp notices - agregious hack, justified only by the fact
1095 # that this functionality will be properly doable in new impending
1096 # exception mechanism:
1097 continue
1098 formed = formed + ('\n%s File "%s", line %d, in %s%s' %
1099 (((recursive and '*') or ' '),
1100 f, lno, nm,
1101 ((ln and '\n ' + string.strip(ln)) or '')))
1102 return formed
1104 def import_trail_msg(msg, tb, modNm):
1105 """Doctor an error message to include the path of the current import, and
1106 a sign that it is a circular import, if so."""
1107 return (msg +
1108 format_tb_msg(tb,
1109 (stack.looped(modNm) and stack.in_process(modNm))))
1111 def compile_source(sourcePath, sourceFile):
1112 """Given python code source path and file obj, Create a compiled version.
1114 Return path of compiled version, or None if file creation is not
1115 successful. (Compilation errors themselves are passed without restraint.)
1117 This is an import-private interface, and not well-behaved for general use.
1119 In particular, we presume the validity of the sourcePath, and that it
1120 includes a '.py' extension."""
1122 compiledPath = sourcePath[:-3] + '.pyc'
1123 try:
1124 compiledFile = open(compiledPath, 'wb')
1125 except IOError:
1126 note("write permission denied to " + compiledPath, 3)
1127 return None
1128 mtime = os.stat(sourcePath)[8]
1130 try:
1131 compiled = compile(sourceFile.read(), sourcePath, 'exec')
1132 except SyntaxError:
1133 # Doctor the exception a bit, to include the source file name in
1134 # the report, and then reraise the doctored version.
1135 os.unlink(compiledFile.name)
1136 sys.exc_value = ((sys.exc_value[0] + ' in ' + sourceFile.name,)
1137 + sys.exc_value[1:])
1138 raise sys.exc_type, sys.exc_value # ==X
1140 # Ok, we have a valid compilation.
1141 try:
1142 compiledFile.write(imp.get_magic()) # compiled magic number
1143 compiledFile.seek(8, 0) # mtime space holder
1144 marshal.dump(compiled, compiledFile) # write the code obj
1145 compiledFile.seek(4, 0) # position for mtime
1146 compiledFile.write(marshal.dumps(mtime)[1:]) # register mtime
1147 compiledFile.flush()
1148 compiledFile.close()
1149 return compiledPath
1150 except IOError:
1151 return None # ==>
1154 got_suffixes = None
1155 got_suffixes_dict = {}
1156 def get_suffixes(ty=None):
1157 """Produce a list of triples, each describing a type of import file.
1159 Triples have the form '(SUFFIX, MODE, TYPE)', where:
1161 SUFFIX is a string found appended to a module name to make a filename for
1162 that type of import file.
1164 MODE is the mode string to be passed to the built-in 'open' function - "r"
1165 for text files, "rb" for binary.
1167 TYPE is the file type:
1169 PY_SOURCE: python source code,
1170 PY_COMPILED: byte-compiled python source,
1171 C_EXTENSION: compiled-code object file,
1172 PY_PACKAGE: python library directory, or
1173 SEARCH_ERROR: no module found. """
1175 # Note: sorted_suffixes() depends on this function's value being invariant.
1176 # sorted_suffixes() must be revised if this becomes untrue.
1178 global got_suffixes, got_suffixes_dict
1180 if not got_suffixes:
1181 # Ensure that the .pyc suffix precedes the .py:
1182 got_suffixes = [('', 'r', PY_PACKAGE)]
1183 got_suffixes_dict[PY_PACKAGE] = ('', 'r', PY_PACKAGE)
1184 py = pyc = None
1185 for suff in imp.get_suffixes():
1186 got_suffixes_dict[suff[2]] = suff
1187 if suff[0] == '.py':
1188 py = suff
1189 elif suff[0] == '.pyc':
1190 pyc = suff
1191 else:
1192 got_suffixes.append(suff)
1193 got_suffixes.append(pyc)
1194 got_suffixes.append(py)
1195 if ty:
1196 return got_suffixes_dict[ty] # ==>
1197 else:
1198 return got_suffixes # ==>
1201 sortedSuffs = [] # State vars for sorted_suffixes(). Go
1202 def sorted_suffixes():
1203 """Helper function ~efficiently~ tracks sorted list of module suffixes."""
1205 # Produce sortedSuffs once - this presumes that get_suffixes does not
1206 # change from call to call during a python session. Needs to be
1207 # corrected if that becomes no longer true.
1209 global sortedsuffs
1210 if not sortedSuffs: # do compute only the "first" time
1211 for item in get_suffixes():
1212 sortedSuffs.append(item[0])
1213 # Sort them in descending order:
1214 sortedSuffs.sort(lambda x, y: (((len(x) > len(y)) and 1) or
1215 ((len(x) < len(y)) and -1)))
1216 sortedSuffs.reverse()
1217 return sortedSuffs
1220 def normalize_pathname(path):
1221 """Given PATHNAME, return an absolute pathname relative to cwd, reducing
1222 unnecessary components where convenient (eg, on Unix)."""
1224 # We do a lot more when we have posix-style paths, eg os.sep == '/'.
1226 if os.sep != '/':
1227 return os.path.join(os.getcwd, path) # ==>
1229 outwards, inwards = 0, []
1230 for nm in string.splitfields(path, os.sep):
1231 if nm != os.curdir:
1232 if nm == os.pardir:
1233 # Translate parent-dir entries to outward notches:
1234 if inwards:
1235 # Pop a containing inwards:
1236 del inwards[-1]
1237 else:
1238 # Register leading outward notches:
1239 outwards = outwards + 1
1240 else:
1241 inwards.append(nm)
1242 inwards = string.joinfields(inwards, os.sep)
1244 if (not inwards) or (inwards[0] != os.sep):
1245 # Relative path - join with current working directory, (ascending
1246 # outwards to account for leading parent-dir components):
1247 cwd = os.getcwd()
1248 if outwards:
1249 cwd = string.splitfields(cwd, os.sep)
1250 cwd = string.joinfields(cwd[:len(cwd) - outwards], os.sep)
1251 if inwards:
1252 return os.path.join(cwd, inwards) # ==>
1253 else:
1254 return cwd # ==>
1255 else:
1256 return inwards # ==>
1259 # exterior(): Utility routine, obtain local and global dicts of environment
1260 # containing/outside the callers environment, ie that of the
1261 # caller's caller. Routines can use exterior() to determine the
1262 # environment from which they were called.
1264 def exterior():
1265 """Return dyad containing locals and globals of caller's caller.
1267 Locals will be None if same as globals, ie env is global env."""
1269 bogus = 'bogus' # A locally usable exception
1270 try: raise bogus # Force an exception object
1271 except bogus:
1272 at = sys.exc_traceback.tb_frame.f_back # The external frame.
1273 if at.f_back: at = at.f_back # And further, if any.
1274 globals, locals = at.f_globals, at.f_locals
1275 if locals == globals: # Exterior is global?
1276 locals = None
1277 return (locals, globals)
1279 #########################################################################
1280 # TESTING FACILITIES #
1282 def note(msg, threshold=2):
1283 if VERBOSE >= threshold: sys.stderr.write('(import: ' + msg + ')\n')
1285 class TestDirHier:
1286 """Populate a transient directory hierarchy according to a definition
1287 template - so we can create package/module hierarchies with which to
1288 exercise the new import facilities..."""
1290 def __init__(self, template, where='/var/tmp'):
1291 """Establish a dir hierarchy, according to TEMPLATE, that will be
1292 deleted upon deletion of this object (or deliberate invocation of the
1293 __del__ method)."""
1294 self.PKG_NM = 'tdh_'
1295 rev = 0
1296 while os.path.exists(os.path.join(where, self.PKG_NM+str(rev))):
1297 rev = rev + 1
1298 sys.exc_traceback = None # Ensure Discard of try/except obj ref
1299 self.PKG_NM = self.PKG_NM + str(rev)
1300 self.root = os.path.join(where, self.PKG_NM)
1301 self.createDir(self.root)
1302 self.add(template)
1304 def __del__(self):
1305 """Cleanup the test hierarchy."""
1306 self.remove()
1307 def add(self, template, root=None):
1308 """Populate directory according to template dictionary.
1310 Keys indicate file names, possibly directories themselves.
1312 String values dictate contents of flat files.
1314 Dictionary values dictate recursively embedded dictionary templates."""
1315 if root == None: root = self.root
1316 for key, val in template.items():
1317 name = os.path.join(root, key)
1318 if type(val) == types.StringType: # flat file
1319 self.createFile(name, val)
1320 elif type(val) == types.DictionaryType: # embedded dir
1321 self.createDir(name)
1322 self.add(val, name)
1323 else:
1324 raise ValueError, ('invalid file-value type, %s' % # ==X
1325 type(val))
1326 def remove(self, name=''):
1327 """Dispose of the NAME (or keys in dictionary), using 'rm -r'."""
1328 name = os.path.join(self.root, name)
1329 sys.exc_traceback = None # Ensure Discard of try/except obj ref
1330 if os.path.exists(name):
1331 print '(TestDirHier: eradicating %s)' % name
1332 os.system('rm -r ' + name)
1333 else:
1334 raise IOError, "can't remove non-existent " + name # ==X
1335 def createFile(self, name, contents=None):
1336 """Establish file NAME with CONTENTS.
1338 If no contents specfied, contents will be 'print NAME'."""
1339 f = open(name, 'w')
1340 if not contents:
1341 f.write("print '" + name + "'\n")
1342 else:
1343 f.write(contents)
1344 f.close
1345 def createDir(self, name):
1346 """Create dir with NAME."""
1347 return os.mkdir(name, 0755)
1349 skipToTest = 0
1350 atTest = 1
1351 def testExec(msg, execList, locals, globals):
1352 global skipToTest, atTest
1353 print 'Import Test:', '(' + str(atTest) + ')', msg, '...'
1354 atTest = atTest + 1
1355 if skipToTest > (atTest - 1):
1356 print ' ... skipping til test', skipToTest
1357 return
1358 else:
1359 print ''
1360 for stmt in execList:
1361 exec stmt in locals, globals
1363 def test(number=0, leaveHiers=0):
1364 """Exercise import functionality, creating a transient dir hierarchy for
1365 the purpose.
1367 We actually install the new import functionality, temporarily, resuming the
1368 existing function on cleanup."""
1370 import __builtin__
1372 global skipToTest, atTest
1373 skipToTest = number
1374 hier = None
1376 def unloadFull(mod):
1377 """Unload module and offspring submodules, if any."""
1378 modMod = ''
1379 if type(mod) == types.StringType:
1380 modNm = mod
1381 elif type(mod) == types.ModuleType:
1382 modNm = modMod.__name__
1383 for subj in sys.modules.keys() + sys.stub_modules.keys():
1384 if subj[0:len(modNm)] == modNm:
1385 unload(subj)
1387 try:
1388 __main__.testMods
1389 except AttributeError:
1390 __main__.testMods = []
1391 testMods = __main__.testMods
1394 # Install the newimp routines, within a try/finally:
1395 try:
1396 sys.exc_traceback = None
1397 wasImport = __builtin__.__import__ # Stash default
1398 wasPath = sys.path
1399 except AttributeError:
1400 wasImport = None
1401 try:
1402 hiers = []; modules = []
1403 global VERBOSE
1404 wasVerbose, VERBOSE = VERBOSE, 1
1405 __builtin__.__import__ = import_module # Install new version
1407 if testMods: # Clear out imports from previous tests
1408 for m in testMods[:]:
1409 unloadFull(m)
1410 testMods.remove(m)
1412 # ------
1413 # Test 1
1414 testExec("already imported module: %s" % sys.modules.keys()[0],
1415 ['import ' + sys.modules.keys()[0]],
1416 vars(), newimp_globals)
1417 no_sirree = 'no_sirree_does_not_exist'
1418 # ------
1419 # Test 2
1420 testExec("non-existent module: %s" % no_sirree,
1421 ['try: import ' + no_sirree +
1422 '\nexcept ImportError: pass'],
1423 vars(), newimp_globals)
1424 got = None
1426 # ------
1427 # Test 3
1428 # Find a module that's not yet loaded, from a list of prospects:
1429 for mod in ['Complex', 'UserDict', 'UserList', 'calendar',
1430 'cmd', 'dis', 'mailbox', 'profile', 'random', 'rfc822']:
1431 if not (mod in sys.modules.keys()):
1432 got = mod
1433 break # ==v
1434 if got:
1435 testExec("not-yet loaded module: %s" % mod,
1436 ['import ' + mod, 'modules.append(got)'],
1437 vars(), newimp_globals)
1438 else:
1439 testExec("not-yet loaded module: list exhausted, never mind",
1440 [], vars(), newimp_globals)
1442 # Now some package stuff.
1444 # ------
1445 # Test 4
1446 # First change the path to include our temp dir, copying so the
1447 # addition can be revoked on cleanup in the finally, below:
1448 sys.path = ['/var/tmp'] + sys.path[:]
1449 # Now create a trivial package:
1450 stmts = ["hier1 = TestDirHier({'a.py': 'print \"a.py executing\"'})",
1451 "hiers.append(hier1)",
1452 "base = hier1.PKG_NM",
1453 "exec 'import ' + base",
1454 "testMods.append(base)"]
1455 testExec("trivial package, with one module, a.py",
1456 stmts, vars(), newimp_globals)
1458 # ------
1459 # Test 5
1460 # Slightly less trivial package - reference to '__':
1461 stmts = [("hier2 = TestDirHier({'ref.py': 'print \"Pkg __:\", __'})"),
1462 "base = hier2.PKG_NM",
1463 "hiers.append(hier2)",
1464 "exec 'import ' + base",
1465 "testMods.append(base)"]
1466 testExec("trivial package, with module that has pkg shorthand ref",
1467 stmts, vars(), newimp_globals)
1469 # ------
1470 # Test 6
1471 # Nested package, plus '__' references:
1473 complexTemplate = {'ref.py': 'print "ref.py loading..."',
1474 'suite': {'s1.py': 'print "s1.py, in pkg:", __',
1475 'subsuite': {'sub1.py':
1476 'print "sub1.py"'}}}
1477 stmts = [('print """%s\n%s\n%s\n%s\n%s\n%s"""' %
1478 ('.../',
1479 ' ref.py\t\t\t"ref.py loading..."',
1480 ' suite/',
1481 ' s1.py \t\t"s1.py, in pkg: xxxx.suite"',
1482 ' subsuite/',
1483 ' sub1.py "sub1.py" ')),
1484 "hier3 = TestDirHier(complexTemplate)",
1485 "base = hier3.PKG_NM",
1486 "hiers.append(hier3)",
1487 "exec 'import ' + base",
1488 "testMods.append(base)"]
1489 testExec("Significantly nestled package:",
1490 stmts, vars(), newimp_globals)
1492 # ------
1493 # Test 7
1494 # Try an elaborate hierarchy which includes an __init__ master in one
1495 # one portion, a ref across packages within the hierarchies, and an
1496 # indirect recursive import which cannot be satisfied (and hence,
1497 # prevents load of part of the hierarchy).
1498 complexTemplate = {'mid':
1499 {'prime':
1500 {'__init__.py': 'import __.easy, __.nother',
1501 'easy.py': 'print "easy.py:", __name__',
1502 'nother.py': ('%s\n%s\n%s\n' %
1503 ('import __.easy',
1504 'print "nother got __.easy"',
1505 # __.__.awry should be found but
1506 # should not load successfully,
1507 # disrupting nother, but not easy
1508 'import __.__.awry'))},
1509 # continuing dict 'mid':
1510 'awry':
1511 {'__init__.py':
1512 ('%s\n%s' %
1513 ('print "got " + __name__',
1514 'from __ import *')),
1515 # This mutual recursion (b->a, a->d->b) should be
1516 # ok, since a.py sets ax before recursing.
1517 'a.py': 'ax = 1; from __.b import bx',
1518 'b.py': 'bx = 1; from __.a import ax'}}}
1519 stmts = ["hier5 = TestDirHier(complexTemplate)",
1520 "base = hier5.PKG_NM",
1521 "testMods.append(base)",
1522 "hiers.append(hier5)",
1523 "exec 'import %s.mid.prime' % base",
1524 "print eval(base)", # Verify the base was bound
1525 "testMods.append(base)"]
1526 testExec("Elaborate, clean hierarchy",
1527 stmts, vars(), newimp_globals)
1529 # ------
1530 # test 8
1531 # Here we disrupt the mutual recursion in the mid.awry package, so the
1532 # import should now fail.
1533 complexTemplate['mid']['awry']['a.py'] = 'from __.b import bx; ax = 1'
1534 complexTemplate['mid']['awry']['b.py'] = 'from __.a import ax; bx = 1'
1535 stmts = ["hier6 = TestDirHier(complexTemplate)",
1536 "base = hier6.PKG_NM",
1537 "testMods.append(base)",
1538 "hiers.append(hier6)",
1539 "work = ('import %s.mid.prime' % base)",
1540 ("try: exec work" +
1541 "\nexcept ImportError: print ' -- import failed, as ought'" +
1542 "\nelse: raise SystemError, sys.exc_value"),
1543 "testMods.append(base)"]
1544 testExec("Elaborate hier w/ deliberately flawed import recursion",
1545 stmts, vars(), newimp_globals)
1547 sys.exc_traceback = None # Signify clean conclusion.
1549 finally:
1550 skipToTest = 0
1551 atTest = 1
1552 sys.path = wasPath
1553 VERBOSE = wasVerbose
1554 if wasImport: # Resurrect prior routine
1555 __builtin__.__import__ = wasImport
1556 else:
1557 del __builtin__.__import__
1558 if leaveHiers:
1559 print 'Cleanup inhibited'
1560 else:
1561 if sys.exc_traceback:
1562 print ' ** Import test FAILURE... cleanup.'
1563 else:
1564 print ' Import test SUCCESS... cleanup'
1565 for h in hiers: h.remove(); del h # Dispose of test directories
1567 init()
1569 if __name__ == '__main__':
1570 test()