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 |
|
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 |
|
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 |
|
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 |
|
|
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