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