blob: e671de8ba2b45ce5e68e3ab7c0f32f1f2da622a1 [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
71__version__ = "$Revision$"
72
Guido van Rossum96044da1995-04-07 09:06:50 +000073# $Id$ First release:
74# 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:
253 if froms == '*':
254 froms = theMod.__dict__.keys() # resolve '*'
255 for item in froms:
256 (envLocals or envGlobals)[item] = theMod.__dict__[item]
257 return theMod
258
259def unload(module):
260 """Remove registration for a module, so import will do a fresh load."""
261 if type(module) == types.ModuleType:
262 module = module.__name__
263 for m in [sys.modules, sys.stub_modules]:
264 try:
265 del m[module]
266 except KeyError:
267 pass
268
269def find_module(name, path, absNm=''):
270 """Locate module NAME on PATH. PATH is pathname string or a list of them.
271
272 Note that up-to-date compiled versions of a module are preferred to plain
273 source, and compilation is automatically performed when necessary and
274 possible.
275
276 Returns a list of the tuples returned by 'find_module_file' (cf), one for
277 each nested level, deepest last."""
278
279 checked = [] # For avoiding redundant dir lists.
280
281 if not absNm: absNm = name
282
283 # Parse name into list of nested components,
284 expNm = string.splitfields(name, '.')
285
286 for curPath in path:
287
288 if (type(curPath) != types.StringType) or (curPath in checked):
289 # Disregard bogus or already investigated path elements:
290 continue # ==^
291 else:
292 # Register it for subsequent disregard.
293 checked.append(curPath)
294
295 if len(expNm) == 1:
296
297 # Non-nested module name:
298
299 got = find_module_file(curPath, absNm)
300 if got:
301 note('using %s' % got[2], 2)
302 return [got] # ===>
303
304 else:
305
306 # Composite name specifying nested module:
307
308 gotList = []; nameAccume = expNm[0]
309
310 got = find_module_file(curPath, nameAccume)
311 if not got: # Continue to next prospective path.
312 continue # ==^
313 else:
314 gotList.append(got)
315 nm, file, fullPath, ty = got
316
317 # Work on successively nested components:
318 for component in expNm[1:]:
319 # 'ty'pe of containing component must be package:
320 if ty[2] != PY_PACKAGE:
321 gotList, got = [], None
322 break # ==v^
323 if nameAccume:
324 nameAccume = nameAccume + '.' + component
325 else:
326 nameAccume = component
327 got = find_module_file(fullPath, nameAccume)
328 if got:
329 gotList.append(got)
330 # ** have to return the *full* name here:
331 nm, file, fullPath, ty = got
332 else:
333 # Clear state vars:
334 gotList, got, nameAccume = [], None, ''
335 break # ==v^
336 # Found nesting all the way to the specified tip:
337 if got:
338 return gotList # ===>
339
340 # Failed.
341 return None
342
343def find_module_file(pathNm, modname):
344 """Find module file given dir PATHNAME and module NAME.
345
346 If successful, returns quadruple consisting of a mod name, file object,
347 PATHNAME for the found file, and a description triple as contained in the
348 list returned by get_suffixes.
349
350 Otherwise, returns None.
351
352 Note that up-to-date compiled versions of a module are preferred to plain
353 source, and compilation is automatically performed, when necessary and
354 possible."""
355
356 relNm = string.splitfields(modname,'.')[-1]
357
358 if pathNm[-1] != '/': pathNm = pathNm + '/'
359
360 for suff, mode, ty in get_suffixes():
361 note('trying ' + pathNm + relNm + suff + '...', 3)
362 fullPath = pathNm + relNm + suff
363 try:
364 modf = open(fullPath, mode)
365 except IOError:
366 # ?? Skip unreadable ones.
367 continue # ==^
368
369 if ty == PY_PACKAGE:
370 # Enforce directory characteristic:
371 if not os.path.isdir(fullPath):
372 note('Skipping non-dir match ' + fullPath)
373 continue # ==^
374 else:
375 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
376
377
378 elif ty == PY_SOURCE:
379 # Try for a compiled version:
380 note('found source ' + fullPath, 2)
381 pyc = fullPath + 'c' # Sadly, we're presuming '.py' suff.
382 if (not os.path.exists(pyc) or
383 (os.stat(fullPath)[8] > os.stat(pyc)[8])):
384 # Try to compile:
385 pyc = compile_source(fullPath, modf)
386 if pyc and (os.stat(fullPath)[8] < os.stat(pyc)[8]):
387 # Either pyc was already newer or we just made it so; in either
388 # case it's what we crave:
389 return (modname, open(pyc, 'rb'), pyc, # ===>
390 ('.pyc', 'rb', PY_COMPILED))
391 # Couldn't get a compiled version - return the source:
392 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
393
394 elif ty == PY_COMPILED:
395 # Make sure it is current, trying to compile if necessary, and
396 # prefer source failing that:
397 note('found compiled ' + fullPath, 2)
398 py = fullPath[:-1] # Sadly again, presuming '.pyc' suff.
399 if not os.path.exists(py):
400 note('found pyc sans py: ' + fullPath)
401 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
402 elif (os.stat(py)[8] > os.stat(fullPath)[8]):
403 note('forced to try compiling: ' + py)
404 pyc = compile_source(py, modf)
405 if pyc:
406 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
407 else:
408 note('failed compile - must use more recent .py')
409 return (modname, # ===>
410 open(py, 'r'), py, ('.py', 'r', PY_SOURCE))
411 else:
412 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
413
414 elif ty == C_EXTENSION:
415 note('found extension ' + fullPath, 2)
416 return (modname, modf, fullPath, (suff, mode, ty)) # ===>
417
418 else:
419 raise SystemError, 'Unanticipated (new?) module type encountered'
420
421 return None
422
423
424def load_module(name, ty, theFile, fromMod=None):
425 """Load module NAME, type TYPE, from file FILE.
426
427 Optional arg fromMod indicated the module from which the load is being done
428 - necessary for detecting import of __ from a package's __main__ module.
429
430 Return the populated module object."""
431
432 # Note: we mint and register intermediate package directories, as necessary
433
434 # Determine packagepath extension:
435
436 # Establish the module object in question:
437 theMod = procure_module(name)
438 nameTail = string.splitfields(name, '.')[-1]
439 thePath = theFile.name
440
441 if ty == PY_SOURCE:
442 exec_into(theFile, theMod, theFile.name)
443
444 elif ty == PY_COMPILED:
445 pyc = open(theFile.name, 'rb').read()
446 if pyc[0:4] != imp.get_magic():
447 raise ImportError, 'bad magic number: ' + theFile.name # ===>
448 code = marshal.loads(pyc[8:])
449 exec_into(code, theMod, theFile.name)
450
451 elif ty == C_EXTENSION:
452 try:
453 theMod = imp.load_dynamic(nameTail, thePath, theFile)
454 except:
455 # ?? Ok to embellish the error message?
456 raise sys.exc_type, ('%s (from %s)' %
457 (str(sys.exc_value), theFile.name))
458
459 elif ty == PY_PACKAGE:
460 # Load constituents:
461 if (os.path.exists(thePath + '/' + PKG_MAIN_NM) and
462 # pkg has a __main__, and this import not already from __main__, so
463 # __main__ can 'import __', or even better, 'from __ import *'
464 ((theMod.__name__ != PKG_MAIN_NM) and (fromMod.__ == theMod))):
465 exec_into(thePath + '/' + PKG_MAIN_NM, theMod, theFile.name)
466 else:
467 # ... or else recursively load constituent modules.
468 prospects = mod_prospects(thePath)
469 for item in prospects:
470 theMod.__dict__[item] = import_module(item,
471 theMod.__dict__,
472 theMod.__dict__,
473 None,
474 theMod)
475
476 else:
477 raise ImportError, 'Unimplemented import type: %s' % ty # ===>
478
479 return theMod
480
481def exec_into(obj, module, path):
482 """Helper for load_module, execfile/exec path or code OBJ within MODULE."""
483
484 # This depends on ability of exec and execfile to mutilate, erhm, mutate
485 # the __dict__ of a module. It will not work if/when this becomes
486 # disallowed, as it is for normal assignments.
487
488 try:
489 if type(obj) == types.FileType:
490 execfile(path, module.__dict__, module.__dict__)
491 elif type(obj) in [types.CodeType, types.StringType]:
492 exec obj in module.__dict__, module.__dict__
493 except:
494 # ?? Ok to embellish the error message?
495 raise sys.exc_type, ('%s (from %s)' %
496 (str(sys.exc_value), path))
497
498
499def mod_prospects(path):
500 """Return a list of prospective modules within directory PATH.
501
502 We actually return the distinct names resulting from stripping the dir
503 entries (excluding '.' and '..') of their suffixes (as represented by
504 'get_suffixes').
505
506 (Note that matches for the PY_PACKAGE type with null suffix are
507 implicitly constrained to be directories.)"""
508
509 # We actually strip the longest matching suffixes, so eg 'dbmmodule.so'
510 # mates with 'module.so' rather than '.so'.
511
512 dirList = os.listdir(path)
513 excludes = ['.', '..']
514 sortedSuffs = sorted_suffixes()
515 entries = []
516 for item in dirList:
517 if item in excludes: continue # ==^
518 for suff in sortedSuffs:
519 sub = -1 * len(suff)
520 if sub == 0:
521 if os.path.isdir(os.path.join(path, item)):
522 entries.append(item)
523 elif item[sub:] == suff:
524 it = item[:sub]
525 if not it in entries:
526 entries.append(it)
527 break # ==v^
528 return entries
529
530
531
532def procure_module(name):
533 """Return an established or else new module object having NAME.
534
535 First checks sys.modules, then sys.stub_modules."""
536
537 if sys.modules.has_key(name):
538 it = sys.modules[name]
539 elif sys.stub_modules.has_key(name):
540 it = sys.stub_modules[name]
541 else:
542 it = new.module(name)
543 return it # ===>
544
545def register_module_nesting(modList, pkg):
546 """Given a find_module()-style NESTING and a parent PACKAGE, register
547 components as stub modules."""
548 container = None
549 for stubModNm, stubModF, stubPath, stubTy in modList:
550 relStubNm = string.splitfields(stubModNm, '.')[-1]
551 if sys.modules.has_key(stubModNm):
552 # Nestle in containing package:
553 stubMod = sys.modules[stubModNm]
554 pkg.__dict__[relStubNm] = stubMod
555 pkg = stubMod # will be parent for next in sequence.
556 elif sys.stub_modules.has_key(stubModNm):
557 stubMod = sys.stub_modules[stubModNm]
558 pkg.__dict__[relStubNm] = stubMod
559 pkg = stubMod
560 else:
561 stubMod = procure_module(stubModNm)
562 # Register as a stub:
563 register_module(stubMod, stubModNm, stubPath, pkg, 1)
564 pkg.__dict__[relStubNm] = stubMod
565 pkg = stubMod
566 if not container:
567 container = stubMod
568 return container
569
570def register_module(theMod, name, path, package, stub=0):
571 """Properly register MODULE, w/ name, path, package, opt, as stub."""
572
573 if stub:
574 sys.stub_modules[name] = theMod
575 else:
576 sys.modules[name] = theMod
577 if sys.stub_modules.has_key(name):
578 del sys.stub_modules[name]
579 theMod.__ = theMod.__dict__[PKG_NM] = package
580 theMod.__dict__[PKG_PATH] = path
581
582
583def compile_source(sourcePath, sourceFile):
584 """Given python code source path and file obj, Create a compiled version.
585
586 Return path of compiled version, or None if file creation is not
587 successful. (Compilation errors themselves are passed without restraint.)
588
589 This is an import-private interface, and not well-behaved for general use.
590
591 In particular, we presume the validity of the sourcePath, and that it
592 includes a '.py' extension."""
593
594 compiledPath = sourcePath[:-3] + '.pyc'
595 try:
596 compiledFile = open(compiledPath, 'wb')
597 except IOError:
598 note("write permission denied to " + compiledPath)
599 return None
600 mtime = os.stat(sourcePath)[8]
601 sourceFile.seek(0) # rewind
602 try:
603 compiledFile.write(imp.get_magic()) # compiled magic number
604 compiledFile.seek(8, 0) # mtime space holder
605 # We let compilation errors go their own way...
606 compiled = compile(sourceFile.read(), sourcePath, 'exec')
607 marshal.dump(compiled, compiledFile) # write the code obj
608 compiledFile.seek(4, 0) # position for mtime
609 compiledFile.write(marshal.dumps(mtime)[1:]) # register mtime
610 compiledFile.flush()
611 compiledFile.close()
612 return compiledPath
613 except IOError:
614 return None
615
616
617def PathExtension(locals, globals): # Probably obsolete.
618 """Determine import search path extension vis-a-vis __pkgpath__ entries.
619
620 local dict __pkgpath__ will preceed global dict __pkgpath__."""
621
622 pathadd = []
623 if globals and globals.has_key(PKG_PATH):
624 pathadd = PrependPath(pathadd, globals[PKG_PATH], 'global')
625 if locals and locals.has_key(PKG_PATH):
626 pathadd = PrependPath(pathadd, locals[PKG_PATH], 'local')
627 if pathadd:
628 note(PKG_PATH + ' extension: ' + pathadd)
629 return pathadd
630
631def PrependPath(path, pre, whence): # Probably obsolete
632 """Return copy of PATH list with string or list-of-strings PRE prepended.
633
634 If PRE is neither a string nor list-of-strings, print warning that
635 locality WHENCE has malformed value."""
636
637 # (There is probably a better way to handle malformed PREs, but raising an
638 # error seems too severe - in that case, a bad setting for
639 # sys.__pkgpath__ would prevent any imports!)
640
641 if type(pre) == types.StringType: return [pre] + path[:] # ===>
642 elif type(pre) == types.ListType: return pre + path[:] # ===>
643 else:
644 print "**Ignoring '%s' bad %s value**" % (whence, PKG_PATH)
645 return path # ===>
646
647got_suffixes = None
648def get_suffixes():
649 """Produce a list of triples, each describing a type of import file.
650
651 Triples have the form '(SUFFIX, MODE, TYPE)', where:
652
653 SUFFIX is a string found appended to a module name to make a filename for
654 that type of import file.
655
656 MODE is the mode string to be passed to the built-in 'open' function - "r"
657 for text files, "rb" for binary.
658
659 TYPE is the file type:
660
661 PY_SOURCE: python source code,
662 PY_COMPILED: byte-compiled python source,
663 C_EXTENSION: compiled-code object file,
664 PY_PACKAGE: python library directory, or
665 SEARCH_ERROR: no module found. """
666
667 # Note: sorted_suffixes() depends on this function's value being invariant.
668 # sorted_suffixes() must be revised if this becomes untrue.
669
670 global got_suffixes
671
672 if got_suffixes:
673 return got_suffixes
674 else:
675 # Ensure that the .pyc suffix precedes the .py:
676 got_suffixes = [('', 'r', PY_PACKAGE)]
677 py = pyc = None
678 for suff in imp.get_suffixes():
679 if suff[0] == '.py':
680 py = suff
681 elif suff[0] == '.pyc':
682 pyc = suff
683 else:
684 got_suffixes.append(suff)
685 got_suffixes.append(pyc)
686 got_suffixes.append(py)
687 return got_suffixes
688
689
690sortedSuffs = [] # State vars for sorted_suffixes(). Go
691def sorted_suffixes():
692 """Helper function ~efficiently~ tracks sorted list of module suffixes."""
693
694 # Produce sortedSuffs once - this presumes that get_suffixes does not
695 # change from call to call during a python session. Needs to be
696 # corrected if that becomes no longer true.
697
698 global sortedsuffs
699 if not sortedSuffs: # do compute only the "first" time
700 for item in get_suffixes():
701 sortedSuffs.append(item[0])
702 # Sort them in descending order:
703 sortedSuffs.sort(lambda x, y: (((len(x) > len(y)) and 1) or
704 ((len(x) < len(y)) and -1)))
705 sortedSuffs.reverse()
706 return sortedSuffs
707
708
709# exterior(): Utility routine, obtain local and global dicts of environment
710# containing/outside the callers environment, ie that of the
711# caller's caller. Routines can use exterior() to determine the
712# environment from which they were called.
713
714def exterior():
715 """Return dyad containing locals and globals of caller's caller.
716
717 Locals will be None if same as globals, ie env is global env."""
718
719 bogus = 'bogus' # A locally usable exception
720 try: raise bogus # Force an exception object
721 except bogus:
722 at = sys.exc_traceback.tb_frame.f_back # The external frame.
723 if at.f_back: at = at.f_back # And further, if any.
724 globals, locals = at.f_globals, at.f_locals
725 if locals == globals: # Exterior is global?
726 locals = None
727 return (locals, globals)
728
729#########################################################################
730# TESTING FACILITIES #
731
732def note(msg, threshold=1):
Guido van Rossum96044da1995-04-07 09:06:50 +0000733 if VERBOSE >= threshold: sys.stderr.write('(import: ' + msg + ')\n')
Guido van Rossum52325901995-04-07 09:03:10 +0000734
735class TestDirHier:
736 """Populate a transient directory hierarchy according to a definition
737 template - so we can create package/module hierarchies with which to
738 exercise the new import facilities..."""
739
740 def __init__(self, template, where='/var/tmp'):
741 """Establish a dir hierarchy, according to TEMPLATE, that will be
742 deleted upon deletion of this object (or deliberate invocation of the
743 __del__ method)."""
744 self.PKG_NM = 'tdh_'
745 rev = 0
746 while os.path.exists(os.path.join(where, self.PKG_NM+str(rev))):
747 rev = rev + 1
748 sys.exc_traceback = None # Ensure Discard of try/except obj ref
749 self.PKG_NM = self.PKG_NM + str(rev)
750 self.root = os.path.join(where, self.PKG_NM)
751 self.createDir(self.root)
752 self.add(template)
753
754 def __del__(self):
755 """Cleanup the test hierarchy."""
756 self.remove()
757 def add(self, template, root=None):
758 """Populate directory according to template dictionary.
759
760 Keys indicate file names, possibly directories themselves.
761
762 String values dictate contents of flat files.
763
764 Dictionary values dictate recursively embedded dictionary templates."""
765 if root == None: root = self.root
766 for key, val in template.items():
767 name = os.path.join(root, key)
768 if type(val) == types.StringType: # flat file
769 self.createFile(name, val)
770 elif type(val) == types.DictionaryType: # embedded dir
771 self.createDir(name)
772 self.add(val, name)
773 else:
774 raise ValueError, 'invalid file-value type, %s' % type(val)
775 def remove(self, name=''):
776 """Dispose of the NAME (or keys in dictionary), using 'rm -r'."""
777 name = os.path.join(self.root, name)
778 sys.exc_traceback = None # Ensure Discard of try/except obj ref
779 if os.path.exists(name):
780 print '(TestDirHier: deleting %s)' % name
781 os.system('rm -r ' + name)
782 else:
783 raise IOError, "can't remove non-existent " + name
784 def createFile(self, name, contents=None):
785 """Establish file NAME with CONTENTS.
786
787 If no contents specfied, contents will be 'print NAME'."""
788 f = open(name, 'w')
789 if not contents:
790 f.write("print '" + name + "'\n")
791 else:
792 f.write(contents)
793 f.close
794 def createDir(self, name):
795 """Create dir with NAME."""
796 return os.mkdir(name, 0755)
797
798skipToTest = 0
799atTest = 1
800def testExec(msg, execList, locals, globals):
801 global skipToTest, atTest
802 print 'Import Test:', '(' + str(atTest) + ')', msg, '...'
803 atTest = atTest + 1
804 if skipToTest > (atTest - 1):
805 print ' ... skipping til test', skipToTest
806 return
807 else:
808 print ''
809 for stmt in execList:
810 exec stmt in locals, globals
811
812def test(number=0):
813 """Exercise import functionality, creating a transient dir hierarchy for
814 the purpose.
815
816 We actually install the new import functionality, temporarily, resuming the
817 existing function on cleanup."""
818
819 import __builtin__
820
821 global skipToTest, atTest
822 skipToTest = number
823 hier = None
824
825 def confPkgVars(mod, locals, globals):
826 if not sys.modules.has_key(mod):
827 print 'import test: missing module "%s"' % mod
828 else:
829 modMod = sys.modules[mod]
830 if not modMod.__dict__.has_key(PKG_SHORT_NM):
831 print ('import test: module "%s" missing %s pkg shorthand' %
832 (mod, PKG_SHORT_NM))
833 if not modMod.__dict__.has_key(PKG_PATH):
834 print ('import test: module "%s" missing %s package path' %
835 (mod, PKG_PATH))
836 def unloadFull(mod):
837 """Unload module and offspring submodules, if any."""
838 modMod = ''
839 if type(mod) == types.StringType:
840 modNm = mod
841 elif type(mod) == types.ModuleType:
842 modNm = modMod.__name__
843 for subj in sys.modules.keys() + sys.stub_modules.keys():
844 if subj[0:len(modNm)] == modNm:
845 unload(subj)
846
847 # First, get the globals and locals to pass to our testExec():
848 exec 'import ' + __name__
849 globals, locals = eval(__name__ + '.__dict__'), vars()
850
851 try:
852 __main__.testMods
853 except AttributeError:
854 __main__.testMods = []
855 testMods = __main__.testMods
856
857
858 # Install the newimp routines, within a try/finally:
859 try:
860 sys.exc_traceback = None
861 wasImport = __builtin__.__import__ # Stash default
862 wasPath = sys.path
863 except AttributeError:
864 wasImport = None
865 try:
866 hiers = []; modules = []
867 global VERBOSE
868 wasVerbose, VERBOSE = VERBOSE, 2
869 __builtin__.__import__ = import_module # Install new version
870
871 if testMods: # Clear out imports from previous tests
872 for m in testMods[:]:
873 unloadFull(m)
874 testMods.remove(m)
875
876 testExec("already imported module: %s" % sys.modules.keys()[0],
877 ['import ' + sys.modules.keys()[0]],
878 locals, globals)
879 try:
880 no_sirree = 'no_sirree_does_not_exist'
881 testExec("non-existent module: %s" % no_sirree,
882 ['import ' + no_sirree],
883 locals, globals)
884 except ImportError:
885 testExec("ok", ['pass'], locals, globals)
886 got = None
887 for mod in ['Complex', 'UserDict', 'UserList', 'calendar',
888 'cmd', 'dis', 'mailbox', 'profile', 'random', 'rfc822']:
889 if not (mod in sys.modules.keys()):
890 got = mod
891 break # ==v
892 if got:
893 testExec("not-yet loaded module: %s" % mod,
894 ['import ' + mod, 'modules.append(got)'],
895 locals, globals)
896 else:
897 print "Import Test: couldn't find unimported module from list"
898
899 # Now some package stuff.
900
901 # First change the path to include our temp dir, copying so the
902 # addition can be revoked on cleanup in the finally, below:
903 sys.path = ['/var/tmp'] + sys.path[:]
904 # Now create a trivial package:
905 stmts = ["hier1 = TestDirHier({'a.py': 'print \"a.py executing\"'})",
906 "hiers.append(hier1)",
907 "root = hier1.PKG_NM",
908 "exec 'import ' + root",
909 "testMods.append(root)",
910 "confPkgVars(sys.modules[root].__name__, locals, globals)",
911 "confPkgVars(sys.modules[root].__name__+'.a',locals,globals)"]
912 testExec("trivial package, with one module, a.py",
913 stmts, locals, globals)
914 # Slightly less trivial package - reference to '__':
915 stmts = [("hier2 = TestDirHier({'ref.py': 'print \"Pkg __:\", __'})"),
916 "root = hier2.PKG_NM",
917 "hiers.append(hier2)",
918 "exec 'import ' + root",
919 "testMods.append(root)"]
920 testExec("trivial package, with module that has pkg shorthand ref",
921 stmts, locals, globals)
922 # Nested package, plus '__' references:
923
924 complexTemplate = {'ref.py': 'print "ref.py loading..."',
925 'suite': {'s1.py': 'print "s1.py, in pkg:", __',
926 'subsuite': {'sub1.py':
927 'print "sub1.py"'}}}
928 stmts = [('print """%s\n%s\n%s\n%s\n%s\n%s"""' %
929 ('.../',
930 ' ref.py\t\t\t"ref.py loading..."',
931 ' suite/',
932 ' s1.py \t\t"s1.py, in pkg: xxxx.suite"',
933 ' subsuite/',
934 ' sub1.py "sub1.py" ')),
935 "hier3 = TestDirHier(complexTemplate)",
936 "root = hier3.PKG_NM",
937 "hiers.append(hier3)",
938 "exec 'import ' + root",
939 "testMods.append(root)"]
940 testExec("Significantly nestled package:",
941 stmts, locals, globals)
942
943 # Now try to do an embedded sibling import, using '__' shorthand -
944 # alter our complexTemplate for a new dirHier:
945 complexTemplate['suite']['s1.py'] = 'import __.subsuite'
946 stmts = ["hier4 = TestDirHier(complexTemplate)",
947 "root = hier4.PKG_NM",
948 "testMods.append(root)",
949 "hiers.append(hier4)",
950 "exec 'import %s.suite.s1' % root",
951 "testMods.append(root)"]
952 testExec("Similar structure, but suite/s1.py imports '__.subsuite'",
953 stmts, locals, globals)
954
955 sys.exc_traceback = None # Signify clean conclusion.
956
957 finally:
958 if sys.exc_traceback:
959 print ' ** Import test FAILURE... cleanup.'
960 else:
961 print ' Import test SUCCESS... cleanup'
962 VERBOSE = wasVerbose
963 skipToTest = 0
964 atTest = 1
965 sys.path = wasPath
966 for h in hiers: h.remove(); del h # Dispose of test directories
967 if wasImport: # Resurrect prior routine
968 __builtin__.__import__ = wasImport
969 else:
970 del __builtin__.__import__
Guido van Rossumfa486a21995-04-07 09:04:01 +0000971
972if __name__ == '__main__':
973 test()