2 # $Id: ImportManager.py,v 1.5 2006/01/27 19:00:20 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.5 $
28 Start Date: 2001/03/30
29 Last Revision Date: $Date: 2006/01/27 19:00:20 $
31 __author__
= "Tavis Rudd <tavis@damnsimple.com>"
32 __revision__
= "$Revision: 1.5 $"[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
90 raise ImportError, 'no os specific module found'
93 def join(a
, b
, sep
=sep
):
97 if lastchar
== '/' or lastchar
== sep
:
102 def dirname(a
, sep
=sep
):
103 for i
in range(len(a
)-1, -1, -1):
105 if c
== '/' or c
== sep
:
115 global _os_path_dirname
116 _os_path_dirname
= dirname
124 for i
in range(len(s
)-1, -1, -1):
134 for j
in range(len(s
)):
143 for i
in range(len(fnm
)-1, -1, -1):
148 def pathIsDir(pathname
):
149 "Local replacement for os.path.isdir()."
151 s
= _os_stat(pathname
)
154 return (s
[0] & 0170000) == 0040000
157 ext
= getPathExt(fnm
)
158 for (suffix
, mode
, typ
) in imp
.get_suffixes():
160 return (suffix
, mode
, typ
)
162 ##################################################
167 """An Owner does imports from a particular piece of turf That is, there's
168 an Owner for each thing on sys.path There are owners for directories and
169 .pyz files. There could be owners for zip files, or even URLs. A
170 shadowpath (a dictionary mapping the names in sys.path to their owners) is
171 used so that sys.path (or a package's __path__) is still a bunch of strings,
174 def __init__(self
, path
):
180 def getmod(self
, nm
):
183 class DirOwner(Owner
):
185 def __init__(self
, path
):
188 if not pathIsDir(path
):
189 raise ValueError, "%s is not a directory" % path
190 Owner
.__init
__(self
, path
)
193 getsuffixes
=imp
.get_suffixes
, loadco
=marshal
.loads
, newmod
=imp
.new_module
):
195 pth
= _os_path_join(self
.path
, nm
)
197 possibles
= [(pth
, 0, None)]
199 possibles
.insert(0, (_os_path_join(pth
, '__init__'), 1, pth
))
201 for pth
, ispkg
, pkgpth
in possibles
:
202 for ext
, mode
, typ
in getsuffixes():
205 st
= _os_stat(attempt
)
209 if typ
== imp
.C_EXTENSION
:
210 fp
= open(attempt
, 'rb')
211 mod
= imp
.load_module(nm
, fp
, attempt
, (ext
, mode
, typ
))
212 mod
.__file
__ = attempt
214 elif typ
== imp
.PY_SOURCE
:
220 if py
is None and pyc
is None:
223 if pyc
is None or py
and pyc
[1][8] < py
[1][8]:
225 co
= compile(open(py
[0], 'r').read()+'\n', py
[0], 'exec')
227 except SyntaxError, e
:
228 print "Invalid syntax in %s" % py
[0]
232 stuff
= open(pyc
[0], 'rb').read()
234 co
= loadco(stuff
[8:])
236 except (ValueError, EOFError):
241 mod
.__file
__ = co
.co_filename
243 mod
.__path
__ = [pkgpth
]
244 subimporter
= PathImportDirector(mod
.__path
__)
245 mod
.__importsub
__ = subimporter
.getmod
250 class ImportDirector(Owner
):
251 """ImportDirectors live on the metapath There's one for builtins, one for
252 frozen modules, and one for sys.path Windows gets one for modules gotten
253 from the Registry Mac would have them for PY_RESOURCE modules etc. A
254 generalization of Owner - their concept of 'turf' is broader"""
258 class BuiltinImportDirector(ImportDirector
):
259 """Directs imports of builtin modules"""
261 self
.path
= 'Builtins'
263 def getmod(self
, nm
, isbuiltin
=imp
.is_builtin
):
265 mod
= imp
.load_module(nm
, None, nm
, ('','',imp
.C_BUILTIN
))
269 class FrozenImportDirector(ImportDirector
):
270 """Directs imports of frozen modules"""
273 self
.path
= 'FrozenModules'
276 isFrozen
=imp
.is_frozen
, loadMod
=imp
.load_module
):
278 mod
= loadMod(nm
, None, nm
, ('','',imp
.PY_FROZEN
))
279 if hasattr(mod
, '__path__'):
280 mod
.__importsub
__ = lambda name
, pname
=nm
, owner
=self
: owner
.getmod(pname
+'.'+name
)
285 class RegistryImportDirector(ImportDirector
):
286 """Directs imports of modules stored in the Windows Registry"""
289 self
.path
= "WindowsRegistry"
297 HKEY_CURRENT_USER
= -2147483647
298 HKEY_LOCAL_MACHINE
= -2147483646
299 KEY_ALL_ACCESS
= 983103
300 subkey
= r
"Software\Python\PythonCore\%s\Modules" % sys
.winver
301 for root
in (HKEY_CURRENT_USER
, HKEY_LOCAL_MACHINE
):
303 hkey
= win32api
.RegOpenKeyEx(root
, subkey
, 0, KEY_ALL_ACCESS
)
307 numsubkeys
, numvalues
, lastmodified
= win32api
.RegQueryInfoKey(hkey
)
308 for i
in range(numsubkeys
):
309 subkeyname
= win32api
.RegEnumKey(hkey
, i
)
310 hskey
= win32api
.RegOpenKeyEx(hkey
, subkeyname
, 0, KEY_ALL_ACCESS
)
311 val
= win32api
.RegQueryValueEx(hskey
, '')
312 desc
= getDescr(val
[0])
313 self
.map[subkeyname
] = (val
[0], desc
)
318 def getmod(self
, nm
):
319 stuff
= self
.map.get(nm
)
323 mod
= imp
.load_module(nm
, fp
, fnm
, desc
)
328 class PathImportDirector(ImportDirector
):
329 """Directs imports of modules stored on the filesystem."""
331 def __init__(self
, pathlist
=None, importers
=None, ownertypes
=None):
336 if ownertypes
== None:
337 self
._ownertypes
= _globalOwnerTypes
339 self
._ownertypes
= ownertypes
341 self
._shadowPath
= importers
343 self
._shadowPath
= {}
344 self
._inMakeOwner
= False
347 def getmod(self
, nm
):
349 for thing
in self
.path
:
350 if type(thing
) is STRINGTYPE
:
351 owner
= self
._shadowPath
.get(thing
, -1)
353 owner
= self
._shadowPath
[thing
] = self
._makeOwner
(thing
)
355 mod
= owner
.getmod(nm
)
357 mod
= thing
.getmod(nm
)
362 def _makeOwner(self
, path
):
363 if self
._building
.get(path
):
365 self
._building
[path
] = 1
367 for klass
in self
._ownertypes
:
369 # this may cause an import, which may cause recursion
370 # hence the protection
376 del self
._building
[path
]
379 #=================ImportManager============================#
380 # The one-and-only ImportManager
381 # ie, the builtin import
386 # really the equivalent of builtin import
389 BuiltinImportDirector(),
390 FrozenImportDirector(),
391 RegistryImportDirector(),
399 def setThreaded(self
):
400 thread
= sys
.modules
.get('thread', None)
401 if thread
and not self
.threaded
:
403 self
.rlock
= thread
.allocate_lock()
404 self
._get
_ident
= thread
.get_ident
408 __builtin__
.__import
__ = self
.importHook
409 __builtin__
.reload = self
.reloadHook
411 def importHook(self
, name
, globals=None, locals=None, fromlist
=None):
412 # first see if we could be importing a relative name
413 #print "importHook(%s, %s, locals, %s)" % (name, globals['__name__'], fromlist)
414 _sys_modules_get
= sys
.modules
.get
417 importernm
= globals.get('__name__', '')
419 if hasattr(_sys_modules_get(importernm
), '__path__'):
420 contexts
.insert(0,importernm
)
422 pkgnm
= packageName(importernm
)
424 contexts
.insert(0,pkgnm
)
425 # so contexts is [pkgnm, None] or just [None]
426 # now break the name being imported up so we get:
428 nmparts
= nameSplit(name
)
429 _self_doimport
= self
.doimport
430 threaded
= self
.threaded
431 for context
in contexts
:
433 for i
in range(len(nmparts
)):
435 #print " importHook trying %s in %s" % (nm, ctx)
437 fqname
= ctx
+ '.' + nm
442 mod
= _sys_modules_get(fqname
, UNTRIED
)
444 mod
= _self_doimport(nm
, ctx
, fqname
)
452 # no break, point i beyond end
458 if ctx
and hasattr(sys
.modules
[ctx
], nmparts
[i
]):
459 #print "importHook done with %s %s %s (case 1)" % (name, globals['__name__'], fromlist)
460 return sys
.modules
[nmparts
[0]]
461 del sys
.modules
[fqname
]
462 raise ImportError, "No module named %s" % fqname
464 #print "importHook done with %s %s %s (case 2)" % (name, globals['__name__'], fromlist)
466 return sys
.modules
[context
+'.'+nmparts
[0]]
467 return sys
.modules
[nmparts
[0]]
468 bottommod
= sys
.modules
[ctx
]
469 if hasattr(bottommod
, '__path__'):
470 fromlist
= list(fromlist
)
472 while i
< len(fromlist
):
475 fromlist
[i
:i
+1] = list(getattr(bottommod
, '__all__', []))
476 if i
>= len(fromlist
):
480 if not hasattr(bottommod
, nm
):
483 mod
= self
.doimport(nm
, ctx
, ctx
+'.'+nm
)
487 raise ImportError, "%s not found in %s" % (nm
, ctx
)
488 #print "importHook done with %s %s %s (case 3)" % (name, globals['__name__'], fromlist)
491 def doimport(self
, nm
, parentnm
, fqname
):
492 # Not that nm is NEVER a dotted name at this point
493 #print "doimport(%s, %s, %s)" % (nm, parentnm, fqname)
495 parent
= sys
.modules
[parentnm
]
496 if hasattr(parent
, '__path__'):
497 importfunc
= getattr(parent
, '__importsub__', None)
499 subimporter
= PathImportDirector(parent
.__path
__)
500 importfunc
= parent
.__importsub
__ = subimporter
.getmod
503 setattr(parent
, nm
, mod
)
505 #print "..parent not a package"
508 # now we're dealing with an absolute import
509 for director
in self
.metapath
:
510 mod
= director
.getmod(nm
)
514 mod
.__name
__ = fqname
515 sys
.modules
[fqname
] = mod
516 if hasattr(mod
, '__co__'):
519 exec co
in mod
.__dict
__
520 if fqname
== 'thread' and not self
.threaded
:
521 ## print "thread detected!"
524 sys
.modules
[fqname
] = None
525 #print "..found %s" % mod
528 def reloadHook(self
, mod
):
530 nm
= nameSplit(fqnm
)[-1]
531 parentnm
= packageName(fqnm
)
532 newmod
= self
.doimport(nm
, parentnm
, fqnm
)
533 mod
.__dict
__.update(newmod
.__dict
__)
537 if self
.rlock
.locked():
538 if self
.locker
== self
._get
_ident
():
539 self
.lockcount
= self
.lockcount
+ 1
540 ## print "_acquire incrementing lockcount to", self.lockcount
543 self
.locker
= self
._get
_ident
()
545 ## print "_acquire first time!"
549 self
.lockcount
= self
.lockcount
- 1
550 ## print "_release decrementing lockcount to", self.lockcount
553 ## print "_release releasing lock!"
556 ##################################################
557 ## MORE CONSTANTS & GLOBALS
559 _globalOwnerTypes
= [