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

Source Code for Module pyamf.util.imports

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