blob: 3f1a6b212e1ccd550f88de60eda2bac6d634a565 [file] [log] [blame]
Guido van Rossum52325901995-04-07 09:03:10 +00001"""Prototype of 'import' functionality enhanced to implement packages.
2
3Why packages? Packages enable module nesting and sibling module
4imports. 'Til now, the python module namespace was flat, which
5means every module had to have a unique name, in order to not
6conflict with names of other modules on the load path. Furthermore,
7suites of modules could not be structurally affiliated with one
8another.
9
10With packages, a suite of, eg, email-oriented modules can include a
11module named 'mailbox', without conflicting with the, eg, 'mailbox'
12module of a shared-memory suite - 'email.mailbox' vs
13'shmem.mailbox'. Packages also enable modules within a suite to
14load other modules within their package without having the package
15name hard-coded. Similarly, package suites of modules can be loaded
16as a unit, by loading the package that contains them.
17
18Usage: once installed (newimp.install(); newimp.revert() to revert to
19the prior __import__ routine), 'import ...' and 'from ... import ...'
20can be used to:
21
22 - import modules from the search path, as before.
23
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.
27
Guido van Rossum96044da1995-04-07 09:06:50 +000028 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'.
Guido van Rossum52325901995-04-07 09:03:10 +000034
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.
38
Guido van Rossum96044da1995-04-07 09:06:50 +000039 The '__' package references are actually names assigned within
Guido van Rossum52325901995-04-07 09:03:10 +000040 modules, to refer to their containing package. This means that
Guido van Rossum96044da1995-04-07 09:06:50 +000041 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'.
Guido van Rossum52325901995-04-07 09:03:10 +000045
Guido van Rossum96044da1995-04-07 09:06:50 +000046 - import an entire package as a unit, by importing the package directory.
47 If there is a module named '__main__.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.
50
51 For example, 'import test' will load the modules of the entire 'test'
52 package, at least until a test failure is encountered.
53
54 In a package, a module with the name '__main__' 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 __main__ 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).
61
62 - perform any combination of the above - have a package that contains
63 packages, etc.
64
65Modules have a few new attributes in support of packages. As mentioned
66above, '__' is a shorthand attribute denoting the modules' parent package,
67also denoted in the module by '__package__'. Additionally, modules have
68associated with them a '__pkgpath__', a path by which sibling modules are
69found."""
Guido van Rossum52325901995-04-07 09:03:10 +000070
Guido van Rossumb1c13151995-05-05 15:50:56 +000071__version__ = "Revision: 1.13"
Guido van Rossum52325901995-04-07 09:03:10 +000072
Guido van Rossumb1c13151995-05-05 15:50:56 +000073# Id: newimp.py,v 1.13 1995/04/13 23:23:33 klm Exp First release:
Guido van Rossum96044da1995-04-07 09:06:50 +000074# Ken.Manheimer@nist.gov, 5-Apr-1995, for python 1.2
Guido van Rossum52325901995-04-07 09:03:10 +000075
76# Developers Notes:
77#
78# - 'sys.stub_modules' registers "incidental" (partially loaded) modules.
79# A stub module is promoted to the fully-loaded 'sys.modules' list when it is
80# explicitly loaded as a unit.
81# - The __main__ loads of '__' have not yet been tested.
82# - The test routines are cool, including a transient directory
83# hierarchy facility, and a means of skipping to later tests by giving
84# the test routine a numeric arg.
85# - This could be substantially optimized, and there are many loose ends
86# lying around, since i wanted to get this released for 1.2.
87
88VERBOSE = 0
89
90import sys, string, regex, types, os, marshal, new, __main__
91try:
92 import imp # Build on this recent addition
93except ImportError:
94 raise ImportError, 'Pkg import module depends on optional "imp" module'
95
96from imp import SEARCH_ERROR, PY_SOURCE, PY_COMPILED, C_EXTENSION
97PY_PACKAGE = 4 # In addition to above PY_*
98
99modes = {SEARCH_ERROR: 'SEARCH_ERROR',
100 PY_SOURCE: 'PY_SOURCE',
101 PY_COMPILED: 'PY_COMPILED',
102 C_EXTENSION: 'C_EXTENSION',
103 PY_PACKAGE: 'PY_PACKAGE'}
104
105# sys.stub_modules tracks modules partially loaded modules, ie loaded only
106# incidental to load of nested components.
107
108try: sys.stub_modules
109except AttributeError:
110 sys.stub_modules = {}
111
112# Environment setup - "root" module, '__python__'
113
114# Establish root package '__python__' in __main__ and newimp envs.
115
116PKG_MAIN_NM = '__main__' # 'pkg/__main__.py' master, if present.
117PKG_NM = '__package__' # Longhand for module's container.
118PKG_SHORT_NM = '__' # Shorthand for module's container.
119PKG_SHORT_NM_LEN = len(PKG_SHORT_NM)
120PKG_PATH = '__pkgpath__' # Var holding package search path,
121 # usually just the path of the pkg dir.
122__python__ = __main__
123sys.modules['__python__'] = __python__ # Register as an importable module.
124__python__.__dict__[PKG_PATH] = sys.path
125
126origImportFunc = None
127def install():
128 """Install newimp import_module() routine, for package support.
129
130 newimp.revert() reverts to __import__ routine that was superceded."""
Guido van Rossum96044da1995-04-07 09:06:50 +0000131 import __builtin__
Guido van Rossum52325901995-04-07 09:03:10 +0000132 global origImportFunc
133 if not origImportFunc:
134 try:
Guido van Rossum52325901995-04-07 09:03:10 +0000135 origImportFunc = __builtin__.__import__
136 except AttributeError:
137 pass
138 __builtin__.__import__ = import_module
139 print 'Enhanced import functionality installed.'
140def revert():
141 """Revert to original __builtin__.__import__ func, if newimp.install() has
142 been executed."""
143 if origImportFunc:
144 import __builtin__
145 __builtin__.__import__ = origImportFunc
146 print 'Original import routine back in place.'
147
148def import_module(name,
149 envLocals=None, envGlobals=None,
150 froms=None,
151 inPkg=None):
152 """Primary service routine implementing 'import' with package nesting."""
153
154 # The job is divided into a few distinct steps:
155 #
156 # - Look for either an already loaded module or a file to be loaded.
157 # * if neither loaded module nor prospect file is found, raise an error.
158 # - If we have a file, not an already loaded module:
159 # - Load the file into a module.
160 # - Register the new module and intermediate package stubs.
161 # (We have a module at this point...)
162 # - Bind requested syms (module or specified 'from' defs) in calling env.
163 # - Return the appropriate component.
164
165 note("import_module: seeking '%s'%s" %
166 (name, ((inPkg and ' (in package %s)' % inPkg.__name__) or '')))
167
168 # We need callers environment dict for local path and resulting module
169 # binding.
170 if not (envLocals or envGlobals):
171 envLocals, envGlobals = exterior()
172
173 modList = theMod = absNm = container = None
174
175 # Get module obj if one already established, or else module file if not:
176
177 if inPkg:
178 # We've been invoked with a specific containing package:
179 pkg, pkgPath, pkgNm = inPkg, inPkg.__dict__[PKG_PATH], inPkg.__name__
180 relNm = name
181 absNm = pkgNm + '.' + name
182
183 elif name[:PKG_SHORT_NM_LEN+1] != PKG_SHORT_NM + '.':
184 # name is NOT '__.something' - setup to seek according to specified
185 # absolute name.
186 pkg = __python__
187 pkgPath = sys.path
188 absNm = name
189 relNm = absNm
190
191 else:
192 # name IS '__.' + something - setup to seek according to relative name,
193 # in current package.
194
195 relNm = name[len(PKG_SHORT_NM)+1:] # Relative portion of name.
196 try:
197 pkg = envGlobals[PKG_NM] # The immediately containing package.
198 pkgPath = pkg.__dict__[PKG_PATH]
199 if pkg == __python__: # At outermost package.
200 absNm = relNm
201 else:
202 absNm = (pkg.__name__ + '.' + relNm)
203 except KeyError: # Missing package, path, or name.
204 note("Can't identify parent package, package name, or pkgpath")
205 pass # ==v
206
207 # Try to find existing module:
208 if sys.modules.has_key(absNm):
209 note('found ' + absNm + ' already imported')
210 theMod = sys.modules[absNm]
211 else:
212 # Try for builtin or frozen first:
213 theMod = imp.init_builtin(absNm)
214 if theMod:
215 note('found builtin ' + absNm)
216 else:
217 theMod = imp.init_frozen(absNm)
218 if theMod:
219 note('found frozen ' + absNm)
220 if not theMod:
221 if type(pkgPath) == types.StringType:
222 pkgPath = [pkgPath]
223 modList = find_module(relNm, pkgPath, absNm)
224 if not modList:
225 raise ImportError, "module '%s' not found" % absNm # ===X
226 # We have a list of successively nested files leading to the
227 # module, register them as stubs:
228 container = register_module_nesting(modList, pkg)
229
230 # Load from file if necessary and possible:
231 modNm, modf, path, ty = modList[-1]
232 note('found type ' + modes[ty[2]] + ' - ' + absNm)
233
234 # Do the load:
235 theMod = load_module(absNm, ty[2], modf, inPkg)
236
237 # Loaded successfully - promote module to full module status:
238 register_module(theMod, theMod.__name__, pkgPath, pkg)
239
240 # Have a loaded module, impose designated components, and return
241 # appropriate thing - according to guido:
242 # "Note that for "from spam.ham import bacon" your function should
243 # return the object denoted by 'spam.ham', while for "import
244 # spam.ham" it should return the object denoted by 'spam' -- the
245 # STORE instructions following the import statement expect it this
246 # way."
247 if not froms:
248 # Establish the module defs in the importing name space:
249 (envLocals or envGlobals)[name] = theMod
250 return (container or theMod)
251 else:
252 # Implement 'from': Populate immediate env with module defs:
Guido van Rossumb1c13151995-05-05 15:50:56 +0000253 while froms:
254 item = froms[0]; del froms[0]
255 if item == '*':
256 froms = theMod.__dict__.keys() + froms
257 else:
258 try:
259 (envLocals or envGlobals)[item] = theMod.__dict__[item]
260 except KeyError:
261 raise ImportError, ("name '%s' not found in module %s" %
262 (item, theMod.__name__))
Guido van Rossum52325901995-04-07 09:03:10 +0000263 return theMod
264
265def unload(module):
266 """Remove registration for a module, so import will do a fresh load."""
267 if type(module) == types.ModuleType:
268 module = module.__name__
269 for m in [sys.modules, sys.stub_modules]:
270 try:
271 del m[module]
272 except KeyError:
273 pass
274
275def find_module(name, path, absNm=''):
276 """Locate module NAME on PATH. PATH is pathname string or a list of them.
277
278 Note that up-to-date compiled versions of a module are preferred to plain
279 source, and compilation is automatically performed when necessary and
280 possible.
281
282 Returns a list of the tuples returned by 'find_module_file' (cf), one for
283 each nested level, deepest last."""
284
285 checked = [] # For avoiding redundant dir lists.
286
287 if not absNm: absNm = name
288
289 # Parse name into list of nested components,
290 expNm = string.splitfields(name, '.')
291
292 for curPath in path:
293
294 if (type(curPath) != types.StringType) or (curPath in checked):
295 # Disregard bogus or already investigated path elements:
296 continue # ==^
297 else:
298 # Register it for subsequent disregard.
299 checked.append(curPath)
300
301 if len(expNm) == 1:
302
303 # Non-nested module name:
304
305 got = find_module_file(curPath, absNm)
306 if got:
307 note('using %s' % got[2], 2)
308 return [got] # ===>
309
310 else:
311
312 # Composite name specifying nested module:
313
314 gotList = []; nameAccume = expNm[0]
315
316 got = find_module_file(curPath, nameAccume)
317 if not got: # Continue to next prospective path.
318 continue # ==^
319 else:
320 gotList.append(got)
321 nm, file, fullPath, ty = got
322
323 # Work on successively nested components:
324 for component in expNm[1:]:
325 # 'ty'pe of containing component must be package:
326 if ty[2] != PY_PACKAGE:
327 gotList, got = [], None
328 break # ==v^
329 if nameAccume:
330 nameAccume = nameAccume + '.' + component
331 else:
332 nameAccume = component
333 got = find_module_file(fullPath, nameAccume)
334 if got:
335 gotList.append(got)
336 # ** have to return the *full* name here:
337 nm, file, fullPath, ty = got
338 else:
339 # Clear state vars:
340 gotList, got, nameAccume = [], None, ''
341 break # ==v^
342 # Found nesting all the way to the specified tip:
343 if got:
344 return gotList # ===>
345
346 # Failed.
347 return None
348
349def find_module_file(pathNm, modname):
350 """Find module file given dir PATHNAME and module NAME.
351
352 If successful, returns quadruple consisting of a mod name, file object,
353 PATHNAME for the found file, and a description triple as contained in the
354 list returned by get_suffixes.
355
356 Otherwise, returns None.
357
358 Note that up-to-date compiled versions of a module are preferred to plain
359 source, and compilation is automatically performed, when necessary and
360 possible."""
361
362 relNm = string.splitfields(modname,'.')[-1]
363
364 if pathNm[-1] != '/': pathNm = pathNm + '/'
365
366 for suff, mode, ty in get_suffixes():
367 note('trying ' + pathNm + relNm + suff + '...', 3)
368 fullPath = pathNm + relNm + suff
369 try:
370 modf = open(fullPath, mode)
371 except IOError:
372 # ?? Skip unreadable ones.
373 continue # ==^
374
375 if ty == PY_PACKAGE:
376 # Enforce directory characteristic:
377 if not os.path.isdir(fullPath):
378 note('Skipping non-dir match ' + fullPath)
379 continue # ==^
380 else:
381 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
382
383
384 elif ty == PY_SOURCE:
385 # Try for a compiled version:
386 note('found source ' + fullPath, 2)
387 pyc = fullPath + 'c' # Sadly, we're presuming '.py' suff.
388 if (not os.path.exists(pyc) or
389 (os.stat(fullPath)[8] > os.stat(pyc)[8])):
390 # Try to compile:
391 pyc = compile_source(fullPath, modf)
392 if pyc and (os.stat(fullPath)[8] < os.stat(pyc)[8]):
393 # Either pyc was already newer or we just made it so; in either
394 # case it's what we crave:
395 return (modname, open(pyc, 'rb'), pyc, # ===>
396 ('.pyc', 'rb', PY_COMPILED))
397 # Couldn't get a compiled version - return the source:
398 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
399
400 elif ty == PY_COMPILED:
401 # Make sure it is current, trying to compile if necessary, and
402 # prefer source failing that:
403 note('found compiled ' + fullPath, 2)
404 py = fullPath[:-1] # Sadly again, presuming '.pyc' suff.
405 if not os.path.exists(py):
406 note('found pyc sans py: ' + fullPath)
407 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
408 elif (os.stat(py)[8] > os.stat(fullPath)[8]):
409 note('forced to try compiling: ' + py)
410 pyc = compile_source(py, modf)
411 if pyc:
412 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
413 else:
414 note('failed compile - must use more recent .py')
415 return (modname, # ===>
416 open(py, 'r'), py, ('.py', 'r', PY_SOURCE))
417 else:
418 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
419
420 elif ty == C_EXTENSION:
421 note('found extension ' + fullPath, 2)
422 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
423
424 else:
425 raise SystemError, 'Unanticipated (new?) module type encountered'
426
427 return None
428
429
430def load_module(name, ty, theFile, fromMod=None):
431 """Load module NAME, type TYPE, from file FILE.
432
433 Optional arg fromMod indicated the module from which the load is being done
434 - necessary for detecting import of __ from a package's __main__ module.
435
436 Return the populated module object."""
437
438 # Note: we mint and register intermediate package directories, as necessary
439
440 # Determine packagepath extension:
441
442 # Establish the module object in question:
443 theMod = procure_module(name)
444 nameTail = string.splitfields(name, '.')[-1]
445 thePath = theFile.name
446
447 if ty == PY_SOURCE:
448 exec_into(theFile, theMod, theFile.name)
449
450 elif ty == PY_COMPILED:
451 pyc = open(theFile.name, 'rb').read()
452 if pyc[0:4] != imp.get_magic():
453 raise ImportError, 'bad magic number: ' + theFile.name # ===>
454 code = marshal.loads(pyc[8:])
455 exec_into(code, theMod, theFile.name)
456
457 elif ty == C_EXTENSION:
458 try:
459 theMod = imp.load_dynamic(nameTail, thePath, theFile)
460 except:
461 # ?? Ok to embellish the error message?
462 raise sys.exc_type, ('%s (from %s)' %
463 (str(sys.exc_value), theFile.name))
464
465 elif ty == PY_PACKAGE:
466 # Load constituents:
467 if (os.path.exists(thePath + '/' + PKG_MAIN_NM) and
468 # pkg has a __main__, and this import not already from __main__, so
469 # __main__ can 'import __', or even better, 'from __ import *'
470 ((theMod.__name__ != PKG_MAIN_NM) and (fromMod.__ == theMod))):
471 exec_into(thePath + '/' + PKG_MAIN_NM, theMod, theFile.name)
472 else:
473 # ... or else recursively load constituent modules.
474 prospects = mod_prospects(thePath)
475 for item in prospects:
476 theMod.__dict__[item] = import_module(item,
477 theMod.__dict__,
478 theMod.__dict__,
479 None,
480 theMod)
481
482 else:
483 raise ImportError, 'Unimplemented import type: %s' % ty # ===>
484
485 return theMod
486
487def exec_into(obj, module, path):
488 """Helper for load_module, execfile/exec path or code OBJ within MODULE."""
489
490 # This depends on ability of exec and execfile to mutilate, erhm, mutate
491 # the __dict__ of a module. It will not work if/when this becomes
492 # disallowed, as it is for normal assignments.
493
494 try:
495 if type(obj) == types.FileType:
496 execfile(path, module.__dict__, module.__dict__)
497 elif type(obj) in [types.CodeType, types.StringType]:
498 exec obj in module.__dict__, module.__dict__
499 except:
500 # ?? Ok to embellish the error message?
501 raise sys.exc_type, ('%s (from %s)' %
502 (str(sys.exc_value), path))
503
504
505def mod_prospects(path):
506 """Return a list of prospective modules within directory PATH.
507
508 We actually return the distinct names resulting from stripping the dir
509 entries (excluding '.' and '..') of their suffixes (as represented by
510 'get_suffixes').
511
512 (Note that matches for the PY_PACKAGE type with null suffix are
513 implicitly constrained to be directories.)"""
514
515 # We actually strip the longest matching suffixes, so eg 'dbmmodule.so'
516 # mates with 'module.so' rather than '.so'.
517
518 dirList = os.listdir(path)
519 excludes = ['.', '..']
520 sortedSuffs = sorted_suffixes()
521 entries = []
522 for item in dirList:
523 if item in excludes: continue # ==^
524 for suff in sortedSuffs:
525 sub = -1 * len(suff)
526 if sub == 0:
527 if os.path.isdir(os.path.join(path, item)):
528 entries.append(item)
529 elif item[sub:] == suff:
530 it = item[:sub]
531 if not it in entries:
532 entries.append(it)
533 break # ==v^
534 return entries
535
536
537
538def procure_module(name):
539 """Return an established or else new module object having NAME.
540
541 First checks sys.modules, then sys.stub_modules."""
542
543 if sys.modules.has_key(name):
544 it = sys.modules[name]
545 elif sys.stub_modules.has_key(name):
546 it = sys.stub_modules[name]
547 else:
548 it = new.module(name)
549 return it # ===>
550
551def register_module_nesting(modList, pkg):
552 """Given a find_module()-style NESTING and a parent PACKAGE, register
553 components as stub modules."""
554 container = None
555 for stubModNm, stubModF, stubPath, stubTy in modList:
556 relStubNm = string.splitfields(stubModNm, '.')[-1]
557 if sys.modules.has_key(stubModNm):
558 # Nestle in containing package:
559 stubMod = sys.modules[stubModNm]
560 pkg.__dict__[relStubNm] = stubMod
561 pkg = stubMod # will be parent for next in sequence.
562 elif sys.stub_modules.has_key(stubModNm):
563 stubMod = sys.stub_modules[stubModNm]
564 pkg.__dict__[relStubNm] = stubMod
565 pkg = stubMod
566 else:
567 stubMod = procure_module(stubModNm)
568 # Register as a stub:
569 register_module(stubMod, stubModNm, stubPath, pkg, 1)
570 pkg.__dict__[relStubNm] = stubMod
571 pkg = stubMod
572 if not container:
573 container = stubMod
574 return container
575
576def register_module(theMod, name, path, package, stub=0):
577 """Properly register MODULE, w/ name, path, package, opt, as stub."""
578
579 if stub:
580 sys.stub_modules[name] = theMod
581 else:
582 sys.modules[name] = theMod
583 if sys.stub_modules.has_key(name):
584 del sys.stub_modules[name]
585 theMod.__ = theMod.__dict__[PKG_NM] = package
586 theMod.__dict__[PKG_PATH] = path
587
588
589def compile_source(sourcePath, sourceFile):
590 """Given python code source path and file obj, Create a compiled version.
591
592 Return path of compiled version, or None if file creation is not
593 successful. (Compilation errors themselves are passed without restraint.)
594
595 This is an import-private interface, and not well-behaved for general use.
596
597 In particular, we presume the validity of the sourcePath, and that it
598 includes a '.py' extension."""
599
600 compiledPath = sourcePath[:-3] + '.pyc'
601 try:
602 compiledFile = open(compiledPath, 'wb')
603 except IOError:
604 note("write permission denied to " + compiledPath)
605 return None
606 mtime = os.stat(sourcePath)[8]
607 sourceFile.seek(0) # rewind
608 try:
609 compiledFile.write(imp.get_magic()) # compiled magic number
610 compiledFile.seek(8, 0) # mtime space holder
611 # We let compilation errors go their own way...
612 compiled = compile(sourceFile.read(), sourcePath, 'exec')
613 marshal.dump(compiled, compiledFile) # write the code obj
614 compiledFile.seek(4, 0) # position for mtime
615 compiledFile.write(marshal.dumps(mtime)[1:]) # register mtime
616 compiledFile.flush()
617 compiledFile.close()
618 return compiledPath
619 except IOError:
620 return None
621
622
623def PathExtension(locals, globals): # Probably obsolete.
624 """Determine import search path extension vis-a-vis __pkgpath__ entries.
625
626 local dict __pkgpath__ will preceed global dict __pkgpath__."""
627
628 pathadd = []
629 if globals and globals.has_key(PKG_PATH):
630 pathadd = PrependPath(pathadd, globals[PKG_PATH], 'global')
631 if locals and locals.has_key(PKG_PATH):
632 pathadd = PrependPath(pathadd, locals[PKG_PATH], 'local')
633 if pathadd:
634 note(PKG_PATH + ' extension: ' + pathadd)
635 return pathadd
636
637def PrependPath(path, pre, whence): # Probably obsolete
638 """Return copy of PATH list with string or list-of-strings PRE prepended.
639
640 If PRE is neither a string nor list-of-strings, print warning that
641 locality WHENCE has malformed value."""
642
643 # (There is probably a better way to handle malformed PREs, but raising an
644 # error seems too severe - in that case, a bad setting for
645 # sys.__pkgpath__ would prevent any imports!)
646
647 if type(pre) == types.StringType: return [pre] + path[:] # ===>
648 elif type(pre) == types.ListType: return pre + path[:] # ===>
649 else:
650 print "**Ignoring '%s' bad %s value**" % (whence, PKG_PATH)
651 return path # ===>
652
653got_suffixes = None
654def get_suffixes():
655 """Produce a list of triples, each describing a type of import file.
656
657 Triples have the form '(SUFFIX, MODE, TYPE)', where:
658
659 SUFFIX is a string found appended to a module name to make a filename for
660 that type of import file.
661
662 MODE is the mode string to be passed to the built-in 'open' function - "r"
663 for text files, "rb" for binary.
664
665 TYPE is the file type:
666
667 PY_SOURCE: python source code,
668 PY_COMPILED: byte-compiled python source,
669 C_EXTENSION: compiled-code object file,
670 PY_PACKAGE: python library directory, or
671 SEARCH_ERROR: no module found. """
672
673 # Note: sorted_suffixes() depends on this function's value being invariant.
674 # sorted_suffixes() must be revised if this becomes untrue.
675
676 global got_suffixes
677
678 if got_suffixes:
679 return got_suffixes
680 else:
681 # Ensure that the .pyc suffix precedes the .py:
682 got_suffixes = [('', 'r', PY_PACKAGE)]
683 py = pyc = None
684 for suff in imp.get_suffixes():
685 if suff[0] == '.py':
686 py = suff
687 elif suff[0] == '.pyc':
688 pyc = suff
689 else:
690 got_suffixes.append(suff)
691 got_suffixes.append(pyc)
692 got_suffixes.append(py)
693 return got_suffixes
694
695
696sortedSuffs = [] # State vars for sorted_suffixes(). Go
697def sorted_suffixes():
698 """Helper function ~efficiently~ tracks sorted list of module suffixes."""
699
700 # Produce sortedSuffs once - this presumes that get_suffixes does not
701 # change from call to call during a python session. Needs to be
702 # corrected if that becomes no longer true.
703
704 global sortedsuffs
705 if not sortedSuffs: # do compute only the "first" time
706 for item in get_suffixes():
707 sortedSuffs.append(item[0])
708 # Sort them in descending order:
709 sortedSuffs.sort(lambda x, y: (((len(x) > len(y)) and 1) or
710 ((len(x) < len(y)) and -1)))
711 sortedSuffs.reverse()
712 return sortedSuffs
713
714
715# exterior(): Utility routine, obtain local and global dicts of environment
716# containing/outside the callers environment, ie that of the
717# caller's caller. Routines can use exterior() to determine the
718# environment from which they were called.
719
720def exterior():
721 """Return dyad containing locals and globals of caller's caller.
722
723 Locals will be None if same as globals, ie env is global env."""
724
725 bogus = 'bogus' # A locally usable exception
726 try: raise bogus # Force an exception object
727 except bogus:
728 at = sys.exc_traceback.tb_frame.f_back # The external frame.
729 if at.f_back: at = at.f_back # And further, if any.
730 globals, locals = at.f_globals, at.f_locals
731 if locals == globals: # Exterior is global?
732 locals = None
733 return (locals, globals)
734
735#########################################################################
736# TESTING FACILITIES #
737
738def note(msg, threshold=1):
Guido van Rossum96044da1995-04-07 09:06:50 +0000739 if VERBOSE >= threshold: sys.stderr.write('(import: ' + msg + ')\n')
Guido van Rossum52325901995-04-07 09:03:10 +0000740
741class TestDirHier:
742 """Populate a transient directory hierarchy according to a definition
743 template - so we can create package/module hierarchies with which to
744 exercise the new import facilities..."""
745
746 def __init__(self, template, where='/var/tmp'):
747 """Establish a dir hierarchy, according to TEMPLATE, that will be
748 deleted upon deletion of this object (or deliberate invocation of the
749 __del__ method)."""
750 self.PKG_NM = 'tdh_'
751 rev = 0
752 while os.path.exists(os.path.join(where, self.PKG_NM+str(rev))):
753 rev = rev + 1
754 sys.exc_traceback = None # Ensure Discard of try/except obj ref
755 self.PKG_NM = self.PKG_NM + str(rev)
756 self.root = os.path.join(where, self.PKG_NM)
757 self.createDir(self.root)
758 self.add(template)
759
760 def __del__(self):
761 """Cleanup the test hierarchy."""
762 self.remove()
763 def add(self, template, root=None):
764 """Populate directory according to template dictionary.
765
766 Keys indicate file names, possibly directories themselves.
767
768 String values dictate contents of flat files.
769
770 Dictionary values dictate recursively embedded dictionary templates."""
771 if root == None: root = self.root
772 for key, val in template.items():
773 name = os.path.join(root, key)
774 if type(val) == types.StringType: # flat file
775 self.createFile(name, val)
776 elif type(val) == types.DictionaryType: # embedded dir
777 self.createDir(name)
778 self.add(val, name)
779 else:
780 raise ValueError, 'invalid file-value type, %s' % type(val)
781 def remove(self, name=''):
782 """Dispose of the NAME (or keys in dictionary), using 'rm -r'."""
783 name = os.path.join(self.root, name)
784 sys.exc_traceback = None # Ensure Discard of try/except obj ref
785 if os.path.exists(name):
786 print '(TestDirHier: deleting %s)' % name
787 os.system('rm -r ' + name)
788 else:
789 raise IOError, "can't remove non-existent " + name
790 def createFile(self, name, contents=None):
791 """Establish file NAME with CONTENTS.
792
793 If no contents specfied, contents will be 'print NAME'."""
794 f = open(name, 'w')
795 if not contents:
796 f.write("print '" + name + "'\n")
797 else:
798 f.write(contents)
799 f.close
800 def createDir(self, name):
801 """Create dir with NAME."""
802 return os.mkdir(name, 0755)
803
804skipToTest = 0
805atTest = 1
806def testExec(msg, execList, locals, globals):
807 global skipToTest, atTest
808 print 'Import Test:', '(' + str(atTest) + ')', msg, '...'
809 atTest = atTest + 1
810 if skipToTest > (atTest - 1):
811 print ' ... skipping til test', skipToTest
812 return
813 else:
814 print ''
815 for stmt in execList:
816 exec stmt in locals, globals
817
818def test(number=0):
819 """Exercise import functionality, creating a transient dir hierarchy for
820 the purpose.
821
822 We actually install the new import functionality, temporarily, resuming the
823 existing function on cleanup."""
824
825 import __builtin__
826
827 global skipToTest, atTest
828 skipToTest = number
829 hier = None
830
831 def confPkgVars(mod, locals, globals):
832 if not sys.modules.has_key(mod):
833 print 'import test: missing module "%s"' % mod
834 else:
835 modMod = sys.modules[mod]
836 if not modMod.__dict__.has_key(PKG_SHORT_NM):
837 print ('import test: module "%s" missing %s pkg shorthand' %
838 (mod, PKG_SHORT_NM))
839 if not modMod.__dict__.has_key(PKG_PATH):
840 print ('import test: module "%s" missing %s package path' %
841 (mod, PKG_PATH))
842 def unloadFull(mod):
843 """Unload module and offspring submodules, if any."""
844 modMod = ''
845 if type(mod) == types.StringType:
846 modNm = mod
847 elif type(mod) == types.ModuleType:
848 modNm = modMod.__name__
849 for subj in sys.modules.keys() + sys.stub_modules.keys():
850 if subj[0:len(modNm)] == modNm:
851 unload(subj)
852
853 # First, get the globals and locals to pass to our testExec():
854 exec 'import ' + __name__
855 globals, locals = eval(__name__ + '.__dict__'), vars()
856
857 try:
858 __main__.testMods
859 except AttributeError:
860 __main__.testMods = []
861 testMods = __main__.testMods
862
863
864 # Install the newimp routines, within a try/finally:
865 try:
866 sys.exc_traceback = None
867 wasImport = __builtin__.__import__ # Stash default
868 wasPath = sys.path
869 except AttributeError:
870 wasImport = None
871 try:
872 hiers = []; modules = []
873 global VERBOSE
874 wasVerbose, VERBOSE = VERBOSE, 2
875 __builtin__.__import__ = import_module # Install new version
876
877 if testMods: # Clear out imports from previous tests
878 for m in testMods[:]:
879 unloadFull(m)
880 testMods.remove(m)
881
882 testExec("already imported module: %s" % sys.modules.keys()[0],
883 ['import ' + sys.modules.keys()[0]],
884 locals, globals)
885 try:
886 no_sirree = 'no_sirree_does_not_exist'
887 testExec("non-existent module: %s" % no_sirree,
888 ['import ' + no_sirree],
889 locals, globals)
890 except ImportError:
891 testExec("ok", ['pass'], locals, globals)
892 got = None
893 for mod in ['Complex', 'UserDict', 'UserList', 'calendar',
894 'cmd', 'dis', 'mailbox', 'profile', 'random', 'rfc822']:
895 if not (mod in sys.modules.keys()):
896 got = mod
897 break # ==v
898 if got:
899 testExec("not-yet loaded module: %s" % mod,
900 ['import ' + mod, 'modules.append(got)'],
901 locals, globals)
902 else:
903 print "Import Test: couldn't find unimported module from list"
904
905 # Now some package stuff.
906
907 # First change the path to include our temp dir, copying so the
908 # addition can be revoked on cleanup in the finally, below:
909 sys.path = ['/var/tmp'] + sys.path[:]
910 # Now create a trivial package:
911 stmts = ["hier1 = TestDirHier({'a.py': 'print \"a.py executing\"'})",
912 "hiers.append(hier1)",
913 "root = hier1.PKG_NM",
914 "exec 'import ' + root",
915 "testMods.append(root)",
916 "confPkgVars(sys.modules[root].__name__, locals, globals)",
917 "confPkgVars(sys.modules[root].__name__+'.a',locals,globals)"]
918 testExec("trivial package, with one module, a.py",
919 stmts, locals, globals)
920 # Slightly less trivial package - reference to '__':
921 stmts = [("hier2 = TestDirHier({'ref.py': 'print \"Pkg __:\", __'})"),
922 "root = hier2.PKG_NM",
923 "hiers.append(hier2)",
924 "exec 'import ' + root",
925 "testMods.append(root)"]
926 testExec("trivial package, with module that has pkg shorthand ref",
927 stmts, locals, globals)
928 # Nested package, plus '__' references:
929
930 complexTemplate = {'ref.py': 'print "ref.py loading..."',
931 'suite': {'s1.py': 'print "s1.py, in pkg:", __',
932 'subsuite': {'sub1.py':
933 'print "sub1.py"'}}}
934 stmts = [('print """%s\n%s\n%s\n%s\n%s\n%s"""' %
935 ('.../',
936 ' ref.py\t\t\t"ref.py loading..."',
937 ' suite/',
938 ' s1.py \t\t"s1.py, in pkg: xxxx.suite"',
939 ' subsuite/',
940 ' sub1.py "sub1.py" ')),
941 "hier3 = TestDirHier(complexTemplate)",
942 "root = hier3.PKG_NM",
943 "hiers.append(hier3)",
944 "exec 'import ' + root",
945 "testMods.append(root)"]
946 testExec("Significantly nestled package:",
947 stmts, locals, globals)
948
949 # Now try to do an embedded sibling import, using '__' shorthand -
950 # alter our complexTemplate for a new dirHier:
951 complexTemplate['suite']['s1.py'] = 'import __.subsuite'
952 stmts = ["hier4 = TestDirHier(complexTemplate)",
953 "root = hier4.PKG_NM",
954 "testMods.append(root)",
955 "hiers.append(hier4)",
956 "exec 'import %s.suite.s1' % root",
957 "testMods.append(root)"]
958 testExec("Similar structure, but suite/s1.py imports '__.subsuite'",
959 stmts, locals, globals)
960
961 sys.exc_traceback = None # Signify clean conclusion.
962
963 finally:
964 if sys.exc_traceback:
965 print ' ** Import test FAILURE... cleanup.'
966 else:
967 print ' Import test SUCCESS... cleanup'
968 VERBOSE = wasVerbose
969 skipToTest = 0
970 atTest = 1
971 sys.path = wasPath
972 for h in hiers: h.remove(); del h # Dispose of test directories
973 if wasImport: # Resurrect prior routine
974 __builtin__.__import__ = wasImport
975 else:
976 del __builtin__.__import__
Guido van Rossumfa486a21995-04-07 09:04:01 +0000977
978if __name__ == '__main__':
979 test()