|
1 # |
|
2 # importers.py |
|
3 # |
|
4 # Demonstration subclasses of imputil.Importer |
|
5 # |
|
6 |
|
7 # There should be consideration for the imports below if it is desirable |
|
8 # to have "all" modules be imported through the imputil system. |
|
9 |
|
10 # these are C extensions |
|
11 import sys |
|
12 import imp |
|
13 import struct |
|
14 import marshal |
|
15 |
|
16 # these are .py modules |
|
17 import imputil |
|
18 import os |
|
19 |
|
20 ###################################################################### |
|
21 |
|
22 _TupleType = type(()) |
|
23 _StringType = type('') |
|
24 |
|
25 ###################################################################### |
|
26 |
|
27 # byte-compiled file suffic character |
|
28 _suffix_char = __debug__ and 'c' or 'o' |
|
29 |
|
30 # byte-compiled file suffix |
|
31 _suffix = '.py' + _suffix_char |
|
32 |
|
33 # the C_EXTENSION suffixes |
|
34 _c_suffixes = filter(lambda x: x[2] == imp.C_EXTENSION, imp.get_suffixes()) |
|
35 |
|
36 def _timestamp(pathname): |
|
37 "Return the file modification time as a Long." |
|
38 try: |
|
39 s = os.stat(pathname) |
|
40 except OSError: |
|
41 return None |
|
42 return long(s[8]) |
|
43 |
|
44 def _fs_import(dir, modname, fqname): |
|
45 "Fetch a module from the filesystem." |
|
46 |
|
47 pathname = os.path.join(dir, modname) |
|
48 if os.path.isdir(pathname): |
|
49 values = { '__pkgdir__' : pathname, '__path__' : [ pathname ] } |
|
50 ispkg = 1 |
|
51 pathname = os.path.join(pathname, '__init__') |
|
52 else: |
|
53 values = { } |
|
54 ispkg = 0 |
|
55 |
|
56 # look for dynload modules |
|
57 for desc in _c_suffixes: |
|
58 file = pathname + desc[0] |
|
59 try: |
|
60 fp = open(file, desc[1]) |
|
61 except IOError: |
|
62 pass |
|
63 else: |
|
64 module = imp.load_module(fqname, fp, file, desc) |
|
65 values['__file__'] = file |
|
66 return 0, module, values |
|
67 |
|
68 t_py = _timestamp(pathname + '.py') |
|
69 t_pyc = _timestamp(pathname + _suffix) |
|
70 if t_py is None and t_pyc is None: |
|
71 return None |
|
72 code = None |
|
73 if t_py is None or (t_pyc is not None and t_pyc >= t_py): |
|
74 file = pathname + _suffix |
|
75 f = open(file, 'rb') |
|
76 if f.read(4) == imp.get_magic(): |
|
77 t = struct.unpack('<I', f.read(4))[0] |
|
78 if t == t_py: |
|
79 code = marshal.load(f) |
|
80 f.close() |
|
81 if code is None: |
|
82 file = pathname + '.py' |
|
83 code = _compile(file, t_py) |
|
84 |
|
85 values['__file__'] = file |
|
86 return ispkg, code, values |
|
87 |
|
88 ###################################################################### |
|
89 # |
|
90 # Simple function-based importer |
|
91 # |
|
92 class FuncImporter(imputil.Importer): |
|
93 "Importer subclass to delegate to a function rather than method overrides." |
|
94 def __init__(self, func): |
|
95 self.func = func |
|
96 def get_code(self, parent, modname, fqname): |
|
97 return self.func(parent, modname, fqname) |
|
98 |
|
99 def install_with(func): |
|
100 FuncImporter(func).install() |
|
101 |
|
102 |
|
103 ###################################################################### |
|
104 # |
|
105 # Base class for archive-based importing |
|
106 # |
|
107 class PackageArchiveImporter(imputil.Importer): |
|
108 """Importer subclass to import from (file) archives. |
|
109 |
|
110 This Importer handles imports of the style <archive>.<subfile>, where |
|
111 <archive> can be located using a subclass-specific mechanism and the |
|
112 <subfile> is found in the archive using a subclass-specific mechanism. |
|
113 |
|
114 This class defines two hooks for subclasses: one to locate an archive |
|
115 (and possibly return some context for future subfile lookups), and one |
|
116 to locate subfiles. |
|
117 """ |
|
118 |
|
119 def get_code(self, parent, modname, fqname): |
|
120 if parent: |
|
121 # the Importer._finish_import logic ensures that we handle imports |
|
122 # under the top level module (package / archive). |
|
123 assert parent.__importer__ == self |
|
124 |
|
125 # if a parent "package" is provided, then we are importing a |
|
126 # sub-file from the archive. |
|
127 result = self.get_subfile(parent.__archive__, modname) |
|
128 if result is None: |
|
129 return None |
|
130 if isinstance(result, _TupleType): |
|
131 assert len(result) == 2 |
|
132 return (0,) + result |
|
133 return 0, result, {} |
|
134 |
|
135 # no parent was provided, so the archive should exist somewhere on the |
|
136 # default "path". |
|
137 archive = self.get_archive(modname) |
|
138 if archive is None: |
|
139 return None |
|
140 return 1, "", {'__archive__':archive} |
|
141 |
|
142 def get_archive(self, modname): |
|
143 """Get an archive of modules. |
|
144 |
|
145 This method should locate an archive and return a value which can be |
|
146 used by get_subfile to load modules from it. The value may be a simple |
|
147 pathname, an open file, or a complex object that caches information |
|
148 for future imports. |
|
149 |
|
150 Return None if the archive was not found. |
|
151 """ |
|
152 raise RuntimeError, "get_archive not implemented" |
|
153 |
|
154 def get_subfile(self, archive, modname): |
|
155 """Get code from a subfile in the specified archive. |
|
156 |
|
157 Given the specified archive (as returned by get_archive()), locate |
|
158 and return a code object for the specified module name. |
|
159 |
|
160 A 2-tuple may be returned, consisting of a code object and a dict |
|
161 of name/values to place into the target module. |
|
162 |
|
163 Return None if the subfile was not found. |
|
164 """ |
|
165 raise RuntimeError, "get_subfile not implemented" |
|
166 |
|
167 |
|
168 class PackageArchive(PackageArchiveImporter): |
|
169 "PackageArchiveImporter subclass that refers to a specific archive." |
|
170 |
|
171 def __init__(self, modname, archive_pathname): |
|
172 self.__modname = modname |
|
173 self.__path = archive_pathname |
|
174 |
|
175 def get_archive(self, modname): |
|
176 if modname == self.__modname: |
|
177 return self.__path |
|
178 return None |
|
179 |
|
180 # get_subfile is passed the full pathname of the archive |
|
181 |
|
182 |
|
183 ###################################################################### |
|
184 # |
|
185 # Emulate the standard directory-based import mechanism |
|
186 # |
|
187 class DirectoryImporter(imputil.Importer): |
|
188 "Importer subclass to emulate the standard importer." |
|
189 |
|
190 def __init__(self, dir): |
|
191 self.dir = dir |
|
192 |
|
193 def get_code(self, parent, modname, fqname): |
|
194 if parent: |
|
195 dir = parent.__pkgdir__ |
|
196 else: |
|
197 dir = self.dir |
|
198 |
|
199 # Return the module (and other info) if found in the specified |
|
200 # directory. Otherwise, return None. |
|
201 return _fs_import(dir, modname, fqname) |
|
202 |
|
203 def __repr__(self): |
|
204 return '<%s.%s for "%s" at 0x%x>' % (self.__class__.__module__, |
|
205 self.__class__.__name__, |
|
206 self.dir, |
|
207 id(self)) |
|
208 |
|
209 |
|
210 ###################################################################### |
|
211 # |
|
212 # Emulate the standard path-style import mechanism |
|
213 # |
|
214 class PathImporter(imputil.Importer): |
|
215 def __init__(self, path=sys.path): |
|
216 self.path = path |
|
217 |
|
218 def get_code(self, parent, modname, fqname): |
|
219 if parent: |
|
220 # we are looking for a module inside of a specific package |
|
221 return _fs_import(parent.__pkgdir__, modname, fqname) |
|
222 |
|
223 # scan sys.path, looking for the requested module |
|
224 for dir in self.path: |
|
225 if isinstance(dir, _StringType): |
|
226 result = _fs_import(dir, modname, fqname) |
|
227 if result: |
|
228 return result |
|
229 |
|
230 # not found |
|
231 return None |
|
232 |
|
233 ###################################################################### |
|
234 |
|
235 def _test_dir(): |
|
236 "Debug/test function to create DirectoryImporters from sys.path." |
|
237 imputil.ImportManager().install() |
|
238 path = sys.path[:] |
|
239 path.reverse() |
|
240 for d in path: |
|
241 sys.path.insert(0, DirectoryImporter(d)) |
|
242 sys.path.insert(0, imputil.BuiltinImporter()) |
|
243 |
|
244 def _test_revamp(): |
|
245 "Debug/test function for the revamped import system." |
|
246 imputil.ImportManager().install() |
|
247 sys.path.insert(0, PathImporter()) |
|
248 sys.path.insert(0, imputil.BuiltinImporter()) |