blob: 6fa9f43cb6ddd4f8efe3507cb426644b9e4f7f1a [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 Rossumf4ef7e61995-06-22 18:50:15 +000071__version__ = "Revision: 1.14"
Guido van Rossum52325901995-04-07 09:03:10 +000072
Guido van Rossumf4ef7e61995-06-22 18:50:15 +000073# Id: newimp.py,v 1.14 1995/05/10 19:15:07 klm Exp
74# First release: 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.
Guido van Rossumf4ef7e61995-06-22 18:50:15 +000085# - This could be substantially optimized, and there are some loose ends
Guido van Rossum52325901995-04-07 09:03:10 +000086# 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:
Guido van Rossumf4ef7e61995-06-22 18:50:15 +0000609 compiled = compile(sourceFile.read(), sourcePath, 'exec')
610 except SyntaxError:
611 # Doctor the exception a bit, to include the source file name in the
612 # report, and then reraise the doctored version.
613 os.unlink(compiledFile.name)
614 sys.exc_value = ((sys.exc_value[0] + ' in ' + sourceFile.name,)
615 + sys.exc_value[1:])
616 raise sys.exc_type, sys.exc_value
617 try:
Guido van Rossum52325901995-04-07 09:03:10 +0000618 compiledFile.write(imp.get_magic()) # compiled magic number
619 compiledFile.seek(8, 0) # mtime space holder
Guido van Rossum52325901995-04-07 09:03:10 +0000620 marshal.dump(compiled, compiledFile) # write the code obj
621 compiledFile.seek(4, 0) # position for mtime
622 compiledFile.write(marshal.dumps(mtime)[1:]) # register mtime
623 compiledFile.flush()
624 compiledFile.close()
625 return compiledPath
626 except IOError:
Guido van Rossumf4ef7e61995-06-22 18:50:15 +0000627 return None # ===>
Guido van Rossum52325901995-04-07 09:03:10 +0000628
629
630def PathExtension(locals, globals): # Probably obsolete.
631 """Determine import search path extension vis-a-vis __pkgpath__ entries.
632
633 local dict __pkgpath__ will preceed global dict __pkgpath__."""
634
635 pathadd = []
636 if globals and globals.has_key(PKG_PATH):
637 pathadd = PrependPath(pathadd, globals[PKG_PATH], 'global')
638 if locals and locals.has_key(PKG_PATH):
639 pathadd = PrependPath(pathadd, locals[PKG_PATH], 'local')
640 if pathadd:
641 note(PKG_PATH + ' extension: ' + pathadd)
642 return pathadd
643
644def PrependPath(path, pre, whence): # Probably obsolete
645 """Return copy of PATH list with string or list-of-strings PRE prepended.
646
647 If PRE is neither a string nor list-of-strings, print warning that
648 locality WHENCE has malformed value."""
649
650 # (There is probably a better way to handle malformed PREs, but raising an
651 # error seems too severe - in that case, a bad setting for
652 # sys.__pkgpath__ would prevent any imports!)
653
654 if type(pre) == types.StringType: return [pre] + path[:] # ===>
655 elif type(pre) == types.ListType: return pre + path[:] # ===>
656 else:
657 print "**Ignoring '%s' bad %s value**" % (whence, PKG_PATH)
658 return path # ===>
659
660got_suffixes = None
661def get_suffixes():
662 """Produce a list of triples, each describing a type of import file.
663
664 Triples have the form '(SUFFIX, MODE, TYPE)', where:
665
666 SUFFIX is a string found appended to a module name to make a filename for
667 that type of import file.
668
669 MODE is the mode string to be passed to the built-in 'open' function - "r"
670 for text files, "rb" for binary.
671
672 TYPE is the file type:
673
674 PY_SOURCE: python source code,
675 PY_COMPILED: byte-compiled python source,
676 C_EXTENSION: compiled-code object file,
677 PY_PACKAGE: python library directory, or
678 SEARCH_ERROR: no module found. """
679
680 # Note: sorted_suffixes() depends on this function's value being invariant.
681 # sorted_suffixes() must be revised if this becomes untrue.
682
683 global got_suffixes
684
685 if got_suffixes:
686 return got_suffixes
687 else:
688 # Ensure that the .pyc suffix precedes the .py:
689 got_suffixes = [('', 'r', PY_PACKAGE)]
690 py = pyc = None
691 for suff in imp.get_suffixes():
692 if suff[0] == '.py':
693 py = suff
694 elif suff[0] == '.pyc':
695 pyc = suff
696 else:
697 got_suffixes.append(suff)
698 got_suffixes.append(pyc)
699 got_suffixes.append(py)
700 return got_suffixes
701
702
703sortedSuffs = [] # State vars for sorted_suffixes(). Go
704def sorted_suffixes():
705 """Helper function ~efficiently~ tracks sorted list of module suffixes."""
706
707 # Produce sortedSuffs once - this presumes that get_suffixes does not
708 # change from call to call during a python session. Needs to be
709 # corrected if that becomes no longer true.
710
711 global sortedsuffs
712 if not sortedSuffs: # do compute only the "first" time
713 for item in get_suffixes():
714 sortedSuffs.append(item[0])
715 # Sort them in descending order:
716 sortedSuffs.sort(lambda x, y: (((len(x) > len(y)) and 1) or
717 ((len(x) < len(y)) and -1)))
718 sortedSuffs.reverse()
719 return sortedSuffs
720
721
722# exterior(): Utility routine, obtain local and global dicts of environment
723# containing/outside the callers environment, ie that of the
724# caller's caller. Routines can use exterior() to determine the
725# environment from which they were called.
726
727def exterior():
728 """Return dyad containing locals and globals of caller's caller.
729
730 Locals will be None if same as globals, ie env is global env."""
731
732 bogus = 'bogus' # A locally usable exception
733 try: raise bogus # Force an exception object
734 except bogus:
735 at = sys.exc_traceback.tb_frame.f_back # The external frame.
736 if at.f_back: at = at.f_back # And further, if any.
737 globals, locals = at.f_globals, at.f_locals
738 if locals == globals: # Exterior is global?
739 locals = None
740 return (locals, globals)
741
742#########################################################################
743# TESTING FACILITIES #
744
745def note(msg, threshold=1):
Guido van Rossum96044da1995-04-07 09:06:50 +0000746 if VERBOSE >= threshold: sys.stderr.write('(import: ' + msg + ')\n')
Guido van Rossum52325901995-04-07 09:03:10 +0000747
748class TestDirHier:
749 """Populate a transient directory hierarchy according to a definition
750 template - so we can create package/module hierarchies with which to
751 exercise the new import facilities..."""
752
753 def __init__(self, template, where='/var/tmp'):
754 """Establish a dir hierarchy, according to TEMPLATE, that will be
755 deleted upon deletion of this object (or deliberate invocation of the
756 __del__ method)."""
757 self.PKG_NM = 'tdh_'
758 rev = 0
759 while os.path.exists(os.path.join(where, self.PKG_NM+str(rev))):
760 rev = rev + 1
761 sys.exc_traceback = None # Ensure Discard of try/except obj ref
762 self.PKG_NM = self.PKG_NM + str(rev)
763 self.root = os.path.join(where, self.PKG_NM)
764 self.createDir(self.root)
765 self.add(template)
766
767 def __del__(self):
768 """Cleanup the test hierarchy."""
769 self.remove()
770 def add(self, template, root=None):
771 """Populate directory according to template dictionary.
772
773 Keys indicate file names, possibly directories themselves.
774
775 String values dictate contents of flat files.
776
777 Dictionary values dictate recursively embedded dictionary templates."""
778 if root == None: root = self.root
779 for key, val in template.items():
780 name = os.path.join(root, key)
781 if type(val) == types.StringType: # flat file
782 self.createFile(name, val)
783 elif type(val) == types.DictionaryType: # embedded dir
784 self.createDir(name)
785 self.add(val, name)
786 else:
787 raise ValueError, 'invalid file-value type, %s' % type(val)
788 def remove(self, name=''):
789 """Dispose of the NAME (or keys in dictionary), using 'rm -r'."""
790 name = os.path.join(self.root, name)
791 sys.exc_traceback = None # Ensure Discard of try/except obj ref
792 if os.path.exists(name):
793 print '(TestDirHier: deleting %s)' % name
794 os.system('rm -r ' + name)
795 else:
796 raise IOError, "can't remove non-existent " + name
797 def createFile(self, name, contents=None):
798 """Establish file NAME with CONTENTS.
799
800 If no contents specfied, contents will be 'print NAME'."""
801 f = open(name, 'w')
802 if not contents:
803 f.write("print '" + name + "'\n")
804 else:
805 f.write(contents)
806 f.close
807 def createDir(self, name):
808 """Create dir with NAME."""
809 return os.mkdir(name, 0755)
810
811skipToTest = 0
812atTest = 1
813def testExec(msg, execList, locals, globals):
814 global skipToTest, atTest
815 print 'Import Test:', '(' + str(atTest) + ')', msg, '...'
816 atTest = atTest + 1
817 if skipToTest > (atTest - 1):
818 print ' ... skipping til test', skipToTest
819 return
820 else:
821 print ''
822 for stmt in execList:
823 exec stmt in locals, globals
824
825def test(number=0):
826 """Exercise import functionality, creating a transient dir hierarchy for
827 the purpose.
828
829 We actually install the new import functionality, temporarily, resuming the
830 existing function on cleanup."""
831
832 import __builtin__
833
834 global skipToTest, atTest
835 skipToTest = number
836 hier = None
837
838 def confPkgVars(mod, locals, globals):
839 if not sys.modules.has_key(mod):
840 print 'import test: missing module "%s"' % mod
841 else:
842 modMod = sys.modules[mod]
843 if not modMod.__dict__.has_key(PKG_SHORT_NM):
844 print ('import test: module "%s" missing %s pkg shorthand' %
845 (mod, PKG_SHORT_NM))
846 if not modMod.__dict__.has_key(PKG_PATH):
847 print ('import test: module "%s" missing %s package path' %
848 (mod, PKG_PATH))
849 def unloadFull(mod):
850 """Unload module and offspring submodules, if any."""
851 modMod = ''
852 if type(mod) == types.StringType:
853 modNm = mod
854 elif type(mod) == types.ModuleType:
855 modNm = modMod.__name__
856 for subj in sys.modules.keys() + sys.stub_modules.keys():
857 if subj[0:len(modNm)] == modNm:
858 unload(subj)
859
860 # First, get the globals and locals to pass to our testExec():
861 exec 'import ' + __name__
862 globals, locals = eval(__name__ + '.__dict__'), vars()
863
864 try:
865 __main__.testMods
866 except AttributeError:
867 __main__.testMods = []
868 testMods = __main__.testMods
869
870
871 # Install the newimp routines, within a try/finally:
872 try:
873 sys.exc_traceback = None
874 wasImport = __builtin__.__import__ # Stash default
875 wasPath = sys.path
876 except AttributeError:
877 wasImport = None
878 try:
879 hiers = []; modules = []
880 global VERBOSE
881 wasVerbose, VERBOSE = VERBOSE, 2
882 __builtin__.__import__ = import_module # Install new version
883
884 if testMods: # Clear out imports from previous tests
885 for m in testMods[:]:
886 unloadFull(m)
887 testMods.remove(m)
888
889 testExec("already imported module: %s" % sys.modules.keys()[0],
890 ['import ' + sys.modules.keys()[0]],
891 locals, globals)
892 try:
893 no_sirree = 'no_sirree_does_not_exist'
894 testExec("non-existent module: %s" % no_sirree,
895 ['import ' + no_sirree],
896 locals, globals)
897 except ImportError:
898 testExec("ok", ['pass'], locals, globals)
899 got = None
900 for mod in ['Complex', 'UserDict', 'UserList', 'calendar',
901 'cmd', 'dis', 'mailbox', 'profile', 'random', 'rfc822']:
902 if not (mod in sys.modules.keys()):
903 got = mod
904 break # ==v
905 if got:
906 testExec("not-yet loaded module: %s" % mod,
907 ['import ' + mod, 'modules.append(got)'],
908 locals, globals)
909 else:
910 print "Import Test: couldn't find unimported module from list"
911
912 # Now some package stuff.
913
914 # First change the path to include our temp dir, copying so the
915 # addition can be revoked on cleanup in the finally, below:
916 sys.path = ['/var/tmp'] + sys.path[:]
917 # Now create a trivial package:
918 stmts = ["hier1 = TestDirHier({'a.py': 'print \"a.py executing\"'})",
919 "hiers.append(hier1)",
920 "root = hier1.PKG_NM",
921 "exec 'import ' + root",
922 "testMods.append(root)",
923 "confPkgVars(sys.modules[root].__name__, locals, globals)",
924 "confPkgVars(sys.modules[root].__name__+'.a',locals,globals)"]
925 testExec("trivial package, with one module, a.py",
926 stmts, locals, globals)
927 # Slightly less trivial package - reference to '__':
928 stmts = [("hier2 = TestDirHier({'ref.py': 'print \"Pkg __:\", __'})"),
929 "root = hier2.PKG_NM",
930 "hiers.append(hier2)",
931 "exec 'import ' + root",
932 "testMods.append(root)"]
933 testExec("trivial package, with module that has pkg shorthand ref",
934 stmts, locals, globals)
935 # Nested package, plus '__' references:
936
937 complexTemplate = {'ref.py': 'print "ref.py loading..."',
938 'suite': {'s1.py': 'print "s1.py, in pkg:", __',
939 'subsuite': {'sub1.py':
940 'print "sub1.py"'}}}
941 stmts = [('print """%s\n%s\n%s\n%s\n%s\n%s"""' %
942 ('.../',
943 ' ref.py\t\t\t"ref.py loading..."',
944 ' suite/',
945 ' s1.py \t\t"s1.py, in pkg: xxxx.suite"',
946 ' subsuite/',
947 ' sub1.py "sub1.py" ')),
948 "hier3 = TestDirHier(complexTemplate)",
949 "root = hier3.PKG_NM",
950 "hiers.append(hier3)",
951 "exec 'import ' + root",
952 "testMods.append(root)"]
953 testExec("Significantly nestled package:",
954 stmts, locals, globals)
955
956 # Now try to do an embedded sibling import, using '__' shorthand -
957 # alter our complexTemplate for a new dirHier:
958 complexTemplate['suite']['s1.py'] = 'import __.subsuite'
959 stmts = ["hier4 = TestDirHier(complexTemplate)",
960 "root = hier4.PKG_NM",
961 "testMods.append(root)",
962 "hiers.append(hier4)",
963 "exec 'import %s.suite.s1' % root",
964 "testMods.append(root)"]
965 testExec("Similar structure, but suite/s1.py imports '__.subsuite'",
966 stmts, locals, globals)
967
968 sys.exc_traceback = None # Signify clean conclusion.
969
970 finally:
971 if sys.exc_traceback:
972 print ' ** Import test FAILURE... cleanup.'
973 else:
974 print ' Import test SUCCESS... cleanup'
975 VERBOSE = wasVerbose
976 skipToTest = 0
977 atTest = 1
978 sys.path = wasPath
979 for h in hiers: h.remove(); del h # Dispose of test directories
980 if wasImport: # Resurrect prior routine
981 __builtin__.__import__ = wasImport
982 else:
983 del __builtin__.__import__
Guido van Rossumfa486a21995-04-07 09:04:01 +0000984
985if __name__ == '__main__':
986 test()