##// END OF EJS Templates
Merge pull request #12734 from skalaydzhiyski/feature-add-autoreload-option-3...
Matthias Bussonnier -
r26472:7e0c8b73 merge
parent child Browse files
Show More
@@ -1,15 +1,21 b''
1 # When making commits that are strictly formatting/style changes, add the
1 # When making commits that are strictly formatting/style changes, add the
2 # commit hash here, so git blame can ignore the change. See docs for more
2 # commit hash here, so git blame can ignore the change. See docs for more
3 # details:
3 # details:
4 # https://git-scm.com/docs/git-config#Documentation/git-config.txt-blameignoreRevsFile
4 # https://git-scm.com/docs/git-config#Documentation/git-config.txt-blameignoreRevsFile
5 #
5 #
6 #
6 #
7 # You should be able to execute either
7 # You should be able to execute either
8 # ./tools/configure-git-blame-ignore-revs.bat or
8 # ./tools/configure-git-blame-ignore-revs.bat or
9 # ./tools/configure-git-blame-ignore-revs.sh
9 # ./tools/configure-git-blame-ignore-revs.sh
10 #
10 #
11 # Example entries:
11 # Example entries:
12 #
12 #
13 # <full commit hash> # initial black-format
13 # <full commit hash> # initial black-format
14 # <full commit hash> # rename something internal
14 # <full commit hash> # rename something internal
15 6e748726282d1acb9a4f9f264ee679c474c4b8f5 # Apply pygrade --36plus on IPython/core/tests/test_inputtransformer.py.
15 6e748726282d1acb9a4f9f264ee679c474c4b8f5 # Apply pygrade --36plus on IPython/core/tests/test_inputtransformer.py.
16 0233e65d8086d0ec34acb8685b7a5411633f0899 # apply pyupgrade to IPython/extensions/tests/test_autoreload.py
17 a6a7e4dd7e51b892147895006d3a2a6c34b79ae6 # apply black to IPython/extensions/tests/test_autoreload.py
18 c5ca5a8f25432dfd6b9eccbbe446a8348bf37cfa # apply pyupgrade to IPython/extensions/autoreload.py
19 50624b84ccdece781750f5eb635a9efbf2fe30d6 # apply black to IPython/extensions/autoreload.py
20 b7aaa47412b96379198705955004930c57f9d74a # apply pyupgrade to IPython/extensions/autoreload.py
21 9c7476a88af3e567426b412f1b3c778401d8f6aa # apply black to IPython/extensions/autoreload.py
@@ -1,550 +1,624 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``
52
53 Reload all modules AND autoload newly added objects
54 every time before executing the Python code typed.
55
51 ``%aimport``
56 ``%aimport``
52
57
53 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.
54
59
55 ``%aimport foo``
60 ``%aimport foo``
56
61
57 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``
58
63
59 ``%aimport foo, bar``
64 ``%aimport foo, bar``
60
65
61 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``
62
67
63 ``%aimport -foo``
68 ``%aimport -foo``
64
69
65 Mark module 'foo' to not be autoreloaded.
70 Mark module 'foo' to not be autoreloaded.
66
71
67 Caveats
72 Caveats
68 =======
73 =======
69
74
70 Reloading Python modules in a reliable way is in general difficult,
75 Reloading Python modules in a reliable way is in general difficult,
71 and unexpected things may occur. ``%autoreload`` tries to work around
76 and unexpected things may occur. ``%autoreload`` tries to work around
72 common pitfalls by replacing function code objects and parts of
77 common pitfalls by replacing function code objects and parts of
73 classes previously in the module with new versions. This makes the
78 classes previously in the module with new versions. This makes the
74 following things to work:
79 following things to work:
75
80
76 - Functions and classes imported via 'from xxx import foo' are upgraded
81 - Functions and classes imported via 'from xxx import foo' are upgraded
77 to new versions when 'xxx' is reloaded.
82 to new versions when 'xxx' is reloaded.
78
83
79 - Methods and properties of classes are upgraded on reload, so that
84 - Methods and properties of classes are upgraded on reload, so that
80 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
81 the new code for 'foo' to be executed.
86 the new code for 'foo' to be executed.
82
87
83 Some of the known remaining caveats are:
88 Some of the known remaining caveats are:
84
89
85 - Replacing code objects does not always succeed: changing a @property
90 - Replacing code objects does not always succeed: changing a @property
86 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
87 can cause problems (but in old objects only).
92 can cause problems (but in old objects only).
88
93
89 - Functions that are removed (eg. via monkey-patching) from a module
94 - Functions that are removed (eg. via monkey-patching) from a module
90 before it is reloaded are not upgraded.
95 before it is reloaded are not upgraded.
91
96
92 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
97 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
93 """
98 """
94
99
95 skip_doctest = True
100 skip_doctest = True
96
101
97 #-----------------------------------------------------------------------------
102 # -----------------------------------------------------------------------------
98 # Copyright (C) 2000 Thomas Heller
103 # Copyright (C) 2000 Thomas Heller
99 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
104 # Copyright (C) 2008 Pauli Virtanen <pav@iki.fi>
100 # Copyright (C) 2012 The IPython Development Team
105 # Copyright (C) 2012 The IPython Development Team
101 #
106 #
102 # 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
103 # the file COPYING, distributed as part of this software.
108 # the file COPYING, distributed as part of this software.
104 #-----------------------------------------------------------------------------
109 # -----------------------------------------------------------------------------
105 #
110 #
106 # 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
107 # code by Thomas Heller.
112 # code by Thomas Heller.
108
113
109 #-----------------------------------------------------------------------------
114 # -----------------------------------------------------------------------------
110 # Imports
115 # Imports
111 #-----------------------------------------------------------------------------
116 # -----------------------------------------------------------------------------
112
117
113 import os
118 import os
114 import sys
119 import sys
115 import traceback
120 import traceback
116 import types
121 import types
117 import weakref
122 import weakref
118 import gc
123 import gc
119 from importlib import import_module
124 from importlib import import_module
120 from importlib.util import source_from_cache
125 from importlib.util import source_from_cache
121 from imp import reload
126 from imp import reload
122
127
123 #------------------------------------------------------------------------------
128 # ------------------------------------------------------------------------------
124 # Autoreload functionality
129 # Autoreload functionality
125 #------------------------------------------------------------------------------
130 # ------------------------------------------------------------------------------
126
131
127 class ModuleReloader(object):
132
133 class ModuleReloader:
128 enabled = False
134 enabled = False
129 """Whether this reloader is enabled"""
135 """Whether this reloader is enabled"""
130
136
131 check_all = True
137 check_all = True
132 """Autoreload all modules, not just those listed in 'modules'"""
138 """Autoreload all modules, not just those listed in 'modules'"""
133
139
134 def __init__(self):
140 autoload_obj = False
141 """Autoreload all modules AND autoload all new objects"""
142
143 def __init__(self, shell=None):
135 # Modules that failed to reload: {module: mtime-on-failed-reload, ...}
144 # Modules that failed to reload: {module: mtime-on-failed-reload, ...}
136 self.failed = {}
145 self.failed = {}
137 # Modules specially marked as autoreloadable.
146 # Modules specially marked as autoreloadable.
138 self.modules = {}
147 self.modules = {}
139 # Modules specially marked as not autoreloadable.
148 # Modules specially marked as not autoreloadable.
140 self.skip_modules = {}
149 self.skip_modules = {}
141 # (module-name, name) -> weakref, for replacing old code objects
150 # (module-name, name) -> weakref, for replacing old code objects
142 self.old_objects = {}
151 self.old_objects = {}
143 # Module modification timestamps
152 # Module modification timestamps
144 self.modules_mtimes = {}
153 self.modules_mtimes = {}
154 self.shell = shell
145
155
146 # Cache module modification times
156 # Cache module modification times
147 self.check(check_all=True, do_reload=False)
157 self.check(check_all=True, do_reload=False)
148
158
149 def mark_module_skipped(self, module_name):
159 def mark_module_skipped(self, module_name):
150 """Skip reloading the named module in the future"""
160 """Skip reloading the named module in the future"""
151 try:
161 try:
152 del self.modules[module_name]
162 del self.modules[module_name]
153 except KeyError:
163 except KeyError:
154 pass
164 pass
155 self.skip_modules[module_name] = True
165 self.skip_modules[module_name] = True
156
166
157 def mark_module_reloadable(self, module_name):
167 def mark_module_reloadable(self, module_name):
158 """Reload the named module in the future (if it is imported)"""
168 """Reload the named module in the future (if it is imported)"""
159 try:
169 try:
160 del self.skip_modules[module_name]
170 del self.skip_modules[module_name]
161 except KeyError:
171 except KeyError:
162 pass
172 pass
163 self.modules[module_name] = True
173 self.modules[module_name] = True
164
174
165 def aimport_module(self, module_name):
175 def aimport_module(self, module_name):
166 """Import a module, and mark it reloadable
176 """Import a module, and mark it reloadable
167
177
168 Returns
178 Returns
169 -------
179 -------
170 top_module : module
180 top_module : module
171 The imported module if it is top-level, or the top-level
181 The imported module if it is top-level, or the top-level
172 top_name : module
182 top_name : module
173 Name of top_module
183 Name of top_module
174
184
175 """
185 """
176 self.mark_module_reloadable(module_name)
186 self.mark_module_reloadable(module_name)
177
187
178 import_module(module_name)
188 import_module(module_name)
179 top_name = module_name.split('.')[0]
189 top_name = module_name.split(".")[0]
180 top_module = sys.modules[top_name]
190 top_module = sys.modules[top_name]
181 return top_module, top_name
191 return top_module, top_name
182
192
183 def filename_and_mtime(self, module):
193 def filename_and_mtime(self, module):
184 if not hasattr(module, '__file__') or module.__file__ is None:
194 if not hasattr(module, "__file__") or module.__file__ is None:
185 return None, None
195 return None, None
186
196
187 if getattr(module, '__name__', None) in [None, '__mp_main__', '__main__']:
197 if getattr(module, "__name__", None) in [None, "__mp_main__", "__main__"]:
188 # we cannot reload(__main__) or reload(__mp_main__)
198 # we cannot reload(__main__) or reload(__mp_main__)
189 return None, None
199 return None, None
190
200
191 filename = module.__file__
201 filename = module.__file__
192 path, ext = os.path.splitext(filename)
202 path, ext = os.path.splitext(filename)
193
203
194 if ext.lower() == '.py':
204 if ext.lower() == ".py":
195 py_filename = filename
205 py_filename = filename
196 else:
206 else:
197 try:
207 try:
198 py_filename = source_from_cache(filename)
208 py_filename = source_from_cache(filename)
199 except ValueError:
209 except ValueError:
200 return None, None
210 return None, None
201
211
202 try:
212 try:
203 pymtime = os.stat(py_filename).st_mtime
213 pymtime = os.stat(py_filename).st_mtime
204 except OSError:
214 except OSError:
205 return None, None
215 return None, None
206
216
207 return py_filename, pymtime
217 return py_filename, pymtime
208
218
209 def check(self, check_all=False, do_reload=True):
219 def check(self, check_all=False, do_reload=True):
210 """Check whether some modules need to be reloaded."""
220 """Check whether some modules need to be reloaded."""
211
221
212 if not self.enabled and not check_all:
222 if not self.enabled and not check_all:
213 return
223 return
214
224
215 if check_all or self.check_all:
225 if check_all or self.check_all:
216 modules = list(sys.modules.keys())
226 modules = list(sys.modules.keys())
217 else:
227 else:
218 modules = list(self.modules.keys())
228 modules = list(self.modules.keys())
219
229
220 for modname in modules:
230 for modname in modules:
221 m = sys.modules.get(modname, None)
231 m = sys.modules.get(modname, None)
222
232
223 if modname in self.skip_modules:
233 if modname in self.skip_modules:
224 continue
234 continue
225
235
226 py_filename, pymtime = self.filename_and_mtime(m)
236 py_filename, pymtime = self.filename_and_mtime(m)
227 if py_filename is None:
237 if py_filename is None:
228 continue
238 continue
229
239
230 try:
240 try:
231 if pymtime <= self.modules_mtimes[modname]:
241 if pymtime <= self.modules_mtimes[modname]:
232 continue
242 continue
233 except KeyError:
243 except KeyError:
234 self.modules_mtimes[modname] = pymtime
244 self.modules_mtimes[modname] = pymtime
235 continue
245 continue
236 else:
246 else:
237 if self.failed.get(py_filename, None) == pymtime:
247 if self.failed.get(py_filename, None) == pymtime:
238 continue
248 continue
239
249
240 self.modules_mtimes[modname] = pymtime
250 self.modules_mtimes[modname] = pymtime
241
251
242 # If we've reached this point, we should try to reload the module
252 # If we've reached this point, we should try to reload the module
243 if do_reload:
253 if do_reload:
244 try:
254 try:
245 superreload(m, reload, self.old_objects)
255 if self.autoload_obj:
256 superreload(m, reload, self.old_objects, self.shell)
257 else:
258 superreload(m, reload, self.old_objects)
246 if py_filename in self.failed:
259 if py_filename in self.failed:
247 del self.failed[py_filename]
260 del self.failed[py_filename]
248 except:
261 except:
249 print("[autoreload of %s failed: %s]" % (
262 print(
250 modname, traceback.format_exc(10)), file=sys.stderr)
263 "[autoreload of {} failed: {}]".format(
264 modname, traceback.format_exc(10)
265 ),
266 file=sys.stderr,
267 )
251 self.failed[py_filename] = pymtime
268 self.failed[py_filename] = pymtime
252
269
253 #------------------------------------------------------------------------------
270
271 # ------------------------------------------------------------------------------
254 # superreload
272 # superreload
255 #------------------------------------------------------------------------------
273 # ------------------------------------------------------------------------------
256
274
257
275
258 func_attrs = ['__code__', '__defaults__', '__doc__',
276 func_attrs = [
259 '__closure__', '__globals__', '__dict__']
277 "__code__",
278 "__defaults__",
279 "__doc__",
280 "__closure__",
281 "__globals__",
282 "__dict__",
283 ]
260
284
261
285
262 def update_function(old, new):
286 def update_function(old, new):
263 """Upgrade the code object of a function"""
287 """Upgrade the code object of a function"""
264 for name in func_attrs:
288 for name in func_attrs:
265 try:
289 try:
266 setattr(old, name, getattr(new, name))
290 setattr(old, name, getattr(new, name))
267 except (AttributeError, TypeError):
291 except (AttributeError, TypeError):
268 pass
292 pass
269
293
270
294
271 def update_instances(old, new):
295 def update_instances(old, new):
272 """Use garbage collector to find all instances that refer to the old
296 """Use garbage collector to find all instances that refer to the old
273 class definition and update their __class__ to point to the new class
297 class definition and update their __class__ to point to the new class
274 definition"""
298 definition"""
275
299
276 refs = gc.get_referrers(old)
300 refs = gc.get_referrers(old)
277
301
278 for ref in refs:
302 for ref in refs:
279 if type(ref) is old:
303 if type(ref) is old:
280 ref.__class__ = new
304 ref.__class__ = new
281
305
282
306
283 def update_class(old, new):
307 def update_class(old, new):
284 """Replace stuff in the __dict__ of a class, and upgrade
308 """Replace stuff in the __dict__ of a class, and upgrade
285 method code objects, and add new methods, if any"""
309 method code objects, and add new methods, if any"""
286 for key in list(old.__dict__.keys()):
310 for key in list(old.__dict__.keys()):
287 old_obj = getattr(old, key)
311 old_obj = getattr(old, key)
288 try:
312 try:
289 new_obj = getattr(new, key)
313 new_obj = getattr(new, key)
290 # explicitly checking that comparison returns True to handle
314 # explicitly checking that comparison returns True to handle
291 # cases where `==` doesn't return a boolean.
315 # cases where `==` doesn't return a boolean.
292 if (old_obj == new_obj) is True:
316 if (old_obj == new_obj) is True:
293 continue
317 continue
294 except AttributeError:
318 except AttributeError:
295 # obsolete attribute: remove it
319 # obsolete attribute: remove it
296 try:
320 try:
297 delattr(old, key)
321 delattr(old, key)
298 except (AttributeError, TypeError):
322 except (AttributeError, TypeError):
299 pass
323 pass
300 continue
324 continue
301
325
302 if update_generic(old_obj, new_obj): continue
326 if update_generic(old_obj, new_obj):
327 continue
303
328
304 try:
329 try:
305 setattr(old, key, getattr(new, key))
330 setattr(old, key, getattr(new, key))
306 except (AttributeError, TypeError):
331 except (AttributeError, TypeError):
307 pass # skip non-writable attributes
332 pass # skip non-writable attributes
308
333
309 for key in list(new.__dict__.keys()):
334 for key in list(new.__dict__.keys()):
310 if key not in list(old.__dict__.keys()):
335 if key not in list(old.__dict__.keys()):
311 try:
336 try:
312 setattr(old, key, getattr(new, key))
337 setattr(old, key, getattr(new, key))
313 except (AttributeError, TypeError):
338 except (AttributeError, TypeError):
314 pass # skip non-writable attributes
339 pass # skip non-writable attributes
315
340
316 # update all instances of class
341 # update all instances of class
317 update_instances(old, new)
342 update_instances(old, new)
318
343
319
344
320 def update_property(old, new):
345 def update_property(old, new):
321 """Replace get/set/del functions of a property"""
346 """Replace get/set/del functions of a property"""
322 update_generic(old.fdel, new.fdel)
347 update_generic(old.fdel, new.fdel)
323 update_generic(old.fget, new.fget)
348 update_generic(old.fget, new.fget)
324 update_generic(old.fset, new.fset)
349 update_generic(old.fset, new.fset)
325
350
326
351
327 def isinstance2(a, b, typ):
352 def isinstance2(a, b, typ):
328 return isinstance(a, typ) and isinstance(b, typ)
353 return isinstance(a, typ) and isinstance(b, typ)
329
354
330
355
331 UPDATE_RULES = [
356 UPDATE_RULES = [
332 (lambda a, b: isinstance2(a, b, type),
357 (lambda a, b: isinstance2(a, b, type), update_class),
333 update_class),
358 (lambda a, b: isinstance2(a, b, types.FunctionType), update_function),
334 (lambda a, b: isinstance2(a, b, types.FunctionType),
359 (lambda a, b: isinstance2(a, b, property), update_property),
335 update_function),
336 (lambda a, b: isinstance2(a, b, property),
337 update_property),
338 ]
360 ]
339 UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType),
361 UPDATE_RULES.extend(
340 lambda a, b: update_function(a.__func__, b.__func__)),
362 [
341 ])
363 (
364 lambda a, b: isinstance2(a, b, types.MethodType),
365 lambda a, b: update_function(a.__func__, b.__func__),
366 ),
367 ]
368 )
342
369
343
370
344 def update_generic(a, b):
371 def update_generic(a, b):
345 for type_check, update in UPDATE_RULES:
372 for type_check, update in UPDATE_RULES:
346 if type_check(a, b):
373 if type_check(a, b):
347 update(a, b)
374 update(a, b)
348 return True
375 return True
349 return False
376 return False
350
377
351
378
352 class StrongRef(object):
379 class StrongRef:
353 def __init__(self, obj):
380 def __init__(self, obj):
354 self.obj = obj
381 self.obj = obj
382
355 def __call__(self):
383 def __call__(self):
356 return self.obj
384 return self.obj
357
385
358
386
359 def superreload(module, reload=reload, old_objects=None):
387 mod_attrs = [
388 "__name__",
389 "__doc__",
390 "__package__",
391 "__loader__",
392 "__spec__",
393 "__file__",
394 "__cached__",
395 "__builtins__",
396 ]
397
398
399 def append_obj(module, d, name, obj, autoload=False):
400 in_module = hasattr(obj, "__module__") and obj.__module__ == module.__name__
401 if autoload:
402 # check needed for module global built-ins
403 if not in_module and name in mod_attrs:
404 return False
405 else:
406 if not in_module:
407 return False
408
409 key = (module.__name__, name)
410 try:
411 d.setdefault(key, []).append(weakref.ref(obj))
412 except TypeError:
413 pass
414 return True
415
416
417 def superreload(module, reload=reload, old_objects=None, shell=None):
360 """Enhanced version of the builtin reload function.
418 """Enhanced version of the builtin reload function.
361
419
362 superreload remembers objects previously in the module, and
420 superreload remembers objects previously in the module, and
363
421
364 - upgrades the class dictionary of every old class in the module
422 - upgrades the class dictionary of every old class in the module
365 - upgrades the code object of every old function and method
423 - upgrades the code object of every old function and method
366 - clears the module's namespace before reloading
424 - clears the module's namespace before reloading
367
425
368 """
426 """
369 if old_objects is None:
427 if old_objects is None:
370 old_objects = {}
428 old_objects = {}
371
429
372 # collect old objects in the module
430 # collect old objects in the module
373 for name, obj in list(module.__dict__.items()):
431 for name, obj in list(module.__dict__.items()):
374 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
432 if not append_obj(module, old_objects, name, obj):
375 continue
433 continue
376 key = (module.__name__, name)
434 key = (module.__name__, name)
377 try:
435 try:
378 old_objects.setdefault(key, []).append(weakref.ref(obj))
436 old_objects.setdefault(key, []).append(weakref.ref(obj))
379 except TypeError:
437 except TypeError:
380 pass
438 pass
381
439
382 # reload module
440 # reload module
383 try:
441 try:
384 # clear namespace first from old cruft
442 # clear namespace first from old cruft
385 old_dict = module.__dict__.copy()
443 old_dict = module.__dict__.copy()
386 old_name = module.__name__
444 old_name = module.__name__
387 module.__dict__.clear()
445 module.__dict__.clear()
388 module.__dict__['__name__'] = old_name
446 module.__dict__["__name__"] = old_name
389 module.__dict__['__loader__'] = old_dict['__loader__']
447 module.__dict__["__loader__"] = old_dict["__loader__"]
390 except (TypeError, AttributeError, KeyError):
448 except (TypeError, AttributeError, KeyError):
391 pass
449 pass
392
450
393 try:
451 try:
394 module = reload(module)
452 module = reload(module)
395 except:
453 except:
396 # restore module dictionary on failed reload
454 # restore module dictionary on failed reload
397 module.__dict__.update(old_dict)
455 module.__dict__.update(old_dict)
398 raise
456 raise
399
457
400 # iterate over all objects and update functions & classes
458 # iterate over all objects and update functions & classes
401 for name, new_obj in list(module.__dict__.items()):
459 for name, new_obj in list(module.__dict__.items()):
402 key = (module.__name__, name)
460 key = (module.__name__, name)
403 if key not in old_objects: continue
461 if key not in old_objects:
462 # here 'shell' acts both as a flag and as an output var
463 if (
464 shell is None
465 or name == "Enum"
466 or not append_obj(module, old_objects, name, new_obj, True)
467 ):
468 continue
469 shell.user_ns[name] = new_obj
404
470
405 new_refs = []
471 new_refs = []
406 for old_ref in old_objects[key]:
472 for old_ref in old_objects[key]:
407 old_obj = old_ref()
473 old_obj = old_ref()
408 if old_obj is None: continue
474 if old_obj is None:
475 continue
409 new_refs.append(old_ref)
476 new_refs.append(old_ref)
410 update_generic(old_obj, new_obj)
477 update_generic(old_obj, new_obj)
411
478
412 if new_refs:
479 if new_refs:
413 old_objects[key] = new_refs
480 old_objects[key] = new_refs
414 else:
481 else:
415 del old_objects[key]
482 del old_objects[key]
416
483
417 return module
484 return module
418
485
419 #------------------------------------------------------------------------------
486
487 # ------------------------------------------------------------------------------
420 # IPython connectivity
488 # IPython connectivity
421 #------------------------------------------------------------------------------
489 # ------------------------------------------------------------------------------
422
490
423 from IPython.core.magic import Magics, magics_class, line_magic
491 from IPython.core.magic import Magics, magics_class, line_magic
424
492
493
425 @magics_class
494 @magics_class
426 class AutoreloadMagics(Magics):
495 class AutoreloadMagics(Magics):
427 def __init__(self, *a, **kw):
496 def __init__(self, *a, **kw):
428 super(AutoreloadMagics, self).__init__(*a, **kw)
497 super().__init__(*a, **kw)
429 self._reloader = ModuleReloader()
498 self._reloader = ModuleReloader(self.shell)
430 self._reloader.check_all = False
499 self._reloader.check_all = False
500 self._reloader.autoload_obj = False
431 self.loaded_modules = set(sys.modules)
501 self.loaded_modules = set(sys.modules)
432
502
433 @line_magic
503 @line_magic
434 def autoreload(self, parameter_s=''):
504 def autoreload(self, parameter_s=""):
435 r"""%autoreload => Reload modules automatically
505 r"""%autoreload => Reload modules automatically
436
506
437 %autoreload
507 %autoreload
438 Reload all modules (except those excluded by %aimport) automatically
508 Reload all modules (except those excluded by %aimport) automatically
439 now.
509 now.
440
510
441 %autoreload 0
511 %autoreload 0
442 Disable automatic reloading.
512 Disable automatic reloading.
443
513
444 %autoreload 1
514 %autoreload 1
445 Reload all modules imported with %aimport every time before executing
515 Reload all modules imported with %aimport every time before executing
446 the Python code typed.
516 the Python code typed.
447
517
448 %autoreload 2
518 %autoreload 2
449 Reload all modules (except those excluded by %aimport) every time
519 Reload all modules (except those excluded by %aimport) every time
450 before executing the Python code typed.
520 before executing the Python code typed.
451
521
452 Reloading Python modules in a reliable way is in general
522 Reloading Python modules in a reliable way is in general
453 difficult, and unexpected things may occur. %autoreload tries to
523 difficult, and unexpected things may occur. %autoreload tries to
454 work around common pitfalls by replacing function code objects and
524 work around common pitfalls by replacing function code objects and
455 parts of classes previously in the module with new versions. This
525 parts of classes previously in the module with new versions. This
456 makes the following things to work:
526 makes the following things to work:
457
527
458 - Functions and classes imported via 'from xxx import foo' are upgraded
528 - Functions and classes imported via 'from xxx import foo' are upgraded
459 to new versions when 'xxx' is reloaded.
529 to new versions when 'xxx' is reloaded.
460
530
461 - Methods and properties of classes are upgraded on reload, so that
531 - Methods and properties of classes are upgraded on reload, so that
462 calling 'c.foo()' on an object 'c' created before the reload causes
532 calling 'c.foo()' on an object 'c' created before the reload causes
463 the new code for 'foo' to be executed.
533 the new code for 'foo' to be executed.
464
534
465 Some of the known remaining caveats are:
535 Some of the known remaining caveats are:
466
536
467 - Replacing code objects does not always succeed: changing a @property
537 - Replacing code objects does not always succeed: changing a @property
468 in a class to an ordinary method or a method to a member variable
538 in a class to an ordinary method or a method to a member variable
469 can cause problems (but in old objects only).
539 can cause problems (but in old objects only).
470
540
471 - Functions that are removed (eg. via monkey-patching) from a module
541 - Functions that are removed (eg. via monkey-patching) from a module
472 before it is reloaded are not upgraded.
542 before it is reloaded are not upgraded.
473
543
474 - C extension modules cannot be reloaded, and so cannot be
544 - C extension modules cannot be reloaded, and so cannot be
475 autoreloaded.
545 autoreloaded.
476
546
477 """
547 """
478 if parameter_s == '':
548 if parameter_s == "":
479 self._reloader.check(True)
549 self._reloader.check(True)
480 elif parameter_s == '0':
550 elif parameter_s == "0":
481 self._reloader.enabled = False
551 self._reloader.enabled = False
482 elif parameter_s == '1':
552 elif parameter_s == "1":
483 self._reloader.check_all = False
553 self._reloader.check_all = False
484 self._reloader.enabled = True
554 self._reloader.enabled = True
485 elif parameter_s == '2':
555 elif parameter_s == "2":
556 self._reloader.check_all = True
557 self._reloader.enabled = True
558 self._reloader.enabled = True
559 elif parameter_s == "3":
486 self._reloader.check_all = True
560 self._reloader.check_all = True
487 self._reloader.enabled = True
561 self._reloader.enabled = True
562 self._reloader.autoload_obj = True
488
563
489 @line_magic
564 @line_magic
490 def aimport(self, parameter_s='', stream=None):
565 def aimport(self, parameter_s="", stream=None):
491 """%aimport => Import modules for automatic reloading.
566 """%aimport => Import modules for automatic reloading.
492
567
493 %aimport
568 %aimport
494 List modules to automatically import and not to import.
569 List modules to automatically import and not to import.
495
570
496 %aimport foo
571 %aimport foo
497 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
572 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
498
573
499 %aimport foo, bar
574 %aimport foo, bar
500 Import modules 'foo', 'bar' and mark them to be autoreloaded for %autoreload 1
575 Import modules 'foo', 'bar' and mark them to be autoreloaded for %autoreload 1
501
576
502 %aimport -foo
577 %aimport -foo
503 Mark module 'foo' to not be autoreloaded for %autoreload 1
578 Mark module 'foo' to not be autoreloaded for %autoreload 1
504 """
579 """
505 modname = parameter_s
580 modname = parameter_s
506 if not modname:
581 if not modname:
507 to_reload = sorted(self._reloader.modules.keys())
582 to_reload = sorted(self._reloader.modules.keys())
508 to_skip = sorted(self._reloader.skip_modules.keys())
583 to_skip = sorted(self._reloader.skip_modules.keys())
509 if stream is None:
584 if stream is None:
510 stream = sys.stdout
585 stream = sys.stdout
511 if self._reloader.check_all:
586 if self._reloader.check_all:
512 stream.write("Modules to reload:\nall-except-skipped\n")
587 stream.write("Modules to reload:\nall-except-skipped\n")
513 else:
588 else:
514 stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload))
589 stream.write("Modules to reload:\n%s\n" % " ".join(to_reload))
515 stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip))
590 stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip))
516 elif modname.startswith('-'):
591 elif modname.startswith("-"):
517 modname = modname[1:]
592 modname = modname[1:]
518 self._reloader.mark_module_skipped(modname)
593 self._reloader.mark_module_skipped(modname)
519 else:
594 else:
520 for _module in ([_.strip() for _ in modname.split(',')]):
595 for _module in [_.strip() for _ in modname.split(",")]:
521 top_module, top_name = self._reloader.aimport_module(_module)
596 top_module, top_name = self._reloader.aimport_module(_module)
522
597
523 # Inject module to user namespace
598 # Inject module to user namespace
524 self.shell.push({top_name: top_module})
599 self.shell.push({top_name: top_module})
525
600
526 def pre_run_cell(self):
601 def pre_run_cell(self):
527 if self._reloader.enabled:
602 if self._reloader.enabled:
528 try:
603 try:
529 self._reloader.check()
604 self._reloader.check()
530 except:
605 except:
531 pass
606 pass
532
607
533 def post_execute_hook(self):
608 def post_execute_hook(self):
534 """Cache the modification times of any modules imported in this execution
609 """Cache the modification times of any modules imported in this execution"""
535 """
536 newly_loaded_modules = set(sys.modules) - self.loaded_modules
610 newly_loaded_modules = set(sys.modules) - self.loaded_modules
537 for modname in newly_loaded_modules:
611 for modname in newly_loaded_modules:
538 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
612 _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname])
539 if pymtime is not None:
613 if pymtime is not None:
540 self._reloader.modules_mtimes[modname] = pymtime
614 self._reloader.modules_mtimes[modname] = pymtime
541
615
542 self.loaded_modules.update(newly_loaded_modules)
616 self.loaded_modules.update(newly_loaded_modules)
543
617
544
618
545 def load_ipython_extension(ip):
619 def load_ipython_extension(ip):
546 """Load the extension in IPython."""
620 """Load the extension in IPython."""
547 auto_reload = AutoreloadMagics(ip)
621 auto_reload = AutoreloadMagics(ip)
548 ip.register_magics(auto_reload)
622 ip.register_magics(auto_reload)
549 ip.events.register('pre_run_cell', auto_reload.pre_run_cell)
623 ip.events.register("pre_run_cell", auto_reload.pre_run_cell)
550 ip.events.register('post_execute', auto_reload.post_execute_hook)
624 ip.events.register("post_execute", auto_reload.post_execute_hook)
@@ -1,447 +1,555 b''
1 """Tests for autoreload extension.
1 """Tests for autoreload extension.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 # -----------------------------------------------------------------------------
4 # Copyright (c) 2012 IPython Development Team.
4 # Copyright (c) 2012 IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 # -----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 # -----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 # -----------------------------------------------------------------------------
14
14
15 import os
15 import os
16 import sys
16 import sys
17 import tempfile
17 import tempfile
18 import textwrap
18 import textwrap
19 import shutil
19 import shutil
20 import random
20 import random
21 import time
21 import time
22 from io import StringIO
22 from io import StringIO
23
23
24 import nose.tools as nt
24 import nose.tools as nt
25 import IPython.testing.tools as tt
25 import IPython.testing.tools as tt
26
26
27 from unittest import TestCase
27 from unittest import TestCase
28
28
29 from IPython.extensions.autoreload import AutoreloadMagics
29 from IPython.extensions.autoreload import AutoreloadMagics
30 from IPython.core.events import EventManager, pre_run_cell
30 from IPython.core.events import EventManager, pre_run_cell
31
31
32 #-----------------------------------------------------------------------------
32 # -----------------------------------------------------------------------------
33 # Test fixture
33 # Test fixture
34 #-----------------------------------------------------------------------------
34 # -----------------------------------------------------------------------------
35
35
36 noop = lambda *a, **kw: None
36 noop = lambda *a, **kw: None
37
37
38 class FakeShell:
39
38
39 class FakeShell:
40 def __init__(self):
40 def __init__(self):
41 self.ns = {}
41 self.ns = {}
42 self.user_ns = self.ns
42 self.user_ns = self.ns
43 self.user_ns_hidden = {}
43 self.user_ns_hidden = {}
44 self.events = EventManager(self, {'pre_run_cell', pre_run_cell})
44 self.events = EventManager(self, {"pre_run_cell", pre_run_cell})
45 self.auto_magics = AutoreloadMagics(shell=self)
45 self.auto_magics = AutoreloadMagics(shell=self)
46 self.events.register('pre_run_cell', self.auto_magics.pre_run_cell)
46 self.events.register("pre_run_cell", self.auto_magics.pre_run_cell)
47
47
48 register_magics = set_hook = noop
48 register_magics = set_hook = noop
49
49
50 def run_code(self, code):
50 def run_code(self, code):
51 self.events.trigger('pre_run_cell')
51 self.events.trigger("pre_run_cell")
52 exec(code, self.user_ns)
52 exec(code, self.user_ns)
53 self.auto_magics.post_execute_hook()
53 self.auto_magics.post_execute_hook()
54
54
55 def push(self, items):
55 def push(self, items):
56 self.ns.update(items)
56 self.ns.update(items)
57
57
58 def magic_autoreload(self, parameter):
58 def magic_autoreload(self, parameter):
59 self.auto_magics.autoreload(parameter)
59 self.auto_magics.autoreload(parameter)
60
60
61 def magic_aimport(self, parameter, stream=None):
61 def magic_aimport(self, parameter, stream=None):
62 self.auto_magics.aimport(parameter, stream=stream)
62 self.auto_magics.aimport(parameter, stream=stream)
63 self.auto_magics.post_execute_hook()
63 self.auto_magics.post_execute_hook()
64
64
65
65
66 class Fixture(TestCase):
66 class Fixture(TestCase):
67 """Fixture for creating test module files"""
67 """Fixture for creating test module files"""
68
68
69 test_dir = None
69 test_dir = None
70 old_sys_path = None
70 old_sys_path = None
71 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
71 filename_chars = "abcdefghijklmopqrstuvwxyz0123456789"
72
72
73 def setUp(self):
73 def setUp(self):
74 self.test_dir = tempfile.mkdtemp()
74 self.test_dir = tempfile.mkdtemp()
75 self.old_sys_path = list(sys.path)
75 self.old_sys_path = list(sys.path)
76 sys.path.insert(0, self.test_dir)
76 sys.path.insert(0, self.test_dir)
77 self.shell = FakeShell()
77 self.shell = FakeShell()
78
78
79 def tearDown(self):
79 def tearDown(self):
80 shutil.rmtree(self.test_dir)
80 shutil.rmtree(self.test_dir)
81 sys.path = self.old_sys_path
81 sys.path = self.old_sys_path
82
82
83 self.test_dir = None
83 self.test_dir = None
84 self.old_sys_path = None
84 self.old_sys_path = None
85 self.shell = None
85 self.shell = None
86
86
87 def get_module(self):
87 def get_module(self):
88 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars,20))
88 module_name = "tmpmod_" + "".join(random.sample(self.filename_chars, 20))
89 if module_name in sys.modules:
89 if module_name in sys.modules:
90 del sys.modules[module_name]
90 del sys.modules[module_name]
91 file_name = os.path.join(self.test_dir, module_name + ".py")
91 file_name = os.path.join(self.test_dir, module_name + ".py")
92 return module_name, file_name
92 return module_name, file_name
93
93
94 def write_file(self, filename, content):
94 def write_file(self, filename, content):
95 """
95 """
96 Write a file, and force a timestamp difference of at least one second
96 Write a file, and force a timestamp difference of at least one second
97
97
98 Notes
98 Notes
99 -----
99 -----
100 Python's .pyc files record the timestamp of their compilation
100 Python's .pyc files record the timestamp of their compilation
101 with a time resolution of one second.
101 with a time resolution of one second.
102
102
103 Therefore, we need to force a timestamp difference between .py
103 Therefore, we need to force a timestamp difference between .py
104 and .pyc, without having the .py file be timestamped in the
104 and .pyc, without having the .py file be timestamped in the
105 future, and without changing the timestamp of the .pyc file
105 future, and without changing the timestamp of the .pyc file
106 (because that is stored in the file). The only reliable way
106 (because that is stored in the file). The only reliable way
107 to achieve this seems to be to sleep.
107 to achieve this seems to be to sleep.
108 """
108 """
109 content = textwrap.dedent(content)
109 content = textwrap.dedent(content)
110 # Sleep one second + eps
110 # Sleep one second + eps
111 time.sleep(1.05)
111 time.sleep(1.05)
112
112
113 # Write
113 # Write
114 with open(filename, 'w') as f:
114 with open(filename, "w") as f:
115 f.write(content)
115 f.write(content)
116
116
117 def new_module(self, code):
117 def new_module(self, code):
118 code = textwrap.dedent(code)
118 code = textwrap.dedent(code)
119 mod_name, mod_fn = self.get_module()
119 mod_name, mod_fn = self.get_module()
120 with open(mod_fn, 'w') as f:
120 with open(mod_fn, "w") as f:
121 f.write(code)
121 f.write(code)
122 return mod_name, mod_fn
122 return mod_name, mod_fn
123
123
124 #-----------------------------------------------------------------------------
124
125 # -----------------------------------------------------------------------------
125 # Test automatic reloading
126 # Test automatic reloading
126 #-----------------------------------------------------------------------------
127 # -----------------------------------------------------------------------------
128
127
129
128 def pickle_get_current_class(obj):
130 def pickle_get_current_class(obj):
129 """
131 """
130 Original issue comes from pickle; hence the name.
132 Original issue comes from pickle; hence the name.
131 """
133 """
132 name = obj.__class__.__name__
134 name = obj.__class__.__name__
133 module_name = getattr(obj, "__module__", None)
135 module_name = getattr(obj, "__module__", None)
134 obj2 = sys.modules[module_name]
136 obj2 = sys.modules[module_name]
135 for subpath in name.split("."):
137 for subpath in name.split("."):
136 obj2 = getattr(obj2, subpath)
138 obj2 = getattr(obj2, subpath)
137 return obj2
139 return obj2
138
140
139 class TestAutoreload(Fixture):
140
141
142 class TestAutoreload(Fixture):
141 def test_reload_enums(self):
143 def test_reload_enums(self):
142 mod_name, mod_fn = self.new_module(textwrap.dedent("""
144 mod_name, mod_fn = self.new_module(
145 textwrap.dedent(
146 """
143 from enum import Enum
147 from enum import Enum
144 class MyEnum(Enum):
148 class MyEnum(Enum):
145 A = 'A'
149 A = 'A'
146 B = 'B'
150 B = 'B'
147 """))
151 """
152 )
153 )
148 self.shell.magic_autoreload("2")
154 self.shell.magic_autoreload("2")
149 self.shell.magic_aimport(mod_name)
155 self.shell.magic_aimport(mod_name)
150 self.write_file(mod_fn, textwrap.dedent("""
156 self.write_file(
157 mod_fn,
158 textwrap.dedent(
159 """
151 from enum import Enum
160 from enum import Enum
152 class MyEnum(Enum):
161 class MyEnum(Enum):
153 A = 'A'
162 A = 'A'
154 B = 'B'
163 B = 'B'
155 C = 'C'
164 C = 'C'
156 """))
165 """
157 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
166 ),
167 )
168 with tt.AssertNotPrints(
169 ("[autoreload of %s failed:" % mod_name), channel="stderr"
170 ):
158 self.shell.run_code("pass") # trigger another reload
171 self.shell.run_code("pass") # trigger another reload
159
172
160 def test_reload_class_type(self):
173 def test_reload_class_type(self):
161 self.shell.magic_autoreload("2")
174 self.shell.magic_autoreload("2")
162 mod_name, mod_fn = self.new_module(
175 mod_name, mod_fn = self.new_module(
163 """
176 """
164 class Test():
177 class Test():
165 def meth(self):
178 def meth(self):
166 return "old"
179 return "old"
167 """
180 """
168 )
181 )
169 assert "test" not in self.shell.ns
182 assert "test" not in self.shell.ns
170 assert "result" not in self.shell.ns
183 assert "result" not in self.shell.ns
171
184
172 self.shell.run_code("from %s import Test" % mod_name)
185 self.shell.run_code("from %s import Test" % mod_name)
173 self.shell.run_code("test = Test()")
186 self.shell.run_code("test = Test()")
174
187
175 self.write_file(
188 self.write_file(
176 mod_fn,
189 mod_fn,
177 """
190 """
178 class Test():
191 class Test():
179 def meth(self):
192 def meth(self):
180 return "new"
193 return "new"
181 """,
194 """,
182 )
195 )
183
196
184 test_object = self.shell.ns["test"]
197 test_object = self.shell.ns["test"]
185
198
186 # important to trigger autoreload logic !
199 # important to trigger autoreload logic !
187 self.shell.run_code("pass")
200 self.shell.run_code("pass")
188
201
189 test_class = pickle_get_current_class(test_object)
202 test_class = pickle_get_current_class(test_object)
190 assert isinstance(test_object, test_class)
203 assert isinstance(test_object, test_class)
191
204
192 # extra check.
205 # extra check.
193 self.shell.run_code("import pickle")
206 self.shell.run_code("import pickle")
194 self.shell.run_code("p = pickle.dumps(test)")
207 self.shell.run_code("p = pickle.dumps(test)")
195
208
196 def test_reload_class_attributes(self):
209 def test_reload_class_attributes(self):
197 self.shell.magic_autoreload("2")
210 self.shell.magic_autoreload("2")
198 mod_name, mod_fn = self.new_module(textwrap.dedent("""
211 mod_name, mod_fn = self.new_module(
212 textwrap.dedent(
213 """
199 class MyClass:
214 class MyClass:
200
215
201 def __init__(self, a=10):
216 def __init__(self, a=10):
202 self.a = a
217 self.a = a
203 self.b = 22
218 self.b = 22
204 # self.toto = 33
219 # self.toto = 33
205
220
206 def square(self):
221 def square(self):
207 print('compute square')
222 print('compute square')
208 return self.a*self.a
223 return self.a*self.a
209 """
224 """
210 )
225 )
211 )
226 )
212 self.shell.run_code("from %s import MyClass" % mod_name)
227 self.shell.run_code("from %s import MyClass" % mod_name)
213 self.shell.run_code("first = MyClass(5)")
228 self.shell.run_code("first = MyClass(5)")
214 self.shell.run_code("first.square()")
229 self.shell.run_code("first.square()")
215 with nt.assert_raises(AttributeError):
230 with nt.assert_raises(AttributeError):
216 self.shell.run_code("first.cube()")
231 self.shell.run_code("first.cube()")
217 with nt.assert_raises(AttributeError):
232 with nt.assert_raises(AttributeError):
218 self.shell.run_code("first.power(5)")
233 self.shell.run_code("first.power(5)")
219 self.shell.run_code("first.b")
234 self.shell.run_code("first.b")
220 with nt.assert_raises(AttributeError):
235 with nt.assert_raises(AttributeError):
221 self.shell.run_code("first.toto")
236 self.shell.run_code("first.toto")
222
237
223 # remove square, add power
238 # remove square, add power
224
239
225 self.write_file(
240 self.write_file(
226 mod_fn,
241 mod_fn,
227 textwrap.dedent(
242 textwrap.dedent(
228 """
243 """
229 class MyClass:
244 class MyClass:
230
245
231 def __init__(self, a=10):
246 def __init__(self, a=10):
232 self.a = a
247 self.a = a
233 self.b = 11
248 self.b = 11
234
249
235 def power(self, p):
250 def power(self, p):
236 print('compute power '+str(p))
251 print('compute power '+str(p))
237 return self.a**p
252 return self.a**p
238 """
253 """
239 ),
254 ),
240 )
255 )
241
256
242 self.shell.run_code("second = MyClass(5)")
257 self.shell.run_code("second = MyClass(5)")
243
258
244 for object_name in {'first', 'second'}:
259 for object_name in {"first", "second"}:
245 self.shell.run_code("{object_name}.power(5)".format(object_name=object_name))
260 self.shell.run_code(f"{object_name}.power(5)")
246 with nt.assert_raises(AttributeError):
261 with nt.assert_raises(AttributeError):
247 self.shell.run_code("{object_name}.cube()".format(object_name=object_name))
262 self.shell.run_code(f"{object_name}.cube()")
248 with nt.assert_raises(AttributeError):
263 with nt.assert_raises(AttributeError):
249 self.shell.run_code("{object_name}.square()".format(object_name=object_name))
264 self.shell.run_code(f"{object_name}.square()")
250 self.shell.run_code("{object_name}.b".format(object_name=object_name))
265 self.shell.run_code(f"{object_name}.b")
251 self.shell.run_code("{object_name}.a".format(object_name=object_name))
266 self.shell.run_code(f"{object_name}.a")
252 with nt.assert_raises(AttributeError):
267 with nt.assert_raises(AttributeError):
253 self.shell.run_code("{object_name}.toto".format(object_name=object_name))
268 self.shell.run_code(f"{object_name}.toto")
269
270 def test_autoload_newly_added_objects(self):
271 self.shell.magic_autoreload("3")
272 mod_code = """
273 def func1(): pass
274 """
275 mod_name, mod_fn = self.new_module(textwrap.dedent(mod_code))
276 self.shell.run_code(f"from {mod_name} import *")
277 self.shell.run_code("func1()")
278 with nt.assert_raises(NameError):
279 self.shell.run_code("func2()")
280 with nt.assert_raises(NameError):
281 self.shell.run_code("t = Test()")
282 with nt.assert_raises(NameError):
283 self.shell.run_code("number")
284
285 # ----------- TEST NEW OBJ LOADED --------------------------
286
287 new_code = """
288 def func1(): pass
289 def func2(): pass
290 class Test: pass
291 number = 0
292 from enum import Enum
293 class TestEnum(Enum):
294 A = 'a'
295 """
296 self.write_file(mod_fn, textwrap.dedent(new_code))
297
298 # test function now exists in shell's namespace namespace
299 self.shell.run_code("func2()")
300 # test function now exists in module's dict
301 self.shell.run_code(f"import sys; sys.modules['{mod_name}'].func2()")
302 # test class now exists
303 self.shell.run_code("t = Test()")
304 # test global built-in var now exists
305 self.shell.run_code("number")
306 # test the enumerations gets loaded succesfully
307 self.shell.run_code("TestEnum.A")
308
309 # ----------- TEST NEW OBJ CAN BE CHANGED --------------------
310
311 new_code = """
312 def func1(): return 'changed'
313 def func2(): return 'changed'
314 class Test:
315 def new_func(self):
316 return 'changed'
317 number = 1
318 from enum import Enum
319 class TestEnum(Enum):
320 A = 'a'
321 B = 'added'
322 """
323 self.write_file(mod_fn, textwrap.dedent(new_code))
324 self.shell.run_code("assert func1() == 'changed'")
325 self.shell.run_code("assert func2() == 'changed'")
326 self.shell.run_code("t = Test(); assert t.new_func() == 'changed'")
327 self.shell.run_code("assert number == 1")
328 self.shell.run_code("assert TestEnum.B.value == 'added'")
329
330 # ----------- TEST IMPORT FROM MODULE --------------------------
331
332 new_mod_code = """
333 from enum import Enum
334 class Ext(Enum):
335 A = 'ext'
336 def ext_func():
337 return 'ext'
338 class ExtTest:
339 def meth(self):
340 return 'ext'
341 ext_int = 2
342 """
343 new_mod_name, new_mod_fn = self.new_module(textwrap.dedent(new_mod_code))
344 current_mod_code = f"""
345 from {new_mod_name} import *
346 """
347 self.write_file(mod_fn, textwrap.dedent(current_mod_code))
348 self.shell.run_code("assert Ext.A.value == 'ext'")
349 self.shell.run_code("assert ext_func() == 'ext'")
350 self.shell.run_code("t = ExtTest(); assert t.meth() == 'ext'")
351 self.shell.run_code("assert ext_int == 2")
254
352
255 def _check_smoketest(self, use_aimport=True):
353 def _check_smoketest(self, use_aimport=True):
256 """
354 """
257 Functional test for the automatic reloader using either
355 Functional test for the automatic reloader using either
258 '%autoreload 1' or '%autoreload 2'
356 '%autoreload 1' or '%autoreload 2'
259 """
357 """
260
358
261 mod_name, mod_fn = self.new_module("""
359 mod_name, mod_fn = self.new_module(
360 """
262 x = 9
361 x = 9
263
362
264 z = 123 # this item will be deleted
363 z = 123 # this item will be deleted
265
364
266 def foo(y):
365 def foo(y):
267 return y + 3
366 return y + 3
268
367
269 class Baz(object):
368 class Baz(object):
270 def __init__(self, x):
369 def __init__(self, x):
271 self.x = x
370 self.x = x
272 def bar(self, y):
371 def bar(self, y):
273 return self.x + y
372 return self.x + y
274 @property
373 @property
275 def quux(self):
374 def quux(self):
276 return 42
375 return 42
277 def zzz(self):
376 def zzz(self):
278 '''This method will be deleted below'''
377 '''This method will be deleted below'''
279 return 99
378 return 99
280
379
281 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
380 class Bar: # old-style class: weakref doesn't work for it on Python < 2.7
282 def foo(self):
381 def foo(self):
283 return 1
382 return 1
284 """)
383 """
384 )
285
385
286 #
386 #
287 # Import module, and mark for reloading
387 # Import module, and mark for reloading
288 #
388 #
289 if use_aimport:
389 if use_aimport:
290 self.shell.magic_autoreload("1")
390 self.shell.magic_autoreload("1")
291 self.shell.magic_aimport(mod_name)
391 self.shell.magic_aimport(mod_name)
292 stream = StringIO()
392 stream = StringIO()
293 self.shell.magic_aimport("", stream=stream)
393 self.shell.magic_aimport("", stream=stream)
294 nt.assert_in(("Modules to reload:\n%s" % mod_name), stream.getvalue())
394 nt.assert_in(("Modules to reload:\n%s" % mod_name), stream.getvalue())
295
395
296 with nt.assert_raises(ImportError):
396 with nt.assert_raises(ImportError):
297 self.shell.magic_aimport("tmpmod_as318989e89ds")
397 self.shell.magic_aimport("tmpmod_as318989e89ds")
298 else:
398 else:
299 self.shell.magic_autoreload("2")
399 self.shell.magic_autoreload("2")
300 self.shell.run_code("import %s" % mod_name)
400 self.shell.run_code("import %s" % mod_name)
301 stream = StringIO()
401 stream = StringIO()
302 self.shell.magic_aimport("", stream=stream)
402 self.shell.magic_aimport("", stream=stream)
303 nt.assert_true("Modules to reload:\nall-except-skipped" in
403 nt.assert_true(
304 stream.getvalue())
404 "Modules to reload:\nall-except-skipped" in stream.getvalue()
405 )
305 nt.assert_in(mod_name, self.shell.ns)
406 nt.assert_in(mod_name, self.shell.ns)
306
407
307 mod = sys.modules[mod_name]
408 mod = sys.modules[mod_name]
308
409
309 #
410 #
310 # Test module contents
411 # Test module contents
311 #
412 #
312 old_foo = mod.foo
413 old_foo = mod.foo
313 old_obj = mod.Baz(9)
414 old_obj = mod.Baz(9)
314 old_obj2 = mod.Bar()
415 old_obj2 = mod.Bar()
315
416
316 def check_module_contents():
417 def check_module_contents():
317 nt.assert_equal(mod.x, 9)
418 nt.assert_equal(mod.x, 9)
318 nt.assert_equal(mod.z, 123)
419 nt.assert_equal(mod.z, 123)
319
420
320 nt.assert_equal(old_foo(0), 3)
421 nt.assert_equal(old_foo(0), 3)
321 nt.assert_equal(mod.foo(0), 3)
422 nt.assert_equal(mod.foo(0), 3)
322
423
323 obj = mod.Baz(9)
424 obj = mod.Baz(9)
324 nt.assert_equal(old_obj.bar(1), 10)
425 nt.assert_equal(old_obj.bar(1), 10)
325 nt.assert_equal(obj.bar(1), 10)
426 nt.assert_equal(obj.bar(1), 10)
326 nt.assert_equal(obj.quux, 42)
427 nt.assert_equal(obj.quux, 42)
327 nt.assert_equal(obj.zzz(), 99)
428 nt.assert_equal(obj.zzz(), 99)
328
429
329 obj2 = mod.Bar()
430 obj2 = mod.Bar()
330 nt.assert_equal(old_obj2.foo(), 1)
431 nt.assert_equal(old_obj2.foo(), 1)
331 nt.assert_equal(obj2.foo(), 1)
432 nt.assert_equal(obj2.foo(), 1)
332
433
333 check_module_contents()
434 check_module_contents()
334
435
335 #
436 #
336 # Simulate a failed reload: no reload should occur and exactly
437 # Simulate a failed reload: no reload should occur and exactly
337 # one error message should be printed
438 # one error message should be printed
338 #
439 #
339 self.write_file(mod_fn, """
440 self.write_file(
441 mod_fn,
442 """
340 a syntax error
443 a syntax error
341 """)
444 """,
445 )
342
446
343 with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
447 with tt.AssertPrints(
344 self.shell.run_code("pass") # trigger reload
448 ("[autoreload of %s failed:" % mod_name), channel="stderr"
345 with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'):
449 ):
346 self.shell.run_code("pass") # trigger another reload
450 self.shell.run_code("pass") # trigger reload
451 with tt.AssertNotPrints(
452 ("[autoreload of %s failed:" % mod_name), channel="stderr"
453 ):
454 self.shell.run_code("pass") # trigger another reload
347 check_module_contents()
455 check_module_contents()
348
456
349 #
457 #
350 # Rewrite module (this time reload should succeed)
458 # Rewrite module (this time reload should succeed)
351 #
459 #
352 self.write_file(mod_fn, """
460 self.write_file(
461 mod_fn,
462 """
353 x = 10
463 x = 10
354
464
355 def foo(y):
465 def foo(y):
356 return y + 4
466 return y + 4
357
467
358 class Baz(object):
468 class Baz(object):
359 def __init__(self, x):
469 def __init__(self, x):
360 self.x = x
470 self.x = x
361 def bar(self, y):
471 def bar(self, y):
362 return self.x + y + 1
472 return self.x + y + 1
363 @property
473 @property
364 def quux(self):
474 def quux(self):
365 return 43
475 return 43
366
476
367 class Bar: # old-style class
477 class Bar: # old-style class
368 def foo(self):
478 def foo(self):
369 return 2
479 return 2
370 """)
480 """,
481 )
371
482
372 def check_module_contents():
483 def check_module_contents():
373 nt.assert_equal(mod.x, 10)
484 nt.assert_equal(mod.x, 10)
374 nt.assert_false(hasattr(mod, 'z'))
485 nt.assert_false(hasattr(mod, "z"))
375
486
376 nt.assert_equal(old_foo(0), 4) # superreload magic!
487 nt.assert_equal(old_foo(0), 4) # superreload magic!
377 nt.assert_equal(mod.foo(0), 4)
488 nt.assert_equal(mod.foo(0), 4)
378
489
379 obj = mod.Baz(9)
490 obj = mod.Baz(9)
380 nt.assert_equal(old_obj.bar(1), 11) # superreload magic!
491 nt.assert_equal(old_obj.bar(1), 11) # superreload magic!
381 nt.assert_equal(obj.bar(1), 11)
492 nt.assert_equal(obj.bar(1), 11)
382
493
383 nt.assert_equal(old_obj.quux, 43)
494 nt.assert_equal(old_obj.quux, 43)
384 nt.assert_equal(obj.quux, 43)
495 nt.assert_equal(obj.quux, 43)
385
496
386 nt.assert_false(hasattr(old_obj, 'zzz'))
497 nt.assert_false(hasattr(old_obj, "zzz"))
387 nt.assert_false(hasattr(obj, 'zzz'))
498 nt.assert_false(hasattr(obj, "zzz"))
388
499
389 obj2 = mod.Bar()
500 obj2 = mod.Bar()
390 nt.assert_equal(old_obj2.foo(), 2)
501 nt.assert_equal(old_obj2.foo(), 2)
391 nt.assert_equal(obj2.foo(), 2)
502 nt.assert_equal(obj2.foo(), 2)
392
503
393 self.shell.run_code("pass") # trigger reload
504 self.shell.run_code("pass") # trigger reload
394 check_module_contents()
505 check_module_contents()
395
506
396 #
507 #
397 # Another failure case: deleted file (shouldn't reload)
508 # Another failure case: deleted file (shouldn't reload)
398 #
509 #
399 os.unlink(mod_fn)
510 os.unlink(mod_fn)
400
511
401 self.shell.run_code("pass") # trigger reload
512 self.shell.run_code("pass") # trigger reload
402 check_module_contents()
513 check_module_contents()
403
514
404 #
515 #
405 # Disable autoreload and rewrite module: no reload should occur
516 # Disable autoreload and rewrite module: no reload should occur
406 #
517 #
407 if use_aimport:
518 if use_aimport:
408 self.shell.magic_aimport("-" + mod_name)
519 self.shell.magic_aimport("-" + mod_name)
409 stream = StringIO()
520 stream = StringIO()
410 self.shell.magic_aimport("", stream=stream)
521 self.shell.magic_aimport("", stream=stream)
411 nt.assert_true(("Modules to skip:\n%s" % mod_name) in
522 nt.assert_true(("Modules to skip:\n%s" % mod_name) in stream.getvalue())
412 stream.getvalue())
413
523
414 # This should succeed, although no such module exists
524 # This should succeed, although no such module exists
415 self.shell.magic_aimport("-tmpmod_as318989e89ds")
525 self.shell.magic_aimport("-tmpmod_as318989e89ds")
416 else:
526 else:
417 self.shell.magic_autoreload("0")
527 self.shell.magic_autoreload("0")
418
528
419 self.write_file(mod_fn, """
529 self.write_file(
530 mod_fn,
531 """
420 x = -99
532 x = -99
421 """)
533 """,
534 )
422
535
423 self.shell.run_code("pass") # trigger reload
536 self.shell.run_code("pass") # trigger reload
424 self.shell.run_code("pass")
537 self.shell.run_code("pass")
425 check_module_contents()
538 check_module_contents()
426
539
427 #
540 #
428 # Re-enable autoreload: reload should now occur
541 # Re-enable autoreload: reload should now occur
429 #
542 #
430 if use_aimport:
543 if use_aimport:
431 self.shell.magic_aimport(mod_name)
544 self.shell.magic_aimport(mod_name)
432 else:
545 else:
433 self.shell.magic_autoreload("")
546 self.shell.magic_autoreload("")
434
547
435 self.shell.run_code("pass") # trigger reload
548 self.shell.run_code("pass") # trigger reload
436 nt.assert_equal(mod.x, -99)
549 nt.assert_equal(mod.x, -99)
437
550
438 def test_smoketest_aimport(self):
551 def test_smoketest_aimport(self):
439 self._check_smoketest(use_aimport=True)
552 self._check_smoketest(use_aimport=True)
440
553
441 def test_smoketest_autoreload(self):
554 def test_smoketest_autoreload(self):
442 self._check_smoketest(use_aimport=False)
555 self._check_smoketest(use_aimport=False)
443
444
445
446
447
@@ -1,163 +1,181 b''
1 =====================
1 =====================
2 Development version
2 Development version
3 =====================
3 =====================
4
4
5 This document describes in-flight development work.
5 This document describes in-flight development work.
6
6
7 .. warning::
7 .. warning::
8
8
9 Please do not edit this file by hand (doing so will likely cause merge
9 Please do not edit this file by hand (doing so will likely cause merge
10 conflicts for other Pull Requests). Instead, create a new file in the
10 conflicts for other Pull Requests). Instead, create a new file in the
11 `docs/source/whatsnew/pr` folder
11 `docs/source/whatsnew/pr` folder
12
12
13
13
14 Released .... ...., 2019
14 Released .... ...., 2019
15
15
16
16
17 Need to be updated:
17 Need to be updated:
18
18
19 .. toctree::
19 .. toctree::
20 :maxdepth: 2
20 :maxdepth: 2
21 :glob:
21 :glob:
22
22
23 pr/*
23 pr/*
24
24
25 IPython 8.0 is bringing a number of new features and improvements to both the
25 IPython 8.0 is bringing a number of new features and improvements to both the
26 user of the terminal and of the kernel via Jupyter. The removal of compatibility
26 user of the terminal and of the kernel via Jupyter. The removal of compatibility
27 with older version of Python is also the opportunity to do a couple of
27 with older version of Python is also the opportunity to do a couple of
28 performance improvement in particular with respect to startup time.
28 performance improvement in particular with respect to startup time.
29
29
30 The main change in IPython 8.0 is the integration of the ``stack_data`` package;
30 The main change in IPython 8.0 is the integration of the ``stack_data`` package;
31 which provide smarter information in traceback; in particular it will highlight
31 which provide smarter information in traceback; in particular it will highlight
32 the AST node where an error occurs which can help to quickly narrow down errors.
32 the AST node where an error occurs which can help to quickly narrow down errors.
33
33
34 For example in the following snippet::
34 For example in the following snippet::
35
35
36 def foo(i):
36 def foo(i):
37 x = [[[0]]]
37 x = [[[0]]]
38 return x[0][i][0]
38 return x[0][i][0]
39
39
40
40
41 def bar():
41 def bar():
42 return foo(0) + foo(
42 return foo(0) + foo(
43 1
43 1
44 ) + foo(2)
44 ) + foo(2)
45
45
46
46
47 Calling ``bar()`` would raise an ``IndexError`` on the return line of ``foo``,
47 Calling ``bar()`` would raise an ``IndexError`` on the return line of ``foo``,
48 IPython 8.0 is capable of telling you, where the index error occurs::
48 IPython 8.0 is capable of telling you, where the index error occurs::
49
49
50 return x[0][i][0]
50 return x[0][i][0]
51 ^
51 ^
52
52
53 To prepare for Python 3.10 we have also started working on removing reliance and
53 To prepare for Python 3.10 we have also started working on removing reliance and
54 any dependency that is not Python 3.10 compatible; that include migrating our
54 any dependency that is not Python 3.10 compatible; that include migrating our
55 test suite to Pytest, and starting to remove nose.
55 test suite to Pytest, and starting to remove nose.
56
56
57 We are also removing support for Python 3.6 allowing internal code to use more
57 We are also removing support for Python 3.6 allowing internal code to use more
58 efficient ``pathlib``, and make better use of type annotations.
58 efficient ``pathlib``, and make better use of type annotations.
59
59
60 The completer has also seen significant updates and make use of newer Jedi API
60 The completer has also seen significant updates and make use of newer Jedi API
61 offering faster and more reliable tab completion.
61 offering faster and more reliable tab completion.
62
62
63 For the terminal users this also enable the auto-suggestion feature, described
63 For the terminal users this also enable the auto-suggestion feature, described
64 below, which show "ghost text" ahead of your cursor you can accept without
64 below, which show "ghost text" ahead of your cursor you can accept without
65 having to press the tab key or ask the completer to suggest completions.
65 having to press the tab key or ask the completer to suggest completions.
66
66
67
67
68 Autosuggestion is a very useful feature available in `fish <https://fishshell.com/>`__, `zsh <https://en.wikipedia.org/wiki/Z_shell>`__, and `prompt-toolkit <https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html#auto-suggestion>`__.
68 Autosuggestion is a very useful feature available in `fish <https://fishshell.com/>`__, `zsh <https://en.wikipedia.org/wiki/Z_shell>`__, and `prompt-toolkit <https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html#auto-suggestion>`__.
69
69
70 `Ptpython <https://github.com/prompt-toolkit/ptpython#ptpython>`__ allows users to enable this feature in
70 `Ptpython <https://github.com/prompt-toolkit/ptpython#ptpython>`__ allows users to enable this feature in
71 `ptpython/config.py <https://github.com/prompt-toolkit/ptpython/blob/master/examples/ptpython_config/config.py#L90>`__.
71 `ptpython/config.py <https://github.com/prompt-toolkit/ptpython/blob/master/examples/ptpython_config/config.py#L90>`__.
72
72
73 This feature allows users to accept autosuggestions with ctrl e, ctrl f,
73 This feature allows users to accept autosuggestions with ctrl e, ctrl f,
74 or right arrow as described below.
74 or right arrow as described below.
75
75
76 1. Start ipython
76 1. Start ipython
77
77
78 .. image:: ../_images/auto_suggest_prompt_no_text.png
78 .. image:: ../_images/auto_suggest_prompt_no_text.png
79
79
80 2. Run ``print("hello")``
80 2. Run ``print("hello")``
81
81
82 .. image:: ../_images/auto_suggest_print_hello_suggest.png
82 .. image:: ../_images/auto_suggest_print_hello_suggest.png
83
83
84 3. Press p to see the autosuggestion
84 3. Press p to see the autosuggestion
85
85
86 .. image:: ../_images/auto_suggest_print_hello_suggest.png
86 .. image:: ../_images/auto_suggest_print_hello_suggest.png
87
87
88 4. Press ctrl f, or ctrl e, or right arrow to accept the suggestion
88 4. Press ctrl f, or ctrl e, or right arrow to accept the suggestion
89
89
90 .. image:: ../_images/auto_suggest_print_hello.png
90 .. image:: ../_images/auto_suggest_print_hello.png
91
91
92 You can also complete word by word:
92 You can also complete word by word:
93
93
94 1. Run ``def say_hello(): print("hello")``
94 1. Run ``def say_hello(): print("hello")``
95
95
96 .. image:: ../_images/auto_suggest_second_prompt.png
96 .. image:: ../_images/auto_suggest_second_prompt.png
97
97
98 2. Press d to see the autosuggestion
98 2. Press d to see the autosuggestion
99
99
100 .. image:: ../_images/audo_suggest_d_phantom.png
100 .. image:: ../_images/audo_suggest_d_phantom.png
101
101
102 3. Press alt f to accept the first word of the suggestion
102 3. Press alt f to accept the first word of the suggestion
103
103
104 .. image:: ../_images/auto_suggest_def_phantom.png
104 .. image:: ../_images/auto_suggest_def_phantom.png
105
105
106 Importantly, this feature does not interfere with tab completion:
106 Importantly, this feature does not interfere with tab completion:
107
107
108 1. After running ``def say_hello(): print("hello")``, press d
108 1. After running ``def say_hello(): print("hello")``, press d
109
109
110 .. image:: ../_images/audo_suggest_d_phantom.png
110 .. image:: ../_images/audo_suggest_d_phantom.png
111
111
112 2. Press Tab to start tab completion
112 2. Press Tab to start tab completion
113
113
114 .. image:: ../_images/auto_suggest_d_completions.png
114 .. image:: ../_images/auto_suggest_d_completions.png
115
115
116 3A. Press Tab again to select the first option
116 3A. Press Tab again to select the first option
117
117
118 .. image:: ../_images/auto_suggest_def_completions.png
118 .. image:: ../_images/auto_suggest_def_completions.png
119
119
120 3B. Press alt f to accept to accept the first word of the suggestion
120 3B. Press alt f to accept to accept the first word of the suggestion
121
121
122 .. image:: ../_images/auto_suggest_def_phantom.png
122 .. image:: ../_images/auto_suggest_def_phantom.png
123
123
124 3C. Press ctrl f or ctrl e to accept the entire suggestion
124 3C. Press ctrl f or ctrl e to accept the entire suggestion
125
125
126 .. image:: ../_images/auto_suggest_match_parens.png
126 .. image:: ../_images/auto_suggest_match_parens.png
127
127
128 To install a version of ipython with autosuggestions enabled, run:
128 To install a version of ipython with autosuggestions enabled, run:
129
129
130 ``pip install git+https://github.com/mskar/ipython@auto_suggest``
130 ``pip install git+https://github.com/mskar/ipython@auto_suggest``
131
131
132 Currently, autosuggestions are only shown in the emacs or vi insert editing modes:
132 Currently, autosuggestions are only shown in the emacs or vi insert editing modes:
133
133
134 - The ctrl e, ctrl f, and alt f shortcuts work by default in emacs mode.
134 - The ctrl e, ctrl f, and alt f shortcuts work by default in emacs mode.
135 - To use these shortcuts in vi insert mode, you will have to create `custom keybindings in your config.py <https://github.com/mskar/setup/commit/2892fcee46f9f80ef7788f0749edc99daccc52f4/>`__.
135 - To use these shortcuts in vi insert mode, you will have to create `custom keybindings in your config.py <https://github.com/mskar/setup/commit/2892fcee46f9f80ef7788f0749edc99daccc52f4/>`__.
136
136
137
137
138 Show pinfo information in ipdb using "?" and "??"
138 Show pinfo information in ipdb using "?" and "??"
139 -------------------------------------------------
139 -------------------------------------------------
140
140
141 In IPDB, it is now possible to show the information about an object using "?"
141 In IPDB, it is now possible to show the information about an object using "?"
142 and "??", in much the same way it can be done when using the IPython prompt::
142 and "??", in much the same way it can be done when using the IPython prompt::
143
143
144 ipdb> partial?
144 ipdb> partial?
145 Init signature: partial(self, /, *args, **kwargs)
145 Init signature: partial(self, /, *args, **kwargs)
146 Docstring:
146 Docstring:
147 partial(func, *args, **keywords) - new function with partial application
147 partial(func, *args, **keywords) - new function with partial application
148 of the given arguments and keywords.
148 of the given arguments and keywords.
149 File: ~/.pyenv/versions/3.8.6/lib/python3.8/functools.py
149 File: ~/.pyenv/versions/3.8.6/lib/python3.8/functools.py
150 Type: type
150 Type: type
151 Subclasses:
151 Subclasses:
152
152
153 Previously, "pinfo" or "pinfo2" command had to be used for this purpose.
153 Previously, "pinfo" or "pinfo2" command had to be used for this purpose.
154
154
155
156 Autoreload 3 feature
157 ====================
158
159 Example: When an IPython session is ran with the 'autoreload' extension loaded,
160 you will now have the option '3' to select which means the following:
161
162 1. replicate all functionality from option 2
163 2. autoload all new funcs/classes/enums/globals from the module when they're added
164 3. autoload all newly imported funcs/classes/enums/globals from external modules
165
166 Try ``%autoreload 3`` in an IPython session after running ``%load_ext autoreload``
167
168 For more information please see unit test -
169 extensions/tests/test_autoreload.py : 'test_autoload_newly_added_objects'
170
171 =======
172
155 .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT.
173 .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT.
156
174
157 As a reminder, IPython master has diverged from the 7.x branch, thus master may
175 As a reminder, IPython master has diverged from the 7.x branch, thus master may
158 have more feature and API changes.
176 have more feature and API changes.
159
177
160 Backwards incompatible changes
178 Backwards incompatible changes
161 ------------------------------
179 ------------------------------
162
180
163 .. DO NOT EDIT THIS LINE BEFORE RELEASE. INCOMPAT INSERTION POINT.
181 .. DO NOT EDIT THIS LINE BEFORE RELEASE. INCOMPAT INSERTION POINT.
General Comments 0
You need to be logged in to leave comments. Login now