##// END OF EJS Templates
Update autoreload.py...
nouman -
Show More
@@ -1,627 +1,631 b''
1 """IPython extension to reload modules before executing user code.
1 """IPython extension to reload modules before executing user code.
2
2
3 ``autoreload`` reloads modules automatically before entering the execution of
3 ``autoreload`` reloads modules automatically before entering the execution of
4 code typed at the IPython prompt.
4 code typed at the IPython prompt.
5
5
6 This makes for example the following workflow possible:
6 This makes for example the following workflow possible:
7
7
8 .. sourcecode:: ipython
8 .. sourcecode:: ipython
9
9
10 In [1]: %load_ext autoreload
10 In [1]: %load_ext autoreload
11
11
12 In [2]: %autoreload 2
12 In [2]: %autoreload 2
13
13
14 In [3]: from foo import some_function
14 In [3]: from foo import some_function
15
15
16 In [4]: some_function()
16 In [4]: some_function()
17 Out[4]: 42
17 Out[4]: 42
18
18
19 In [5]: # open foo.py in an editor and change some_function to return 43
19 In [5]: # open foo.py in an editor and change some_function to return 43
20
20
21 In [6]: some_function()
21 In [6]: some_function()
22 Out[6]: 43
22 Out[6]: 43
23
23
24 The module was reloaded without reloading it explicitly, and the object
24 The module was reloaded without reloading it explicitly, and the object
25 imported with ``from foo import ...`` was also updated.
25 imported with ``from foo import ...`` was also updated.
26
26
27 Usage
27 Usage
28 =====
28 =====
29
29
30 The following magic commands are provided:
30 The following magic commands are provided:
31
31
32 ``%autoreload``
32 ``%autoreload``
33
33
34 Reload all modules (except those excluded by ``%aimport``)
34 Reload all modules (except those excluded by ``%aimport``)
35 automatically now.
35 automatically now.
36
36
37 ``%autoreload 0``
37 ``%autoreload 0``
38
38
39 Disable automatic reloading.
39 Disable automatic reloading.
40
40
41 ``%autoreload 1``
41 ``%autoreload 1``
42
42
43 Reload all modules imported with ``%aimport`` every time before
43 Reload all modules imported with ``%aimport`` every time before
44 executing the Python code typed.
44 executing the Python code typed.
45
45
46 ``%autoreload 2``
46 ``%autoreload 2``
47
47
48 Reload all modules (except those excluded by ``%aimport``) every
48 Reload all modules (except those excluded by ``%aimport``) every
49 time before executing the Python code typed.
49 time before executing the Python code typed.
50
50
51 ``%autoreload 3``
51 ``%autoreload 3``
52
52
53 Reload all modules AND autoload newly added objects
53 Reload all modules AND autoload newly added objects
54 every time before executing the Python code typed.
54 every time before executing the Python code typed.
55
55
56 ``%aimport``
56 ``%aimport``
57
57
58 List modules which are to be automatically imported or not to be imported.
58 List modules which are to be automatically imported or not to be imported.
59
59
60 ``%aimport foo``
60 ``%aimport foo``
61
61
62 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
62 Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1``
63
63
64 ``%aimport foo, bar``
64 ``%aimport foo, bar``
65
65
66 Import modules 'foo', 'bar' and mark them to be autoreloaded for ``%autoreload 1``
66 Import modules 'foo', 'bar' and mark them to be autoreloaded for ``%autoreload 1``
67
67
68 ``%aimport -foo``
68 ``%aimport -foo``
69
69
70 Mark module 'foo' to not be autoreloaded.
70 Mark module 'foo' to not be autoreloaded.
71
71
72 Caveats
72 Caveats
73 =======
73 =======
74
74
75 Reloading Python modules in a reliable way is in general difficult,
75 Reloading Python modules in a reliable way is in general difficult,
76 and unexpected things may occur. ``%autoreload`` tries to work around
76 and unexpected things may occur. ``%autoreload`` tries to work around
77 common pitfalls by replacing function code objects and parts of
77 common pitfalls by replacing function code objects and parts of
78 classes previously in the module with new versions. This makes the
78 classes previously in the module with new versions. This makes the
79 following things to work:
79 following things to work:
80
80
81 - Functions and classes imported via 'from xxx import foo' are upgraded
81 - Functions and classes imported via 'from xxx import foo' are upgraded
82 to new versions when 'xxx' is reloaded.
82 to new versions when 'xxx' is reloaded.
83
83
84 - Methods and properties of classes are upgraded on reload, so that
84 - Methods and properties of classes are upgraded on reload, so that
85 calling 'c.foo()' on an object 'c' created before the reload causes
85 calling 'c.foo()' on an object 'c' created before the reload causes
86 the new code for 'foo' to be executed.
86 the new code for 'foo' to be executed.
87
87
88 Some of the known remaining caveats are:
88 Some of the known remaining caveats are:
89
89
90 - Replacing code objects does not always succeed: changing a @property
90 - Replacing code objects does not always succeed: changing a @property
91 in a class to an ordinary method or a method to a member variable
91 in a class to an ordinary method or a method to a member variable
92 can cause problems (but in old objects only).
92 can cause problems (but in old objects only).
93
93
94 - Functions that are removed (eg. via monkey-patching) from a module
94 - Functions that are removed (eg. via monkey-patching) from a module
95 before it is reloaded are not upgraded.
95 before it is reloaded are not upgraded.
96
96
97 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
97 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
98
99 - While comparing Enum and Flag, the 'is' Identity Operator is used (even in the case '==' has been used (Similar to the 'None' keyword)).
100
101 - Reloading a module, or importing the same module by a different name, creates new Enums. These may look the same, but are not.
98 """
102 """
99
103
100 __skip_doctest__ = True
104 __skip_doctest__ = True
101
105
102 # -----------------------------------------------------------------------------
106 # -----------------------------------------------------------------------------
103 # Copyright (C) 2000 Thomas Heller
107 # Copyright (C) 2000 Thomas Heller
104 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
108 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
105 # Copyright (C) 2012 The IPython Development Team
109 # Copyright (C) 2012 The IPython Development Team
106 #
110 #
107 # Distributed under the terms of the BSD License. The full license is in
111 # Distributed under the terms of the BSD License. The full license is in
108 # the file COPYING, distributed as part of this software.
112 # the file COPYING, distributed as part of this software.
109 # -----------------------------------------------------------------------------
113 # -----------------------------------------------------------------------------
110 #
114 #
111 # This IPython module is written by Pauli Virtanen, based on the autoreload
115 # This IPython module is written by Pauli Virtanen, based on the autoreload
112 # code by Thomas Heller.
116 # code by Thomas Heller.
113
117
114 # -----------------------------------------------------------------------------
118 # -----------------------------------------------------------------------------
115 # Imports
119 # Imports
116 # -----------------------------------------------------------------------------
120 # -----------------------------------------------------------------------------
117
121
118 import os
122 import os
119 import sys
123 import sys
120 import traceback
124 import traceback
121 import types
125 import types
122 import weakref
126 import weakref
123 import gc
127 import gc
124 from importlib import import_module, reload
128 from importlib import import_module, reload
125 from importlib.util import source_from_cache
129 from importlib.util import source_from_cache
126
130
127 # ------------------------------------------------------------------------------
131 # ------------------------------------------------------------------------------
128 # Autoreload functionality
132 # Autoreload functionality
129 # ------------------------------------------------------------------------------
133 # ------------------------------------------------------------------------------
130
134
131
135
132 class ModuleReloader:
136 class ModuleReloader:
133 enabled = False
137 enabled = False
134 """Whether this reloader is enabled"""
138 """Whether this reloader is enabled"""
135
139
136 check_all = True
140 check_all = True
137 """Autoreload all modules, not just those listed in 'modules'"""
141 """Autoreload all modules, not just those listed in 'modules'"""
138
142
139 autoload_obj = False
143 autoload_obj = False
140 """Autoreload all modules AND autoload all new objects"""
144 """Autoreload all modules AND autoload all new objects"""
141
145
142 def __init__(self, shell=None):
146 def __init__(self, shell=None):
143 # Modules that failed to reload: {module: mtime-on-failed-reload, ...}
147 # Modules that failed to reload: {module: mtime-on-failed-reload, ...}
144 self.failed = {}
148 self.failed = {}
145 # Modules specially marked as autoreloadable.
149 # Modules specially marked as autoreloadable.
146 self.modules = {}
150 self.modules = {}
147 # Modules specially marked as not autoreloadable.
151 # Modules specially marked as not autoreloadable.
148 self.skip_modules = {}
152 self.skip_modules = {}
149 # (module-name, name) -> weakref, for replacing old code objects
153 # (module-name, name) -> weakref, for replacing old code objects
150 self.old_objects = {}
154 self.old_objects = {}
151 # Module modification timestamps
155 # Module modification timestamps
152 self.modules_mtimes = {}
156 self.modules_mtimes = {}
153 self.shell = shell
157 self.shell = shell
154
158
155 # Cache module modification times
159 # Cache module modification times
156 self.check(check_all=True, do_reload=False)
160 self.check(check_all=True, do_reload=False)
157
161
158 def mark_module_skipped(self, module_name):
162 def mark_module_skipped(self, module_name):
159 """Skip reloading the named module in the future"""
163 """Skip reloading the named module in the future"""
160 try:
164 try:
161 del self.modules[module_name]
165 del self.modules[module_name]
162 except KeyError:
166 except KeyError:
163 pass
167 pass
164 self.skip_modules[module_name] = True
168 self.skip_modules[module_name] = True
165
169
166 def mark_module_reloadable(self, module_name):
170 def mark_module_reloadable(self, module_name):
167 """Reload the named module in the future (if it is imported)"""
171 """Reload the named module in the future (if it is imported)"""
168 try:
172 try:
169 del self.skip_modules[module_name]
173 del self.skip_modules[module_name]
170 except KeyError:
174 except KeyError:
171 pass
175 pass
172 self.modules[module_name] = True
176 self.modules[module_name] = True
173
177
174 def aimport_module(self, module_name):
178 def aimport_module(self, module_name):
175 """Import a module, and mark it reloadable
179 """Import a module, and mark it reloadable
176
180
177 Returns
181 Returns
178 -------
182 -------
179 top_module : module
183 top_module : module
180 The imported module if it is top-level, or the top-level
184 The imported module if it is top-level, or the top-level
181 top_name : module
185 top_name : module
182 Name of top_module
186 Name of top_module
183
187
184 """
188 """
185 self.mark_module_reloadable(module_name)
189 self.mark_module_reloadable(module_name)
186
190
187 import_module(module_name)
191 import_module(module_name)
188 top_name = module_name.split(".")[0]
192 top_name = module_name.split(".")[0]
189 top_module = sys.modules[top_name]
193 top_module = sys.modules[top_name]
190 return top_module, top_name
194 return top_module, top_name
191
195
192 def filename_and_mtime(self, module):
196 def filename_and_mtime(self, module):
193 if not hasattr(module, "__file__") or module.__file__ is None:
197 if not hasattr(module, "__file__") or module.__file__ is None:
194 return None, None
198 return None, None
195
199
196 if getattr(module, "__name__", None) in [None, "__mp_main__", "__main__"]:
200 if getattr(module, "__name__", None) in [None, "__mp_main__", "__main__"]:
197 # we cannot reload(__main__) or reload(__mp_main__)
201 # we cannot reload(__main__) or reload(__mp_main__)
198 return None, None
202 return None, None
199
203
200 filename = module.__file__
204 filename = module.__file__
201 path, ext = os.path.splitext(filename)
205 path, ext = os.path.splitext(filename)
202
206
203 if ext.lower() == ".py":
207 if ext.lower() == ".py":
204 py_filename = filename
208 py_filename = filename
205 else:
209 else:
206 try:
210 try:
207 py_filename = source_from_cache(filename)
211 py_filename = source_from_cache(filename)
208 except ValueError:
212 except ValueError:
209 return None, None
213 return None, None
210
214
211 try:
215 try:
212 pymtime = os.stat(py_filename).st_mtime
216 pymtime = os.stat(py_filename).st_mtime
213 except OSError:
217 except OSError:
214 return None, None
218 return None, None
215
219
216 return py_filename, pymtime
220 return py_filename, pymtime
217
221
218 def check(self, check_all=False, do_reload=True):
222 def check(self, check_all=False, do_reload=True):
219 """Check whether some modules need to be reloaded."""
223 """Check whether some modules need to be reloaded."""
220
224
221 if not self.enabled and not check_all:
225 if not self.enabled and not check_all:
222 return
226 return
223
227
224 if check_all or self.check_all:
228 if check_all or self.check_all:
225 modules = list(sys.modules.keys())
229 modules = list(sys.modules.keys())
226 else:
230 else:
227 modules = list(self.modules.keys())
231 modules = list(self.modules.keys())
228
232
229 for modname in modules:
233 for modname in modules:
230 m = sys.modules.get(modname, None)
234 m = sys.modules.get(modname, None)
231
235
232 if modname in self.skip_modules:
236 if modname in self.skip_modules:
233 continue
237 continue
234
238
235 py_filename, pymtime = self.filename_and_mtime(m)
239 py_filename, pymtime = self.filename_and_mtime(m)
236 if py_filename is None:
240 if py_filename is None:
237 continue
241 continue
238
242
239 try:
243 try:
240 if pymtime <= self.modules_mtimes[modname]:
244 if pymtime <= self.modules_mtimes[modname]:
241 continue
245 continue
242 except KeyError:
246 except KeyError:
243 self.modules_mtimes[modname] = pymtime
247 self.modules_mtimes[modname] = pymtime
244 continue
248 continue
245 else:
249 else:
246 if self.failed.get(py_filename, None) == pymtime:
250 if self.failed.get(py_filename, None) == pymtime:
247 continue
251 continue
248
252
249 self.modules_mtimes[modname] = pymtime
253 self.modules_mtimes[modname] = pymtime
250
254
251 # If we've reached this point, we should try to reload the module
255 # If we've reached this point, we should try to reload the module
252 if do_reload:
256 if do_reload:
253 try:
257 try:
254 if self.autoload_obj:
258 if self.autoload_obj:
255 superreload(m, reload, self.old_objects, self.shell)
259 superreload(m, reload, self.old_objects, self.shell)
256 else:
260 else:
257 superreload(m, reload, self.old_objects)
261 superreload(m, reload, self.old_objects)
258 if py_filename in self.failed:
262 if py_filename in self.failed:
259 del self.failed[py_filename]
263 del self.failed[py_filename]
260 except:
264 except:
261 print(
265 print(
262 "[autoreload of {} failed: {}]".format(
266 "[autoreload of {} failed: {}]".format(
263 modname, traceback.format_exc(10)
267 modname, traceback.format_exc(10)
264 ),
268 ),
265 file=sys.stderr,
269 file=sys.stderr,
266 )
270 )
267 self.failed[py_filename] = pymtime
271 self.failed[py_filename] = pymtime
268
272
269
273
270 # ------------------------------------------------------------------------------
274 # ------------------------------------------------------------------------------
271 # superreload
275 # superreload
272 # ------------------------------------------------------------------------------
276 # ------------------------------------------------------------------------------
273
277
274
278
275 func_attrs = [
279 func_attrs = [
276 "__code__",
280 "__code__",
277 "__defaults__",
281 "__defaults__",
278 "__doc__",
282 "__doc__",
279 "__closure__",
283 "__closure__",
280 "__globals__",
284 "__globals__",
281 "__dict__",
285 "__dict__",
282 ]
286 ]
283
287
284
288
285 def update_function(old, new):
289 def update_function(old, new):
286 """Upgrade the code object of a function"""
290 """Upgrade the code object of a function"""
287 for name in func_attrs:
291 for name in func_attrs:
288 try:
292 try:
289 setattr(old, name, getattr(new, name))
293 setattr(old, name, getattr(new, name))
290 except (AttributeError, TypeError):
294 except (AttributeError, TypeError):
291 pass
295 pass
292
296
293
297
294 def update_instances(old, new):
298 def update_instances(old, new):
295 """Use garbage collector to find all instances that refer to the old
299 """Use garbage collector to find all instances that refer to the old
296 class definition and update their __class__ to point to the new class
300 class definition and update their __class__ to point to the new class
297 definition"""
301 definition"""
298
302
299 refs = gc.get_referrers(old)
303 refs = gc.get_referrers(old)
300
304
301 for ref in refs:
305 for ref in refs:
302 if type(ref) is old:
306 if type(ref) is old:
303 object.__setattr__(ref, "__class__", new)
307 object.__setattr__(ref, "__class__", new)
304
308
305
309
306 def update_class(old, new):
310 def update_class(old, new):
307 """Replace stuff in the __dict__ of a class, and upgrade
311 """Replace stuff in the __dict__ of a class, and upgrade
308 method code objects, and add new methods, if any"""
312 method code objects, and add new methods, if any"""
309 for key in list(old.__dict__.keys()):
313 for key in list(old.__dict__.keys()):
310 old_obj = getattr(old, key)
314 old_obj = getattr(old, key)
311 try:
315 try:
312 new_obj = getattr(new, key)
316 new_obj = getattr(new, key)
313 # explicitly checking that comparison returns True to handle
317 # explicitly checking that comparison returns True to handle
314 # cases where `==` doesn't return a boolean.
318 # cases where `==` doesn't return a boolean.
315 if (old_obj == new_obj) is True:
319 if (old_obj == new_obj) is True:
316 continue
320 continue
317 except AttributeError:
321 except AttributeError:
318 # obsolete attribute: remove it
322 # obsolete attribute: remove it
319 try:
323 try:
320 delattr(old, key)
324 delattr(old, key)
321 except (AttributeError, TypeError):
325 except (AttributeError, TypeError):
322 pass
326 pass
323 continue
327 continue
324 except ValueError:
328 except ValueError:
325 # can't compare nested structures containing
329 # can't compare nested structures containing
326 # numpy arrays using `==`
330 # numpy arrays using `==`
327 pass
331 pass
328
332
329 if update_generic(old_obj, new_obj):
333 if update_generic(old_obj, new_obj):
330 continue
334 continue
331
335
332 try:
336 try:
333 setattr(old, key, getattr(new, key))
337 setattr(old, key, getattr(new, key))
334 except (AttributeError, TypeError):
338 except (AttributeError, TypeError):
335 pass # skip non-writable attributes
339 pass # skip non-writable attributes
336
340
337 for key in list(new.__dict__.keys()):
341 for key in list(new.__dict__.keys()):
338 if key not in list(old.__dict__.keys()):
342 if key not in list(old.__dict__.keys()):
339 try:
343 try:
340 setattr(old, key, getattr(new, key))
344 setattr(old, key, getattr(new, key))
341 except (AttributeError, TypeError):
345 except (AttributeError, TypeError):
342 pass # skip non-writable attributes
346 pass # skip non-writable attributes
343
347
344 # update all instances of class
348 # update all instances of class
345 update_instances(old, new)
349 update_instances(old, new)
346
350
347
351
348 def update_property(old, new):
352 def update_property(old, new):
349 """Replace get/set/del functions of a property"""
353 """Replace get/set/del functions of a property"""
350 update_generic(old.fdel, new.fdel)
354 update_generic(old.fdel, new.fdel)
351 update_generic(old.fget, new.fget)
355 update_generic(old.fget, new.fget)
352 update_generic(old.fset, new.fset)
356 update_generic(old.fset, new.fset)
353
357
354
358
355 def isinstance2(a, b, typ):
359 def isinstance2(a, b, typ):
356 return isinstance(a, typ) and isinstance(b, typ)
360 return isinstance(a, typ) and isinstance(b, typ)
357
361
358
362
359 UPDATE_RULES = [
363 UPDATE_RULES = [
360 (lambda a, b: isinstance2(a, b, type), update_class),
364 (lambda a, b: isinstance2(a, b, type), update_class),
361 (lambda a, b: isinstance2(a, b, types.FunctionType), update_function),
365 (lambda a, b: isinstance2(a, b, types.FunctionType), update_function),
362 (lambda a, b: isinstance2(a, b, property), update_property),
366 (lambda a, b: isinstance2(a, b, property), update_property),
363 ]
367 ]
364 UPDATE_RULES.extend(
368 UPDATE_RULES.extend(
365 [
369 [
366 (
370 (
367 lambda a, b: isinstance2(a, b, types.MethodType),
371 lambda a, b: isinstance2(a, b, types.MethodType),
368 lambda a, b: update_function(a.__func__, b.__func__),
372 lambda a, b: update_function(a.__func__, b.__func__),
369 ),
373 ),
370 ]
374 ]
371 )
375 )
372
376
373
377
374 def update_generic(a, b):
378 def update_generic(a, b):
375 for type_check, update in UPDATE_RULES:
379 for type_check, update in UPDATE_RULES:
376 if type_check(a, b):
380 if type_check(a, b):
377 update(a, b)
381 update(a, b)
378 return True
382 return True
379 return False
383 return False
380
384
381
385
382 class StrongRef:
386 class StrongRef:
383 def __init__(self, obj):
387 def __init__(self, obj):
384 self.obj = obj
388 self.obj = obj
385
389
386 def __call__(self):
390 def __call__(self):
387 return self.obj
391 return self.obj
388
392
389
393
390 mod_attrs = [
394 mod_attrs = [
391 "__name__",
395 "__name__",
392 "__doc__",
396 "__doc__",
393 "__package__",
397 "__package__",
394 "__loader__",
398 "__loader__",
395 "__spec__",
399 "__spec__",
396 "__file__",
400 "__file__",
397 "__cached__",
401 "__cached__",
398 "__builtins__",
402 "__builtins__",
399 ]
403 ]
400
404
401
405
402 def append_obj(module, d, name, obj, autoload=False):
406 def append_obj(module, d, name, obj, autoload=False):
403 in_module = hasattr(obj, "__module__") and obj.__module__ == module.__name__
407 in_module = hasattr(obj, "__module__") and obj.__module__ == module.__name__
404 if autoload:
408 if autoload:
405 # check needed for module global built-ins
409 # check needed for module global built-ins
406 if not in_module and name in mod_attrs:
410 if not in_module and name in mod_attrs:
407 return False
411 return False
408 else:
412 else:
409 if not in_module:
413 if not in_module:
410 return False
414 return False
411
415
412 key = (module.__name__, name)
416 key = (module.__name__, name)
413 try:
417 try:
414 d.setdefault(key, []).append(weakref.ref(obj))
418 d.setdefault(key, []).append(weakref.ref(obj))
415 except TypeError:
419 except TypeError:
416 pass
420 pass
417 return True
421 return True
418
422
419
423
420 def superreload(module, reload=reload, old_objects=None, shell=None):
424 def superreload(module, reload=reload, old_objects=None, shell=None):
421 """Enhanced version of the builtin reload function.
425 """Enhanced version of the builtin reload function.
422
426
423 superreload remembers objects previously in the module, and
427 superreload remembers objects previously in the module, and
424
428
425 - upgrades the class dictionary of every old class in the module
429 - upgrades the class dictionary of every old class in the module
426 - upgrades the code object of every old function and method
430 - upgrades the code object of every old function and method
427 - clears the module's namespace before reloading
431 - clears the module's namespace before reloading
428
432
429 """
433 """
430 if old_objects is None:
434 if old_objects is None:
431 old_objects = {}
435 old_objects = {}
432
436
433 # collect old objects in the module
437 # collect old objects in the module
434 for name, obj in list(module.__dict__.items()):
438 for name, obj in list(module.__dict__.items()):
435 if not append_obj(module, old_objects, name, obj):
439 if not append_obj(module, old_objects, name, obj):
436 continue
440 continue
437 key = (module.__name__, name)
441 key = (module.__name__, name)
438 try:
442 try:
439 old_objects.setdefault(key, []).append(weakref.ref(obj))
443 old_objects.setdefault(key, []).append(weakref.ref(obj))
440 except TypeError:
444 except TypeError:
441 pass
445 pass
442
446
443 # reload module
447 # reload module
444 try:
448 try:
445 # clear namespace first from old cruft
449 # clear namespace first from old cruft
446 old_dict = module.__dict__.copy()
450 old_dict = module.__dict__.copy()
447 old_name = module.__name__
451 old_name = module.__name__
448 module.__dict__.clear()
452 module.__dict__.clear()
449 module.__dict__["__name__"] = old_name
453 module.__dict__["__name__"] = old_name
450 module.__dict__["__loader__"] = old_dict["__loader__"]
454 module.__dict__["__loader__"] = old_dict["__loader__"]
451 except (TypeError, AttributeError, KeyError):
455 except (TypeError, AttributeError, KeyError):
452 pass
456 pass
453
457
454 try:
458 try:
455 module = reload(module)
459 module = reload(module)
456 except:
460 except:
457 # restore module dictionary on failed reload
461 # restore module dictionary on failed reload
458 module.__dict__.update(old_dict)
462 module.__dict__.update(old_dict)
459 raise
463 raise
460
464
461 # iterate over all objects and update functions & classes
465 # iterate over all objects and update functions & classes
462 for name, new_obj in list(module.__dict__.items()):
466 for name, new_obj in list(module.__dict__.items()):
463 key = (module.__name__, name)
467 key = (module.__name__, name)
464 if key not in old_objects:
468 if key not in old_objects:
465 # here 'shell' acts both as a flag and as an output var
469 # here 'shell' acts both as a flag and as an output var
466 if (
470 if (
467 shell is None
471 shell is None
468 or name == "Enum"
472 or name == "Enum"
469 or not append_obj(module, old_objects, name, new_obj, True)
473 or not append_obj(module, old_objects, name, new_obj, True)
470 ):
474 ):
471 continue
475 continue
472 shell.user_ns[name] = new_obj
476 shell.user_ns[name] = new_obj
473
477
474 new_refs = []
478 new_refs = []
475 for old_ref in old_objects[key]:
479 for old_ref in old_objects[key]:
476 old_obj = old_ref()
480 old_obj = old_ref()
477 if old_obj is None:
481 if old_obj is None:
478 continue
482 continue
479 new_refs.append(old_ref)
483 new_refs.append(old_ref)
480 update_generic(old_obj, new_obj)
484 update_generic(old_obj, new_obj)
481
485
482 if new_refs:
486 if new_refs:
483 old_objects[key] = new_refs
487 old_objects[key] = new_refs
484 else:
488 else:
485 del old_objects[key]
489 del old_objects[key]
486
490
487 return module
491 return module
488
492
489
493
490 # ------------------------------------------------------------------------------
494 # ------------------------------------------------------------------------------
491 # IPython connectivity
495 # IPython connectivity
492 # ------------------------------------------------------------------------------
496 # ------------------------------------------------------------------------------
493
497
494 from IPython.core.magic import Magics, magics_class, line_magic
498 from IPython.core.magic import Magics, magics_class, line_magic
495
499
496
500
497 @magics_class
501 @magics_class
498 class AutoreloadMagics(Magics):
502 class AutoreloadMagics(Magics):
499 def __init__(self, *a, **kw):
503 def __init__(self, *a, **kw):
500 super().__init__(*a, **kw)
504 super().__init__(*a, **kw)
501 self._reloader = ModuleReloader(self.shell)
505 self._reloader = ModuleReloader(self.shell)
502 self._reloader.check_all = False
506 self._reloader.check_all = False
503 self._reloader.autoload_obj = False
507 self._reloader.autoload_obj = False
504 self.loaded_modules = set(sys.modules)
508 self.loaded_modules = set(sys.modules)
505
509
506 @line_magic
510 @line_magic
507 def autoreload(self, parameter_s=""):
511 def autoreload(self, parameter_s=""):
508 r"""%autoreload => Reload modules automatically
512 r"""%autoreload => Reload modules automatically
509
513
510 %autoreload
514 %autoreload
511 Reload all modules (except those excluded by %aimport) automatically
515 Reload all modules (except those excluded by %aimport) automatically
512 now.
516 now.
513
517
514 %autoreload 0
518 %autoreload 0
515 Disable automatic reloading.
519 Disable automatic reloading.
516
520
517 %autoreload 1
521 %autoreload 1
518 Reload all modules imported with %aimport every time before executing
522 Reload all modules imported with %aimport every time before executing
519 the Python code typed.
523 the Python code typed.
520
524
521 %autoreload 2
525 %autoreload 2
522 Reload all modules (except those excluded by %aimport) every time
526 Reload all modules (except those excluded by %aimport) every time
523 before executing the Python code typed.
527 before executing the Python code typed.
524
528
525 Reloading Python modules in a reliable way is in general
529 Reloading Python modules in a reliable way is in general
526 difficult, and unexpected things may occur. %autoreload tries to
530 difficult, and unexpected things may occur. %autoreload tries to
527 work around common pitfalls by replacing function code objects and
531 work around common pitfalls by replacing function code objects and
528 parts of classes previously in the module with new versions. This
532 parts of classes previously in the module with new versions. This
529 makes the following things to work:
533 makes the following things to work:
530
534
531 - Functions and classes imported via 'from xxx import foo' are upgraded
535 - Functions and classes imported via 'from xxx import foo' are upgraded
532 to new versions when 'xxx' is reloaded.
536 to new versions when 'xxx' is reloaded.
533
537
534 - Methods and properties of classes are upgraded on reload, so that
538 - Methods and properties of classes are upgraded on reload, so that
535 calling 'c.foo()' on an object 'c' created before the reload causes
539 calling 'c.foo()' on an object 'c' created before the reload causes
536 the new code for 'foo' to be executed.
540 the new code for 'foo' to be executed.
537
541
538 Some of the known remaining caveats are:
542 Some of the known remaining caveats are:
539
543
540 - Replacing code objects does not always succeed: changing a @property
544 - Replacing code objects does not always succeed: changing a @property
541 in a class to an ordinary method or a method to a member variable
545 in a class to an ordinary method or a method to a member variable
542 can cause problems (but in old objects only).
546 can cause problems (but in old objects only).
543
547
544 - Functions that are removed (eg. via monkey-patching) from a module
548 - Functions that are removed (eg. via monkey-patching) from a module
545 before it is reloaded are not upgraded.
549 before it is reloaded are not upgraded.
546
550
547 - C extension modules cannot be reloaded, and so cannot be
551 - C extension modules cannot be reloaded, and so cannot be
548 autoreloaded.
552 autoreloaded.
549
553
550 """
554 """
551 if parameter_s == "":
555 if parameter_s == "":
552 self._reloader.check(True)
556 self._reloader.check(True)
553 elif parameter_s == "0":
557 elif parameter_s == "0":
554 self._reloader.enabled = False
558 self._reloader.enabled = False
555 elif parameter_s == "1":
559 elif parameter_s == "1":
556 self._reloader.check_all = False
560 self._reloader.check_all = False
557 self._reloader.enabled = True
561 self._reloader.enabled = True
558 elif parameter_s == "2":
562 elif parameter_s == "2":
559 self._reloader.check_all = True
563 self._reloader.check_all = True
560 self._reloader.enabled = True
564 self._reloader.enabled = True
561 self._reloader.enabled = True
565 self._reloader.enabled = True
562 elif parameter_s == "3":
566 elif parameter_s == "3":
563 self._reloader.check_all = True
567 self._reloader.check_all = True
564 self._reloader.enabled = True
568 self._reloader.enabled = True
565 self._reloader.autoload_obj = True
569 self._reloader.autoload_obj = True
566
570
567 @line_magic
571 @line_magic
568 def aimport(self, parameter_s="", stream=None):
572 def aimport(self, parameter_s="", stream=None):
569 """%aimport => Import modules for automatic reloading.
573 """%aimport => Import modules for automatic reloading.
570
574
571 %aimport
575 %aimport
572 List modules to automatically import and not to import.
576 List modules to automatically import and not to import.
573
577
574 %aimport foo
578 %aimport foo
575 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
579 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
576
580
577 %aimport foo, bar
581 %aimport foo, bar
578 Import modules 'foo', 'bar' and mark them to be autoreloaded for %autoreload 1
582 Import modules 'foo', 'bar' and mark them to be autoreloaded for %autoreload 1
579
583
580 %aimport -foo
584 %aimport -foo
581 Mark module 'foo' to not be autoreloaded for %autoreload 1
585 Mark module 'foo' to not be autoreloaded for %autoreload 1
582 """
586 """
583 modname = parameter_s
587 modname = parameter_s
584 if not modname:
588 if not modname:
585 to_reload = sorted(self._reloader.modules.keys())
589 to_reload = sorted(self._reloader.modules.keys())
586 to_skip = sorted(self._reloader.skip_modules.keys())
590 to_skip = sorted(self._reloader.skip_modules.keys())
587 if stream is None:
591 if stream is None:
588 stream = sys.stdout
592 stream = sys.stdout
589 if self._reloader.check_all:
593 if self._reloader.check_all:
590 stream.write("Modules to reload:\nall-except-skipped\n")
594 stream.write("Modules to reload:\nall-except-skipped\n")
591 else:
595 else:
592 stream.write("Modules to reload:\n%s\n" % " ".join(to_reload))
596 stream.write("Modules to reload:\n%s\n" % " ".join(to_reload))
593 stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip))
597 stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip))
594 elif modname.startswith("-"):
598 elif modname.startswith("-"):
595 modname = modname[1:]
599 modname = modname[1:]
596 self._reloader.mark_module_skipped(modname)
600 self._reloader.mark_module_skipped(modname)
597 else:
601 else:
598 for _module in [_.strip() for _ in modname.split(",")]:
602 for _module in [_.strip() for _ in modname.split(",")]:
599 top_module, top_name = self._reloader.aimport_module(_module)
603 top_module, top_name = self._reloader.aimport_module(_module)
600
604
601 # Inject module to user namespace
605 # Inject module to user namespace
602 self.shell.push({top_name: top_module})
606 self.shell.push({top_name: top_module})
603
607
604 def pre_run_cell(self):
608 def pre_run_cell(self):
605 if self._reloader.enabled:
609 if self._reloader.enabled:
606 try:
610 try:
607 self._reloader.check()
611 self._reloader.check()
608 except:
612 except:
609 pass
613 pass
610
614
611 def post_execute_hook(self):
615 def post_execute_hook(self):
612 """Cache the modification times of any modules imported in this execution"""
616 """Cache the modification times of any modules imported in this execution"""
613 newly_loaded_modules = set(sys.modules) - self.loaded_modules
617 newly_loaded_modules = set(sys.modules) - self.loaded_modules
614 for modname in newly_loaded_modules:
618 for modname in newly_loaded_modules:
615 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
619 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
616 if pymtime is not None:
620 if pymtime is not None:
617 self._reloader.modules_mtimes[modname] = pymtime
621 self._reloader.modules_mtimes[modname] = pymtime
618
622
619 self.loaded_modules.update(newly_loaded_modules)
623 self.loaded_modules.update(newly_loaded_modules)
620
624
621
625
622 def load_ipython_extension(ip):
626 def load_ipython_extension(ip):
623 """Load the extension in IPython."""
627 """Load the extension in IPython."""
624 auto_reload = AutoreloadMagics(ip)
628 auto_reload = AutoreloadMagics(ip)
625 ip.register_magics(auto_reload)
629 ip.register_magics(auto_reload)
626 ip.events.register("pre_run_cell", auto_reload.pre_run_cell)
630 ip.events.register("pre_run_cell", auto_reload.pre_run_cell)
627 ip.events.register("post_execute", auto_reload.post_execute_hook)
631 ip.events.register("post_execute", auto_reload.post_execute_hook)
General Comments 0
You need to be logged in to leave comments. Login now