|
1 # Copyright (C) 2005, Giovanni Bajo |
|
2 # Based on previous work under copyright (c) 2002 McMillan Enterprises, Inc. |
|
3 # |
|
4 # This program is free software; you can redistribute it and/or |
|
5 # modify it under the terms of the GNU General Public License |
|
6 # as published by the Free Software Foundation; either version 2 |
|
7 # of the License, or (at your option) any later version. |
|
8 # |
|
9 # In addition to the permissions in the GNU General Public License, the |
|
10 # authors give you unlimited permission to link or embed the compiled |
|
11 # version of this file into combinations with other programs, and to |
|
12 # distribute those combinations without any restriction coming from the |
|
13 # use of this file. (The General Public License restrictions do apply in |
|
14 # other respects; for example, they cover modification of the file, and |
|
15 # distribution when not linked into a combine executable.) |
|
16 # |
|
17 # This program is distributed in the hope that it will be useful, |
|
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 # GNU General Public License for more details. |
|
21 # |
|
22 # You should have received a copy of the GNU General Public License |
|
23 # along with this program; if not, write to the Free Software |
|
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|
25 |
|
26 # **NOTE** This module is used during bootstrap. Import *ONLY* builtin modules. |
|
27 import sys |
|
28 import imp |
|
29 import marshal |
|
30 |
|
31 try: |
|
32 py_version = sys.version_info |
|
33 except AttributeError: |
|
34 py_version = (1,5) |
|
35 |
|
36 #=======================Owners==========================# |
|
37 # An Owner does imports from a particular piece of turf |
|
38 # That is, there's an Owner for each thing on sys.path |
|
39 # There are owners for directories and .pyz files. |
|
40 # There could be owners for zip files, or even URLs. |
|
41 # A shadowpath (a dictionary mapping the names in |
|
42 # sys.path to their owners) is used so that sys.path |
|
43 # (or a package's __path__) is still a bunch of strings, |
|
44 |
|
45 STRINGTYPE = type('') |
|
46 |
|
47 class Owner: |
|
48 def __init__(self, path): |
|
49 self.path = path |
|
50 def __str__(self): |
|
51 return self.path |
|
52 def getmod(self, nm): |
|
53 return None |
|
54 class DirOwner(Owner): |
|
55 def __init__(self, path): |
|
56 if path == '': |
|
57 path = _os_getcwd() |
|
58 if not pathisdir(path): |
|
59 raise ValueError, "%s is not a directory" % path |
|
60 Owner.__init__(self, path) |
|
61 def getmod(self, nm, getsuffixes=imp.get_suffixes, loadco=marshal.loads, newmod=imp.new_module): |
|
62 pth = _os_path_join(self.path, nm) |
|
63 possibles = [(pth, 0, None)] |
|
64 if pathisdir(pth): |
|
65 possibles.insert(0, (_os_path_join(pth, '__init__'), 1, pth)) |
|
66 py = pyc = None |
|
67 for pth, ispkg, pkgpth in possibles: |
|
68 for ext, mode, typ in getsuffixes(): |
|
69 attempt = pth+ext |
|
70 try: |
|
71 st = _os_stat(attempt) |
|
72 except: |
|
73 pass |
|
74 else: |
|
75 if typ == imp.C_EXTENSION: |
|
76 fp = open(attempt, 'rb') |
|
77 mod = imp.load_module(nm, fp, attempt, (ext, mode, typ)) |
|
78 mod.__file__ = attempt |
|
79 return mod |
|
80 elif typ == imp.PY_SOURCE: |
|
81 py = (attempt, st) |
|
82 else: |
|
83 pyc = (attempt, st) |
|
84 if py or pyc: |
|
85 break |
|
86 if py is None and pyc is None: |
|
87 return None |
|
88 while 1: |
|
89 if pyc is None or py and pyc[1][8] < py[1][8]: |
|
90 try: |
|
91 co = compile(open(py[0], 'r').read()+'\n', py[0], 'exec') |
|
92 break |
|
93 except SyntaxError, e: |
|
94 print "Invalid syntax in %s" % py[0] |
|
95 print e.args |
|
96 raise |
|
97 elif pyc: |
|
98 stuff = open(pyc[0], 'rb').read() |
|
99 try: |
|
100 co = loadco(stuff[8:]) |
|
101 break |
|
102 except (ValueError, EOFError): |
|
103 pyc = None |
|
104 else: |
|
105 return None |
|
106 mod = newmod(nm) |
|
107 mod.__file__ = co.co_filename |
|
108 if ispkg: |
|
109 mod.__path__ = [pkgpth] |
|
110 subimporter = PathImportDirector(mod.__path__) |
|
111 mod.__importsub__ = subimporter.getmod |
|
112 mod.__co__ = co |
|
113 return mod |
|
114 |
|
115 _globalownertypes = [ |
|
116 DirOwner, |
|
117 Owner, |
|
118 ] |
|
119 |
|
120 #===================Import Directors====================================# |
|
121 # ImportDirectors live on the metapath |
|
122 # There's one for builtins, one for frozen modules, and one for sys.path |
|
123 # Windows gets one for modules gotten from the Registry |
|
124 # Mac would have them for PY_RESOURCE modules etc. |
|
125 # A generalization of Owner - their concept of "turf" is broader |
|
126 |
|
127 class ImportDirector(Owner): |
|
128 pass |
|
129 class BuiltinImportDirector(ImportDirector): |
|
130 def __init__(self): |
|
131 self.path = 'Builtins' |
|
132 def getmod(self, nm, isbuiltin=imp.is_builtin): |
|
133 if isbuiltin(nm): |
|
134 mod = imp.load_module(nm, None, nm, ('','',imp.C_BUILTIN)) |
|
135 return mod |
|
136 return None |
|
137 class FrozenImportDirector(ImportDirector): |
|
138 def __init__(self): |
|
139 self.path = 'FrozenModules' |
|
140 def getmod(self, nm, isfrozen=imp.is_frozen): |
|
141 if isfrozen(nm): |
|
142 mod = imp.load_module(nm, None, nm, ('','',imp.PY_FROZEN)) |
|
143 if hasattr(mod, '__path__'): |
|
144 mod.__importsub__ = lambda name, pname=nm, owner=self: owner.getmod(pname+'.'+name) |
|
145 return mod |
|
146 return None |
|
147 class RegistryImportDirector(ImportDirector): |
|
148 # for Windows only |
|
149 def __init__(self): |
|
150 self.path = "WindowsRegistry" |
|
151 self.map = {} |
|
152 try: |
|
153 import win32api |
|
154 ## import win32con |
|
155 except ImportError: |
|
156 pass |
|
157 else: |
|
158 HKEY_CURRENT_USER = -2147483647 |
|
159 HKEY_LOCAL_MACHINE = -2147483646 |
|
160 KEY_ALL_ACCESS = 983103 |
|
161 KEY_READ = 131097 |
|
162 subkey = r"Software\Python\PythonCore\%s\Modules" % sys.winver |
|
163 for root in (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE): |
|
164 try: |
|
165 #hkey = win32api.RegOpenKeyEx(root, subkey, 0, KEY_ALL_ACCESS) |
|
166 hkey = win32api.RegOpenKeyEx(root, subkey, 0, KEY_READ) |
|
167 except: |
|
168 pass |
|
169 else: |
|
170 numsubkeys, numvalues, lastmodified = win32api.RegQueryInfoKey(hkey) |
|
171 for i in range(numsubkeys): |
|
172 subkeyname = win32api.RegEnumKey(hkey, i) |
|
173 #hskey = win32api.RegOpenKeyEx(hkey, subkeyname, 0, KEY_ALL_ACCESS) |
|
174 hskey = win32api.RegOpenKeyEx(hkey, subkeyname, 0, KEY_READ) |
|
175 val = win32api.RegQueryValueEx(hskey, '') |
|
176 desc = getDescr(val[0]) |
|
177 self.map[subkeyname] = (val[0], desc) |
|
178 hskey.Close() |
|
179 hkey.Close() |
|
180 break |
|
181 def getmod(self, nm): |
|
182 stuff = self.map.get(nm) |
|
183 if stuff: |
|
184 fnm, desc = stuff |
|
185 fp = open(fnm, 'rb') |
|
186 mod = imp.load_module(nm, fp, fnm, desc) |
|
187 mod.__file__ = fnm |
|
188 return mod |
|
189 return None |
|
190 |
|
191 class PathImportDirector(ImportDirector): |
|
192 def __init__(self, pathlist=None, importers=None, ownertypes=None): |
|
193 if pathlist is None: |
|
194 self.path = sys.path |
|
195 else: |
|
196 self.path = pathlist |
|
197 if ownertypes == None: |
|
198 self.ownertypes = _globalownertypes |
|
199 else: |
|
200 self.ownertypes = ownertypes |
|
201 if importers: |
|
202 self.shadowpath = importers |
|
203 else: |
|
204 self.shadowpath = {} |
|
205 self.inMakeOwner = 0 |
|
206 self.building = {} |
|
207 def getmod(self, nm): |
|
208 mod = None |
|
209 for thing in self.path: |
|
210 if type(thing) is STRINGTYPE: |
|
211 owner = self.shadowpath.get(thing, -1) |
|
212 if owner == -1: |
|
213 owner = self.shadowpath[thing] = self.makeOwner(thing) |
|
214 if owner: |
|
215 mod = owner.getmod(nm) |
|
216 else: |
|
217 mod = thing.getmod(nm) |
|
218 if mod: |
|
219 break |
|
220 return mod |
|
221 def makeOwner(self, path): |
|
222 if self.building.get(path): |
|
223 return None |
|
224 self.building[path] = 1 |
|
225 owner = None |
|
226 for klass in self.ownertypes: |
|
227 try: |
|
228 # this may cause an import, which may cause recursion |
|
229 # hence the protection |
|
230 owner = klass(path) |
|
231 except: |
|
232 pass |
|
233 else: |
|
234 break |
|
235 del self.building[path] |
|
236 return owner |
|
237 |
|
238 def getDescr(fnm): |
|
239 ext = getpathext(fnm) |
|
240 for (suffix, mode, typ) in imp.get_suffixes(): |
|
241 if suffix == ext: |
|
242 return (suffix, mode, typ) |
|
243 |
|
244 #=================ImportManager============================# |
|
245 # The one-and-only ImportManager |
|
246 # ie, the builtin import |
|
247 |
|
248 UNTRIED = -1 |
|
249 |
|
250 class ImportManager: |
|
251 # really the equivalent of builtin import |
|
252 def __init__(self): |
|
253 self.metapath = [ |
|
254 BuiltinImportDirector(), |
|
255 FrozenImportDirector(), |
|
256 RegistryImportDirector(), |
|
257 PathImportDirector() |
|
258 ] |
|
259 self.threaded = 0 |
|
260 self.rlock = None |
|
261 self.locker = None |
|
262 self.setThreaded() |
|
263 def setThreaded(self): |
|
264 thread = sys.modules.get('thread', None) |
|
265 if thread and not self.threaded: |
|
266 ## print "iu setting threaded" |
|
267 self.threaded = 1 |
|
268 self.rlock = thread.allocate_lock() |
|
269 self._get_ident = thread.get_ident |
|
270 def install(self): |
|
271 import __builtin__ |
|
272 __builtin__.__import__ = self.importHook |
|
273 __builtin__.reload = self.reloadHook |
|
274 def importHook(self, name, globals=None, locals=None, fromlist=None): |
|
275 # first see if we could be importing a relative name |
|
276 #print "importHook(%s, %s, locals, %s)" % (name, globals['__name__'], fromlist) |
|
277 _sys_modules_get = sys.modules.get |
|
278 contexts = [None] |
|
279 if globals: |
|
280 importernm = globals.get('__name__', '') |
|
281 if importernm: |
|
282 if hasattr(_sys_modules_get(importernm), '__path__'): |
|
283 # If you use the "from __init__ import" syntax, the package |
|
284 # name will have a __init__ in it. We want to strip it. |
|
285 if importernm[-len(".__init__"):] == ".__init__": |
|
286 importernm = importernm[:-len(".__init__")] |
|
287 contexts.insert(0,importernm) |
|
288 else: |
|
289 pkgnm = packagename(importernm) |
|
290 if pkgnm: |
|
291 contexts.insert(0,pkgnm) |
|
292 # so contexts is [pkgnm, None] or just [None] |
|
293 # now break the name being imported up so we get: |
|
294 # a.b.c -> [a, b, c] |
|
295 nmparts = namesplit(name) |
|
296 _self_doimport = self.doimport |
|
297 threaded = self.threaded |
|
298 for context in contexts: |
|
299 ctx = context |
|
300 for i in range(len(nmparts)): |
|
301 nm = nmparts[i] |
|
302 #print " importHook trying %s in %s" % (nm, ctx) |
|
303 if ctx: |
|
304 fqname = ctx + '.' + nm |
|
305 else: |
|
306 fqname = nm |
|
307 if threaded: |
|
308 self._acquire() |
|
309 mod = _sys_modules_get(fqname, UNTRIED) |
|
310 if mod is UNTRIED: |
|
311 try: |
|
312 mod = _self_doimport(nm, ctx, fqname) |
|
313 except: |
|
314 if threaded: |
|
315 self._release() |
|
316 raise |
|
317 if threaded: |
|
318 self._release() |
|
319 if mod: |
|
320 ctx = fqname |
|
321 else: |
|
322 break |
|
323 else: |
|
324 # no break, point i beyond end |
|
325 i = i + 1 |
|
326 if i: |
|
327 break |
|
328 |
|
329 if i<len(nmparts): |
|
330 if ctx and hasattr(sys.modules[ctx], nmparts[i]): |
|
331 #print "importHook done with %s %s %s (case 1)" % (name, globals['__name__'], fromlist) |
|
332 return sys.modules[nmparts[0]] |
|
333 del sys.modules[fqname] |
|
334 raise ImportError, "No module named %s" % fqname |
|
335 if fromlist is None: |
|
336 #print "importHook done with %s %s %s (case 2)" % (name, globals['__name__'], fromlist) |
|
337 if context: |
|
338 return sys.modules[context+'.'+nmparts[0]] |
|
339 return sys.modules[nmparts[0]] |
|
340 bottommod = sys.modules[ctx] |
|
341 if hasattr(bottommod, '__path__'): |
|
342 fromlist = list(fromlist) |
|
343 i = 0 |
|
344 while i < len(fromlist): |
|
345 nm = fromlist[i] |
|
346 if nm == '*': |
|
347 fromlist[i:i+1] = list(getattr(bottommod, '__all__', [])) |
|
348 if i >= len(fromlist): |
|
349 break |
|
350 nm = fromlist[i] |
|
351 i = i + 1 |
|
352 if not hasattr(bottommod, nm): |
|
353 if threaded: |
|
354 self._acquire() |
|
355 try: |
|
356 mod = self.doimport(nm, ctx, ctx+'.'+nm) |
|
357 except: |
|
358 pass |
|
359 if threaded: |
|
360 self._release() |
|
361 #print "importHook done with %s %s %s (case 3)" % (name, globals['__name__'], fromlist) |
|
362 return bottommod |
|
363 def doimport(self, nm, parentnm, fqname, reload=0): |
|
364 # Not that nm is NEVER a dotted name at this point |
|
365 #print "doimport(%s, %s, %s)" % (nm, parentnm, fqname) |
|
366 if parentnm: |
|
367 parent = sys.modules[parentnm] |
|
368 if hasattr(parent, '__path__'): |
|
369 importfunc = getattr(parent, '__importsub__', None) |
|
370 if not importfunc: |
|
371 subimporter = PathImportDirector(parent.__path__) |
|
372 importfunc = parent.__importsub__ = subimporter.getmod |
|
373 mod = importfunc(nm) |
|
374 if mod and not reload: |
|
375 setattr(parent, nm, mod) |
|
376 else: |
|
377 #print "..parent not a package" |
|
378 return None |
|
379 else: |
|
380 # now we're dealing with an absolute import |
|
381 for director in self.metapath: |
|
382 mod = director.getmod(nm) |
|
383 if mod: |
|
384 break |
|
385 if mod: |
|
386 mod.__name__ = fqname |
|
387 if reload: |
|
388 sys.modules[fqname].__dict__.update(mod.__dict__) |
|
389 else: |
|
390 sys.modules[fqname] = mod |
|
391 if hasattr(mod, '__co__'): |
|
392 co = mod.__co__ |
|
393 del mod.__co__ |
|
394 try: |
|
395 if reload: |
|
396 exec co in sys.modules[fqname].__dict__ |
|
397 else: |
|
398 exec co in mod.__dict__ |
|
399 except: |
|
400 # In Python 2.4 and above, sys.modules is left clean |
|
401 # after a broken import. We need to do the same to |
|
402 # achieve perfect compatibility (see ticket #32). |
|
403 if py_version >= (2,4,0): |
|
404 # FIXME: how can we recover from a broken reload()? |
|
405 # Should we save the mod dict and restore it in case |
|
406 # of failure? |
|
407 if not reload: |
|
408 del sys.modules[fqname] |
|
409 raise |
|
410 if fqname == 'thread' and not self.threaded: |
|
411 ## print "thread detected!" |
|
412 self.setThreaded() |
|
413 else: |
|
414 sys.modules[fqname] = None |
|
415 #print "..found %s" % mod |
|
416 return mod |
|
417 def reloadHook(self, mod): |
|
418 fqnm = mod.__name__ |
|
419 nm = namesplit(fqnm)[-1] |
|
420 parentnm = packagename(fqnm) |
|
421 newmod = self.doimport(nm, parentnm, fqnm, reload=1) |
|
422 #mod.__dict__.update(newmod.__dict__) |
|
423 return newmod |
|
424 def _acquire(self): |
|
425 if self.rlock.locked(): |
|
426 if self.locker == self._get_ident(): |
|
427 self.lockcount = self.lockcount + 1 |
|
428 ## print "_acquire incrementing lockcount to", self.lockcount |
|
429 return |
|
430 self.rlock.acquire() |
|
431 self.locker = self._get_ident() |
|
432 self.lockcount = 0 |
|
433 ## print "_acquire first time!" |
|
434 def _release(self): |
|
435 if self.lockcount: |
|
436 self.lockcount = self.lockcount - 1 |
|
437 ## print "_release decrementing lockcount to", self.lockcount |
|
438 else: |
|
439 self.locker = None |
|
440 self.rlock.release() |
|
441 ## print "_release releasing lock!" |
|
442 |
|
443 #=========some helper functions=============================# |
|
444 |
|
445 def packagename(s): |
|
446 for i in range(len(s)-1, -1, -1): |
|
447 if s[i] == '.': |
|
448 break |
|
449 else: |
|
450 return '' |
|
451 return s[:i] |
|
452 |
|
453 def namesplit(s): |
|
454 rslt = [] |
|
455 i = j = 0 |
|
456 for j in range(len(s)): |
|
457 if s[j] == '.': |
|
458 rslt.append(s[i:j]) |
|
459 i = j+1 |
|
460 if i < len(s): |
|
461 rslt.append(s[i:]) |
|
462 return rslt |
|
463 |
|
464 def getpathext(fnm): |
|
465 for i in range(len(fnm)-1, -1, -1): |
|
466 if fnm[i] == '.': |
|
467 return fnm[i:] |
|
468 return '' |
|
469 |
|
470 def pathisdir(pathname): |
|
471 "Local replacement for os.path.isdir()." |
|
472 try: |
|
473 s = _os_stat(pathname) |
|
474 except OSError: |
|
475 return None |
|
476 return (s[0] & 0170000) == 0040000 |
|
477 |
|
478 _os_stat = _os_path_join = _os_getcwd = _os_path_dirname = None |
|
479 def _os_bootstrap(): |
|
480 "Set up 'os' module replacement functions for use during import bootstrap." |
|
481 |
|
482 names = sys.builtin_module_names |
|
483 |
|
484 join = dirname = None |
|
485 mindirlen = 0 |
|
486 if 'posix' in names: |
|
487 sep = '/' |
|
488 mindirlen = 1 |
|
489 from posix import stat, getcwd |
|
490 elif 'nt' in names: |
|
491 sep = '\\' |
|
492 mindirlen = 3 |
|
493 from nt import stat, getcwd |
|
494 elif 'dos' in names: |
|
495 sep = '\\' |
|
496 mindirlen = 3 |
|
497 from dos import stat, getcwd |
|
498 elif 'os2' in names: |
|
499 sep = '\\' |
|
500 from os2 import stat, getcwd |
|
501 elif 'mac' in names: |
|
502 from mac import stat, getcwd |
|
503 def join(a, b): |
|
504 if a == '': |
|
505 return b |
|
506 path = s |
|
507 if ':' not in a: |
|
508 a = ':' + a |
|
509 if a[-1:] != ':': |
|
510 a = a + ':' |
|
511 return a + b |
|
512 else: |
|
513 raise ImportError, 'no os specific module found' |
|
514 |
|
515 if join is None: |
|
516 def join(a, b, sep=sep): |
|
517 if a == '': |
|
518 return b |
|
519 lastchar = a[-1:] |
|
520 if lastchar == '/' or lastchar == sep: |
|
521 return a + b |
|
522 return a + sep + b |
|
523 |
|
524 if dirname is None: |
|
525 def dirname(a, sep=sep, mindirlen=mindirlen): |
|
526 for i in range(len(a)-1, -1, -1): |
|
527 c = a[i] |
|
528 if c == '/' or c == sep: |
|
529 if i < mindirlen: |
|
530 return a[:i+1] |
|
531 return a[:i] |
|
532 return '' |
|
533 |
|
534 global _os_stat |
|
535 _os_stat = stat |
|
536 |
|
537 global _os_path_join |
|
538 _os_path_join = join |
|
539 |
|
540 global _os_path_dirname |
|
541 _os_path_dirname = dirname |
|
542 |
|
543 global _os_getcwd |
|
544 _os_getcwd = getcwd |
|
545 |
|
546 _string_replace = _string_join = _string_split = None |
|
547 def _string_bootstrap(): |
|
548 """ |
|
549 Set up 'string' module replacement functions for use during import bootstrap. |
|
550 |
|
551 During bootstrap, we can use only builtin modules since import does not work |
|
552 yet. For Python 2.0+, we can use string methods so this is not a problem. |
|
553 For Python 1.5, we would need the string module, so we need replacements. |
|
554 """ |
|
555 s = type('') |
|
556 |
|
557 global _string_replace, _string_join, _string_split |
|
558 |
|
559 if hasattr(s, "join"): |
|
560 _string_join = s.join |
|
561 else: |
|
562 def join(sep, words): |
|
563 res = '' |
|
564 for w in words: |
|
565 res = res + (sep + w) |
|
566 return res[len(sep):] |
|
567 _string_join = join |
|
568 |
|
569 if hasattr(s, "split"): |
|
570 _string_split = s.split |
|
571 else: |
|
572 def split(s, sep, maxsplit=0): |
|
573 res = [] |
|
574 nsep = len(sep) |
|
575 if nsep == 0: |
|
576 return [s] |
|
577 ns = len(s) |
|
578 if maxsplit <= 0: maxsplit = ns |
|
579 i = j = 0 |
|
580 count = 0 |
|
581 while j+nsep <= ns: |
|
582 if s[j:j+nsep] == sep: |
|
583 count = count + 1 |
|
584 res.append(s[i:j]) |
|
585 i = j = j + nsep |
|
586 if count >= maxsplit: break |
|
587 else: |
|
588 j = j + 1 |
|
589 res.append(s[i:]) |
|
590 return res |
|
591 _string_split = split |
|
592 |
|
593 if hasattr(s, "replace"): |
|
594 _string_replace = s.replace |
|
595 else: |
|
596 def replace(str, old, new): |
|
597 return _string_join(new, _string_split(str, old)) |
|
598 _string_replace = replace |
|
599 |
|
600 |
|
601 |
|
602 _os_bootstrap() |
|
603 _string_bootstrap() |