##// END OF EJS Templates
Update deepreload to use a rewritten knee.py. Fixes dreload(numpy)....
Bradley M. Froehle -
Show More
@@ -1,188 +1,320 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 A module to change reload() so that it acts recursively.
3 A module to change reload() so that it acts recursively.
4 To enable it type::
4 To enable it type::
5
5
6 import __builtin__, deepreload
6 import __builtin__, deepreload
7 __builtin__.reload = deepreload.reload
7 __builtin__.reload = deepreload.reload
8
8
9 You can then disable it with::
9 You can then disable it with::
10
10
11 __builtin__.reload = deepreload.original_reload
11 __builtin__.reload = deepreload.original_reload
12
12
13 Alternatively, you can add a dreload builtin alongside normal reload with::
13 Alternatively, you can add a dreload builtin alongside normal reload with::
14
14
15 __builtin__.dreload = deepreload.reload
15 __builtin__.dreload = deepreload.reload
16
16
17 This code is almost entirely based on knee.py from the standard library.
17 This code is almost entirely based on knee.py, which is a Python
18 re-implementation of hierarchical module import.
18 """
19 """
19
20 #*****************************************************************************
20 #*****************************************************************************
21 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
21 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
22 #
22 #
23 # Distributed under the terms of the BSD License. The full license is in
23 # Distributed under the terms of the BSD License. The full license is in
24 # the file COPYING, distributed as part of this software.
24 # the file COPYING, distributed as part of this software.
25 #*****************************************************************************
25 #*****************************************************************************
26
26
27 import __builtin__
27 import __builtin__
28 import imp
28 import imp
29 import sys
29 import sys
30
30
31 # Replacement for __import__()
31 from types import ModuleType
32 def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1):
32 from warnings import warn
33 # For now level is ignored, it's just there to prevent crash
34 # with from __future__ import absolute_import
35 parent = determine_parent(globals)
36 q, tail = find_head_package(parent, name)
37 m = load_tail(q, tail)
38 if not fromlist:
39 return q
40 if hasattr(m, "__path__"):
41 ensure_fromlist(m, fromlist)
42 return m
43
33
44 def determine_parent(globals):
34 def get_parent(globals, level):
45 if not globals or not globals.has_key("__name__"):
35 """
46 return None
36 parent, name = get_parent(globals, level)
47 pname = globals['__name__']
37
48 if globals.has_key("__path__"):
38 Return the package that an import is being performed in. If globals comes
49 parent = sys.modules[pname]
39 from the module foo.bar.bat (not itself a package), this returns the
50 assert globals is parent.__dict__
40 sys.modules entry for foo.bar. If globals is from a package's __init__.py,
51 return parent
41 the package's entry in sys.modules is returned.
52 if '.' in pname:
42
53 i = pname.rfind('.')
43 If globals doesn't come from a package or a module in a package, or a
54 pname = pname[:i]
44 corresponding entry is not found in sys.modules, None is returned.
55 parent = sys.modules[pname]
45 """
56 assert parent.__name__ == pname
46 orig_level = level
57 return parent
58 return None
59
47
60 def find_head_package(parent, name):
48 if not level or not isinstance(globals, dict):
61 # Import the first
49 return None, ''
62 if '.' in name:
50
63 # 'some.nested.package' -> head = 'some', tail = 'nested.package'
51 pkgname = globals.get('__package__', None)
64 i = name.find('.')
52
65 head = name[:i]
53 if pkgname is not None:
66 tail = name[i+1:]
54 # __package__ is set, so use it
55 if not hasattr(pkgname, 'rindex'):
56 raise ValueError('__package__ set to non-string')
57 if len(pkgname) == 0:
58 if level > 0:
59 raise ValueError('Attempted relative import in non-package')
60 return None, ''
61 name = pkgname
67 else:
62 else:
68 # 'packagename' -> head = 'packagename', tail = ''
63 # __package__ not set, so figure it out and set it
69 head = name
64 if '__name__' not in globals:
70 tail = ""
65 return None, ''
71 if parent:
66 modname = globals['__name__']
72 # If this is a subpackage then qname = parent's name + head
67
73 qname = "%s.%s" % (parent.__name__, head)
68 if '__path__' in globals:
69 # __path__ is set, so modname is already the package name
70 globals['__package__'] = name = modname
74 else:
71 else:
75 qname = head
72 # Normal module, so work out the package name if any
76 q = import_module(head, qname, parent)
73 lastdot = modname.rfind('.')
77 if q: return q, tail
74 if lastdot < 0 and level > 0:
78 if parent:
75 raise ValueError("Attempted relative import in non-package")
79 qname = head
76 if lastdot < 0:
80 parent = None
77 globals['__package__'] = None
81 q = import_module(head, qname, parent)
78 return None, ''
82 if q: return q, tail
79 globals['__package__'] = name = modname[:lastdot]
83 raise ImportError, "No module named " + qname
84
85 def load_tail(q, tail):
86 m = q
87 while tail:
88 i = tail.find('.')
89 if i < 0: i = len(tail)
90 head, tail = tail[:i], tail[i+1:]
91
92 # fperez: fix dotted.name reloading failures by changing:
93 #mname = "%s.%s" % (m.__name__, head)
94 # to:
95 mname = m.__name__
96 # This needs more testing!!! (I don't understand this module too well)
97
98 #print '** head,tail=|%s|->|%s|, mname=|%s|' % (head,tail,mname) # dbg
99 m = import_module(head, mname, m)
100 if not m:
101 raise ImportError, "No module named " + mname
102 return m
103
80
104 def ensure_fromlist(m, fromlist, recursive=0):
81 dot = len(name)
105 for sub in fromlist:
82 for x in xrange(level, 1, -1):
106 if sub == "*":
107 if not recursive:
108 try:
83 try:
109 all = m.__all__
84 dot = name.rindex('.', 0, dot)
110 except AttributeError:
85 except ValueError:
111 pass
86 raise ValueError("attempted relative import beyond top-level "
87 "package")
88 name = name[:dot]
89
90 try:
91 parent = sys.modules[name]
92 except:
93 if orig_level < 1:
94 warn("Parent module '%.200s' not found while handling absolute "
95 "import" % name)
96 parent = None
97 else:
98 raise SystemError("Parent module '%.200s' not loaded, cannot "
99 "perform relative import" % name)
100
101 # We expect, but can't guarantee, if parent != None, that:
102 # - parent.__name__ == name
103 # - parent.__dict__ is globals
104 # If this is violated... Who cares?
105 return parent, name
106
107 def load_next(mod, altmod, name, buf):
108 """
109 mod, name, buf = load_next(mod, altmod, name, buf)
110
111 altmod is either None or same as mod
112 """
113
114 if len(name) == 0:
115 # completely empty module name should only happen in
116 # 'from . import' (or '__import__("")')
117 return mod, None, buf
118
119 dot = name.find('.')
120 if dot == 0:
121 raise ValueError('Empty module name')
122
123 if dot < 0:
124 subname = name
125 next = None
112 else:
126 else:
113 ensure_fromlist(m, all, 1)
127 subname = name[:dot]
114 continue
128 next = name[dot+1:]
115 if sub != "*" and not hasattr(m, sub):
129
116 subname = "%s.%s" % (m.__name__, sub)
130 if buf != '':
117 submod = import_module(sub, subname, m)
131 buf += '.'
118 if not submod:
132 buf += subname
119 raise ImportError, "No module named " + subname
133
134 result = import_submodule(mod, subname, buf)
135 if result is None and mod != altmod:
136 result = import_submodule(altmod, subname, subname)
137 if result is not None:
138 buf = subname
139
140 if result is None:
141 raise ImportError("No module named %.200s" % name)
142
143 return result, next, buf
120
144
121 # Need to keep track of what we've already reloaded to prevent cyclic evil
145 # Need to keep track of what we've already reloaded to prevent cyclic evil
122 found_now = {}
146 found_now = {}
123
147
124 def import_module(partname, fqname, parent):
148 def import_submodule(mod, subname, fullname):
149 """m = import_submodule(mod, subname, fullname)"""
150 # Require:
151 # if mod == None: subname == fullname
152 # else: mod.__name__ + "." + subname == fullname
153
125 global found_now
154 global found_now
126 if found_now.has_key(fqname):
155 if fullname in found_now and fullname in sys.modules:
127 try:
156 m = sys.modules[fullname]
128 return sys.modules[fqname]
157 else:
129 except KeyError:
158 print 'Reloading', fullname
130 pass
159 found_now[fullname] = 1
160 oldm = sys.modules.get(fullname, None)
131
161
132 print 'Reloading', fqname #, sys.excepthook is sys.__excepthook__, \
162 if mod is None:
133 #sys.displayhook is sys.__displayhook__
163 path = None
164 elif hasattr(mod, '__path__'):
165 path = mod.__path__
166 else:
167 return None
134
168
135 found_now[fqname] = 1
136 try:
169 try:
137 fp, pathname, stuff = imp.find_module(partname,
170 fp, filename, stuff = imp.find_module(subname, path)
138 parent and parent.__path__)
139 except ImportError:
171 except ImportError:
140 return None
172 return None
141
173
142 try:
174 try:
143 m = imp.load_module(fqname, fp, pathname, stuff)
175 m = imp.load_module(fullname, fp, filename, stuff)
176 except:
177 # load_module probably removed name from modules because of
178 # the error. Put back the original module object.
179 if oldm:
180 sys.modules[fullname] = oldm
181 raise
144 finally:
182 finally:
145 if fp: fp.close()
183 if fp: fp.close()
146
184
147 if parent:
185 add_submodule(mod, m, fullname, subname)
148 setattr(parent, partname, m)
149
186
150 return m
187 return m
151
188
152 def deep_reload_hook(module):
189 def add_submodule(mod, submod, fullname, subname):
153 name = module.__name__
190 """mod.{subname} = submod"""
154 if '.' not in name:
191 if mod is None:
155 return import_module(name, name, None)
192 return #Nothing to do here.
156 i = name.rfind('.')
193
157 pname = name[:i]
194 if submod is None:
158 parent = sys.modules[pname]
195 submod = sys.modules[fullname]
159 return import_module(name[i+1:], name, parent)
196
197 setattr(mod, subname, submod)
198
199 return
200
201 def ensure_fromlist(mod, fromlist, buf, recursive):
202 """Handle 'from module import a, b, c' imports."""
203 if not hasattr(mod, '__path__'):
204 return
205 for item in fromlist:
206 if not hasattr(item, 'rindex'):
207 raise TypeError("Item in ``from list'' not a string")
208 if item == '*':
209 if recursive:
210 continue # avoid endless recursion
211 try:
212 all = mod.__all__
213 except AttributeError:
214 pass
215 else:
216 ret = ensure_fromlist(mod, all, buf, 1)
217 if not ret:
218 return 0
219 elif not hasattr(mod, item):
220 import_submodule(mod, item, buf + '.' + item)
221
222 def import_module_level(name, globals=None, locals=None, fromlist=None, level=-1):
223 """Replacement for __import__()"""
224 parent, buf = get_parent(globals, level)
225
226 head, name, buf = load_next(parent, None if level < 0 else parent, name, buf)
227
228 tail = head
229 while name:
230 tail, name, buf = load_next(tail, tail, name, buf)
231
232 # If tail is None, both get_parent and load_next found
233 # an empty module name: someone called __import__("") or
234 # doctored faulty bytecode
235 if tail is None:
236 raise ValueError('Empty module name')
237
238 if not fromlist:
239 return head
240
241 ensure_fromlist(tail, fromlist, buf, 0)
242 return tail
243
244 modules_reloading = {}
245
246 def reload_module(m):
247 """Replacement for reload()."""
248 if not isinstance(m, ModuleType):
249 raise TypeError("reload() argument must be module")
250
251 name = m.__name__
252
253 if name not in sys.modules:
254 raise ImportError("reload(): module %.200s not in sys.modules" % name)
255
256 global modules_reloading
257 try:
258 return modules_reloading[name]
259 except:
260 modules_reloading[name] = m
261
262 dot = name.rfind('.')
263 if dot < 0:
264 subname = name
265 path = None
266 else:
267 try:
268 parent = sys.modules[name[:dot]]
269 except KeyError:
270 modules_reloading.clear()
271 raise ImportError("reload(): parent %.200s not in sys.modules" % name[:dot])
272 subname = name[dot+1:]
273 path = getattr(parent, "__path__", None)
274
275 try:
276 fp, filename, stuff = imp.find_module(subname, path)
277 finally:
278 modules_reloading.clear()
279
280 try:
281 newm = imp.load_module(name, fp, filename, stuff)
282 except:
283 # load_module probably removed name from modules because of
284 # the error. Put back the original module object.
285 sys.modules[name] = m
286 raise
287 finally:
288 if fp: fp.close()
289
290 modules_reloading.clear()
291 return newm
160
292
161 # Save the original hooks
293 # Save the original hooks
162 try:
294 try:
163 original_reload = __builtin__.reload
295 original_reload = __builtin__.reload
164 except AttributeError:
296 except AttributeError:
165 original_reload = imp.reload # Python 3
297 original_reload = imp.reload # Python 3
166
298
167 # Replacement for reload()
299 # Replacement for reload()
168 def reload(module, exclude=['sys', '__builtin__', '__main__']):
300 def reload(module, exclude=['sys', 'os.path', '__builtin__', '__main__']):
169 """Recursively reload all modules used in the given module. Optionally
301 """Recursively reload all modules used in the given module. Optionally
170 takes a list of modules to exclude from reloading. The default exclude
302 takes a list of modules to exclude from reloading. The default exclude
171 list contains sys, __main__, and __builtin__, to prevent, e.g., resetting
303 list contains sys, __main__, and __builtin__, to prevent, e.g., resetting
172 display, exception, and io hooks.
304 display, exception, and io hooks.
173 """
305 """
174 global found_now
306 global found_now
175 for i in exclude:
307 for i in exclude:
176 found_now[i] = 1
308 found_now[i] = 1
177 original_import = __builtin__.__import__
309 original_import = __builtin__.__import__
178 __builtin__.__import__ = deep_import_hook
310 __builtin__.__import__ = import_module_level
179 try:
311 try:
180 ret = deep_reload_hook(module)
312 ret = reload_module(module)
181 finally:
313 finally:
182 __builtin__.__import__ = original_import
314 __builtin__.__import__ = original_import
183 found_now = {}
315 found_now = {}
184 return ret
316 return ret
185
317
186 # Uncomment the following to automatically activate deep reloading whenever
318 # Uncomment the following to automatically activate deep reloading whenever
187 # this module is imported
319 # this module is imported
188 #__builtin__.reload = reload
320 #__builtin__.reload = reload
General Comments 0
You need to be logged in to leave comments. Login now