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