##// END OF EJS Templates
Store timestamps for modules to autoreload...
Thomas Kluyver -
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 = os.stat(py_filename).st_mtime
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 except:
233 print("[autoreload of %s failed: %s]" % (
234 modname, traceback.format_exc(1)), file=sys.stderr)
235 self.failed[py_filename] = pymtime
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_true(("Modules to reload:\n%s" % mod_name) in
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