##// 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 1 # -*- coding: utf-8 -*-
2 2 """
3 3 A module to change reload() so that it acts recursively.
4 4 To enable it type::
5 5
6 6 import __builtin__, deepreload
7 7 __builtin__.reload = deepreload.reload
8 8
9 9 You can then disable it with::
10 10
11 11 __builtin__.reload = deepreload.original_reload
12 12
13 13 Alternatively, you can add a dreload builtin alongside normal reload with::
14 14
15 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 21 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
22 22 #
23 23 # Distributed under the terms of the BSD License. The full license is in
24 24 # the file COPYING, distributed as part of this software.
25 25 #*****************************************************************************
26 26
27 27 import __builtin__
28 28 import imp
29 29 import sys
30 30
31 # Replacement for __import__()
32 def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1):
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
31 from types import ModuleType
32 from warnings import warn
43 33
44 def determine_parent(globals):
45 if not globals or not globals.has_key("__name__"):
46 return None
47 pname = globals['__name__']
48 if globals.has_key("__path__"):
49 parent = sys.modules[pname]
50 assert globals is parent.__dict__
51 return parent
52 if '.' in pname:
53 i = pname.rfind('.')
54 pname = pname[:i]
55 parent = sys.modules[pname]
56 assert parent.__name__ == pname
57 return parent
58 return None
34 def get_parent(globals, level):
35 """
36 parent, name = get_parent(globals, level)
37
38 Return the package that an import is being performed in. If globals comes
39 from the module foo.bar.bat (not itself a package), this returns the
40 sys.modules entry for foo.bar. If globals is from a package's __init__.py,
41 the package's entry in sys.modules is returned.
42
43 If globals doesn't come from a package or a module in a package, or a
44 corresponding entry is not found in sys.modules, None is returned.
45 """
46 orig_level = level
59 47
60 def find_head_package(parent, name):
61 # Import the first
62 if '.' in name:
63 # 'some.nested.package' -> head = 'some', tail = 'nested.package'
64 i = name.find('.')
65 head = name[:i]
66 tail = name[i+1:]
48 if not level or not isinstance(globals, dict):
49 return None, ''
50
51 pkgname = globals.get('__package__', None)
52
53 if pkgname is not None:
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 62 else:
68 # 'packagename' -> head = 'packagename', tail = ''
69 head = name
70 tail = ""
71 if parent:
72 # If this is a subpackage then qname = parent's name + head
73 qname = "%s.%s" % (parent.__name__, head)
63 # __package__ not set, so figure it out and set it
64 if '__name__' not in globals:
65 return None, ''
66 modname = globals['__name__']
67
68 if '__path__' in globals:
69 # __path__ is set, so modname is already the package name
70 globals['__package__'] = name = modname
74 71 else:
75 qname = head
76 q = import_module(head, qname, parent)
77 if q: return q, tail
78 if parent:
79 qname = head
80 parent = None
81 q = import_module(head, qname, parent)
82 if q: return q, tail
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
72 # Normal module, so work out the package name if any
73 lastdot = modname.rfind('.')
74 if lastdot < 0 and level > 0:
75 raise ValueError("Attempted relative import in non-package")
76 if lastdot < 0:
77 globals['__package__'] = None
78 return None, ''
79 globals['__package__'] = name = modname[:lastdot]
103 80
104 def ensure_fromlist(m, fromlist, recursive=0):
105 for sub in fromlist:
106 if sub == "*":
107 if not recursive:
81 dot = len(name)
82 for x in xrange(level, 1, -1):
108 83 try:
109 all = m.__all__
110 except AttributeError:
111 pass
84 dot = name.rindex('.', 0, dot)
85 except ValueError:
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 126 else:
113 ensure_fromlist(m, all, 1)
114 continue
115 if sub != "*" and not hasattr(m, sub):
116 subname = "%s.%s" % (m.__name__, sub)
117 submod = import_module(sub, subname, m)
118 if not submod:
119 raise ImportError, "No module named " + subname
127 subname = name[:dot]
128 next = name[dot+1:]
129
130 if buf != '':
131 buf += '.'
132 buf += 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 145 # Need to keep track of what we've already reloaded to prevent cyclic evil
122 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 154 global found_now
126 if found_now.has_key(fqname):
127 try:
128 return sys.modules[fqname]
129 except KeyError:
130 pass
155 if fullname in found_now and fullname in sys.modules:
156 m = sys.modules[fullname]
157 else:
158 print 'Reloading', fullname
159 found_now[fullname] = 1
160 oldm = sys.modules.get(fullname, None)
131 161
132 print 'Reloading', fqname #, sys.excepthook is sys.__excepthook__, \
133 #sys.displayhook is sys.__displayhook__
162 if mod is None:
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 169 try:
137 fp, pathname, stuff = imp.find_module(partname,
138 parent and parent.__path__)
170 fp, filename, stuff = imp.find_module(subname, path)
139 171 except ImportError:
140 172 return None
141 173
142 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 182 finally:
145 183 if fp: fp.close()
146 184
147 if parent:
148 setattr(parent, partname, m)
185 add_submodule(mod, m, fullname, subname)
149 186
150 187 return m
151 188
152 def deep_reload_hook(module):
153 name = module.__name__
154 if '.' not in name:
155 return import_module(name, name, None)
156 i = name.rfind('.')
157 pname = name[:i]
158 parent = sys.modules[pname]
159 return import_module(name[i+1:], name, parent)
189 def add_submodule(mod, submod, fullname, subname):
190 """mod.{subname} = submod"""
191 if mod is None:
192 return #Nothing to do here.
193
194 if submod is None:
195 submod = sys.modules[fullname]
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 293 # Save the original hooks
162 294 try:
163 295 original_reload = __builtin__.reload
164 296 except AttributeError:
165 297 original_reload = imp.reload # Python 3
166 298
167 299 # Replacement for reload()
168 def reload(module, exclude=['sys', '__builtin__', '__main__']):
300 def reload(module, exclude=['sys', 'os.path', '__builtin__', '__main__']):
169 301 """Recursively reload all modules used in the given module. Optionally
170 302 takes a list of modules to exclude from reloading. The default exclude
171 303 list contains sys, __main__, and __builtin__, to prevent, e.g., resetting
172 304 display, exception, and io hooks.
173 305 """
174 306 global found_now
175 307 for i in exclude:
176 308 found_now[i] = 1
177 309 original_import = __builtin__.__import__
178 __builtin__.__import__ = deep_import_hook
310 __builtin__.__import__ = import_module_level
179 311 try:
180 ret = deep_reload_hook(module)
312 ret = reload_module(module)
181 313 finally:
182 314 __builtin__.__import__ = original_import
183 315 found_now = {}
184 316 return ret
185 317
186 318 # Uncomment the following to automatically activate deep reloading whenever
187 319 # this module is imported
188 320 #__builtin__.reload = reload
General Comments 0
You need to be logged in to leave comments. Login now