Show More
@@ -130,20 +130,23 b' class ModuleReloader(object):' | |||||
130 | enabled = False |
|
130 | enabled = False | |
131 | """Whether this reloader is enabled""" |
|
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 | check_all = True |
|
133 | check_all = True | |
143 | """Autoreload all modules, not just those listed in 'modules'""" |
|
134 | """Autoreload all modules, not just those listed in 'modules'""" | |
144 |
|
135 | |||
145 | old_objects = {} |
|
136 | def __init__(self): | |
146 | """(module-name, name) -> weakref, for replacing old code objects""" |
|
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 | def mark_module_skipped(self, module_name): |
|
151 | def mark_module_skipped(self, module_name): | |
149 | """Skip reloading the named module in the future""" |
|
152 | """Skip reloading the named module in the future""" | |
@@ -179,7 +182,33 b' class ModuleReloader(object):' | |||||
179 | top_module = sys.modules[top_name] |
|
182 | top_module = sys.modules[top_name] | |
180 | return top_module, top_name |
|
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 | """Check whether some modules need to be reloaded.""" |
|
212 | """Check whether some modules need to be reloaded.""" | |
184 |
|
213 | |||
185 | if not self.enabled and not check_all: |
|
214 | if not self.enabled and not check_all: | |
@@ -196,43 +225,32 b' class ModuleReloader(object):' | |||||
196 | if modname in self.skip_modules: |
|
225 | if modname in self.skip_modules: | |
197 | continue |
|
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 | continue |
|
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 | try: |
|
232 | try: | |
220 |
pymtime = |
|
233 | if pymtime <= self.modules_mtimes[modname]: | |
221 | if pymtime <= os.stat(pyc_filename).st_mtime: |
|
|||
222 | continue |
|
234 | continue | |
|
235 | except KeyError: | |||
|
236 | self.modules_mtimes[modname] = pymtime | |||
|
237 | continue | |||
|
238 | else: | |||
223 | if self.failed.get(py_filename, None) == pymtime: |
|
239 | if self.failed.get(py_filename, None) == pymtime: | |
224 | continue |
|
240 | continue | |
225 | except OSError: |
|
|||
226 | continue |
|
|||
227 |
|
241 | |||
228 | try: |
|
242 | self.modules_mtimes[modname] = pymtime | |
229 | superreload(m, reload, self.old_objects) |
|
243 | ||
230 | if py_filename in self.failed: |
|
244 | # If we've reached this point, we should try to reload the module | |
231 | del self.failed[py_filename] |
|
245 | if do_reload: | |
232 |
|
|
246 | try: | |
233 | print("[autoreload of %s failed: %s]" % ( |
|
247 | superreload(m, reload, self.old_objects) | |
234 | modname, traceback.format_exc(1)), file=sys.stderr) |
|
248 | if py_filename in self.failed: | |
235 |
self.failed[py_filename] |
|
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 | # superreload |
|
256 | # superreload | |
@@ -400,6 +418,7 b' class AutoreloadMagics(Magics):' | |||||
400 | super(AutoreloadMagics, self).__init__(*a, **kw) |
|
418 | super(AutoreloadMagics, self).__init__(*a, **kw) | |
401 | self._reloader = ModuleReloader() |
|
419 | self._reloader = ModuleReloader() | |
402 | self._reloader.check_all = False |
|
420 | self._reloader.check_all = False | |
|
421 | self.loaded_modules = set(sys.modules) | |||
403 |
|
422 | |||
404 | @line_magic |
|
423 | @line_magic | |
405 | def autoreload(self, parameter_s=''): |
|
424 | def autoreload(self, parameter_s=''): | |
@@ -497,9 +516,21 b' class AutoreloadMagics(Magics):' | |||||
497 | except: |
|
516 | except: | |
498 | pass |
|
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 | def load_ipython_extension(ip): |
|
531 | def load_ipython_extension(ip): | |
502 | """Load the extension in IPython.""" |
|
532 | """Load the extension in IPython.""" | |
503 | auto_reload = AutoreloadMagics(ip) |
|
533 | auto_reload = AutoreloadMagics(ip) | |
504 | ip.register_magics(auto_reload) |
|
534 | ip.register_magics(auto_reload) | |
505 | ip.events.register('pre_run_cell', auto_reload.pre_run_cell) |
|
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 | def run_code(self, code): |
|
50 | def run_code(self, code): | |
51 | self.events.trigger('pre_run_cell') |
|
51 | self.events.trigger('pre_run_cell') | |
52 | exec(code, self.ns) |
|
52 | exec(code, self.ns) | |
|
53 | self.auto_magics.post_execute_hook() | |||
53 |
|
54 | |||
54 | def push(self, items): |
|
55 | def push(self, items): | |
55 | self.ns.update(items) |
|
56 | self.ns.update(items) | |
@@ -59,6 +60,7 b' class FakeShell(object):' | |||||
59 |
|
60 | |||
60 | def magic_aimport(self, parameter, stream=None): |
|
61 | def magic_aimport(self, parameter, stream=None): | |
61 | self.auto_magics.aimport(parameter, stream=stream) |
|
62 | self.auto_magics.aimport(parameter, stream=stream) | |
|
63 | self.auto_magics.post_execute_hook() | |||
62 |
|
64 | |||
63 |
|
65 | |||
64 | class Fixture(object): |
|
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 | self.shell.magic_aimport(mod_name) |
|
170 | self.shell.magic_aimport(mod_name) | |
169 | stream = StringIO() |
|
171 | stream = StringIO() | |
170 | self.shell.magic_aimport("", stream=stream) |
|
172 | self.shell.magic_aimport("", stream=stream) | |
171 |
nt.assert_ |
|
173 | nt.assert_in(("Modules to reload:\n%s" % mod_name), stream.getvalue()) | |
172 | stream.getvalue()) |
|
|||
173 |
|
174 | |||
174 | nt.assert_raises( |
|
175 | with nt.assert_raises(ImportError): | |
175 | ImportError, |
|
176 | self.shell.magic_aimport("tmpmod_as318989e89ds") | |
176 | self.shell.magic_aimport, "tmpmod_as318989e89ds") |
|
|||
177 | else: |
|
177 | else: | |
178 | self.shell.magic_autoreload("2") |
|
178 | self.shell.magic_autoreload("2") | |
179 | self.shell.run_code("import %s" % mod_name) |
|
179 | self.shell.run_code("import %s" % mod_name) |
General Comments 0
You need to be logged in to leave comments.
Login now