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 = |
|
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 |
|
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 |
|
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 |
|
134 | if py_filename in self.failed: | |
92 |
del self.failed[filename |
|
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 |
|
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( |
|
272 | class AutoreloadInterface(object): | |
230 |
def __init__(self, |
|
273 | def __init__(self, *a, **kw): | |
231 |
super(Autoreload, self).__init__( |
|
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 |
|
|
356 | stream.write("Modules to reload:\nall-except-skipped\n") | |
326 | else: |
|
357 | else: | |
327 |
|
|
358 | stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload)) | |
328 |
|
|
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 |
|
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