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