##// END OF EJS Templates
ENH: extensions/autoreload: move methods out of the Plugin class, and rewrite some code to be cleaner
Pauli Virtanen -
Show More
@@ -30,6 +30,9 def _get_compiled_ext():
30 PY_COMPILED_EXT = _get_compiled_ext()
30 PY_COMPILED_EXT = _get_compiled_ext()
31
31
32 class ModuleReloader(object):
32 class ModuleReloader(object):
33 enabled = False
34 """Whether this reloader is enabled"""
35
33 failed = {}
36 failed = {}
34 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
37 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
35
38
@@ -45,9 +48,46 class ModuleReloader(object):
45 old_objects = {}
48 old_objects = {}
46 """(module-name, name) -> weakref, for replacing old code objects"""
49 """(module-name, name) -> weakref, for replacing old code objects"""
47
50
51 def mark_module_skipped(self, module_name):
52 """Skip reloading the named module in the future"""
53 try:
54 del self.modules[module_name]
55 except KeyError:
56 pass
57 self.skip_modules[module_name] = True
58
59 def mark_module_reloadable(self, module_name):
60 """Reload the named module in the future (if it is imported)"""
61 try:
62 del self.skip_modules[module_name]
63 except KeyError:
64 pass
65 self.modules[module_name] = True
66
67 def aimport_module(self, module_name):
68 """Import a module, and mark it reloadable
69
70 Returns
71 -------
72 top_module : module
73 The imported module if it is top-level, or the top-level
74 top_name : module
75 Name of top_module
76
77 """
78 self.mark_module_reloadable(module_name)
79
80 __import__(module_name)
81 top_name = module_name.split('.')[0]
82 top_module = sys.modules[top_name]
83 return top_module, top_name
84
48 def check(self, check_all=False):
85 def check(self, check_all=False):
49 """Check whether some modules need to be reloaded."""
86 """Check whether some modules need to be reloaded."""
50
87
88 if not self.enabled and not check_all:
89 return
90
51 if check_all or self.check_all:
91 if check_all or self.check_all:
52 modules = sys.modules.keys()
92 modules = sys.modules.keys()
53 else:
93 else:
@@ -67,33 +107,36 class ModuleReloader(object):
67 continue
107 continue
68
108
69 filename = m.__file__
109 filename = m.__file__
70 dirname = os.path.dirname(filename)
71 path, ext = os.path.splitext(filename)
110 path, ext = os.path.splitext(filename)
72
111
73 if ext.lower() == '.py':
112 if ext.lower() == '.py':
74 ext = PY_COMPILED_EXT
113 ext = PY_COMPILED_EXT
75 filename = os.path.join(dirname, path + PY_COMPILED_EXT)
114 pyc_filename = path + PY_COMPILED_EXT
115 py_filename = filename
116 else:
117 pyc_filename = filename
118 py_filename = filename[:-1]
76
119
77 if ext != PY_COMPILED_EXT:
120 if ext != PY_COMPILED_EXT:
78 continue
121 continue
79
122
80 try:
123 try:
81 pymtime = os.stat(filename[:-1]).st_mtime
124 pymtime = os.stat(py_filename).st_mtime
82 if pymtime <= os.stat(filename).st_mtime:
125 if pymtime <= os.stat(pyc_filename).st_mtime:
83 continue
126 continue
84 if self.failed.get(filename[:-1], None) == pymtime:
127 if self.failed.get(py_filename, None) == pymtime:
85 continue
128 continue
86 except OSError:
129 except OSError:
87 continue
130 continue
88
131
89 try:
132 try:
90 superreload(m, reload, self.old_objects)
133 superreload(m, reload, self.old_objects)
91 if filename[:-1] in self.failed:
134 if py_filename in self.failed:
92 del self.failed[filename[:-1]]
135 del self.failed[py_filename]
93 except:
136 except:
94 print >> sys.stderr, "[autoreload of %s failed: %s]" % (
137 print >> sys.stderr, "[autoreload of %s failed: %s]" % (
95 modname, traceback.format_exc(1))
138 modname, traceback.format_exc(1))
96 self.failed[filename[:-1]] = pymtime
139 self.failed[py_filename] = pymtime
97
140
98 #------------------------------------------------------------------------------
141 #------------------------------------------------------------------------------
99 # superreload
142 # superreload
@@ -226,26 +269,12 def superreload(module, reload=reload, old_objects={}):
226 from IPython.core.plugin import Plugin
269 from IPython.core.plugin import Plugin
227 from IPython.core.hooks import TryNext
270 from IPython.core.hooks import TryNext
228
271
229 class Autoreload(Plugin):
272 class AutoreloadInterface(object):
230 def __init__(self, shell=None, config=None):
273 def __init__(self, *a, **kw):
231 super(Autoreload, self).__init__(shell=shell, config=config)
274 super(AutoreloadInterface, self).__init__(*a, **kw)
232
233 self.shell.define_magic('autoreload', self.magic_autoreload)
234 self.shell.define_magic('aimport', self.magic_aimport)
235 self.shell.set_hook('pre_run_code_hook', self.pre_run_code_hook)
236
237 self._enabled = False
238 self._reloader = ModuleReloader()
275 self._reloader = ModuleReloader()
239 self._reloader.check_all = False
276 self._reloader.check_all = False
240
277
241 def pre_run_code_hook(self, ipself):
242 if not self._enabled:
243 raise TryNext
244 try:
245 self._reloader.check()
246 except:
247 pass
248
249 def magic_autoreload(self, ipself, parameter_s=''):
278 def magic_autoreload(self, ipself, parameter_s=''):
250 r"""%autoreload => Reload modules automatically
279 r"""%autoreload => Reload modules automatically
251
280
@@ -293,15 +322,15 class Autoreload(Plugin):
293 if parameter_s == '':
322 if parameter_s == '':
294 self._reloader.check(True)
323 self._reloader.check(True)
295 elif parameter_s == '0':
324 elif parameter_s == '0':
296 self._enabled = False
325 self._reloader.enabled = False
297 elif parameter_s == '1':
326 elif parameter_s == '1':
298 self._reloader.check_all = False
327 self._reloader.check_all = False
299 self._enabled = True
328 self._reloader.enabled = True
300 elif parameter_s == '2':
329 elif parameter_s == '2':
301 self._reloader.check_all = True
330 self._reloader.check_all = True
302 self._enabled = True
331 self._reloader.enabled = True
303
332
304 def magic_aimport(self, ipself, parameter_s=''):
333 def magic_aimport(self, ipself, parameter_s='', stream=None):
305 """%aimport => Import modules for automatic reloading.
334 """%aimport => Import modules for automatic reloading.
306
335
307 %aimport
336 %aimport
@@ -321,31 +350,37 class Autoreload(Plugin):
321 to_reload.sort()
350 to_reload.sort()
322 to_skip = self._reloader.skip_modules.keys()
351 to_skip = self._reloader.skip_modules.keys()
323 to_skip.sort()
352 to_skip.sort()
353 if stream is None:
354 stream = sys.stdout
324 if self._reloader.check_all:
355 if self._reloader.check_all:
325 print "Modules to reload:\nall-expect-skipped"
356 stream.write("Modules to reload:\nall-except-skipped\n")
326 else:
357 else:
327 print "Modules to reload:\n%s" % ' '.join(to_reload)
358 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
328 print "\nModules to skip:\n%s" % ' '.join(to_skip)
359 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
329 elif modname.startswith('-'):
360 elif modname.startswith('-'):
330 modname = modname[1:]
361 modname = modname[1:]
331 try:
362 self._reloader.mark_module_skipped(modname)
332 del self._reloader.modules[modname]
333 except KeyError:
334 pass
335 self._reloader.skip_modules[modname] = True
336 else:
363 else:
337 try:
364 top_module, top_name = self._reloader.aimport_module(modname)
338 del self._reloader.skip_modules[modname]
339 except KeyError:
340 pass
341 self._reloader.modules[modname] = True
342
365
343 # Inject module to user namespace; handle also submodules properly
366 # Inject module to user namespace
344 __import__(modname)
367 ipself.push({top_name: top_module})
345 basename = modname.split('.')[0]
346 mod = sys.modules[basename]
347 ipself.push({basename: mod})
348
368
369 def pre_run_code_hook(self, ipself):
370 if not self._reloader.enabled:
371 raise TryNext
372 try:
373 self._reloader.check()
374 except:
375 pass
376
377 class AutoreloadPlugin(AutoreloadInterface, Plugin):
378 def __init__(self, shell=None, config=None):
379 super(AutoreloadPlugin, self).__init__(shell=shell, config=config)
380
381 self.shell.define_magic('autoreload', self.magic_autoreload)
382 self.shell.define_magic('aimport', self.magic_aimport)
383 self.shell.set_hook('pre_run_code_hook', self.pre_run_code_hook)
349
384
350 _loaded = False
385 _loaded = False
351
386
@@ -353,6 +388,6 def load_ipython_extension(ip):
353 """Load the extension in IPython."""
388 """Load the extension in IPython."""
354 global _loaded
389 global _loaded
355 if not _loaded:
390 if not _loaded:
356 plugin = Autoreload(shell=ip, config=ip.config)
391 plugin = AutoreloadPlugin(shell=ip, config=ip.config)
357 ip.plugin_manager.register_plugin('autoreload', plugin)
392 ip.plugin_manager.register_plugin('autoreload', plugin)
358 _loaded = True
393 _loaded = True
General Comments 0
You need to be logged in to leave comments. Login now