##// END OF EJS Templates
Merge pull request #7262 from takluyver/executionresult...
Matthias Bussonnier -
r19685:6c5a2190 merge
parent child Browse files
Show More
@@ -30,6 +30,8 b' class DisplayHook(Configurable):'
30 """
30 """
31
31
32 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
32 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
33 exec_result = Instance('IPython.core.interactiveshell.ExecutionResult',
34 allow_none=True)
33 cull_fraction = Float(0.2)
35 cull_fraction = Float(0.2)
34
36
35 def __init__(self, shell=None, cache_size=1000, **kwargs):
37 def __init__(self, shell=None, cache_size=1000, **kwargs):
@@ -198,6 +200,10 b' class DisplayHook(Configurable):'
198 self.shell.push(to_main, interactive=False)
200 self.shell.push(to_main, interactive=False)
199 self.shell.user_ns['_oh'][self.prompt_count] = result
201 self.shell.user_ns['_oh'][self.prompt_count] = result
200
202
203 def fill_exec_result(self, result):
204 if self.exec_result is not None:
205 self.exec_result.result = result
206
201 def log_output(self, format_dict):
207 def log_output(self, format_dict):
202 """Log the output."""
208 """Log the output."""
203 if 'text/plain' not in format_dict:
209 if 'text/plain' not in format_dict:
@@ -225,6 +231,7 b' class DisplayHook(Configurable):'
225 self.write_output_prompt()
231 self.write_output_prompt()
226 format_dict, md_dict = self.compute_format_data(result)
232 format_dict, md_dict = self.compute_format_data(result)
227 self.update_user_ns(result)
233 self.update_user_ns(result)
234 self.fill_exec_result(result)
228 if format_dict:
235 if format_dict:
229 self.write_format_data(format_dict, md_dict)
236 self.write_format_data(format_dict, md_dict)
230 self.log_output(format_dict)
237 self.log_output(format_dict)
@@ -192,9 +192,21 b' class DummyMod(object):'
192 a namespace must be assigned to the module's __dict__."""
192 a namespace must be assigned to the module's __dict__."""
193 pass
193 pass
194
194
195 #-----------------------------------------------------------------------------
195
196 # Main IPython class
196 class ExecutionResult(object):
197 #-----------------------------------------------------------------------------
197 """The result of a call to :meth:`InteractiveShell.run_cell`
198
199 Stores information about what took place.
200 """
201 execution_count = None
202 error_before_exec = None
203 error_in_exec = None
204 result = None
205
206 @property
207 def success(self):
208 return (self.error_before_exec is None) and (self.error_in_exec is None)
209
198
210
199 class InteractiveShell(SingletonConfigurable):
211 class InteractiveShell(SingletonConfigurable):
200 """An enhanced, interactive shell for Python."""
212 """An enhanced, interactive shell for Python."""
@@ -2760,13 +2772,26 b' class InteractiveShell(SingletonConfigurable):'
2760 shell. It will both be affected by previous __future__ imports, and
2772 shell. It will both be affected by previous __future__ imports, and
2761 any __future__ imports in the code will affect the shell. If False,
2773 any __future__ imports in the code will affect the shell. If False,
2762 __future__ imports are not shared in either direction.
2774 __future__ imports are not shared in either direction.
2775
2776 Returns
2777 -------
2778 result : :class:`ExecutionResult`
2763 """
2779 """
2780 result = ExecutionResult()
2781
2764 if (not raw_cell) or raw_cell.isspace():
2782 if (not raw_cell) or raw_cell.isspace():
2765 return
2783 return result
2766
2784
2767 if silent:
2785 if silent:
2768 store_history = False
2786 store_history = False
2769
2787
2788 if store_history:
2789 result.execution_count = self.execution_count
2790
2791 def error_before_exec(value):
2792 result.error_before_exec = value
2793 return result
2794
2770 self.events.trigger('pre_execute')
2795 self.events.trigger('pre_execute')
2771 if not silent:
2796 if not silent:
2772 self.events.trigger('pre_run_cell')
2797 self.events.trigger('pre_run_cell')
@@ -2806,7 +2831,7 b' class InteractiveShell(SingletonConfigurable):'
2806 self.showtraceback(preprocessing_exc_tuple)
2831 self.showtraceback(preprocessing_exc_tuple)
2807 if store_history:
2832 if store_history:
2808 self.execution_count += 1
2833 self.execution_count += 1
2809 return
2834 return error_before_exec(preprocessing_exc_tuple[2])
2810
2835
2811 # Our own compiler remembers the __future__ environment. If we want to
2836 # Our own compiler remembers the __future__ environment. If we want to
2812 # run code with a separate __future__ environment, use the default
2837 # run code with a separate __future__ environment, use the default
@@ -2820,32 +2845,40 b' class InteractiveShell(SingletonConfigurable):'
2820 # Compile to bytecode
2845 # Compile to bytecode
2821 try:
2846 try:
2822 code_ast = compiler.ast_parse(cell, filename=cell_name)
2847 code_ast = compiler.ast_parse(cell, filename=cell_name)
2823 except IndentationError:
2848 except IndentationError as e:
2824 self.showindentationerror()
2849 self.showindentationerror()
2825 if store_history:
2850 if store_history:
2826 self.execution_count += 1
2851 self.execution_count += 1
2827 return None
2852 return error_before_exec(e)
2828 except (OverflowError, SyntaxError, ValueError, TypeError,
2853 except (OverflowError, SyntaxError, ValueError, TypeError,
2829 MemoryError):
2854 MemoryError) as e:
2830 self.showsyntaxerror()
2855 self.showsyntaxerror()
2831 if store_history:
2856 if store_history:
2832 self.execution_count += 1
2857 self.execution_count += 1
2833 return None
2858 return error_before_exec(e)
2834
2859
2835 # Apply AST transformations
2860 # Apply AST transformations
2836 try:
2861 try:
2837 code_ast = self.transform_ast(code_ast)
2862 code_ast = self.transform_ast(code_ast)
2838 except InputRejected:
2863 except InputRejected as e:
2839 self.showtraceback()
2864 self.showtraceback()
2840 if store_history:
2865 if store_history:
2841 self.execution_count += 1
2866 self.execution_count += 1
2842 return None
2867 return error_before_exec(e)
2868
2869 # Give the displayhook a reference to our ExecutionResult so it
2870 # can fill in the output value.
2871 self.displayhook.exec_result = result
2843
2872
2844 # Execute the user code
2873 # Execute the user code
2845 interactivity = "none" if silent else self.ast_node_interactivity
2874 interactivity = "none" if silent else self.ast_node_interactivity
2846 self.run_ast_nodes(code_ast.body, cell_name,
2875 self.run_ast_nodes(code_ast.body, cell_name,
2847 interactivity=interactivity, compiler=compiler)
2876 interactivity=interactivity, compiler=compiler, result=result)
2848
2877
2878 # Reset this so later displayed values do not modify the
2879 # ExecutionResult
2880 self.displayhook.exec_result = None
2881
2849 self.events.trigger('post_execute')
2882 self.events.trigger('post_execute')
2850 if not silent:
2883 if not silent:
2851 self.events.trigger('post_run_cell')
2884 self.events.trigger('post_run_cell')
@@ -2856,6 +2889,8 b' class InteractiveShell(SingletonConfigurable):'
2856 self.history_manager.store_output(self.execution_count)
2889 self.history_manager.store_output(self.execution_count)
2857 # Each cell is a *single* input, regardless of how many lines it has
2890 # Each cell is a *single* input, regardless of how many lines it has
2858 self.execution_count += 1
2891 self.execution_count += 1
2892
2893 return result
2859
2894
2860 def transform_ast(self, node):
2895 def transform_ast(self, node):
2861 """Apply the AST transformations from self.ast_transformers
2896 """Apply the AST transformations from self.ast_transformers
@@ -2890,7 +2925,7 b' class InteractiveShell(SingletonConfigurable):'
2890
2925
2891
2926
2892 def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr',
2927 def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr',
2893 compiler=compile):
2928 compiler=compile, result=None):
2894 """Run a sequence of AST nodes. The execution mode depends on the
2929 """Run a sequence of AST nodes. The execution mode depends on the
2895 interactivity parameter.
2930 interactivity parameter.
2896
2931
@@ -2910,6 +2945,13 b' class InteractiveShell(SingletonConfigurable):'
2910 compiler : callable
2945 compiler : callable
2911 A function with the same interface as the built-in compile(), to turn
2946 A function with the same interface as the built-in compile(), to turn
2912 the AST nodes into code objects. Default is the built-in compile().
2947 the AST nodes into code objects. Default is the built-in compile().
2948 result : ExecutionResult, optional
2949 An object to store exceptions that occur during execution.
2950
2951 Returns
2952 -------
2953 True if an exception occurred while running code, False if it finished
2954 running.
2913 """
2955 """
2914 if not nodelist:
2956 if not nodelist:
2915 return
2957 return
@@ -2935,13 +2977,13 b' class InteractiveShell(SingletonConfigurable):'
2935 for i, node in enumerate(to_run_exec):
2977 for i, node in enumerate(to_run_exec):
2936 mod = ast.Module([node])
2978 mod = ast.Module([node])
2937 code = compiler(mod, cell_name, "exec")
2979 code = compiler(mod, cell_name, "exec")
2938 if self.run_code(code):
2980 if self.run_code(code, result):
2939 return True
2981 return True
2940
2982
2941 for i, node in enumerate(to_run_interactive):
2983 for i, node in enumerate(to_run_interactive):
2942 mod = ast.Interactive([node])
2984 mod = ast.Interactive([node])
2943 code = compiler(mod, cell_name, "single")
2985 code = compiler(mod, cell_name, "single")
2944 if self.run_code(code):
2986 if self.run_code(code, result):
2945 return True
2987 return True
2946
2988
2947 # Flush softspace
2989 # Flush softspace
@@ -2958,11 +3000,14 b' class InteractiveShell(SingletonConfigurable):'
2958 # We do only one try/except outside the loop to minimize the impact
3000 # We do only one try/except outside the loop to minimize the impact
2959 # on runtime, and also because if any node in the node list is
3001 # on runtime, and also because if any node in the node list is
2960 # broken, we should stop execution completely.
3002 # broken, we should stop execution completely.
3003 if result:
3004 result.error_before_exec = sys.exc_info()[1]
2961 self.showtraceback()
3005 self.showtraceback()
3006 return True
2962
3007
2963 return False
3008 return False
2964
3009
2965 def run_code(self, code_obj):
3010 def run_code(self, code_obj, result=None):
2966 """Execute a code object.
3011 """Execute a code object.
2967
3012
2968 When an exception occurs, self.showtraceback() is called to display a
3013 When an exception occurs, self.showtraceback() is called to display a
@@ -2972,6 +3017,8 b' class InteractiveShell(SingletonConfigurable):'
2972 ----------
3017 ----------
2973 code_obj : code object
3018 code_obj : code object
2974 A compiled code object, to be executed
3019 A compiled code object, to be executed
3020 result : ExecutionResult, optional
3021 An object to store exceptions that occur during execution.
2975
3022
2976 Returns
3023 Returns
2977 -------
3024 -------
@@ -2982,6 +3029,11 b' class InteractiveShell(SingletonConfigurable):'
2982 # directly, so that the IPython crash handler doesn't get triggered
3029 # directly, so that the IPython crash handler doesn't get triggered
2983 old_excepthook, sys.excepthook = sys.excepthook, self.excepthook
3030 old_excepthook, sys.excepthook = sys.excepthook, self.excepthook
2984
3031
3032 # Convenience function to set result.error_in_exec
3033 def set_result_exc(value=None):
3034 if result is not None:
3035 result.error_in_exec = value if (value is not None) else sys.exc_info()[1]
3036
2985 # we save the original sys.excepthook in the instance, in case config
3037 # we save the original sys.excepthook in the instance, in case config
2986 # code (such as magics) needs access to it.
3038 # code (such as magics) needs access to it.
2987 self.sys_excepthook = old_excepthook
3039 self.sys_excepthook = old_excepthook
@@ -2994,13 +3046,16 b' class InteractiveShell(SingletonConfigurable):'
2994 finally:
3046 finally:
2995 # Reset our crash handler in place
3047 # Reset our crash handler in place
2996 sys.excepthook = old_excepthook
3048 sys.excepthook = old_excepthook
2997 except SystemExit:
3049 except SystemExit as e:
3050 set_result_exc(e)
2998 self.showtraceback(exception_only=True)
3051 self.showtraceback(exception_only=True)
2999 warn("To exit: use 'exit', 'quit', or Ctrl-D.", level=1)
3052 warn("To exit: use 'exit', 'quit', or Ctrl-D.", level=1)
3000 except self.custom_exceptions:
3053 except self.custom_exceptions:
3001 etype, value, tb = sys.exc_info()
3054 etype, value, tb = sys.exc_info()
3055 set_result_exc(value)
3002 self.CustomTB(etype, value, tb)
3056 self.CustomTB(etype, value, tb)
3003 except:
3057 except:
3058 set_result_exc()
3004 self.showtraceback()
3059 self.showtraceback()
3005 else:
3060 else:
3006 outflag = 0
3061 outflag = 0
@@ -64,8 +64,9 b' class InteractiveShellTestCase(unittest.TestCase):'
64 """Just make sure we don't get a horrible error with a blank
64 """Just make sure we don't get a horrible error with a blank
65 cell of input. Yes, I did overlook that."""
65 cell of input. Yes, I did overlook that."""
66 old_xc = ip.execution_count
66 old_xc = ip.execution_count
67 ip.run_cell('')
67 res = ip.run_cell('')
68 self.assertEqual(ip.execution_count, old_xc)
68 self.assertEqual(ip.execution_count, old_xc)
69 self.assertEqual(res.execution_count, None)
69
70
70 def test_run_cell_multiline(self):
71 def test_run_cell_multiline(self):
71 """Multi-block, multi-line cells must execute correctly.
72 """Multi-block, multi-line cells must execute correctly.
@@ -75,24 +76,29 b' class InteractiveShellTestCase(unittest.TestCase):'
75 "if 1:",
76 "if 1:",
76 " x += 1",
77 " x += 1",
77 " y += 1",])
78 " y += 1",])
78 ip.run_cell(src)
79 res = ip.run_cell(src)
79 self.assertEqual(ip.user_ns['x'], 2)
80 self.assertEqual(ip.user_ns['x'], 2)
80 self.assertEqual(ip.user_ns['y'], 3)
81 self.assertEqual(ip.user_ns['y'], 3)
82 self.assertEqual(res.success, True)
83 self.assertEqual(res.result, None)
81
84
82 def test_multiline_string_cells(self):
85 def test_multiline_string_cells(self):
83 "Code sprinkled with multiline strings should execute (GH-306)"
86 "Code sprinkled with multiline strings should execute (GH-306)"
84 ip.run_cell('tmp=0')
87 ip.run_cell('tmp=0')
85 self.assertEqual(ip.user_ns['tmp'], 0)
88 self.assertEqual(ip.user_ns['tmp'], 0)
86 ip.run_cell('tmp=1;"""a\nb"""\n')
89 res = ip.run_cell('tmp=1;"""a\nb"""\n')
87 self.assertEqual(ip.user_ns['tmp'], 1)
90 self.assertEqual(ip.user_ns['tmp'], 1)
91 self.assertEqual(res.success, True)
92 self.assertEqual(res.result, "a\nb")
88
93
89 def test_dont_cache_with_semicolon(self):
94 def test_dont_cache_with_semicolon(self):
90 "Ending a line with semicolon should not cache the returned object (GH-307)"
95 "Ending a line with semicolon should not cache the returned object (GH-307)"
91 oldlen = len(ip.user_ns['Out'])
96 oldlen = len(ip.user_ns['Out'])
92 for cell in ['1;', '1;1;']:
97 for cell in ['1;', '1;1;']:
93 ip.run_cell(cell, store_history=True)
98 res = ip.run_cell(cell, store_history=True)
94 newlen = len(ip.user_ns['Out'])
99 newlen = len(ip.user_ns['Out'])
95 self.assertEqual(oldlen, newlen)
100 self.assertEqual(oldlen, newlen)
101 self.assertIsNone(res.result)
96 i = 0
102 i = 0
97 #also test the default caching behavior
103 #also test the default caching behavior
98 for cell in ['1', '1;1']:
104 for cell in ['1', '1;1']:
@@ -101,6 +107,10 b' class InteractiveShellTestCase(unittest.TestCase):'
101 i += 1
107 i += 1
102 self.assertEqual(oldlen+i, newlen)
108 self.assertEqual(oldlen+i, newlen)
103
109
110 def test_syntax_error(self):
111 res = ip.run_cell("raise = 3")
112 self.assertIsInstance(res.error_before_exec, SyntaxError)
113
104 def test_In_variable(self):
114 def test_In_variable(self):
105 "Verify that In variable grows with user input (GH-284)"
115 "Verify that In variable grows with user input (GH-284)"
106 oldlen = len(ip.user_ns['In'])
116 oldlen = len(ip.user_ns['In'])
@@ -330,8 +340,9 b' class InteractiveShellTestCase(unittest.TestCase):'
330
340
331 try:
341 try:
332 trap.hook = failing_hook
342 trap.hook = failing_hook
333 ip.run_cell("1", silent=True)
343 res = ip.run_cell("1", silent=True)
334 self.assertFalse(d['called'])
344 self.assertFalse(d['called'])
345 self.assertIsNone(res.result)
335 # double-check that non-silent exec did what we expected
346 # double-check that non-silent exec did what we expected
336 # silent to avoid
347 # silent to avoid
337 ip.run_cell("1")
348 ip.run_cell("1")
@@ -443,9 +454,11 b' class InteractiveShellTestCase(unittest.TestCase):'
443
454
444 ip.set_custom_exc((ValueError,), my_handler)
455 ip.set_custom_exc((ValueError,), my_handler)
445 try:
456 try:
446 ip.run_cell("raise ValueError('test')")
457 res = ip.run_cell("raise ValueError('test')")
447 # Check that this was called, and only once.
458 # Check that this was called, and only once.
448 self.assertEqual(called, [ValueError])
459 self.assertEqual(called, [ValueError])
460 # Check that the error is on the result object
461 self.assertIsInstance(res.error_in_exec, ValueError)
449 finally:
462 finally:
450 # Reset the custom exception hook
463 # Reset the custom exception hook
451 ip.set_custom_exc((), None)
464 ip.set_custom_exc((), None)
@@ -760,7 +773,9 b' class TestAstTransformInputRejection(unittest.TestCase):'
760 ip.run_cell("'unsafe'")
773 ip.run_cell("'unsafe'")
761
774
762 with expect_exception_tb, expect_no_cell_output:
775 with expect_exception_tb, expect_no_cell_output:
763 ip.run_cell("'unsafe'")
776 res = ip.run_cell("'unsafe'")
777
778 self.assertIsInstance(res.error_before_exec, InputRejected)
764
779
765 def test__IPYTHON__():
780 def test__IPYTHON__():
766 # This shouldn't raise a NameError, that's all
781 # This shouldn't raise a NameError, that's all
General Comments 0
You need to be logged in to leave comments. Login now