Show More
@@ -0,0 +1,22 b'' | |||
|
1 | IPython can now trigger the display hook on last assignment of cells. | |
|
2 | Up until 6.0 the following code wouldn't show the value of the assigned | |
|
3 | variable:: | |
|
4 | ||
|
5 | In[1]: xyz = "something" | |
|
6 | # nothing shown | |
|
7 | ||
|
8 | You would have to actually make it the last statement:: | |
|
9 | ||
|
10 | In [2]: xyz = "something else" | |
|
11 | ... : xyz | |
|
12 | Out[2]: "something else" | |
|
13 | ||
|
14 | With the option ``InteractiveShell.ast_node_interactivity='last_expr_or_assign'`` | |
|
15 | you can now do:: | |
|
16 | ||
|
17 | In [2]: xyz = "something else" | |
|
18 | Out[2]: "something else" | |
|
19 | ||
|
20 | This option can be toggled at runtime with the ``%config`` magic, and will | |
|
21 | trigger on assignment ``a = 1``, augmented assignment ``+=``, ``-=``, ``|=`` ... | |
|
22 | as well as type annotated assignments: ``a:int = 2``. |
@@ -79,6 +79,9 b' from warnings import warn' | |||
|
79 | 79 | from logging import error |
|
80 | 80 | import IPython.core.hooks |
|
81 | 81 | |
|
82 | from typing import List as ListType | |
|
83 | from ast import AST | |
|
84 | ||
|
82 | 85 | # NoOpContext is deprecated, but ipykernel imports it from here. |
|
83 | 86 | # See https://github.com/ipython/ipykernel/issues/157 |
|
84 | 87 | from IPython.utils.contexts import NoOpContext |
@@ -102,6 +105,13 b' class ProvisionalWarning(DeprecationWarning):' | |||
|
102 | 105 | """ |
|
103 | 106 | pass |
|
104 | 107 | |
|
108 | if sys.version_info > (3,6): | |
|
109 | _assign_nodes = (ast.AugAssign, ast.AnnAssign, ast.Assign) | |
|
110 | _single_targets_nodes = (ast.AugAssign, ast.AnnAssign) | |
|
111 | else: | |
|
112 | _assign_nodes = (ast.AugAssign, ast.Assign ) | |
|
113 | _single_targets_nodes = (ast.AugAssign, ) | |
|
114 | ||
|
105 | 115 | #----------------------------------------------------------------------------- |
|
106 | 116 | # Globals |
|
107 | 117 | #----------------------------------------------------------------------------- |
@@ -376,11 +386,12 b' class InteractiveShell(SingletonConfigurable):' | |||
|
376 | 386 | """ |
|
377 | 387 | ).tag(config=True) |
|
378 | 388 | |
|
379 | ast_node_interactivity = Enum(['all', 'last', 'last_expr', 'none'], | |
|
389 | ast_node_interactivity = Enum(['all', 'last', 'last_expr', 'none', 'last_expr_or_assign'], | |
|
380 | 390 | default_value='last_expr', |
|
381 | 391 | help=""" |
|
382 |
'all', 'last', 'last_expr' or 'none', specifying |
|
|
383 |
run interactively (displaying output from expressions). |
|
|
392 | 'all', 'last', 'last_expr' or 'none', 'last_expr_or_assign' specifying | |
|
393 | which nodes should be run interactively (displaying output from expressions). | |
|
394 | """ | |
|
384 | 395 | ).tag(config=True) |
|
385 | 396 | |
|
386 | 397 | # TODO: this part of prompt management should be moved to the frontends. |
@@ -2749,7 +2760,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2749 | 2760 | return node |
|
2750 | 2761 | |
|
2751 | 2762 | |
|
2752 | def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr', | |
|
2763 | def run_ast_nodes(self, nodelist:ListType[AST], cell_name:str, interactivity='last_expr', | |
|
2753 | 2764 | compiler=compile, result=None): |
|
2754 | 2765 | """Run a sequence of AST nodes. The execution mode depends on the |
|
2755 | 2766 | interactivity parameter. |
@@ -2762,11 +2773,13 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2762 | 2773 | Will be passed to the compiler as the filename of the cell. Typically |
|
2763 | 2774 | the value returned by ip.compile.cache(cell). |
|
2764 | 2775 | interactivity : str |
|
2765 |
'all', 'last', 'last_expr' |
|
|
2766 | run interactively (displaying output from expressions). 'last_expr' | |
|
2767 | will run the last node interactively only if it is an expression (i.e. | |
|
2768 | expressions in loops or other blocks are not displayed. Other values | |
|
2769 | for this parameter will raise a ValueError. | |
|
2776 | 'all', 'last', 'last_expr' , 'last_expr_or_assign' or 'none', | |
|
2777 | specifying which nodes should be run interactively (displaying output | |
|
2778 | from expressions). 'last_expr' will run the last node interactively | |
|
2779 | only if it is an expression (i.e. expressions in loops or other blocks | |
|
2780 | are not displayed) 'last_expr_or_assign' will run the last expression | |
|
2781 | or the last assignment. Other values for this parameter will raise a | |
|
2782 | ValueError. | |
|
2770 | 2783 | compiler : callable |
|
2771 | 2784 | A function with the same interface as the built-in compile(), to turn |
|
2772 | 2785 | the AST nodes into code objects. Default is the built-in compile(). |
@@ -2781,6 +2794,21 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2781 | 2794 | if not nodelist: |
|
2782 | 2795 | return |
|
2783 | 2796 | |
|
2797 | if interactivity == 'last_expr_or_assign': | |
|
2798 | if isinstance(nodelist[-1], _assign_nodes): | |
|
2799 | asg = nodelist[-1] | |
|
2800 | if isinstance(asg, ast.Assign) and len(asg.targets) == 1: | |
|
2801 | target = asg.targets[0] | |
|
2802 | elif isinstance(asg, _single_targets_nodes): | |
|
2803 | target = asg.target | |
|
2804 | else: | |
|
2805 | target = None | |
|
2806 | if isinstance(target, ast.Name): | |
|
2807 | nnode = ast.Expr(ast.Name(target.id, ast.Load())) | |
|
2808 | ast.fix_missing_locations(nnode) | |
|
2809 | nodelist.append(nnode) | |
|
2810 | interactivity = 'last_expr' | |
|
2811 | ||
|
2784 | 2812 | if interactivity == 'last_expr': |
|
2785 | 2813 | if isinstance(nodelist[-1], ast.Expr): |
|
2786 | 2814 | interactivity = "last" |
@@ -53,3 +53,51 b' def test_underscore_no_overrite_builtins():' | |||
|
53 | 53 | ip.run_cell('print(_)', store_history=True) |
|
54 | 54 | ip.run_cell('import builtins; del builtins._') |
|
55 | 55 | |
|
56 | ||
|
57 | def test_interactivehooks_ast_modes(): | |
|
58 | """ | |
|
59 | Test that ast nodes can be triggerd with different modes | |
|
60 | """ | |
|
61 | saved_mode = ip.ast_node_interactivity | |
|
62 | ip.ast_node_interactivity = 'last_expr_or_assign' | |
|
63 | ||
|
64 | try: | |
|
65 | with AssertPrints('2'): | |
|
66 | ip.run_cell('a = 1+1', store_history=True) | |
|
67 | ||
|
68 | with AssertPrints('9'): | |
|
69 | ip.run_cell('b = 1+8 # comment with a semicolon;', store_history=False) | |
|
70 | ||
|
71 | with AssertPrints('7'): | |
|
72 | ip.run_cell('c = 1+6\n#commented_out_function();', store_history=True) | |
|
73 | ||
|
74 | ip.run_cell('d = 11', store_history=True) | |
|
75 | with AssertPrints('12'): | |
|
76 | ip.run_cell('d += 1', store_history=True) | |
|
77 | ||
|
78 | with AssertNotPrints('42'): | |
|
79 | ip.run_cell('(u,v) = (41+1, 43-1)') | |
|
80 | ||
|
81 | finally: | |
|
82 | ip.ast_node_interactivity = saved_mode | |
|
83 | ||
|
84 | def test_interactivehooks_ast_modes_semi_supress(): | |
|
85 | """ | |
|
86 | Test that ast nodes can be triggerd with different modes and supressed | |
|
87 | by semicolon | |
|
88 | """ | |
|
89 | saved_mode = ip.ast_node_interactivity | |
|
90 | ip.ast_node_interactivity = 'last_expr_or_assign' | |
|
91 | ||
|
92 | try: | |
|
93 | with AssertNotPrints('2'): | |
|
94 | ip.run_cell('x = 1+1;', store_history=True) | |
|
95 | ||
|
96 | with AssertNotPrints('7'): | |
|
97 | ip.run_cell('y = 1+6; # comment with a semicolon', store_history=True) | |
|
98 | ||
|
99 | with AssertNotPrints('9'): | |
|
100 | ip.run_cell('z = 1+8;\n#commented_out_function()', store_history=True) | |
|
101 | ||
|
102 | finally: | |
|
103 | ip.ast_node_interactivity = saved_mode |
General Comments 0
You need to be logged in to leave comments.
Login now