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