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