Show More
@@ -130,20 +130,23 b' class ModuleReloader(object):' | |||
|
130 | 130 | enabled = False |
|
131 | 131 | """Whether this reloader is enabled""" |
|
132 | 132 | |
|
133 | failed = {} | |
|
134 | """Modules that failed to reload: {module: mtime-on-failed-reload, ...}""" | |
|
135 | ||
|
136 | modules = {} | |
|
137 | """Modules specially marked as autoreloadable.""" | |
|
138 | ||
|
139 | skip_modules = {} | |
|
140 | """Modules specially marked as not autoreloadable.""" | |
|
141 | ||
|
142 | 133 | check_all = True |
|
143 | 134 | """Autoreload all modules, not just those listed in 'modules'""" |
|
144 | 135 | |
|
145 | old_objects = {} | |
|
146 | """(module-name, name) -> weakref, for replacing old code objects""" | |
|
136 | def __init__(self): | |
|
137 | # Modules that failed to reload: {module: mtime-on-failed-reload, ...} | |
|
138 | self.failed = {} | |
|
139 | # Modules specially marked as autoreloadable. | |
|
140 | self.modules = {} | |
|
141 | # Modules specially marked as not autoreloadable. | |
|
142 | self.skip_modules = {} | |
|
143 | # (module-name, name) -> weakref, for replacing old code objects | |
|
144 | self.old_objects = {} | |
|
145 | # Module modification timestamps | |
|
146 | self.modules_mtimes = {} | |
|
147 | ||
|
148 | # Cache module modification times | |
|
149 | self.check(check_all=True, do_reload=False) | |
|
147 | 150 | |
|
148 | 151 | def mark_module_skipped(self, module_name): |
|
149 | 152 | """Skip reloading the named module in the future""" |
@@ -179,7 +182,33 b' class ModuleReloader(object):' | |||
|
179 | 182 | top_module = sys.modules[top_name] |
|
180 | 183 | return top_module, top_name |
|
181 | 184 | |
|
182 | def check(self, check_all=False): | |
|
185 | def filename_and_mtime(self, module): | |
|
186 | if not hasattr(module, '__file__'): | |
|
187 | return None, None | |
|
188 | ||
|
189 | if module.__name__ == '__main__': | |
|
190 | # we cannot reload(__main__) | |
|
191 | return None, None | |
|
192 | ||
|
193 | filename = module.__file__ | |
|
194 | path, ext = os.path.splitext(filename) | |
|
195 | ||
|
196 | if ext.lower() == '.py': | |
|
197 | py_filename = filename | |
|
198 | else: | |
|
199 | try: | |
|
200 | py_filename = openpy.source_from_cache(filename) | |
|
201 | except ValueError: | |
|
202 | return None, None | |
|
203 | ||
|
204 | try: | |
|
205 | pymtime = os.stat(py_filename).st_mtime | |
|
206 | except OSError: | |
|
207 | return None, None | |
|
208 | ||
|
209 | return py_filename, pymtime | |
|
210 | ||
|
211 | def check(self, check_all=False, do_reload=True): | |
|
183 | 212 | """Check whether some modules need to be reloaded.""" |
|
184 | 213 | |
|
185 | 214 | if not self.enabled and not check_all: |
@@ -196,43 +225,32 b' class ModuleReloader(object):' | |||
|
196 | 225 | if modname in self.skip_modules: |
|
197 | 226 | continue |
|
198 | 227 | |
|
199 | if not hasattr(m, '__file__'): | |
|
228 | py_filename, pymtime = self.filename_and_mtime(m) | |
|
229 | if py_filename is None: | |
|
200 | 230 | continue |
|
201 | 231 | |
|
202 | if m.__name__ == '__main__': | |
|
203 | # we cannot reload(__main__) | |
|
204 | continue | |
|
205 | ||
|
206 | filename = m.__file__ | |
|
207 | path, ext = os.path.splitext(filename) | |
|
208 | ||
|
209 | if ext.lower() == '.py': | |
|
210 | pyc_filename = openpy.cache_from_source(filename) | |
|
211 | py_filename = filename | |
|
212 | else: | |
|
213 | pyc_filename = filename | |
|
214 | try: | |
|
215 | py_filename = openpy.source_from_cache(filename) | |
|
216 | except ValueError: | |
|
217 | continue | |
|
218 | ||
|
219 | 232 | try: |
|
220 |
pymtime = |
|
|
221 | if pymtime <= os.stat(pyc_filename).st_mtime: | |
|
233 | if pymtime <= self.modules_mtimes[modname]: | |
|
222 | 234 | continue |
|
235 | except KeyError: | |
|
236 | self.modules_mtimes[modname] = pymtime | |
|
237 | continue | |
|
238 | else: | |
|
223 | 239 | if self.failed.get(py_filename, None) == pymtime: |
|
224 | 240 | continue |
|
225 | except OSError: | |
|
226 | continue | |
|
227 | 241 | |
|
228 | try: | |
|
229 | superreload(m, reload, self.old_objects) | |
|
230 | if py_filename in self.failed: | |
|
231 | del self.failed[py_filename] | |
|
232 |
|
|
|
233 | print("[autoreload of %s failed: %s]" % ( | |
|
234 | modname, traceback.format_exc(1)), file=sys.stderr) | |
|
235 |
self.failed[py_filename] |
|
|
242 | self.modules_mtimes[modname] = pymtime | |
|
243 | ||
|
244 | # If we've reached this point, we should try to reload the module | |
|
245 | if do_reload: | |
|
246 | try: | |
|
247 | superreload(m, reload, self.old_objects) | |
|
248 | if py_filename in self.failed: | |
|
249 | del self.failed[py_filename] | |
|
250 | except: | |
|
251 | print("[autoreload of %s failed: %s]" % ( | |
|
252 | modname, traceback.format_exc(1)), file=sys.stderr) | |
|
253 | self.failed[py_filename] = pymtime | |
|
236 | 254 | |
|
237 | 255 | #------------------------------------------------------------------------------ |
|
238 | 256 | # superreload |
@@ -400,6 +418,7 b' class AutoreloadMagics(Magics):' | |||
|
400 | 418 | super(AutoreloadMagics, self).__init__(*a, **kw) |
|
401 | 419 | self._reloader = ModuleReloader() |
|
402 | 420 | self._reloader.check_all = False |
|
421 | self.loaded_modules = set(sys.modules) | |
|
403 | 422 | |
|
404 | 423 | @line_magic |
|
405 | 424 | def autoreload(self, parameter_s=''): |
@@ -497,9 +516,21 b' class AutoreloadMagics(Magics):' | |||
|
497 | 516 | except: |
|
498 | 517 | pass |
|
499 | 518 | |
|
519 | def post_execute_hook(self): | |
|
520 | """Cache the modification times of any modules imported in this execution | |
|
521 | """ | |
|
522 | newly_loaded_modules = set(sys.modules) - self.loaded_modules | |
|
523 | for modname in newly_loaded_modules: | |
|
524 | _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname]) | |
|
525 | if pymtime is not None: | |
|
526 | self._reloader.modules_mtimes[modname] = pymtime | |
|
527 | ||
|
528 | self.loaded_modules.update(newly_loaded_modules) | |
|
529 | ||
|
500 | 530 | |
|
501 | 531 | def load_ipython_extension(ip): |
|
502 | 532 | """Load the extension in IPython.""" |
|
503 | 533 | auto_reload = AutoreloadMagics(ip) |
|
504 | 534 | ip.register_magics(auto_reload) |
|
505 | 535 | ip.events.register('pre_run_cell', auto_reload.pre_run_cell) |
|
536 | ip.register_post_execute(auto_reload.post_execute_hook) |
@@ -50,6 +50,7 b' class FakeShell(object):' | |||
|
50 | 50 | def run_code(self, code): |
|
51 | 51 | self.events.trigger('pre_run_cell') |
|
52 | 52 | exec(code, self.ns) |
|
53 | self.auto_magics.post_execute_hook() | |
|
53 | 54 | |
|
54 | 55 | def push(self, items): |
|
55 | 56 | self.ns.update(items) |
@@ -59,6 +60,7 b' class FakeShell(object):' | |||
|
59 | 60 | |
|
60 | 61 | def magic_aimport(self, parameter, stream=None): |
|
61 | 62 | self.auto_magics.aimport(parameter, stream=stream) |
|
63 | self.auto_magics.post_execute_hook() | |
|
62 | 64 | |
|
63 | 65 | |
|
64 | 66 | class Fixture(object): |
@@ -168,12 +170,10 b" class Bar: # old-style class: weakref doesn't work for it on Python < 2.7" | |||
|
168 | 170 | self.shell.magic_aimport(mod_name) |
|
169 | 171 | stream = StringIO() |
|
170 | 172 | self.shell.magic_aimport("", stream=stream) |
|
171 |
nt.assert_ |
|
|
172 | stream.getvalue()) | |
|
173 | nt.assert_in(("Modules to reload:\n%s" % mod_name), stream.getvalue()) | |
|
173 | 174 | |
|
174 | nt.assert_raises( | |
|
175 | ImportError, | |
|
176 | self.shell.magic_aimport, "tmpmod_as318989e89ds") | |
|
175 | with nt.assert_raises(ImportError): | |
|
176 | self.shell.magic_aimport("tmpmod_as318989e89ds") | |
|
177 | 177 | else: |
|
178 | 178 | self.shell.magic_autoreload("2") |
|
179 | 179 | self.shell.run_code("import %s" % mod_name) |
General Comments 0
You need to be logged in to leave comments.
Login now