2 # $Id: ImportManager.py,v 1.6 2007/04/03 01:56:24 tavis_rudd Exp $
4 """Provides an emulator/replacement for Python's standard import system.
6 @@TR: Be warned that Import Hooks are in the deepest, darkest corner of Python's
7 jungle. If you need to start hacking with this, be prepared to get lost for a
8 while. Also note, this module predates the newstyle import hooks in Python 2.3
9 http://www.python.org/peps/pep-0302.html.
12 This is a hacked/documented version of Gordon McMillan's iu.py. I have:
14 - made it a little less terse
16 - added docstrings and explanatations
18 - standardized the variable naming scheme
20 - reorganized the code layout to enhance readability
23 ================================================================================
24 Author: Tavis Rudd <tavis@damnsimple.com> based on Gordon McMillan's iu.py
25 License: This software is released for unlimited distribution under the
26 terms of the MIT license. See the LICENSE file.
27 Version: $Revision: 1.6 $
28 Start Date: 2001/03/30
29 Last Revision Date: $Date: 2007/04/03 01:56:24 $
31 __author__
= "Tavis Rudd <tavis@damnsimple.com>"
32 __revision__
= "$Revision: 1.6 $"[11:-2]
34 ##################################################
41 ##################################################
42 ## CONSTANTS & GLOBALS
47 True, False = (1==1),(1==0)
53 # _globalOwnerTypes is defined at the bottom of this file
55 _os_stat
= _os_path_join
= _os_getcwd
= _os_path_dirname
= None
57 ##################################################
61 """Set up 'os' module replacement functions for use during import bootstrap."""
63 names
= sys
.builtin_module_names
68 from posix
import stat
, getcwd
71 from nt
import stat
, getcwd
74 from dos
import stat
, getcwd
77 from os2
import stat
, getcwd
79 from mac
import stat
, getcwd
89 raise ImportError, 'no os specific module found'
92 def join(a
, b
, sep
=sep
):
96 if lastchar
== '/' or lastchar
== sep
:
101 def dirname(a
, sep
=sep
):
102 for i
in range(len(a
)-1, -1, -1):
104 if c
== '/' or c
== sep
:
114 global _os_path_dirname
115 _os_path_dirname
= dirname
123 for i
in range(len(s
)-1, -1, -1):
133 for j
in range(len(s
)):
142 for i
in range(len(fnm
)-1, -1, -1):
147 def pathIsDir(pathname
):
148 "Local replacement for os.path.isdir()."
150 s
= _os_stat(pathname
)
153 return (s
[0] & 0170000) == 0040000
156 ext
= getPathExt(fnm
)
157 for (suffix
, mode
, typ
) in imp
.get_suffixes():
159 return (suffix
, mode
, typ
)
161 ##################################################
166 """An Owner does imports from a particular piece of turf That is, there's
167 an Owner for each thing on sys.path There are owners for directories and
168 .pyz files. There could be owners for zip files, or even URLs. A
169 shadowpath (a dictionary mapping the names in sys.path to their owners) is
170 used so that sys.path (or a package's __path__) is still a bunch of strings,
173 def __init__(self
, path
):
179 def getmod(self
, nm
):
182 class DirOwner(Owner
):
184 def __init__(self
, path
):
187 if not pathIsDir(path
):
188 raise ValueError, "%s is not a directory" % path
189 Owner
.__init
__(self
, path
)
192 getsuffixes
=imp
.get_suffixes
, loadco
=marshal
.loads
, newmod
=imp
.new_module
):
194 pth
= _os_path_join(self
.path
, nm
)
196 possibles
= [(pth
, 0, None)]
198 possibles
.insert(0, (_os_path_join(pth
, '__init__'), 1, pth
))
200 for pth
, ispkg
, pkgpth
in possibles
:
201 for ext
, mode
, typ
in getsuffixes():
204 st
= _os_stat(attempt
)
208 if typ
== imp
.C_EXTENSION
:
209 fp
= open(attempt
, 'rb')
210 mod
= imp
.load_module(nm
, fp
, attempt
, (ext
, mode
, typ
))
211 mod
.__file
__ = attempt
213 elif typ
== imp
.PY_SOURCE
:
219 if py
is None and pyc
is None:
222 if pyc
is None or py
and pyc
[1][8] < py
[1][8]:
224 co
= compile(open(py
[0], 'r').read()+'\n', py
[0], 'exec')
226 except SyntaxError, e
:
227 print "Invalid syntax in %s" % py
[0]
231 stuff
= open(pyc
[0], 'rb').read()
233 co
= loadco(stuff
[8:])
235 except (ValueError, EOFError):
240 mod
.__file
__ = co
.co_filename
242 mod
.__path
__ = [pkgpth
]
243 subimporter
= PathImportDirector(mod
.__path
__)
244 mod
.__importsub
__ = subimporter
.getmod
249 class ImportDirector(Owner
):
250 """ImportDirectors live on the metapath There's one for builtins, one for
251 frozen modules, and one for sys.path Windows gets one for modules gotten
252 from the Registry Mac would have them for PY_RESOURCE modules etc. A
253 generalization of Owner - their concept of 'turf' is broader"""
257 class BuiltinImportDirector(ImportDirector
):
258 """Directs imports of builtin modules"""
260 self
.path
= 'Builtins'
262 def getmod(self
, nm
, isbuiltin
=imp
.is_builtin
):
264 mod
= imp
.load_module(nm
, None, nm
, ('','',imp
.C_BUILTIN
))
268 class FrozenImportDirector(ImportDirector
):
269 """Directs imports of frozen modules"""
272 self
.path
= 'FrozenModules'
275 isFrozen
=imp
.is_frozen
, loadMod
=imp
.load_module
):
277 mod
= loadMod(nm
, None, nm
, ('','',imp
.PY_FROZEN
))
278 if hasattr(mod
, '__path__'):
279 mod
.__importsub
__ = lambda name
, pname
=nm
, owner
=self
: owner
.getmod(pname
+'.'+name
)
284 class RegistryImportDirector(ImportDirector
):
285 """Directs imports of modules stored in the Windows Registry"""
288 self
.path
= "WindowsRegistry"
296 HKEY_CURRENT_USER
= -2147483647
297 HKEY_LOCAL_MACHINE
= -2147483646
298 KEY_ALL_ACCESS
= 983103
299 subkey
= r
"Software\Python\PythonCore\%s\Modules" % sys
.winver
300 for root
in (HKEY_CURRENT_USER
, HKEY_LOCAL_MACHINE
):
302 hkey
= win32api
.RegOpenKeyEx(root
, subkey
, 0, KEY_ALL_ACCESS
)
306 numsubkeys
, numvalues
, lastmodified
= win32api
.RegQueryInfoKey(hkey
)
307 for i
in range(numsubkeys
):
308 subkeyname
= win32api
.RegEnumKey(hkey
, i
)
309 hskey
= win32api
.RegOpenKeyEx(hkey
, subkeyname
, 0, KEY_ALL_ACCESS
)
310 val
= win32api
.RegQueryValueEx(hskey
, '')
311 desc
= getDescr(val
[0])
312 self
.map[subkeyname
] = (val
[0], desc
)
317 def getmod(self
, nm
):
318 stuff
= self
.map.get(nm
)
322 mod
= imp
.load_module(nm
, fp
, fnm
, desc
)
327 class PathImportDirector(ImportDirector
):
328 """Directs imports of modules stored on the filesystem."""
330 def __init__(self
, pathlist
=None, importers
=None, ownertypes
=None):
335 if ownertypes
== None:
336 self
._ownertypes
= _globalOwnerTypes
338 self
._ownertypes
= ownertypes
340 self
._shadowPath
= importers
342 self
._shadowPath
= {}
343 self
._inMakeOwner
= False
346 def getmod(self
, nm
):
348 for thing
in self
.path
:
349 if type(thing
) is STRINGTYPE
:
350 owner
= self
._shadowPath
.get(thing
, -1)
352 owner
= self
._shadowPath
[thing
] = self
._makeOwner
(thing
)
354 mod
= owner
.getmod(nm
)
356 mod
= thing
.getmod(nm
)
361 def _makeOwner(self
, path
):
362 if self
._building
.get(path
):
364 self
._building
[path
] = 1
366 for klass
in self
._ownertypes
:
368 # this may cause an import, which may cause recursion
369 # hence the protection
375 del self
._building
[path
]
378 #=================ImportManager============================#
379 # The one-and-only ImportManager
380 # ie, the builtin import
385 # really the equivalent of builtin import
388 BuiltinImportDirector(),
389 FrozenImportDirector(),
390 RegistryImportDirector(),
398 def setThreaded(self
):
399 thread
= sys
.modules
.get('thread', None)
400 if thread
and not self
.threaded
:
402 self
.rlock
= thread
.allocate_lock()
403 self
._get
_ident
= thread
.get_ident
407 __builtin__
.__import
__ = self
.importHook
408 __builtin__
.reload = self
.reloadHook
410 def importHook(self
, name
, globals=None, locals=None, fromlist
=None):
411 # first see if we could be importing a relative name
412 #print "importHook(%s, %s, locals, %s)" % (name, globals['__name__'], fromlist)
413 _sys_modules_get
= sys
.modules
.get
416 importernm
= globals.get('__name__', '')
418 if hasattr(_sys_modules_get(importernm
), '__path__'):
419 contexts
.insert(0,importernm
)
421 pkgnm
= packageName(importernm
)
423 contexts
.insert(0,pkgnm
)
424 # so contexts is [pkgnm, None] or just [None]
425 # now break the name being imported up so we get:
427 nmparts
= nameSplit(name
)
428 _self_doimport
= self
.doimport
429 threaded
= self
.threaded
430 for context
in contexts
:
432 for i
in range(len(nmparts
)):
434 #print " importHook trying %s in %s" % (nm, ctx)
436 fqname
= ctx
+ '.' + nm
441 mod
= _sys_modules_get(fqname
, UNTRIED
)
443 mod
= _self_doimport(nm
, ctx
, fqname
)
451 # no break, point i beyond end
457 if ctx
and hasattr(sys
.modules
[ctx
], nmparts
[i
]):
458 #print "importHook done with %s %s %s (case 1)" % (name, globals['__name__'], fromlist)
459 return sys
.modules
[nmparts
[0]]
460 del sys
.modules
[fqname
]
461 raise ImportError, "No module named %s" % fqname
463 #print "importHook done with %s %s %s (case 2)" % (name, globals['__name__'], fromlist)
465 return sys
.modules
[context
+'.'+nmparts
[0]]
466 return sys
.modules
[nmparts
[0]]
467 bottommod
= sys
.modules
[ctx
]
468 if hasattr(bottommod
, '__path__'):
469 fromlist
= list(fromlist
)
471 while i
< len(fromlist
):
474 fromlist
[i
:i
+1] = list(getattr(bottommod
, '__all__', []))
475 if i
>= len(fromlist
):
479 if not hasattr(bottommod
, nm
):
482 mod
= self
.doimport(nm
, ctx
, ctx
+'.'+nm
)
486 raise ImportError, "%s not found in %s" % (nm
, ctx
)
487 #print "importHook done with %s %s %s (case 3)" % (name, globals['__name__'], fromlist)
490 def doimport(self
, nm
, parentnm
, fqname
):
491 # Not that nm is NEVER a dotted name at this point
492 #print "doimport(%s, %s, %s)" % (nm, parentnm, fqname)
494 parent
= sys
.modules
[parentnm
]
495 if hasattr(parent
, '__path__'):
496 importfunc
= getattr(parent
, '__importsub__', None)
498 subimporter
= PathImportDirector(parent
.__path
__)
499 importfunc
= parent
.__importsub
__ = subimporter
.getmod
502 setattr(parent
, nm
, mod
)
504 #print "..parent not a package"
507 # now we're dealing with an absolute import
508 for director
in self
.metapath
:
509 mod
= director
.getmod(nm
)
513 mod
.__name
__ = fqname
514 sys
.modules
[fqname
] = mod
515 if hasattr(mod
, '__co__'):
518 exec co
in mod
.__dict
__
519 if fqname
== 'thread' and not self
.threaded
:
520 ## print "thread detected!"
523 sys
.modules
[fqname
] = None
524 #print "..found %s" % mod
527 def reloadHook(self
, mod
):
529 nm
= nameSplit(fqnm
)[-1]
530 parentnm
= packageName(fqnm
)
531 newmod
= self
.doimport(nm
, parentnm
, fqnm
)
532 mod
.__dict
__.update(newmod
.__dict
__)
536 if self
.rlock
.locked():
537 if self
.locker
== self
._get
_ident
():
538 self
.lockcount
= self
.lockcount
+ 1
539 ## print "_acquire incrementing lockcount to", self.lockcount
542 self
.locker
= self
._get
_ident
()
544 ## print "_acquire first time!"
548 self
.lockcount
= self
.lockcount
- 1
549 ## print "_release decrementing lockcount to", self.lockcount
552 ## print "_release releasing lock!"
555 ##################################################
556 ## MORE CONSTANTS & GLOBALS
558 _globalOwnerTypes
= [