Package pyamf :: Package util :: Module imports
[hide private]
[frames] | no frames]

Source Code for Module pyamf.util.imports

  1  # Copyright (c) 2007-2009 The PyAMF Project. 
  2  # See LICENSE for details. 
  3   
  4  """ 
  5  Tools for doing dynamic imports 
  6   
  7  This module has been borrowed from the Importing package. 
  8   
  9  @see: U{http://pypi.python.org/pypi/Importing} 
 10  @see: U{http://peak.telecommunity.com/DevCenter/Importing} 
 11   
 12  Original author: U{Phillip J. Eby<peak@eby-sarna.com>} 
 13   
 14  @since: 0.3.0 
 15  """ 
 16   
 17  __all__ = [ 
 18      'lazyModule', 'joinPath', 'whenImported', 'getModuleHooks', 
 19  ] 
 20   
 21  import sys, os.path 
 22  from types import ModuleType 
 23   
 24  postLoadHooks = {} 
 25  loadedModules = [] 
 26   
 27  try: 
 28      from imp import find_module 
 29   
 30      # google app engine requires this because it checks to ensure that the 
 31      # find_module function is functioning at least basically 
 32      # most appengine patchers just stub the function 
 33      find_module('pyamf.util.imports') 
 34  except ImportError: 
35 - def find_module(subname, path=None):
36 # the dev_appserver freaks out if you have pyc, pyo in here as 37 # we're hooking pyamf.amf0 and pyamf.amf3 in the gae adapter and 38 # monkey-patching it. It rightly complains as the byte-compiled module 39 # is different to the 'final' module. 40 PY_EXT = ('.py',) 41 42 if path is None: 43 path = sys.path 44 45 for p in path: 46 py = os.path.join(p, subname) 47 48 for full in PY_EXT: 49 full = py + full 50 51 if os.path.exists(full): 52 return open(full), full, None 53 54 py = os.path.join(p, subname, '__init__') 55 56 for full in PY_EXT: 57 full = py + full 58 59 if os.path.exists(full): 60 return None, os.path.join(p, subname), None 61 62 raise ImportError('No module named %s' % subname)
63
64 -class SubModuleLoadHook(object):
65 - def __init__(self, parent, child, hook, *args, **kwargs):
66 self.parent = parent 67 self.child = child 68 self.hook = hook 69 self.args = args 70 self.kwargs = kwargs
71
72 - def __eq__(self, other):
73 if not isinstance(other, SubModuleLoadHook): 74 return False 75 76 return self.parent == other.parent and self.child == other.child
77
78 - def __call__(self, module):
79 return self.hook(*self.args, **self.kwargs)
80
81 -class AlreadyRead(Exception):
82 pass
83
84 -class LazyModule(ModuleType):
85 __slots__ = () 86 __reserved_attrs__ = ('__name__', '__file__', '__path__') 87
88 - def __init__(self, name, file, path=None):
89 ModuleType.__setattr__(self, '__name__', name) 90 ModuleType.__setattr__(self, '__file__', file) 91 92 if path is not None: 93 ModuleType.__setattr__(self, '__path__', path)
94
95 - def __getattribute__(self, attr):
96 if attr not in LazyModule.__reserved_attrs__: 97 _loadModule(self) 98 99 return ModuleType.__getattribute__(self, attr)
100
101 - def __setattr__(self, attr, value):
102 if attr not in LazyModule.__reserved_attrs__: 103 _loadModule(self) 104 105 return ModuleType.__setattr__(self, attr, value)
106
107 -def _loadModule(module):
108 if _isLazy(module) and module not in loadedModules: 109 _loadAndRunHooks(module)
110
111 -def joinPath(modname, relativePath):
112 """ 113 Adjust a module name by a '/'-separated, relative or absolute path 114 """ 115 module = modname.split('.') 116 117 for p in relativePath.split('/'): 118 if p == '..': 119 module.pop() 120 elif not p: 121 module = [] 122 elif p != '.': 123 module.append(p) 124 125 return '.'.join(module)
126
127 -def lazyModule(modname, relativePath=None):
128 """ 129 Return module 'modname', but with its contents loaded "on demand" 130 131 This function returns 'sys.modules[modname]', if present. Otherwise 132 it creates a 'LazyModule' object for the specified module, caches it 133 in 'sys.modules', and returns it. 134 135 'LazyModule' is a subclass of the standard Python module type, that 136 remains empty until an attempt is made to access one of its 137 attributes. At that moment, the module is loaded into memory, and 138 any hooks that were defined via 'whenImported()' are invoked. 139 140 Note that calling 'lazyModule' with the name of a non-existent or 141 unimportable module will delay the 'ImportError' until the moment 142 access is attempted. The 'ImportError' will occur every time an 143 attribute access is attempted, until the problem is corrected. 144 145 This function also takes an optional second parameter, 'relativePath', 146 which will be interpreted as a '/'-separated path string relative to 147 'modname'. If a 'relativePath' is supplied, the module found by 148 traversing the path will be loaded instead of 'modname'. In the path, 149 '.' refers to the current module, and '..' to the current module's 150 parent. For example:: 151 152 fooBaz = lazyModule('foo.bar','../baz') 153 154 will return the module 'foo.baz'. The main use of the 'relativePath' 155 feature is to allow relative imports in modules that are intended for 156 use with module inheritance. Where an absolute import would be carried 157 over as-is into the inheriting module, an import relative to '__name__' 158 will be relative to the inheriting module, e.g.:: 159 160 something = lazyModule(__name__,'../path/to/something') 161 162 The above code will have different results in each module that inherits 163 it. 164 165 (Note: 'relativePath' can also be an absolute path (starting with '/'); 166 this is mainly useful for module '__bases__' lists.) 167 """ 168 if relativePath: 169 modname = joinPath(modname, relativePath) 170 171 if modname not in sys.modules: 172 file_name = path = None 173 174 if '.' in modname: 175 splitpos = modname.rindex('.') 176 177 parent = sys.modules[modname[:splitpos]] 178 file_name = find_module(modname[splitpos + 1:], parent.__path__)[1] 179 else: 180 file_name = find_module(modname)[1] 181 182 if os.path.isdir(file_name): 183 path = [file_name] 184 py = os.path.join(file_name, '__init__') 185 186 for full in ('.pyo', '.pyc', '.py'): 187 full = py + full 188 189 if os.path.exists(full): 190 break 191 else: 192 raise ImportError('No module name %d' % modname) 193 194 file_name = full 195 196 getModuleHooks(modname) # force an empty hook list into existence 197 sys.modules[modname] = LazyModule(modname, file_name, path) 198 199 if '.' in modname: 200 # ensure parent module/package is in sys.modules 201 # and parent.modname=module, as soon as the parent is imported 202 203 splitpos = modname.rindex('.') 204 205 whenImported( 206 modname[:splitpos], 207 lambda m: setattr(m, modname[splitpos + 1:], sys.modules[modname]) 208 ) 209 210 return sys.modules[modname]
211
212 -def _isLazy(module):
213 """ 214 Checks to see if the supplied C{module} is lazy 215 """ 216 if module.__name__ not in postLoadHooks.keys(): 217 return False 218 219 return postLoadHooks[module.__name__] is not None
220
221 -def _loadAndRunHooks(module):
222 """ 223 Load an unactivated "lazy" module object 224 """ 225 if _isLazy(module): # don't reload if already loaded! 226 loadedModules.append(module) 227 reload(module) 228 229 try: 230 for hook in getModuleHooks(module.__name__): 231 hook(module) 232 finally: 233 # Ensure hooks are not called again, even if they fail 234 postLoadHooks[module.__name__] = None
235
236 -def getModuleHooks(moduleName):
237 """ 238 Get list of hooks for 'moduleName'; error if module already loaded 239 """ 240 hooks = postLoadHooks.setdefault(moduleName, []) 241 242 if hooks is None: 243 raise AlreadyRead("Module already imported", moduleName) 244 245 return hooks
246
247 -def _setModuleHook(moduleName, hook):
248 if moduleName in sys.modules and postLoadHooks.get(moduleName) is None: 249 # Module is already imported/loaded, just call the hook 250 module = sys.modules[moduleName] 251 hook(module) 252 253 return module 254 255 getModuleHooks(moduleName).append(hook) 256 257 return lazyModule(moduleName)
258
259 -def whenImported(moduleName, hook):
260 """ 261 Call 'hook(module)' when module named 'moduleName' is first used 262 263 'hook' must accept one argument: the module object named by 'moduleName', 264 which must be a fully qualified (i.e. absolute) module name. The hook 265 should not raise any exceptions, or it may prevent later hooks from 266 running. 267 268 If the module has already been imported normally, 'hook(module)' is 269 called immediately, and the module object is returned from this function. 270 If the module has not been imported, or has only been imported lazily, 271 then the hook is called when the module is first used, and a lazy import 272 of the module is returned from this function. If the module was imported 273 lazily and used before calling this function, the hook is called 274 immediately, and the loaded module is returned from this function. 275 276 Note that using this function implies a possible lazy import of the 277 specified module, and lazy importing means that any 'ImportError' will be 278 deferred until the module is used. 279 """ 280 if '.' in moduleName: 281 # If parent is not yet imported, delay hook installation until the 282 # parent is imported. 283 splitpos = moduleName.rindex('.') 284 285 sub_hook = SubModuleLoadHook(moduleName[:splitpos], 286 moduleName[splitpos + 1:], _setModuleHook, moduleName, hook) 287 288 if moduleName[:splitpos] not in postLoadHooks.keys(): 289 whenImported(moduleName[:splitpos], sub_hook) 290 elif postLoadHooks[moduleName[:splitpos]] is None: 291 whenImported(moduleName[:splitpos], sub_hook) 292 elif sub_hook not in postLoadHooks[moduleName[:splitpos]]: 293 whenImported(moduleName[:splitpos], sub_hook) 294 else: 295 postLoadHooks[moduleName[:splitpos]].append(sub_hook) 296 else: 297 return _setModuleHook(moduleName, hook)
298