1
2
3
4 """
5 Tools for doing dynamic imports
6
7 This module has been borrowed from the Importing package.
8
9 @see: U{http://pypi.python.org/pypi/Importing}
10 @see: U{http://peak.telecommunity.com/DevCenter/Importing}
11
12 Original author: U{Phillip J. Eby<peak@eby-sarna.com>}
13
14 @since: 0.3.0
15 """
16
17 __all__ = [
18 'lazyModule', 'joinPath', 'whenImported', 'getModuleHooks',
19 ]
20
21 import sys, os.path
22 from types import ModuleType
23
24 postLoadHooks = {}
25 loadedModules = []
26
27 try:
28 from imp import find_module
29
30
31
32
33 find_module('pyamf.util.imports')
34 except ImportError:
36
37
38
39
40 PY_EXT = ('.py',)
41
42 if path is None:
43 path = sys.path
44
45 for p in path:
46 py = os.path.join(p, subname)
47
48 for full in PY_EXT:
49 full = py + full
50
51 if os.path.exists(full):
52 return open(full), full, None
53
54 py = os.path.join(p, subname, '__init__')
55
56 for full in PY_EXT:
57 full = py + full
58
59 if os.path.exists(full):
60 return None, os.path.join(p, subname), None
61
62 raise ImportError('No module named %s' % subname)
63
65 - def __init__(self, parent, child, hook, *args, **kwargs):
66 self.parent = parent
67 self.child = child
68 self.hook = hook
69 self.args = args
70 self.kwargs = kwargs
71
73 if not isinstance(other, SubModuleLoadHook):
74 return False
75
76 return self.parent == other.parent and self.child == other.child
77
79 return self.hook(*self.args, **self.kwargs)
80
83
85 __slots__ = ()
86 __reserved_attrs__ = ('__name__', '__file__', '__path__')
87
88 - def __init__(self, name, file, path=None):
94
100
106
110
112 """
113 Adjust a module name by a '/'-separated, relative or absolute path
114 """
115 module = modname.split('.')
116
117 for p in relativePath.split('/'):
118 if p == '..':
119 module.pop()
120 elif not p:
121 module = []
122 elif p != '.':
123 module.append(p)
124
125 return '.'.join(module)
126
128 """
129 Return module 'modname', but with its contents loaded "on demand"
130
131 This function returns 'sys.modules[modname]', if present. Otherwise
132 it creates a 'LazyModule' object for the specified module, caches it
133 in 'sys.modules', and returns it.
134
135 'LazyModule' is a subclass of the standard Python module type, that
136 remains empty until an attempt is made to access one of its
137 attributes. At that moment, the module is loaded into memory, and
138 any hooks that were defined via 'whenImported()' are invoked.
139
140 Note that calling 'lazyModule' with the name of a non-existent or
141 unimportable module will delay the 'ImportError' until the moment
142 access is attempted. The 'ImportError' will occur every time an
143 attribute access is attempted, until the problem is corrected.
144
145 This function also takes an optional second parameter, 'relativePath',
146 which will be interpreted as a '/'-separated path string relative to
147 'modname'. If a 'relativePath' is supplied, the module found by
148 traversing the path will be loaded instead of 'modname'. In the path,
149 '.' refers to the current module, and '..' to the current module's
150 parent. For example::
151
152 fooBaz = lazyModule('foo.bar','../baz')
153
154 will return the module 'foo.baz'. The main use of the 'relativePath'
155 feature is to allow relative imports in modules that are intended for
156 use with module inheritance. Where an absolute import would be carried
157 over as-is into the inheriting module, an import relative to '__name__'
158 will be relative to the inheriting module, e.g.::
159
160 something = lazyModule(__name__,'../path/to/something')
161
162 The above code will have different results in each module that inherits
163 it.
164
165 (Note: 'relativePath' can also be an absolute path (starting with '/');
166 this is mainly useful for module '__bases__' lists.)
167 """
168 if relativePath:
169 modname = joinPath(modname, relativePath)
170
171 if modname not in sys.modules:
172 file_name = path = None
173
174 if '.' in modname:
175 splitpos = modname.rindex('.')
176
177 parent = sys.modules[modname[:splitpos]]
178 file_name = find_module(modname[splitpos + 1:], parent.__path__)[1]
179 else:
180 file_name = find_module(modname)[1]
181
182 if os.path.isdir(file_name):
183 path = [file_name]
184 py = os.path.join(file_name, '__init__')
185
186 for full in ('.pyo', '.pyc', '.py'):
187 full = py + full
188
189 if os.path.exists(full):
190 break
191 else:
192 raise ImportError('No module name %d' % modname)
193
194 file_name = full
195
196 getModuleHooks(modname)
197 sys.modules[modname] = LazyModule(modname, file_name, path)
198
199 if '.' in modname:
200
201
202
203 splitpos = modname.rindex('.')
204
205 whenImported(
206 modname[:splitpos],
207 lambda m: setattr(m, modname[splitpos + 1:], sys.modules[modname])
208 )
209
210 return sys.modules[modname]
211
220
235
237 """
238 Get list of hooks for 'moduleName'; error if module already loaded
239 """
240 hooks = postLoadHooks.setdefault(moduleName, [])
241
242 if hooks is None:
243 raise AlreadyRead("Module already imported", moduleName)
244
245 return hooks
246
248 if moduleName in sys.modules and postLoadHooks.get(moduleName) is None:
249
250 module = sys.modules[moduleName]
251 hook(module)
252
253 return module
254
255 getModuleHooks(moduleName).append(hook)
256
257 return lazyModule(moduleName)
258
260 """
261 Call 'hook(module)' when module named 'moduleName' is first used
262
263 'hook' must accept one argument: the module object named by 'moduleName',
264 which must be a fully qualified (i.e. absolute) module name. The hook
265 should not raise any exceptions, or it may prevent later hooks from
266 running.
267
268 If the module has already been imported normally, 'hook(module)' is
269 called immediately, and the module object is returned from this function.
270 If the module has not been imported, or has only been imported lazily,
271 then the hook is called when the module is first used, and a lazy import
272 of the module is returned from this function. If the module was imported
273 lazily and used before calling this function, the hook is called
274 immediately, and the loaded module is returned from this function.
275
276 Note that using this function implies a possible lazy import of the
277 specified module, and lazy importing means that any 'ImportError' will be
278 deferred until the module is used.
279 """
280 if '.' in moduleName:
281
282
283 splitpos = moduleName.rindex('.')
284
285 sub_hook = SubModuleLoadHook(moduleName[:splitpos],
286 moduleName[splitpos + 1:], _setModuleHook, moduleName, hook)
287
288 if moduleName[:splitpos] not in postLoadHooks.keys():
289 whenImported(moduleName[:splitpos], sub_hook)
290 elif postLoadHooks[moduleName[:splitpos]] is None:
291 whenImported(moduleName[:splitpos], sub_hook)
292 elif sub_hook not in postLoadHooks[moduleName[:splitpos]]:
293 whenImported(moduleName[:splitpos], sub_hook)
294 else:
295 postLoadHooks[moduleName[:splitpos]].append(sub_hook)
296 else:
297 return _setModuleHook(moduleName, hook)
298