980 lines
33 KiB
Python
Executable File
980 lines
33 KiB
Python
Executable File
"""Prototype of 'import' functionality enhanced to implement packages.
|
|
|
|
Why packages? Packages enable module nesting and sibling module
|
|
imports. 'Til now, the python module namespace was flat, which
|
|
means every module had to have a unique name, in order to not
|
|
conflict with names of other modules on the load path. Furthermore,
|
|
suites of modules could not be structurally affiliated with one
|
|
another.
|
|
|
|
With packages, a suite of, eg, email-oriented modules can include a
|
|
module named 'mailbox', without conflicting with the, eg, 'mailbox'
|
|
module of a shared-memory suite - 'email.mailbox' vs
|
|
'shmem.mailbox'. Packages also enable modules within a suite to
|
|
load other modules within their package without having the package
|
|
name hard-coded. Similarly, package suites of modules can be loaded
|
|
as a unit, by loading the package that contains them.
|
|
|
|
Usage: once installed (newimp.install(); newimp.revert() to revert to
|
|
the prior __import__ routine), 'import ...' and 'from ... import ...'
|
|
can be used to:
|
|
|
|
- import modules from the search path, as before.
|
|
|
|
- import modules from within other directory "packages" on the search
|
|
path using a '.' dot-delimited nesting syntax. The nesting is fully
|
|
recursive.
|
|
|
|
For example, 'import test.test_types' will import the test_types
|
|
module within the 'test' package. The calling environment would
|
|
then access the module as 'test.test_types', which is the name of
|
|
the fully-loaded 'test_types' module. It is found contained within
|
|
the stub (ie, only partially loaded) 'test' module, hence accessed as
|
|
'test.test_types'.
|
|
|
|
- import siblings from modules within a package, using '__.' as a shorthand
|
|
prefix to refer to the parent package. This enables referential
|
|
transparency - package modules need not know their package name.
|
|
|
|
The '__' package references are actually names assigned within
|
|
modules, to refer to their containing package. This means that
|
|
variable references can be made to imported modules, or to variables
|
|
defined via 'import ... from', also using the '__.var' shorthand
|
|
notation. This establishes a proper equivalence between the import
|
|
reference '__.sibling' and the var reference '__.sibling'.
|
|
|
|
- import an entire package as a unit, by importing the package directory.
|
|
If there is a module named '__main__.py' in the package, it controls the
|
|
load. Otherwise, all the modules in the dir, including packages, are
|
|
inherently loaded into the package module's namespace.
|
|
|
|
For example, 'import test' will load the modules of the entire 'test'
|
|
package, at least until a test failure is encountered.
|
|
|
|
In a package, a module with the name '__main__' has a special role.
|
|
If present in a package directory, then it is loaded into the package
|
|
module, instead of loading the contents of the directory. This
|
|
enables the __main__ module to control the load, possibly loading
|
|
the entire directory deliberately (using 'import __', or even
|
|
'from __ import *', to load all the module contents directly into the
|
|
package module).
|
|
|
|
- perform any combination of the above - have a package that contains
|
|
packages, etc.
|
|
|
|
Modules have a few new attributes in support of packages. As mentioned
|
|
above, '__' is a shorthand attribute denoting the modules' parent package,
|
|
also denoted in the module by '__package__'. Additionally, modules have
|
|
associated with them a '__pkgpath__', a path by which sibling modules are
|
|
found."""
|
|
|
|
__version__ = "Revision: 1.13"
|
|
|
|
# Id: newimp.py,v 1.13 1995/04/13 23:23:33 klm Exp First release:
|
|
# Ken.Manheimer@nist.gov, 5-Apr-1995, for python 1.2
|
|
|
|
# Developers Notes:
|
|
#
|
|
# - 'sys.stub_modules' registers "incidental" (partially loaded) modules.
|
|
# A stub module is promoted to the fully-loaded 'sys.modules' list when it is
|
|
# explicitly loaded as a unit.
|
|
# - The __main__ loads of '__' have not yet been tested.
|
|
# - The test routines are cool, including a transient directory
|
|
# hierarchy facility, and a means of skipping to later tests by giving
|
|
# the test routine a numeric arg.
|
|
# - This could be substantially optimized, and there are many loose ends
|
|
# lying around, since i wanted to get this released for 1.2.
|
|
|
|
VERBOSE = 0
|
|
|
|
import sys, string, regex, types, os, marshal, new, __main__
|
|
try:
|
|
import imp # Build on this recent addition
|
|
except ImportError:
|
|
raise ImportError, 'Pkg import module depends on optional "imp" module'
|
|
|
|
from imp import SEARCH_ERROR, PY_SOURCE, PY_COMPILED, C_EXTENSION
|
|
PY_PACKAGE = 4 # In addition to above PY_*
|
|
|
|
modes = {SEARCH_ERROR: 'SEARCH_ERROR',
|
|
PY_SOURCE: 'PY_SOURCE',
|
|
PY_COMPILED: 'PY_COMPILED',
|
|
C_EXTENSION: 'C_EXTENSION',
|
|
PY_PACKAGE: 'PY_PACKAGE'}
|
|
|
|
# sys.stub_modules tracks modules partially loaded modules, ie loaded only
|
|
# incidental to load of nested components.
|
|
|
|
try: sys.stub_modules
|
|
except AttributeError:
|
|
sys.stub_modules = {}
|
|
|
|
# Environment setup - "root" module, '__python__'
|
|
|
|
# Establish root package '__python__' in __main__ and newimp envs.
|
|
|
|
PKG_MAIN_NM = '__main__' # 'pkg/__main__.py' master, if present.
|
|
PKG_NM = '__package__' # Longhand for module's container.
|
|
PKG_SHORT_NM = '__' # Shorthand for module's container.
|
|
PKG_SHORT_NM_LEN = len(PKG_SHORT_NM)
|
|
PKG_PATH = '__pkgpath__' # Var holding package search path,
|
|
# usually just the path of the pkg dir.
|
|
__python__ = __main__
|
|
sys.modules['__python__'] = __python__ # Register as an importable module.
|
|
__python__.__dict__[PKG_PATH] = sys.path
|
|
|
|
origImportFunc = None
|
|
def install():
|
|
"""Install newimp import_module() routine, for package support.
|
|
|
|
newimp.revert() reverts to __import__ routine that was superceded."""
|
|
import __builtin__
|
|
global origImportFunc
|
|
if not origImportFunc:
|
|
try:
|
|
origImportFunc = __builtin__.__import__
|
|
except AttributeError:
|
|
pass
|
|
__builtin__.__import__ = import_module
|
|
print 'Enhanced import functionality installed.'
|
|
def revert():
|
|
"""Revert to original __builtin__.__import__ func, if newimp.install() has
|
|
been executed."""
|
|
if origImportFunc:
|
|
import __builtin__
|
|
__builtin__.__import__ = origImportFunc
|
|
print 'Original import routine back in place.'
|
|
|
|
def import_module(name,
|
|
envLocals=None, envGlobals=None,
|
|
froms=None,
|
|
inPkg=None):
|
|
"""Primary service routine implementing 'import' with package nesting."""
|
|
|
|
# The job is divided into a few distinct steps:
|
|
#
|
|
# - Look for either an already loaded module or a file to be loaded.
|
|
# * if neither loaded module nor prospect file is found, raise an error.
|
|
# - If we have a file, not an already loaded module:
|
|
# - Load the file into a module.
|
|
# - Register the new module and intermediate package stubs.
|
|
# (We have a module at this point...)
|
|
# - Bind requested syms (module or specified 'from' defs) in calling env.
|
|
# - Return the appropriate component.
|
|
|
|
note("import_module: seeking '%s'%s" %
|
|
(name, ((inPkg and ' (in package %s)' % inPkg.__name__) or '')))
|
|
|
|
# We need callers environment dict for local path and resulting module
|
|
# binding.
|
|
if not (envLocals or envGlobals):
|
|
envLocals, envGlobals = exterior()
|
|
|
|
modList = theMod = absNm = container = None
|
|
|
|
# Get module obj if one already established, or else module file if not:
|
|
|
|
if inPkg:
|
|
# We've been invoked with a specific containing package:
|
|
pkg, pkgPath, pkgNm = inPkg, inPkg.__dict__[PKG_PATH], inPkg.__name__
|
|
relNm = name
|
|
absNm = pkgNm + '.' + name
|
|
|
|
elif name[:PKG_SHORT_NM_LEN+1] != PKG_SHORT_NM + '.':
|
|
# name is NOT '__.something' - setup to seek according to specified
|
|
# absolute name.
|
|
pkg = __python__
|
|
pkgPath = sys.path
|
|
absNm = name
|
|
relNm = absNm
|
|
|
|
else:
|
|
# name IS '__.' + something - setup to seek according to relative name,
|
|
# in current package.
|
|
|
|
relNm = name[len(PKG_SHORT_NM)+1:] # Relative portion of name.
|
|
try:
|
|
pkg = envGlobals[PKG_NM] # The immediately containing package.
|
|
pkgPath = pkg.__dict__[PKG_PATH]
|
|
if pkg == __python__: # At outermost package.
|
|
absNm = relNm
|
|
else:
|
|
absNm = (pkg.__name__ + '.' + relNm)
|
|
except KeyError: # Missing package, path, or name.
|
|
note("Can't identify parent package, package name, or pkgpath")
|
|
pass # ==v
|
|
|
|
# Try to find existing module:
|
|
if sys.modules.has_key(absNm):
|
|
note('found ' + absNm + ' already imported')
|
|
theMod = sys.modules[absNm]
|
|
else:
|
|
# Try for builtin or frozen first:
|
|
theMod = imp.init_builtin(absNm)
|
|
if theMod:
|
|
note('found builtin ' + absNm)
|
|
else:
|
|
theMod = imp.init_frozen(absNm)
|
|
if theMod:
|
|
note('found frozen ' + absNm)
|
|
if not theMod:
|
|
if type(pkgPath) == types.StringType:
|
|
pkgPath = [pkgPath]
|
|
modList = find_module(relNm, pkgPath, absNm)
|
|
if not modList:
|
|
raise ImportError, "module '%s' not found" % absNm # ===X
|
|
# We have a list of successively nested files leading to the
|
|
# module, register them as stubs:
|
|
container = register_module_nesting(modList, pkg)
|
|
|
|
# Load from file if necessary and possible:
|
|
modNm, modf, path, ty = modList[-1]
|
|
note('found type ' + modes[ty[2]] + ' - ' + absNm)
|
|
|
|
# Do the load:
|
|
theMod = load_module(absNm, ty[2], modf, inPkg)
|
|
|
|
# Loaded successfully - promote module to full module status:
|
|
register_module(theMod, theMod.__name__, pkgPath, pkg)
|
|
|
|
# Have a loaded module, impose designated components, and return
|
|
# appropriate thing - according to guido:
|
|
# "Note that for "from spam.ham import bacon" your function should
|
|
# return the object denoted by 'spam.ham', while for "import
|
|
# spam.ham" it should return the object denoted by 'spam' -- the
|
|
# STORE instructions following the import statement expect it this
|
|
# way."
|
|
if not froms:
|
|
# Establish the module defs in the importing name space:
|
|
(envLocals or envGlobals)[name] = theMod
|
|
return (container or theMod)
|
|
else:
|
|
# Implement 'from': Populate immediate env with module defs:
|
|
while froms:
|
|
item = froms[0]; del froms[0]
|
|
if item == '*':
|
|
froms = theMod.__dict__.keys() + froms
|
|
else:
|
|
try:
|
|
(envLocals or envGlobals)[item] = theMod.__dict__[item]
|
|
except KeyError:
|
|
raise ImportError, ("name '%s' not found in module %s" %
|
|
(item, theMod.__name__))
|
|
return theMod
|
|
|
|
def unload(module):
|
|
"""Remove registration for a module, so import will do a fresh load."""
|
|
if type(module) == types.ModuleType:
|
|
module = module.__name__
|
|
for m in [sys.modules, sys.stub_modules]:
|
|
try:
|
|
del m[module]
|
|
except KeyError:
|
|
pass
|
|
|
|
def find_module(name, path, absNm=''):
|
|
"""Locate module NAME on PATH. PATH is pathname string or a list of them.
|
|
|
|
Note that up-to-date compiled versions of a module are preferred to plain
|
|
source, and compilation is automatically performed when necessary and
|
|
possible.
|
|
|
|
Returns a list of the tuples returned by 'find_module_file' (cf), one for
|
|
each nested level, deepest last."""
|
|
|
|
checked = [] # For avoiding redundant dir lists.
|
|
|
|
if not absNm: absNm = name
|
|
|
|
# Parse name into list of nested components,
|
|
expNm = string.splitfields(name, '.')
|
|
|
|
for curPath in path:
|
|
|
|
if (type(curPath) != types.StringType) or (curPath in checked):
|
|
# Disregard bogus or already investigated path elements:
|
|
continue # ==^
|
|
else:
|
|
# Register it for subsequent disregard.
|
|
checked.append(curPath)
|
|
|
|
if len(expNm) == 1:
|
|
|
|
# Non-nested module name:
|
|
|
|
got = find_module_file(curPath, absNm)
|
|
if got:
|
|
note('using %s' % got[2], 2)
|
|
return [got] # ===>
|
|
|
|
else:
|
|
|
|
# Composite name specifying nested module:
|
|
|
|
gotList = []; nameAccume = expNm[0]
|
|
|
|
got = find_module_file(curPath, nameAccume)
|
|
if not got: # Continue to next prospective path.
|
|
continue # ==^
|
|
else:
|
|
gotList.append(got)
|
|
nm, file, fullPath, ty = got
|
|
|
|
# Work on successively nested components:
|
|
for component in expNm[1:]:
|
|
# 'ty'pe of containing component must be package:
|
|
if ty[2] != PY_PACKAGE:
|
|
gotList, got = [], None
|
|
break # ==v^
|
|
if nameAccume:
|
|
nameAccume = nameAccume + '.' + component
|
|
else:
|
|
nameAccume = component
|
|
got = find_module_file(fullPath, nameAccume)
|
|
if got:
|
|
gotList.append(got)
|
|
# ** have to return the *full* name here:
|
|
nm, file, fullPath, ty = got
|
|
else:
|
|
# Clear state vars:
|
|
gotList, got, nameAccume = [], None, ''
|
|
break # ==v^
|
|
# Found nesting all the way to the specified tip:
|
|
if got:
|
|
return gotList # ===>
|
|
|
|
# Failed.
|
|
return None
|
|
|
|
def find_module_file(pathNm, modname):
|
|
"""Find module file given dir PATHNAME and module NAME.
|
|
|
|
If successful, returns quadruple consisting of a mod name, file object,
|
|
PATHNAME for the found file, and a description triple as contained in the
|
|
list returned by get_suffixes.
|
|
|
|
Otherwise, returns None.
|
|
|
|
Note that up-to-date compiled versions of a module are preferred to plain
|
|
source, and compilation is automatically performed, when necessary and
|
|
possible."""
|
|
|
|
relNm = string.splitfields(modname,'.')[-1]
|
|
|
|
if pathNm[-1] != '/': pathNm = pathNm + '/'
|
|
|
|
for suff, mode, ty in get_suffixes():
|
|
note('trying ' + pathNm + relNm + suff + '...', 3)
|
|
fullPath = pathNm + relNm + suff
|
|
try:
|
|
modf = open(fullPath, mode)
|
|
except IOError:
|
|
# ?? Skip unreadable ones.
|
|
continue # ==^
|
|
|
|
if ty == PY_PACKAGE:
|
|
# Enforce directory characteristic:
|
|
if not os.path.isdir(fullPath):
|
|
note('Skipping non-dir match ' + fullPath)
|
|
continue # ==^
|
|
else:
|
|
return (modname, modf, fullPath, (suff, mode, ty)) # ===>
|
|
|
|
|
|
elif ty == PY_SOURCE:
|
|
# Try for a compiled version:
|
|
note('found source ' + fullPath, 2)
|
|
pyc = fullPath + 'c' # Sadly, we're presuming '.py' suff.
|
|
if (not os.path.exists(pyc) or
|
|
(os.stat(fullPath)[8] > os.stat(pyc)[8])):
|
|
# Try to compile:
|
|
pyc = compile_source(fullPath, modf)
|
|
if pyc and (os.stat(fullPath)[8] < os.stat(pyc)[8]):
|
|
# Either pyc was already newer or we just made it so; in either
|
|
# case it's what we crave:
|
|
return (modname, open(pyc, 'rb'), pyc, # ===>
|
|
('.pyc', 'rb', PY_COMPILED))
|
|
# Couldn't get a compiled version - return the source:
|
|
return (modname, modf, fullPath, (suff, mode, ty)) # ===>
|
|
|
|
elif ty == PY_COMPILED:
|
|
# Make sure it is current, trying to compile if necessary, and
|
|
# prefer source failing that:
|
|
note('found compiled ' + fullPath, 2)
|
|
py = fullPath[:-1] # Sadly again, presuming '.pyc' suff.
|
|
if not os.path.exists(py):
|
|
note('found pyc sans py: ' + fullPath)
|
|
return (modname, modf, fullPath, (suff, mode, ty)) # ===>
|
|
elif (os.stat(py)[8] > os.stat(fullPath)[8]):
|
|
note('forced to try compiling: ' + py)
|
|
pyc = compile_source(py, modf)
|
|
if pyc:
|
|
return (modname, modf, fullPath, (suff, mode, ty)) # ===>
|
|
else:
|
|
note('failed compile - must use more recent .py')
|
|
return (modname, # ===>
|
|
open(py, 'r'), py, ('.py', 'r', PY_SOURCE))
|
|
else:
|
|
return (modname, modf, fullPath, (suff, mode, ty)) # ===>
|
|
|
|
elif ty == C_EXTENSION:
|
|
note('found extension ' + fullPath, 2)
|
|
return (modname, modf, fullPath, (suff, mode, ty)) # ===>
|
|
|
|
else:
|
|
raise SystemError, 'Unanticipated (new?) module type encountered'
|
|
|
|
return None
|
|
|
|
|
|
def load_module(name, ty, theFile, fromMod=None):
|
|
"""Load module NAME, type TYPE, from file FILE.
|
|
|
|
Optional arg fromMod indicated the module from which the load is being done
|
|
- necessary for detecting import of __ from a package's __main__ module.
|
|
|
|
Return the populated module object."""
|
|
|
|
# Note: we mint and register intermediate package directories, as necessary
|
|
|
|
# Determine packagepath extension:
|
|
|
|
# Establish the module object in question:
|
|
theMod = procure_module(name)
|
|
nameTail = string.splitfields(name, '.')[-1]
|
|
thePath = theFile.name
|
|
|
|
if ty == PY_SOURCE:
|
|
exec_into(theFile, theMod, theFile.name)
|
|
|
|
elif ty == PY_COMPILED:
|
|
pyc = open(theFile.name, 'rb').read()
|
|
if pyc[0:4] != imp.get_magic():
|
|
raise ImportError, 'bad magic number: ' + theFile.name # ===>
|
|
code = marshal.loads(pyc[8:])
|
|
exec_into(code, theMod, theFile.name)
|
|
|
|
elif ty == C_EXTENSION:
|
|
try:
|
|
theMod = imp.load_dynamic(nameTail, thePath, theFile)
|
|
except:
|
|
# ?? Ok to embellish the error message?
|
|
raise sys.exc_type, ('%s (from %s)' %
|
|
(str(sys.exc_value), theFile.name))
|
|
|
|
elif ty == PY_PACKAGE:
|
|
# Load constituents:
|
|
if (os.path.exists(thePath + '/' + PKG_MAIN_NM) and
|
|
# pkg has a __main__, and this import not already from __main__, so
|
|
# __main__ can 'import __', or even better, 'from __ import *'
|
|
((theMod.__name__ != PKG_MAIN_NM) and (fromMod.__ == theMod))):
|
|
exec_into(thePath + '/' + PKG_MAIN_NM, theMod, theFile.name)
|
|
else:
|
|
# ... or else recursively load constituent modules.
|
|
prospects = mod_prospects(thePath)
|
|
for item in prospects:
|
|
theMod.__dict__[item] = import_module(item,
|
|
theMod.__dict__,
|
|
theMod.__dict__,
|
|
None,
|
|
theMod)
|
|
|
|
else:
|
|
raise ImportError, 'Unimplemented import type: %s' % ty # ===>
|
|
|
|
return theMod
|
|
|
|
def exec_into(obj, module, path):
|
|
"""Helper for load_module, execfile/exec path or code OBJ within MODULE."""
|
|
|
|
# This depends on ability of exec and execfile to mutilate, erhm, mutate
|
|
# the __dict__ of a module. It will not work if/when this becomes
|
|
# disallowed, as it is for normal assignments.
|
|
|
|
try:
|
|
if type(obj) == types.FileType:
|
|
execfile(path, module.__dict__, module.__dict__)
|
|
elif type(obj) in [types.CodeType, types.StringType]:
|
|
exec obj in module.__dict__, module.__dict__
|
|
except:
|
|
# ?? Ok to embellish the error message?
|
|
raise sys.exc_type, ('%s (from %s)' %
|
|
(str(sys.exc_value), path))
|
|
|
|
|
|
def mod_prospects(path):
|
|
"""Return a list of prospective modules within directory PATH.
|
|
|
|
We actually return the distinct names resulting from stripping the dir
|
|
entries (excluding '.' and '..') of their suffixes (as represented by
|
|
'get_suffixes').
|
|
|
|
(Note that matches for the PY_PACKAGE type with null suffix are
|
|
implicitly constrained to be directories.)"""
|
|
|
|
# We actually strip the longest matching suffixes, so eg 'dbmmodule.so'
|
|
# mates with 'module.so' rather than '.so'.
|
|
|
|
dirList = os.listdir(path)
|
|
excludes = ['.', '..']
|
|
sortedSuffs = sorted_suffixes()
|
|
entries = []
|
|
for item in dirList:
|
|
if item in excludes: continue # ==^
|
|
for suff in sortedSuffs:
|
|
sub = -1 * len(suff)
|
|
if sub == 0:
|
|
if os.path.isdir(os.path.join(path, item)):
|
|
entries.append(item)
|
|
elif item[sub:] == suff:
|
|
it = item[:sub]
|
|
if not it in entries:
|
|
entries.append(it)
|
|
break # ==v^
|
|
return entries
|
|
|
|
|
|
|
|
def procure_module(name):
|
|
"""Return an established or else new module object having NAME.
|
|
|
|
First checks sys.modules, then sys.stub_modules."""
|
|
|
|
if sys.modules.has_key(name):
|
|
it = sys.modules[name]
|
|
elif sys.stub_modules.has_key(name):
|
|
it = sys.stub_modules[name]
|
|
else:
|
|
it = new.module(name)
|
|
return it # ===>
|
|
|
|
def register_module_nesting(modList, pkg):
|
|
"""Given a find_module()-style NESTING and a parent PACKAGE, register
|
|
components as stub modules."""
|
|
container = None
|
|
for stubModNm, stubModF, stubPath, stubTy in modList:
|
|
relStubNm = string.splitfields(stubModNm, '.')[-1]
|
|
if sys.modules.has_key(stubModNm):
|
|
# Nestle in containing package:
|
|
stubMod = sys.modules[stubModNm]
|
|
pkg.__dict__[relStubNm] = stubMod
|
|
pkg = stubMod # will be parent for next in sequence.
|
|
elif sys.stub_modules.has_key(stubModNm):
|
|
stubMod = sys.stub_modules[stubModNm]
|
|
pkg.__dict__[relStubNm] = stubMod
|
|
pkg = stubMod
|
|
else:
|
|
stubMod = procure_module(stubModNm)
|
|
# Register as a stub:
|
|
register_module(stubMod, stubModNm, stubPath, pkg, 1)
|
|
pkg.__dict__[relStubNm] = stubMod
|
|
pkg = stubMod
|
|
if not container:
|
|
container = stubMod
|
|
return container
|
|
|
|
def register_module(theMod, name, path, package, stub=0):
|
|
"""Properly register MODULE, w/ name, path, package, opt, as stub."""
|
|
|
|
if stub:
|
|
sys.stub_modules[name] = theMod
|
|
else:
|
|
sys.modules[name] = theMod
|
|
if sys.stub_modules.has_key(name):
|
|
del sys.stub_modules[name]
|
|
theMod.__ = theMod.__dict__[PKG_NM] = package
|
|
theMod.__dict__[PKG_PATH] = path
|
|
|
|
|
|
def compile_source(sourcePath, sourceFile):
|
|
"""Given python code source path and file obj, Create a compiled version.
|
|
|
|
Return path of compiled version, or None if file creation is not
|
|
successful. (Compilation errors themselves are passed without restraint.)
|
|
|
|
This is an import-private interface, and not well-behaved for general use.
|
|
|
|
In particular, we presume the validity of the sourcePath, and that it
|
|
includes a '.py' extension."""
|
|
|
|
compiledPath = sourcePath[:-3] + '.pyc'
|
|
try:
|
|
compiledFile = open(compiledPath, 'wb')
|
|
except IOError:
|
|
note("write permission denied to " + compiledPath)
|
|
return None
|
|
mtime = os.stat(sourcePath)[8]
|
|
sourceFile.seek(0) # rewind
|
|
try:
|
|
compiledFile.write(imp.get_magic()) # compiled magic number
|
|
compiledFile.seek(8, 0) # mtime space holder
|
|
# We let compilation errors go their own way...
|
|
compiled = compile(sourceFile.read(), sourcePath, 'exec')
|
|
marshal.dump(compiled, compiledFile) # write the code obj
|
|
compiledFile.seek(4, 0) # position for mtime
|
|
compiledFile.write(marshal.dumps(mtime)[1:]) # register mtime
|
|
compiledFile.flush()
|
|
compiledFile.close()
|
|
return compiledPath
|
|
except IOError:
|
|
return None
|
|
|
|
|
|
def PathExtension(locals, globals): # Probably obsolete.
|
|
"""Determine import search path extension vis-a-vis __pkgpath__ entries.
|
|
|
|
local dict __pkgpath__ will preceed global dict __pkgpath__."""
|
|
|
|
pathadd = []
|
|
if globals and globals.has_key(PKG_PATH):
|
|
pathadd = PrependPath(pathadd, globals[PKG_PATH], 'global')
|
|
if locals and locals.has_key(PKG_PATH):
|
|
pathadd = PrependPath(pathadd, locals[PKG_PATH], 'local')
|
|
if pathadd:
|
|
note(PKG_PATH + ' extension: ' + pathadd)
|
|
return pathadd
|
|
|
|
def PrependPath(path, pre, whence): # Probably obsolete
|
|
"""Return copy of PATH list with string or list-of-strings PRE prepended.
|
|
|
|
If PRE is neither a string nor list-of-strings, print warning that
|
|
locality WHENCE has malformed value."""
|
|
|
|
# (There is probably a better way to handle malformed PREs, but raising an
|
|
# error seems too severe - in that case, a bad setting for
|
|
# sys.__pkgpath__ would prevent any imports!)
|
|
|
|
if type(pre) == types.StringType: return [pre] + path[:] # ===>
|
|
elif type(pre) == types.ListType: return pre + path[:] # ===>
|
|
else:
|
|
print "**Ignoring '%s' bad %s value**" % (whence, PKG_PATH)
|
|
return path # ===>
|
|
|
|
got_suffixes = None
|
|
def get_suffixes():
|
|
"""Produce a list of triples, each describing a type of import file.
|
|
|
|
Triples have the form '(SUFFIX, MODE, TYPE)', where:
|
|
|
|
SUFFIX is a string found appended to a module name to make a filename for
|
|
that type of import file.
|
|
|
|
MODE is the mode string to be passed to the built-in 'open' function - "r"
|
|
for text files, "rb" for binary.
|
|
|
|
TYPE is the file type:
|
|
|
|
PY_SOURCE: python source code,
|
|
PY_COMPILED: byte-compiled python source,
|
|
C_EXTENSION: compiled-code object file,
|
|
PY_PACKAGE: python library directory, or
|
|
SEARCH_ERROR: no module found. """
|
|
|
|
# Note: sorted_suffixes() depends on this function's value being invariant.
|
|
# sorted_suffixes() must be revised if this becomes untrue.
|
|
|
|
global got_suffixes
|
|
|
|
if got_suffixes:
|
|
return got_suffixes
|
|
else:
|
|
# Ensure that the .pyc suffix precedes the .py:
|
|
got_suffixes = [('', 'r', PY_PACKAGE)]
|
|
py = pyc = None
|
|
for suff in imp.get_suffixes():
|
|
if suff[0] == '.py':
|
|
py = suff
|
|
elif suff[0] == '.pyc':
|
|
pyc = suff
|
|
else:
|
|
got_suffixes.append(suff)
|
|
got_suffixes.append(pyc)
|
|
got_suffixes.append(py)
|
|
return got_suffixes
|
|
|
|
|
|
sortedSuffs = [] # State vars for sorted_suffixes(). Go
|
|
def sorted_suffixes():
|
|
"""Helper function ~efficiently~ tracks sorted list of module suffixes."""
|
|
|
|
# Produce sortedSuffs once - this presumes that get_suffixes does not
|
|
# change from call to call during a python session. Needs to be
|
|
# corrected if that becomes no longer true.
|
|
|
|
global sortedsuffs
|
|
if not sortedSuffs: # do compute only the "first" time
|
|
for item in get_suffixes():
|
|
sortedSuffs.append(item[0])
|
|
# Sort them in descending order:
|
|
sortedSuffs.sort(lambda x, y: (((len(x) > len(y)) and 1) or
|
|
((len(x) < len(y)) and -1)))
|
|
sortedSuffs.reverse()
|
|
return sortedSuffs
|
|
|
|
|
|
# exterior(): Utility routine, obtain local and global dicts of environment
|
|
# containing/outside the callers environment, ie that of the
|
|
# caller's caller. Routines can use exterior() to determine the
|
|
# environment from which they were called.
|
|
|
|
def exterior():
|
|
"""Return dyad containing locals and globals of caller's caller.
|
|
|
|
Locals will be None if same as globals, ie env is global env."""
|
|
|
|
bogus = 'bogus' # A locally usable exception
|
|
try: raise bogus # Force an exception object
|
|
except bogus:
|
|
at = sys.exc_traceback.tb_frame.f_back # The external frame.
|
|
if at.f_back: at = at.f_back # And further, if any.
|
|
globals, locals = at.f_globals, at.f_locals
|
|
if locals == globals: # Exterior is global?
|
|
locals = None
|
|
return (locals, globals)
|
|
|
|
#########################################################################
|
|
# TESTING FACILITIES #
|
|
|
|
def note(msg, threshold=1):
|
|
if VERBOSE >= threshold: sys.stderr.write('(import: ' + msg + ')\n')
|
|
|
|
class TestDirHier:
|
|
"""Populate a transient directory hierarchy according to a definition
|
|
template - so we can create package/module hierarchies with which to
|
|
exercise the new import facilities..."""
|
|
|
|
def __init__(self, template, where='/var/tmp'):
|
|
"""Establish a dir hierarchy, according to TEMPLATE, that will be
|
|
deleted upon deletion of this object (or deliberate invocation of the
|
|
__del__ method)."""
|
|
self.PKG_NM = 'tdh_'
|
|
rev = 0
|
|
while os.path.exists(os.path.join(where, self.PKG_NM+str(rev))):
|
|
rev = rev + 1
|
|
sys.exc_traceback = None # Ensure Discard of try/except obj ref
|
|
self.PKG_NM = self.PKG_NM + str(rev)
|
|
self.root = os.path.join(where, self.PKG_NM)
|
|
self.createDir(self.root)
|
|
self.add(template)
|
|
|
|
def __del__(self):
|
|
"""Cleanup the test hierarchy."""
|
|
self.remove()
|
|
def add(self, template, root=None):
|
|
"""Populate directory according to template dictionary.
|
|
|
|
Keys indicate file names, possibly directories themselves.
|
|
|
|
String values dictate contents of flat files.
|
|
|
|
Dictionary values dictate recursively embedded dictionary templates."""
|
|
if root == None: root = self.root
|
|
for key, val in template.items():
|
|
name = os.path.join(root, key)
|
|
if type(val) == types.StringType: # flat file
|
|
self.createFile(name, val)
|
|
elif type(val) == types.DictionaryType: # embedded dir
|
|
self.createDir(name)
|
|
self.add(val, name)
|
|
else:
|
|
raise ValueError, 'invalid file-value type, %s' % type(val)
|
|
def remove(self, name=''):
|
|
"""Dispose of the NAME (or keys in dictionary), using 'rm -r'."""
|
|
name = os.path.join(self.root, name)
|
|
sys.exc_traceback = None # Ensure Discard of try/except obj ref
|
|
if os.path.exists(name):
|
|
print '(TestDirHier: deleting %s)' % name
|
|
os.system('rm -r ' + name)
|
|
else:
|
|
raise IOError, "can't remove non-existent " + name
|
|
def createFile(self, name, contents=None):
|
|
"""Establish file NAME with CONTENTS.
|
|
|
|
If no contents specfied, contents will be 'print NAME'."""
|
|
f = open(name, 'w')
|
|
if not contents:
|
|
f.write("print '" + name + "'\n")
|
|
else:
|
|
f.write(contents)
|
|
f.close
|
|
def createDir(self, name):
|
|
"""Create dir with NAME."""
|
|
return os.mkdir(name, 0755)
|
|
|
|
skipToTest = 0
|
|
atTest = 1
|
|
def testExec(msg, execList, locals, globals):
|
|
global skipToTest, atTest
|
|
print 'Import Test:', '(' + str(atTest) + ')', msg, '...'
|
|
atTest = atTest + 1
|
|
if skipToTest > (atTest - 1):
|
|
print ' ... skipping til test', skipToTest
|
|
return
|
|
else:
|
|
print ''
|
|
for stmt in execList:
|
|
exec stmt in locals, globals
|
|
|
|
def test(number=0):
|
|
"""Exercise import functionality, creating a transient dir hierarchy for
|
|
the purpose.
|
|
|
|
We actually install the new import functionality, temporarily, resuming the
|
|
existing function on cleanup."""
|
|
|
|
import __builtin__
|
|
|
|
global skipToTest, atTest
|
|
skipToTest = number
|
|
hier = None
|
|
|
|
def confPkgVars(mod, locals, globals):
|
|
if not sys.modules.has_key(mod):
|
|
print 'import test: missing module "%s"' % mod
|
|
else:
|
|
modMod = sys.modules[mod]
|
|
if not modMod.__dict__.has_key(PKG_SHORT_NM):
|
|
print ('import test: module "%s" missing %s pkg shorthand' %
|
|
(mod, PKG_SHORT_NM))
|
|
if not modMod.__dict__.has_key(PKG_PATH):
|
|
print ('import test: module "%s" missing %s package path' %
|
|
(mod, PKG_PATH))
|
|
def unloadFull(mod):
|
|
"""Unload module and offspring submodules, if any."""
|
|
modMod = ''
|
|
if type(mod) == types.StringType:
|
|
modNm = mod
|
|
elif type(mod) == types.ModuleType:
|
|
modNm = modMod.__name__
|
|
for subj in sys.modules.keys() + sys.stub_modules.keys():
|
|
if subj[0:len(modNm)] == modNm:
|
|
unload(subj)
|
|
|
|
# First, get the globals and locals to pass to our testExec():
|
|
exec 'import ' + __name__
|
|
globals, locals = eval(__name__ + '.__dict__'), vars()
|
|
|
|
try:
|
|
__main__.testMods
|
|
except AttributeError:
|
|
__main__.testMods = []
|
|
testMods = __main__.testMods
|
|
|
|
|
|
# Install the newimp routines, within a try/finally:
|
|
try:
|
|
sys.exc_traceback = None
|
|
wasImport = __builtin__.__import__ # Stash default
|
|
wasPath = sys.path
|
|
except AttributeError:
|
|
wasImport = None
|
|
try:
|
|
hiers = []; modules = []
|
|
global VERBOSE
|
|
wasVerbose, VERBOSE = VERBOSE, 2
|
|
__builtin__.__import__ = import_module # Install new version
|
|
|
|
if testMods: # Clear out imports from previous tests
|
|
for m in testMods[:]:
|
|
unloadFull(m)
|
|
testMods.remove(m)
|
|
|
|
testExec("already imported module: %s" % sys.modules.keys()[0],
|
|
['import ' + sys.modules.keys()[0]],
|
|
locals, globals)
|
|
try:
|
|
no_sirree = 'no_sirree_does_not_exist'
|
|
testExec("non-existent module: %s" % no_sirree,
|
|
['import ' + no_sirree],
|
|
locals, globals)
|
|
except ImportError:
|
|
testExec("ok", ['pass'], locals, globals)
|
|
got = None
|
|
for mod in ['Complex', 'UserDict', 'UserList', 'calendar',
|
|
'cmd', 'dis', 'mailbox', 'profile', 'random', 'rfc822']:
|
|
if not (mod in sys.modules.keys()):
|
|
got = mod
|
|
break # ==v
|
|
if got:
|
|
testExec("not-yet loaded module: %s" % mod,
|
|
['import ' + mod, 'modules.append(got)'],
|
|
locals, globals)
|
|
else:
|
|
print "Import Test: couldn't find unimported module from list"
|
|
|
|
# Now some package stuff.
|
|
|
|
# First change the path to include our temp dir, copying so the
|
|
# addition can be revoked on cleanup in the finally, below:
|
|
sys.path = ['/var/tmp'] + sys.path[:]
|
|
# Now create a trivial package:
|
|
stmts = ["hier1 = TestDirHier({'a.py': 'print \"a.py executing\"'})",
|
|
"hiers.append(hier1)",
|
|
"root = hier1.PKG_NM",
|
|
"exec 'import ' + root",
|
|
"testMods.append(root)",
|
|
"confPkgVars(sys.modules[root].__name__, locals, globals)",
|
|
"confPkgVars(sys.modules[root].__name__+'.a',locals,globals)"]
|
|
testExec("trivial package, with one module, a.py",
|
|
stmts, locals, globals)
|
|
# Slightly less trivial package - reference to '__':
|
|
stmts = [("hier2 = TestDirHier({'ref.py': 'print \"Pkg __:\", __'})"),
|
|
"root = hier2.PKG_NM",
|
|
"hiers.append(hier2)",
|
|
"exec 'import ' + root",
|
|
"testMods.append(root)"]
|
|
testExec("trivial package, with module that has pkg shorthand ref",
|
|
stmts, locals, globals)
|
|
# Nested package, plus '__' references:
|
|
|
|
complexTemplate = {'ref.py': 'print "ref.py loading..."',
|
|
'suite': {'s1.py': 'print "s1.py, in pkg:", __',
|
|
'subsuite': {'sub1.py':
|
|
'print "sub1.py"'}}}
|
|
stmts = [('print """%s\n%s\n%s\n%s\n%s\n%s"""' %
|
|
('.../',
|
|
' ref.py\t\t\t"ref.py loading..."',
|
|
' suite/',
|
|
' s1.py \t\t"s1.py, in pkg: xxxx.suite"',
|
|
' subsuite/',
|
|
' sub1.py "sub1.py" ')),
|
|
"hier3 = TestDirHier(complexTemplate)",
|
|
"root = hier3.PKG_NM",
|
|
"hiers.append(hier3)",
|
|
"exec 'import ' + root",
|
|
"testMods.append(root)"]
|
|
testExec("Significantly nestled package:",
|
|
stmts, locals, globals)
|
|
|
|
# Now try to do an embedded sibling import, using '__' shorthand -
|
|
# alter our complexTemplate for a new dirHier:
|
|
complexTemplate['suite']['s1.py'] = 'import __.subsuite'
|
|
stmts = ["hier4 = TestDirHier(complexTemplate)",
|
|
"root = hier4.PKG_NM",
|
|
"testMods.append(root)",
|
|
"hiers.append(hier4)",
|
|
"exec 'import %s.suite.s1' % root",
|
|
"testMods.append(root)"]
|
|
testExec("Similar structure, but suite/s1.py imports '__.subsuite'",
|
|
stmts, locals, globals)
|
|
|
|
sys.exc_traceback = None # Signify clean conclusion.
|
|
|
|
finally:
|
|
if sys.exc_traceback:
|
|
print ' ** Import test FAILURE... cleanup.'
|
|
else:
|
|
print ' Import test SUCCESS... cleanup'
|
|
VERBOSE = wasVerbose
|
|
skipToTest = 0
|
|
atTest = 1
|
|
sys.path = wasPath
|
|
for h in hiers: h.remove(); del h # Dispose of test directories
|
|
if wasImport: # Resurrect prior routine
|
|
__builtin__.__import__ = wasImport
|
|
else:
|
|
del __builtin__.__import__
|
|
|
|
if __name__ == '__main__':
|
|
test()
|