1""" 2Provides an emulator/replacement for Python's standard import system. 3 4@@TR: Be warned that Import Hooks are in the deepest, darkest corner of Python's 5jungle. If you need to start hacking with this, be prepared to get lost for a 6while. Also note, this module predates the newstyle import hooks in Python 2.3 7http://www.python.org/peps/pep-0302.html. 8 9 10This is a hacked/documented version of Gordon McMillan's iu.py. I have: 11 12 - made it a little less terse 13 14 - added docstrings and explanatations 15 16 - standardized the variable naming scheme 17 18 - reorganized the code layout to enhance readability 19 20""" 21 22import sys 23import imp 24import marshal 25 26_installed = False 27 28# _globalOwnerTypes is defined at the bottom of this file 29 30_os_stat = _os_path_join = _os_getcwd = _os_path_dirname = None 31 32################################################## 33## FUNCTIONS 34 35def _os_bootstrap(): 36 """Set up 'os' module replacement functions for use during import bootstrap.""" 37 38 names = sys.builtin_module_names 39 40 join = dirname = None 41 if 'posix' in names: 42 sep = '/' 43 from posix import stat, getcwd 44 elif 'nt' in names: 45 sep = '\\' 46 from nt import stat, getcwd 47 elif 'dos' in names: 48 sep = '\\' 49 from dos import stat, getcwd 50 elif 'os2' in names: 51 sep = '\\' 52 from os2 import stat, getcwd 53 elif 'mac' in names: 54 from mac import stat, getcwd 55 def join(a, b): 56 if a == '': 57 return b 58 if ':' not in a: 59 a = ':' + a 60 if a[-1:] != ':': 61 a = a + ':' 62 return a + b 63 else: 64 raise ImportError('no os specific module found') 65 66 if join is None: 67 def join(a, b, sep=sep): 68 if a == '': 69 return b 70 lastchar = a[-1:] 71 if lastchar == '/' or lastchar == sep: 72 return a + b 73 return a + sep + b 74 75 if dirname is None: 76 def dirname(a, sep=sep): 77 for i in range(len(a)-1, -1, -1): 78 c = a[i] 79 if c == '/' or c == sep: 80 return a[:i] 81 return '' 82 83 global _os_stat 84 _os_stat = stat 85 86 global _os_path_join 87 _os_path_join = join 88 89 global _os_path_dirname 90 _os_path_dirname = dirname 91 92 global _os_getcwd 93 _os_getcwd = getcwd 94 95_os_bootstrap() 96 97def packageName(s): 98 for i in range(len(s)-1, -1, -1): 99 if s[i] == '.': 100 break 101 else: 102 return '' 103 return s[:i] 104 105def nameSplit(s): 106 rslt = [] 107 i = j = 0 108 for j in range(len(s)): 109 if s[j] == '.': 110 rslt.append(s[i:j]) 111 i = j+1 112 if i < len(s): 113 rslt.append(s[i:]) 114 return rslt 115 116def getPathExt(fnm): 117 for i in range(len(fnm)-1, -1, -1): 118 if fnm[i] == '.': 119 return fnm[i:] 120 return '' 121 122def pathIsDir(pathname): 123 "Local replacement for os.path.isdir()." 124 try: 125 s = _os_stat(pathname) 126 except OSError: 127 return None 128 return (s[0] & 0170000) == 0040000 129 130def getDescr(fnm): 131 ext = getPathExt(fnm) 132 for (suffix, mode, typ) in imp.get_suffixes(): 133 if suffix == ext: 134 return (suffix, mode, typ) 135 136################################################## 137## CLASSES 138 139class Owner: 140 141 """An Owner does imports from a particular piece of turf That is, there's 142 an Owner for each thing on sys.path There are owners for directories and 143 .pyz files. There could be owners for zip files, or even URLs. A 144 shadowpath (a dictionary mapping the names in sys.path to their owners) is 145 used so that sys.path (or a package's __path__) is still a bunch of strings, 146 """ 147 148 def __init__(self, path): 149 self.path = path 150 151 def __str__(self): 152 return self.path 153 154 def getmod(self, nm): 155 return None 156 157class DirOwner(Owner): 158 159 def __init__(self, path): 160 if path == '': 161 path = _os_getcwd() 162 if not pathIsDir(path): 163 raise ValueError("%s is not a directory" % path) 164 Owner.__init__(self, path) 165 166 def getmod(self, nm, 167 getsuffixes=imp.get_suffixes, loadco=marshal.loads, newmod=imp.new_module): 168 169 pth = _os_path_join(self.path, nm) 170 171 possibles = [(pth, 0, None)] 172 if pathIsDir(pth): 173 possibles.insert(0, (_os_path_join(pth, '__init__'), 1, pth)) 174 py = pyc = None 175 for pth, ispkg, pkgpth in possibles: 176 for ext, mode, typ in getsuffixes(): 177 attempt = pth+ext 178 try: 179 st = _os_stat(attempt) 180 except: 181 pass 182 else: 183 if typ == imp.C_EXTENSION: 184 fp = open(attempt, 'rb') 185 mod = imp.load_module(nm, fp, attempt, (ext, mode, typ)) 186 mod.__file__ = attempt 187 return mod 188 elif typ == imp.PY_SOURCE: 189 py = (attempt, st) 190 else: 191 pyc = (attempt, st) 192 if py or pyc: 193 break 194 if py is None and pyc is None: 195 return None 196 while True: 197 if pyc is None or py and pyc[1][8] < py[1][8]: 198 try: 199 co = compile(open(py[0], 'r').read()+'\n', py[0], 'exec') 200 break 201 except SyntaxError, e: 202 print("Invalid syntax in %s" % py[0]) 203 print(e.args) 204 raise 205 elif pyc: 206 stuff = open(pyc[0], 'rb').read() 207 try: 208 co = loadco(stuff[8:]) 209 break 210 except (ValueError, EOFError): 211 pyc = None 212 else: 213 return None 214 mod = newmod(nm) 215 mod.__file__ = co.co_filename 216 if ispkg: 217 mod.__path__ = [pkgpth] 218 subimporter = PathImportDirector(mod.__path__) 219 mod.__importsub__ = subimporter.getmod 220 mod.__co__ = co 221 return mod 222 223 224class ImportDirector(Owner): 225 """ImportDirectors live on the metapath There's one for builtins, one for 226 frozen modules, and one for sys.path Windows gets one for modules gotten 227 from the Registry Mac would have them for PY_RESOURCE modules etc. A 228 generalization of Owner - their concept of 'turf' is broader""" 229 230 pass 231 232class BuiltinImportDirector(ImportDirector): 233 """Directs imports of builtin modules""" 234 def __init__(self): 235 self.path = 'Builtins' 236 237 def getmod(self, nm, isbuiltin=imp.is_builtin): 238 if isbuiltin(nm): 239 mod = imp.load_module(nm, None, nm, ('', '', imp.C_BUILTIN)) 240 return mod 241 return None 242 243class FrozenImportDirector(ImportDirector): 244 """Directs imports of frozen modules""" 245 246 def __init__(self): 247 self.path = 'FrozenModules' 248 249 def getmod(self, nm, 250 isFrozen=imp.is_frozen, loadMod=imp.load_module): 251 if isFrozen(nm): 252 mod = loadMod(nm, None, nm, ('', '', imp.PY_FROZEN)) 253 if hasattr(mod, '__path__'): 254 mod.__importsub__ = lambda name, pname=nm, owner=self: owner.getmod(pname+'.'+name) 255 return mod 256 return None 257 258 259class RegistryImportDirector(ImportDirector): 260 """Directs imports of modules stored in the Windows Registry""" 261 262 def __init__(self): 263 self.path = "WindowsRegistry" 264 self.map = {} 265 try: 266 import win32api 267 ## import win32con 268 except ImportError: 269 pass 270 else: 271 HKEY_CURRENT_USER = -2147483647 272 HKEY_LOCAL_MACHINE = -2147483646 273 KEY_ALL_ACCESS = 983103 274 subkey = r"Software\Python\PythonCore\%s\Modules" % sys.winver 275 for root in (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE): 276 try: 277 hkey = win32api.RegOpenKeyEx(root, subkey, 0, KEY_ALL_ACCESS) 278 except: 279 pass 280 else: 281 numsubkeys, numvalues, lastmodified = win32api.RegQueryInfoKey(hkey) 282 for i in range(numsubkeys): 283 subkeyname = win32api.RegEnumKey(hkey, i) 284 hskey = win32api.RegOpenKeyEx(hkey, subkeyname, 0, KEY_ALL_ACCESS) 285 val = win32api.RegQueryValueEx(hskey, '') 286 desc = getDescr(val[0]) 287 self.map[subkeyname] = (val[0], desc) 288 hskey.Close() 289 hkey.Close() 290 break 291 292 def getmod(self, nm): 293 stuff = self.map.get(nm) 294 if stuff: 295 fnm, desc = stuff 296 fp = open(fnm, 'rb') 297 mod = imp.load_module(nm, fp, fnm, desc) 298 mod.__file__ = fnm 299 return mod 300 return None 301 302class PathImportDirector(ImportDirector): 303 """Directs imports of modules stored on the filesystem.""" 304 305 def __init__(self, pathlist=None, importers=None, ownertypes=None): 306 if pathlist is None: 307 self.path = sys.path 308 else: 309 self.path = pathlist 310 if ownertypes == None: 311 self._ownertypes = _globalOwnerTypes 312 else: 313 self._ownertypes = ownertypes 314 if importers: 315 self._shadowPath = importers 316 else: 317 self._shadowPath = {} 318 self._inMakeOwner = False 319 self._building = {} 320 321 def getmod(self, nm): 322 mod = None 323 for thing in self.path: 324 if isinstance(thing, basestring): 325 owner = self._shadowPath.get(thing, -1) 326 if owner == -1: 327 owner = self._shadowPath[thing] = self._makeOwner(thing) 328 if owner: 329 mod = owner.getmod(nm) 330 else: 331 mod = thing.getmod(nm) 332 if mod: 333 break 334 return mod 335 336 def _makeOwner(self, path): 337 if self._building.get(path): 338 return None 339 self._building[path] = 1 340 owner = None 341 for klass in self._ownertypes: 342 try: 343 # this may cause an import, which may cause recursion 344 # hence the protection 345 owner = klass(path) 346 except: 347 pass 348 else: 349 break 350 del self._building[path] 351 return owner 352 353#=================ImportManager============================# 354# The one-and-only ImportManager 355# ie, the builtin import 356 357UNTRIED = -1 358 359class ImportManager: 360 # really the equivalent of builtin import 361 def __init__(self): 362 self.metapath = [ 363 BuiltinImportDirector(), 364 FrozenImportDirector(), 365 RegistryImportDirector(), 366 PathImportDirector() 367 ] 368 self.threaded = 0 369 self.rlock = None 370 self.locker = None 371 self.setThreaded() 372 373 def setThreaded(self): 374 thread = sys.modules.get('thread', None) 375 if thread and not self.threaded: 376 self.threaded = 1 377 self.rlock = thread.allocate_lock() 378 self._get_ident = thread.get_ident 379 380 def install(self): 381 import __builtin__ 382 __builtin__.__import__ = self.importHook 383 __builtin__.reload = self.reloadHook 384 385 def importHook(self, name, globals=None, locals=None, fromlist=None, level=-1): 386 ''' 387 NOTE: Currently importHook will accept the keyword-argument "level" 388 but it will *NOT* use it (currently). Details about the "level" keyword 389 argument can be found here: http://www.python.org/doc/2.5.2/lib/built-in-funcs.html 390 ''' 391 # first see if we could be importing a relative name 392 #print "importHook(%s, %s, locals, %s)" % (name, globals['__name__'], fromlist) 393 _sys_modules_get = sys.modules.get 394 contexts = [None] 395 if globals: 396 importernm = globals.get('__name__', '') 397 if importernm: 398 if hasattr(_sys_modules_get(importernm), '__path__'): 399 contexts.insert(0, importernm) 400 else: 401 pkgnm = packageName(importernm) 402 if pkgnm: 403 contexts.insert(0, pkgnm) 404 # so contexts is [pkgnm, None] or just [None] 405 # now break the name being imported up so we get: 406 # a.b.c -> [a, b, c] 407 nmparts = nameSplit(name) 408 _self_doimport = self.doimport 409 threaded = self.threaded 410 for context in contexts: 411 ctx = context 412 for i in range(len(nmparts)): 413 nm = nmparts[i] 414 #print " importHook trying %s in %s" % (nm, ctx) 415 if ctx: 416 fqname = ctx + '.' + nm 417 else: 418 fqname = nm 419 if threaded: 420 self._acquire() 421 mod = _sys_modules_get(fqname, UNTRIED) 422 if mod is UNTRIED: 423 mod = _self_doimport(nm, ctx, fqname) 424 if threaded: 425 self._release() 426 if mod: 427 ctx = fqname 428 else: 429 break 430 else: 431 # no break, point i beyond end 432 i = i + 1 433 if i: 434 break 435 436 if i<len(nmparts): 437 if ctx and hasattr(sys.modules[ctx], nmparts[i]): 438 #print "importHook done with %s %s %s (case 1)" % (name, globals['__name__'], fromlist) 439 return sys.modules[nmparts[0]] 440 del sys.modules[fqname] 441 raise ImportError("No module named %s" % fqname) 442 if fromlist is None: 443 #print "importHook done with %s %s %s (case 2)" % (name, globals['__name__'], fromlist) 444 if context: 445 return sys.modules[context+'.'+nmparts[0]] 446 return sys.modules[nmparts[0]] 447 bottommod = sys.modules[ctx] 448 if hasattr(bottommod, '__path__'): 449 fromlist = list(fromlist) 450 i = 0 451 while i < len(fromlist): 452 nm = fromlist[i] 453 if nm == '*': 454 fromlist[i:i+1] = list(getattr(bottommod, '__all__', [])) 455 if i >= len(fromlist): 456 break 457 nm = fromlist[i] 458 i = i + 1 459 if not hasattr(bottommod, nm): 460 if self.threaded: 461 self._acquire() 462 mod = self.doimport(nm, ctx, ctx+'.'+nm) 463 if self.threaded: 464 self._release() 465 if not mod: 466 raise ImportError("%s not found in %s" % (nm, ctx)) 467 #print "importHook done with %s %s %s (case 3)" % (name, globals['__name__'], fromlist) 468 return bottommod 469 470 def doimport(self, nm, parentnm, fqname): 471 # Not that nm is NEVER a dotted name at this point 472 #print "doimport(%s, %s, %s)" % (nm, parentnm, fqname) 473 if parentnm: 474 parent = sys.modules[parentnm] 475 if hasattr(parent, '__path__'): 476 importfunc = getattr(parent, '__importsub__', None) 477 if not importfunc: 478 subimporter = PathImportDirector(parent.__path__) 479 importfunc = parent.__importsub__ = subimporter.getmod 480 mod = importfunc(nm) 481 if mod: 482 setattr(parent, nm, mod) 483 else: 484 #print "..parent not a package" 485 return None 486 else: 487 # now we're dealing with an absolute import 488 for director in self.metapath: 489 mod = director.getmod(nm) 490 if mod: 491 break 492 if mod: 493 mod.__name__ = fqname 494 sys.modules[fqname] = mod 495 if hasattr(mod, '__co__'): 496 co = mod.__co__ 497 del mod.__co__ 498 exec(co, mod.__dict__) 499 if fqname == 'thread' and not self.threaded: 500## print "thread detected!" 501 self.setThreaded() 502 else: 503 sys.modules[fqname] = None 504 #print "..found %s" % mod 505 return mod 506 507 def reloadHook(self, mod): 508 fqnm = mod.__name__ 509 nm = nameSplit(fqnm)[-1] 510 parentnm = packageName(fqnm) 511 newmod = self.doimport(nm, parentnm, fqnm) 512 mod.__dict__.update(newmod.__dict__) 513## return newmod 514 515 def _acquire(self): 516 if self.rlock.locked(): 517 if self.locker == self._get_ident(): 518 self.lockcount = self.lockcount + 1 519## print "_acquire incrementing lockcount to", self.lockcount 520 return 521 self.rlock.acquire() 522 self.locker = self._get_ident() 523 self.lockcount = 0 524## print "_acquire first time!" 525 526 def _release(self): 527 if self.lockcount: 528 self.lockcount = self.lockcount - 1 529## print "_release decrementing lockcount to", self.lockcount 530 else: 531 self.rlock.release() 532## print "_release releasing lock!" 533 534 535################################################## 536## MORE CONSTANTS & GLOBALS 537 538_globalOwnerTypes = [ 539 DirOwner, 540 Owner, 541] 542