##// END OF EJS Templates
Add support for "finally" event callbacks....
Fabio Niephaus -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,6 b''
1 Two new event callbacks have been added: ``finally_execute`` and ``finally_run_cell``.
2 They work similar to the corresponding *post* callbacks, but are guaranteed to be triggered (even when, for example, a ``SyntaxError`` was raised).
3 Also, the execution result is provided as an argument for further inspection.
4
5 * `GitHub issue <https://github.com/ipython/ipython/issues/10774>`__
6 * `Updated docs <http://ipython.readthedocs.io/en/stable/config/callbacks.html?highlight=finally>`__
@@ -1,130 +1,153 b''
1 """Infrastructure for registering and firing callbacks on application events.
1 """Infrastructure for registering and firing callbacks on application events.
2
2
3 Unlike :mod:`IPython.core.hooks`, which lets end users set single functions to
3 Unlike :mod:`IPython.core.hooks`, which lets end users set single functions to
4 be called at specific times, or a collection of alternative methods to try,
4 be called at specific times, or a collection of alternative methods to try,
5 callbacks are designed to be used by extension authors. A number of callbacks
5 callbacks are designed to be used by extension authors. A number of callbacks
6 can be registered for the same event without needing to be aware of one another.
6 can be registered for the same event without needing to be aware of one another.
7
7
8 The functions defined in this module are no-ops indicating the names of available
8 The functions defined in this module are no-ops indicating the names of available
9 events and the arguments which will be passed to them.
9 events and the arguments which will be passed to them.
10
10
11 .. note::
11 .. note::
12
12
13 This API is experimental in IPython 2.0, and may be revised in future versions.
13 This API is experimental in IPython 2.0, and may be revised in future versions.
14 """
14 """
15
15
16 class EventManager(object):
16 class EventManager(object):
17 """Manage a collection of events and a sequence of callbacks for each.
17 """Manage a collection of events and a sequence of callbacks for each.
18
18
19 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
19 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
20 instances as an ``events`` attribute.
20 instances as an ``events`` attribute.
21
21
22 .. note::
22 .. note::
23
23
24 This API is experimental in IPython 2.0, and may be revised in future versions.
24 This API is experimental in IPython 2.0, and may be revised in future versions.
25 """
25 """
26 def __init__(self, shell, available_events):
26 def __init__(self, shell, available_events):
27 """Initialise the :class:`CallbackManager`.
27 """Initialise the :class:`CallbackManager`.
28
28
29 Parameters
29 Parameters
30 ----------
30 ----------
31 shell
31 shell
32 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
32 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
33 available_callbacks
33 available_callbacks
34 An iterable of names for callback events.
34 An iterable of names for callback events.
35 """
35 """
36 self.shell = shell
36 self.shell = shell
37 self.callbacks = {n:[] for n in available_events}
37 self.callbacks = {n:[] for n in available_events}
38
38
39 def register(self, event, function):
39 def register(self, event, function):
40 """Register a new event callback
40 """Register a new event callback
41
41
42 Parameters
42 Parameters
43 ----------
43 ----------
44 event : str
44 event : str
45 The event for which to register this callback.
45 The event for which to register this callback.
46 function : callable
46 function : callable
47 A function to be called on the given event. It should take the same
47 A function to be called on the given event. It should take the same
48 parameters as the appropriate callback prototype.
48 parameters as the appropriate callback prototype.
49
49
50 Raises
50 Raises
51 ------
51 ------
52 TypeError
52 TypeError
53 If ``function`` is not callable.
53 If ``function`` is not callable.
54 KeyError
54 KeyError
55 If ``event`` is not one of the known events.
55 If ``event`` is not one of the known events.
56 """
56 """
57 if not callable(function):
57 if not callable(function):
58 raise TypeError('Need a callable, got %r' % function)
58 raise TypeError('Need a callable, got %r' % function)
59 self.callbacks[event].append(function)
59 self.callbacks[event].append(function)
60
60
61 def unregister(self, event, function):
61 def unregister(self, event, function):
62 """Remove a callback from the given event."""
62 """Remove a callback from the given event."""
63 self.callbacks[event].remove(function)
63 self.callbacks[event].remove(function)
64
64
65 def trigger(self, event, *args, **kwargs):
65 def trigger(self, event, *args, **kwargs):
66 """Call callbacks for ``event``.
66 """Call callbacks for ``event``.
67
67
68 Any additional arguments are passed to all callbacks registered for this
68 Any additional arguments are passed to all callbacks registered for this
69 event. Exceptions raised by callbacks are caught, and a message printed.
69 event. Exceptions raised by callbacks are caught, and a message printed.
70 """
70 """
71 for func in self.callbacks[event][:]:
71 for func in self.callbacks[event][:]:
72 try:
72 try:
73 func(*args, **kwargs)
73 func(*args, **kwargs)
74 except Exception:
74 except Exception:
75 print("Error in callback {} (for {}):".format(func, event))
75 print("Error in callback {} (for {}):".format(func, event))
76 self.shell.showtraceback()
76 self.shell.showtraceback()
77
77
78 # event_name -> prototype mapping
78 # event_name -> prototype mapping
79 available_events = {}
79 available_events = {}
80
80
81 def _define_event(callback_proto):
81 def _define_event(callback_proto):
82 available_events[callback_proto.__name__] = callback_proto
82 available_events[callback_proto.__name__] = callback_proto
83 return callback_proto
83 return callback_proto
84
84
85 # ------------------------------------------------------------------------------
85 # ------------------------------------------------------------------------------
86 # Callback prototypes
86 # Callback prototypes
87 #
87 #
88 # No-op functions which describe the names of available events and the
88 # No-op functions which describe the names of available events and the
89 # signatures of callbacks for those events.
89 # signatures of callbacks for those events.
90 # ------------------------------------------------------------------------------
90 # ------------------------------------------------------------------------------
91
91
92 @_define_event
92 @_define_event
93 def pre_execute():
93 def pre_execute():
94 """Fires before code is executed in response to user/frontend action.
94 """Fires before code is executed in response to user/frontend action.
95
95
96 This includes comm and widget messages and silent execution, as well as user
96 This includes comm and widget messages and silent execution, as well as user
97 code cells."""
97 code cells."""
98 pass
98 pass
99
99
100 @_define_event
100 @_define_event
101 def pre_run_cell():
101 def pre_run_cell():
102 """Fires before user-entered code runs."""
102 """Fires before user-entered code runs."""
103 pass
103 pass
104
104
105 @_define_event
105 @_define_event
106 def post_execute():
106 def post_execute():
107 """Fires after code is executed in response to user/frontend action.
107 """Fires after code is executed in response to user/frontend action.
108
108
109 This includes comm and widget messages and silent execution, as well as user
109 This includes comm and widget messages and silent execution, as well as user
110 code cells."""
110 code cells."""
111 pass
111 pass
112
112
113 @_define_event
113 @_define_event
114 def post_run_cell():
114 def post_run_cell():
115 """Fires after user-entered code runs."""
115 """Fires after user-entered code runs."""
116 pass
116 pass
117
117
118 @_define_event
118 @_define_event
119 def finally_execute(result):
120 """Always fires after code is executed in response to user/frontend action.
121
122 This includes comm and widget messages and silent execution, as well as user
123 code cells.
124
125 Parameters
126 ----------
127 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
128 """
129 pass
130
131 @_define_event
132 def finally_run_cell(result):
133 """Always fires after user-entered code runs.
134
135 Parameters
136 ----------
137 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
138 """
139 pass
140
141 @_define_event
119 def shell_initialized(ip):
142 def shell_initialized(ip):
120 """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
143 """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
121
144
122 This is before extensions and startup scripts are loaded, so it can only be
145 This is before extensions and startup scripts are loaded, so it can only be
123 set by subclassing.
146 set by subclassing.
124
147
125 Parameters
148 Parameters
126 ----------
149 ----------
127 ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
150 ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
128 The newly initialised shell.
151 The newly initialised shell.
129 """
152 """
130 pass
153 pass
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,912 +1,932 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the key interactiveshell module.
2 """Tests for the key interactiveshell module.
3
3
4 Historically the main classes in interactiveshell have been under-tested. This
4 Historically the main classes in interactiveshell have been under-tested. This
5 module should grow as many single-method tests as possible to trap many of the
5 module should grow as many single-method tests as possible to trap many of the
6 recurring bugs we seem to encounter with high-level interaction.
6 recurring bugs we seem to encounter with high-level interaction.
7 """
7 """
8
8
9 # Copyright (c) IPython Development Team.
9 # Copyright (c) IPython Development Team.
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11
11
12 import ast
12 import ast
13 import os
13 import os
14 import signal
14 import signal
15 import shutil
15 import shutil
16 import sys
16 import sys
17 import tempfile
17 import tempfile
18 import unittest
18 import unittest
19 from unittest import mock
19 from unittest import mock
20 from io import StringIO
20 from io import StringIO
21
21
22 from os.path import join
22 from os.path import join
23
23
24 import nose.tools as nt
24 import nose.tools as nt
25
25
26 from IPython.core.error import InputRejected
26 from IPython.core.error import InputRejected
27 from IPython.core.inputtransformer import InputTransformer
27 from IPython.core.inputtransformer import InputTransformer
28 from IPython.testing.decorators import (
28 from IPython.testing.decorators import (
29 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
29 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
30 )
30 )
31 from IPython.testing import tools as tt
31 from IPython.testing import tools as tt
32 from IPython.utils.process import find_cmd
32 from IPython.utils.process import find_cmd
33 from IPython.utils import py3compat
33 from IPython.utils import py3compat
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Globals
36 # Globals
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38 # This is used by every single test, no point repeating it ad nauseam
38 # This is used by every single test, no point repeating it ad nauseam
39 ip = get_ipython()
39 ip = get_ipython()
40
40
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # Tests
42 # Tests
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44
44
45 class DerivedInterrupt(KeyboardInterrupt):
45 class DerivedInterrupt(KeyboardInterrupt):
46 pass
46 pass
47
47
48 class InteractiveShellTestCase(unittest.TestCase):
48 class InteractiveShellTestCase(unittest.TestCase):
49 def test_naked_string_cells(self):
49 def test_naked_string_cells(self):
50 """Test that cells with only naked strings are fully executed"""
50 """Test that cells with only naked strings are fully executed"""
51 # First, single-line inputs
51 # First, single-line inputs
52 ip.run_cell('"a"\n')
52 ip.run_cell('"a"\n')
53 self.assertEqual(ip.user_ns['_'], 'a')
53 self.assertEqual(ip.user_ns['_'], 'a')
54 # And also multi-line cells
54 # And also multi-line cells
55 ip.run_cell('"""a\nb"""\n')
55 ip.run_cell('"""a\nb"""\n')
56 self.assertEqual(ip.user_ns['_'], 'a\nb')
56 self.assertEqual(ip.user_ns['_'], 'a\nb')
57
57
58 def test_run_empty_cell(self):
58 def test_run_empty_cell(self):
59 """Just make sure we don't get a horrible error with a blank
59 """Just make sure we don't get a horrible error with a blank
60 cell of input. Yes, I did overlook that."""
60 cell of input. Yes, I did overlook that."""
61 old_xc = ip.execution_count
61 old_xc = ip.execution_count
62 res = ip.run_cell('')
62 res = ip.run_cell('')
63 self.assertEqual(ip.execution_count, old_xc)
63 self.assertEqual(ip.execution_count, old_xc)
64 self.assertEqual(res.execution_count, None)
64 self.assertEqual(res.execution_count, None)
65
65
66 def test_run_cell_multiline(self):
66 def test_run_cell_multiline(self):
67 """Multi-block, multi-line cells must execute correctly.
67 """Multi-block, multi-line cells must execute correctly.
68 """
68 """
69 src = '\n'.join(["x=1",
69 src = '\n'.join(["x=1",
70 "y=2",
70 "y=2",
71 "if 1:",
71 "if 1:",
72 " x += 1",
72 " x += 1",
73 " y += 1",])
73 " y += 1",])
74 res = ip.run_cell(src)
74 res = ip.run_cell(src)
75 self.assertEqual(ip.user_ns['x'], 2)
75 self.assertEqual(ip.user_ns['x'], 2)
76 self.assertEqual(ip.user_ns['y'], 3)
76 self.assertEqual(ip.user_ns['y'], 3)
77 self.assertEqual(res.success, True)
77 self.assertEqual(res.success, True)
78 self.assertEqual(res.result, None)
78 self.assertEqual(res.result, None)
79
79
80 def test_multiline_string_cells(self):
80 def test_multiline_string_cells(self):
81 "Code sprinkled with multiline strings should execute (GH-306)"
81 "Code sprinkled with multiline strings should execute (GH-306)"
82 ip.run_cell('tmp=0')
82 ip.run_cell('tmp=0')
83 self.assertEqual(ip.user_ns['tmp'], 0)
83 self.assertEqual(ip.user_ns['tmp'], 0)
84 res = ip.run_cell('tmp=1;"""a\nb"""\n')
84 res = ip.run_cell('tmp=1;"""a\nb"""\n')
85 self.assertEqual(ip.user_ns['tmp'], 1)
85 self.assertEqual(ip.user_ns['tmp'], 1)
86 self.assertEqual(res.success, True)
86 self.assertEqual(res.success, True)
87 self.assertEqual(res.result, "a\nb")
87 self.assertEqual(res.result, "a\nb")
88
88
89 def test_dont_cache_with_semicolon(self):
89 def test_dont_cache_with_semicolon(self):
90 "Ending a line with semicolon should not cache the returned object (GH-307)"
90 "Ending a line with semicolon should not cache the returned object (GH-307)"
91 oldlen = len(ip.user_ns['Out'])
91 oldlen = len(ip.user_ns['Out'])
92 for cell in ['1;', '1;1;']:
92 for cell in ['1;', '1;1;']:
93 res = ip.run_cell(cell, store_history=True)
93 res = ip.run_cell(cell, store_history=True)
94 newlen = len(ip.user_ns['Out'])
94 newlen = len(ip.user_ns['Out'])
95 self.assertEqual(oldlen, newlen)
95 self.assertEqual(oldlen, newlen)
96 self.assertIsNone(res.result)
96 self.assertIsNone(res.result)
97 i = 0
97 i = 0
98 #also test the default caching behavior
98 #also test the default caching behavior
99 for cell in ['1', '1;1']:
99 for cell in ['1', '1;1']:
100 ip.run_cell(cell, store_history=True)
100 ip.run_cell(cell, store_history=True)
101 newlen = len(ip.user_ns['Out'])
101 newlen = len(ip.user_ns['Out'])
102 i += 1
102 i += 1
103 self.assertEqual(oldlen+i, newlen)
103 self.assertEqual(oldlen+i, newlen)
104
104
105 def test_syntax_error(self):
105 def test_syntax_error(self):
106 res = ip.run_cell("raise = 3")
106 res = ip.run_cell("raise = 3")
107 self.assertIsInstance(res.error_before_exec, SyntaxError)
107 self.assertIsInstance(res.error_before_exec, SyntaxError)
108
108
109 def test_In_variable(self):
109 def test_In_variable(self):
110 "Verify that In variable grows with user input (GH-284)"
110 "Verify that In variable grows with user input (GH-284)"
111 oldlen = len(ip.user_ns['In'])
111 oldlen = len(ip.user_ns['In'])
112 ip.run_cell('1;', store_history=True)
112 ip.run_cell('1;', store_history=True)
113 newlen = len(ip.user_ns['In'])
113 newlen = len(ip.user_ns['In'])
114 self.assertEqual(oldlen+1, newlen)
114 self.assertEqual(oldlen+1, newlen)
115 self.assertEqual(ip.user_ns['In'][-1],'1;')
115 self.assertEqual(ip.user_ns['In'][-1],'1;')
116
116
117 def test_magic_names_in_string(self):
117 def test_magic_names_in_string(self):
118 ip.run_cell('a = """\n%exit\n"""')
118 ip.run_cell('a = """\n%exit\n"""')
119 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
119 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
120
120
121 def test_trailing_newline(self):
121 def test_trailing_newline(self):
122 """test that running !(command) does not raise a SyntaxError"""
122 """test that running !(command) does not raise a SyntaxError"""
123 ip.run_cell('!(true)\n', False)
123 ip.run_cell('!(true)\n', False)
124 ip.run_cell('!(true)\n\n\n', False)
124 ip.run_cell('!(true)\n\n\n', False)
125
125
126 def test_gh_597(self):
126 def test_gh_597(self):
127 """Pretty-printing lists of objects with non-ascii reprs may cause
127 """Pretty-printing lists of objects with non-ascii reprs may cause
128 problems."""
128 problems."""
129 class Spam(object):
129 class Spam(object):
130 def __repr__(self):
130 def __repr__(self):
131 return "\xe9"*50
131 return "\xe9"*50
132 import IPython.core.formatters
132 import IPython.core.formatters
133 f = IPython.core.formatters.PlainTextFormatter()
133 f = IPython.core.formatters.PlainTextFormatter()
134 f([Spam(),Spam()])
134 f([Spam(),Spam()])
135
135
136
136
137 def test_future_flags(self):
137 def test_future_flags(self):
138 """Check that future flags are used for parsing code (gh-777)"""
138 """Check that future flags are used for parsing code (gh-777)"""
139 ip.run_cell('from __future__ import barry_as_FLUFL')
139 ip.run_cell('from __future__ import barry_as_FLUFL')
140 try:
140 try:
141 ip.run_cell('prfunc_return_val = 1 <> 2')
141 ip.run_cell('prfunc_return_val = 1 <> 2')
142 assert 'prfunc_return_val' in ip.user_ns
142 assert 'prfunc_return_val' in ip.user_ns
143 finally:
143 finally:
144 # Reset compiler flags so we don't mess up other tests.
144 # Reset compiler flags so we don't mess up other tests.
145 ip.compile.reset_compiler_flags()
145 ip.compile.reset_compiler_flags()
146
146
147 def test_can_pickle(self):
147 def test_can_pickle(self):
148 "Can we pickle objects defined interactively (GH-29)"
148 "Can we pickle objects defined interactively (GH-29)"
149 ip = get_ipython()
149 ip = get_ipython()
150 ip.reset()
150 ip.reset()
151 ip.run_cell(("class Mylist(list):\n"
151 ip.run_cell(("class Mylist(list):\n"
152 " def __init__(self,x=[]):\n"
152 " def __init__(self,x=[]):\n"
153 " list.__init__(self,x)"))
153 " list.__init__(self,x)"))
154 ip.run_cell("w=Mylist([1,2,3])")
154 ip.run_cell("w=Mylist([1,2,3])")
155
155
156 from pickle import dumps
156 from pickle import dumps
157
157
158 # We need to swap in our main module - this is only necessary
158 # We need to swap in our main module - this is only necessary
159 # inside the test framework, because IPython puts the interactive module
159 # inside the test framework, because IPython puts the interactive module
160 # in place (but the test framework undoes this).
160 # in place (but the test framework undoes this).
161 _main = sys.modules['__main__']
161 _main = sys.modules['__main__']
162 sys.modules['__main__'] = ip.user_module
162 sys.modules['__main__'] = ip.user_module
163 try:
163 try:
164 res = dumps(ip.user_ns["w"])
164 res = dumps(ip.user_ns["w"])
165 finally:
165 finally:
166 sys.modules['__main__'] = _main
166 sys.modules['__main__'] = _main
167 self.assertTrue(isinstance(res, bytes))
167 self.assertTrue(isinstance(res, bytes))
168
168
169 def test_global_ns(self):
169 def test_global_ns(self):
170 "Code in functions must be able to access variables outside them."
170 "Code in functions must be able to access variables outside them."
171 ip = get_ipython()
171 ip = get_ipython()
172 ip.run_cell("a = 10")
172 ip.run_cell("a = 10")
173 ip.run_cell(("def f(x):\n"
173 ip.run_cell(("def f(x):\n"
174 " return x + a"))
174 " return x + a"))
175 ip.run_cell("b = f(12)")
175 ip.run_cell("b = f(12)")
176 self.assertEqual(ip.user_ns["b"], 22)
176 self.assertEqual(ip.user_ns["b"], 22)
177
177
178 def test_bad_custom_tb(self):
178 def test_bad_custom_tb(self):
179 """Check that InteractiveShell is protected from bad custom exception handlers"""
179 """Check that InteractiveShell is protected from bad custom exception handlers"""
180 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
180 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
181 self.assertEqual(ip.custom_exceptions, (IOError,))
181 self.assertEqual(ip.custom_exceptions, (IOError,))
182 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
182 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
183 ip.run_cell(u'raise IOError("foo")')
183 ip.run_cell(u'raise IOError("foo")')
184 self.assertEqual(ip.custom_exceptions, ())
184 self.assertEqual(ip.custom_exceptions, ())
185
185
186 def test_bad_custom_tb_return(self):
186 def test_bad_custom_tb_return(self):
187 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
187 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
188 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
188 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
189 self.assertEqual(ip.custom_exceptions, (NameError,))
189 self.assertEqual(ip.custom_exceptions, (NameError,))
190 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
190 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
191 ip.run_cell(u'a=abracadabra')
191 ip.run_cell(u'a=abracadabra')
192 self.assertEqual(ip.custom_exceptions, ())
192 self.assertEqual(ip.custom_exceptions, ())
193
193
194 def test_drop_by_id(self):
194 def test_drop_by_id(self):
195 myvars = {"a":object(), "b":object(), "c": object()}
195 myvars = {"a":object(), "b":object(), "c": object()}
196 ip.push(myvars, interactive=False)
196 ip.push(myvars, interactive=False)
197 for name in myvars:
197 for name in myvars:
198 assert name in ip.user_ns, name
198 assert name in ip.user_ns, name
199 assert name in ip.user_ns_hidden, name
199 assert name in ip.user_ns_hidden, name
200 ip.user_ns['b'] = 12
200 ip.user_ns['b'] = 12
201 ip.drop_by_id(myvars)
201 ip.drop_by_id(myvars)
202 for name in ["a", "c"]:
202 for name in ["a", "c"]:
203 assert name not in ip.user_ns, name
203 assert name not in ip.user_ns, name
204 assert name not in ip.user_ns_hidden, name
204 assert name not in ip.user_ns_hidden, name
205 assert ip.user_ns['b'] == 12
205 assert ip.user_ns['b'] == 12
206 ip.reset()
206 ip.reset()
207
207
208 def test_var_expand(self):
208 def test_var_expand(self):
209 ip.user_ns['f'] = u'Ca\xf1o'
209 ip.user_ns['f'] = u'Ca\xf1o'
210 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
210 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
211 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
211 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
212 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
212 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
213 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
213 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
214
214
215 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
215 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
216
216
217 ip.user_ns['f'] = b'Ca\xc3\xb1o'
217 ip.user_ns['f'] = b'Ca\xc3\xb1o'
218 # This should not raise any exception:
218 # This should not raise any exception:
219 ip.var_expand(u'echo $f')
219 ip.var_expand(u'echo $f')
220
220
221 def test_var_expand_local(self):
221 def test_var_expand_local(self):
222 """Test local variable expansion in !system and %magic calls"""
222 """Test local variable expansion in !system and %magic calls"""
223 # !system
223 # !system
224 ip.run_cell('def test():\n'
224 ip.run_cell('def test():\n'
225 ' lvar = "ttt"\n'
225 ' lvar = "ttt"\n'
226 ' ret = !echo {lvar}\n'
226 ' ret = !echo {lvar}\n'
227 ' return ret[0]\n')
227 ' return ret[0]\n')
228 res = ip.user_ns['test']()
228 res = ip.user_ns['test']()
229 nt.assert_in('ttt', res)
229 nt.assert_in('ttt', res)
230
230
231 # %magic
231 # %magic
232 ip.run_cell('def makemacro():\n'
232 ip.run_cell('def makemacro():\n'
233 ' macroname = "macro_var_expand_locals"\n'
233 ' macroname = "macro_var_expand_locals"\n'
234 ' %macro {macroname} codestr\n')
234 ' %macro {macroname} codestr\n')
235 ip.user_ns['codestr'] = "str(12)"
235 ip.user_ns['codestr'] = "str(12)"
236 ip.run_cell('makemacro()')
236 ip.run_cell('makemacro()')
237 nt.assert_in('macro_var_expand_locals', ip.user_ns)
237 nt.assert_in('macro_var_expand_locals', ip.user_ns)
238
238
239 def test_var_expand_self(self):
239 def test_var_expand_self(self):
240 """Test variable expansion with the name 'self', which was failing.
240 """Test variable expansion with the name 'self', which was failing.
241
241
242 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
242 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
243 """
243 """
244 ip.run_cell('class cTest:\n'
244 ip.run_cell('class cTest:\n'
245 ' classvar="see me"\n'
245 ' classvar="see me"\n'
246 ' def test(self):\n'
246 ' def test(self):\n'
247 ' res = !echo Variable: {self.classvar}\n'
247 ' res = !echo Variable: {self.classvar}\n'
248 ' return res[0]\n')
248 ' return res[0]\n')
249 nt.assert_in('see me', ip.user_ns['cTest']().test())
249 nt.assert_in('see me', ip.user_ns['cTest']().test())
250
250
251 def test_bad_var_expand(self):
251 def test_bad_var_expand(self):
252 """var_expand on invalid formats shouldn't raise"""
252 """var_expand on invalid formats shouldn't raise"""
253 # SyntaxError
253 # SyntaxError
254 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
254 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
255 # NameError
255 # NameError
256 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
256 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
257 # ZeroDivisionError
257 # ZeroDivisionError
258 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
258 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
259
259
260 def test_silent_postexec(self):
260 def test_silent_postexec(self):
261 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
261 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
262 pre_explicit = mock.Mock()
262 pre_explicit = mock.Mock()
263 pre_always = mock.Mock()
263 pre_always = mock.Mock()
264 post_explicit = mock.Mock()
264 post_explicit = mock.Mock()
265 post_always = mock.Mock()
265 post_always = mock.Mock()
266 finally_explicit = mock.Mock()
267 finally_always = mock.Mock()
268 all_mocks = [pre_explicit, pre_always, post_explicit, post_always,
269 finally_explicit,finally_always]
266
270
267 ip.events.register('pre_run_cell', pre_explicit)
271 ip.events.register('pre_run_cell', pre_explicit)
268 ip.events.register('pre_execute', pre_always)
272 ip.events.register('pre_execute', pre_always)
269 ip.events.register('post_run_cell', post_explicit)
273 ip.events.register('post_run_cell', post_explicit)
270 ip.events.register('post_execute', post_always)
274 ip.events.register('post_execute', post_always)
275 ip.events.register('finally_run_cell', finally_explicit)
276 ip.events.register('finally_execute', finally_always)
271
277
272 try:
278 try:
273 ip.run_cell("1", silent=True)
279 ip.run_cell("1", silent=True)
274 assert pre_always.called
280 assert pre_always.called
275 assert not pre_explicit.called
281 assert not pre_explicit.called
276 assert post_always.called
282 assert post_always.called
277 assert not post_explicit.called
283 assert not post_explicit.called
284 assert finally_always.called
285 assert not finally_explicit.called
278 # double-check that non-silent exec did what we expected
286 # double-check that non-silent exec did what we expected
279 # silent to avoid
287 # silent to avoid
280 ip.run_cell("1")
288 ip.run_cell("1")
281 assert pre_explicit.called
289 assert pre_explicit.called
282 assert post_explicit.called
290 assert post_explicit.called
291 assert finally_explicit.called
292 # check that finally hooks are always called
293 [m.reset_mock() for m in all_mocks]
294 ip.run_cell("syntax error")
295 assert pre_always.called
296 assert pre_explicit.called
297 assert not post_always.called # because of `SyntaxError`
298 assert not post_explicit.called
299 assert finally_explicit.called
300 assert finally_always.called
283 finally:
301 finally:
284 # remove post-exec
302 # remove post-exec
285 ip.events.unregister('pre_run_cell', pre_explicit)
303 ip.events.unregister('pre_run_cell', pre_explicit)
286 ip.events.unregister('pre_execute', pre_always)
304 ip.events.unregister('pre_execute', pre_always)
287 ip.events.unregister('post_run_cell', post_explicit)
305 ip.events.unregister('post_run_cell', post_explicit)
288 ip.events.unregister('post_execute', post_always)
306 ip.events.unregister('post_execute', post_always)
307 ip.events.unregister('finally_run_cell', finally_explicit)
308 ip.events.unregister('finally_execute', finally_always)
289
309
290 def test_silent_noadvance(self):
310 def test_silent_noadvance(self):
291 """run_cell(silent=True) doesn't advance execution_count"""
311 """run_cell(silent=True) doesn't advance execution_count"""
292 ec = ip.execution_count
312 ec = ip.execution_count
293 # silent should force store_history=False
313 # silent should force store_history=False
294 ip.run_cell("1", store_history=True, silent=True)
314 ip.run_cell("1", store_history=True, silent=True)
295
315
296 self.assertEqual(ec, ip.execution_count)
316 self.assertEqual(ec, ip.execution_count)
297 # double-check that non-silent exec did what we expected
317 # double-check that non-silent exec did what we expected
298 # silent to avoid
318 # silent to avoid
299 ip.run_cell("1", store_history=True)
319 ip.run_cell("1", store_history=True)
300 self.assertEqual(ec+1, ip.execution_count)
320 self.assertEqual(ec+1, ip.execution_count)
301
321
302 def test_silent_nodisplayhook(self):
322 def test_silent_nodisplayhook(self):
303 """run_cell(silent=True) doesn't trigger displayhook"""
323 """run_cell(silent=True) doesn't trigger displayhook"""
304 d = dict(called=False)
324 d = dict(called=False)
305
325
306 trap = ip.display_trap
326 trap = ip.display_trap
307 save_hook = trap.hook
327 save_hook = trap.hook
308
328
309 def failing_hook(*args, **kwargs):
329 def failing_hook(*args, **kwargs):
310 d['called'] = True
330 d['called'] = True
311
331
312 try:
332 try:
313 trap.hook = failing_hook
333 trap.hook = failing_hook
314 res = ip.run_cell("1", silent=True)
334 res = ip.run_cell("1", silent=True)
315 self.assertFalse(d['called'])
335 self.assertFalse(d['called'])
316 self.assertIsNone(res.result)
336 self.assertIsNone(res.result)
317 # double-check that non-silent exec did what we expected
337 # double-check that non-silent exec did what we expected
318 # silent to avoid
338 # silent to avoid
319 ip.run_cell("1")
339 ip.run_cell("1")
320 self.assertTrue(d['called'])
340 self.assertTrue(d['called'])
321 finally:
341 finally:
322 trap.hook = save_hook
342 trap.hook = save_hook
323
343
324 def test_ofind_line_magic(self):
344 def test_ofind_line_magic(self):
325 from IPython.core.magic import register_line_magic
345 from IPython.core.magic import register_line_magic
326
346
327 @register_line_magic
347 @register_line_magic
328 def lmagic(line):
348 def lmagic(line):
329 "A line magic"
349 "A line magic"
330
350
331 # Get info on line magic
351 # Get info on line magic
332 lfind = ip._ofind('lmagic')
352 lfind = ip._ofind('lmagic')
333 info = dict(found=True, isalias=False, ismagic=True,
353 info = dict(found=True, isalias=False, ismagic=True,
334 namespace = 'IPython internal', obj= lmagic.__wrapped__,
354 namespace = 'IPython internal', obj= lmagic.__wrapped__,
335 parent = None)
355 parent = None)
336 nt.assert_equal(lfind, info)
356 nt.assert_equal(lfind, info)
337
357
338 def test_ofind_cell_magic(self):
358 def test_ofind_cell_magic(self):
339 from IPython.core.magic import register_cell_magic
359 from IPython.core.magic import register_cell_magic
340
360
341 @register_cell_magic
361 @register_cell_magic
342 def cmagic(line, cell):
362 def cmagic(line, cell):
343 "A cell magic"
363 "A cell magic"
344
364
345 # Get info on cell magic
365 # Get info on cell magic
346 find = ip._ofind('cmagic')
366 find = ip._ofind('cmagic')
347 info = dict(found=True, isalias=False, ismagic=True,
367 info = dict(found=True, isalias=False, ismagic=True,
348 namespace = 'IPython internal', obj= cmagic.__wrapped__,
368 namespace = 'IPython internal', obj= cmagic.__wrapped__,
349 parent = None)
369 parent = None)
350 nt.assert_equal(find, info)
370 nt.assert_equal(find, info)
351
371
352 def test_ofind_property_with_error(self):
372 def test_ofind_property_with_error(self):
353 class A(object):
373 class A(object):
354 @property
374 @property
355 def foo(self):
375 def foo(self):
356 raise NotImplementedError()
376 raise NotImplementedError()
357 a = A()
377 a = A()
358
378
359 found = ip._ofind('a.foo', [('locals', locals())])
379 found = ip._ofind('a.foo', [('locals', locals())])
360 info = dict(found=True, isalias=False, ismagic=False,
380 info = dict(found=True, isalias=False, ismagic=False,
361 namespace='locals', obj=A.foo, parent=a)
381 namespace='locals', obj=A.foo, parent=a)
362 nt.assert_equal(found, info)
382 nt.assert_equal(found, info)
363
383
364 def test_ofind_multiple_attribute_lookups(self):
384 def test_ofind_multiple_attribute_lookups(self):
365 class A(object):
385 class A(object):
366 @property
386 @property
367 def foo(self):
387 def foo(self):
368 raise NotImplementedError()
388 raise NotImplementedError()
369
389
370 a = A()
390 a = A()
371 a.a = A()
391 a.a = A()
372 a.a.a = A()
392 a.a.a = A()
373
393
374 found = ip._ofind('a.a.a.foo', [('locals', locals())])
394 found = ip._ofind('a.a.a.foo', [('locals', locals())])
375 info = dict(found=True, isalias=False, ismagic=False,
395 info = dict(found=True, isalias=False, ismagic=False,
376 namespace='locals', obj=A.foo, parent=a.a.a)
396 namespace='locals', obj=A.foo, parent=a.a.a)
377 nt.assert_equal(found, info)
397 nt.assert_equal(found, info)
378
398
379 def test_ofind_slotted_attributes(self):
399 def test_ofind_slotted_attributes(self):
380 class A(object):
400 class A(object):
381 __slots__ = ['foo']
401 __slots__ = ['foo']
382 def __init__(self):
402 def __init__(self):
383 self.foo = 'bar'
403 self.foo = 'bar'
384
404
385 a = A()
405 a = A()
386 found = ip._ofind('a.foo', [('locals', locals())])
406 found = ip._ofind('a.foo', [('locals', locals())])
387 info = dict(found=True, isalias=False, ismagic=False,
407 info = dict(found=True, isalias=False, ismagic=False,
388 namespace='locals', obj=a.foo, parent=a)
408 namespace='locals', obj=a.foo, parent=a)
389 nt.assert_equal(found, info)
409 nt.assert_equal(found, info)
390
410
391 found = ip._ofind('a.bar', [('locals', locals())])
411 found = ip._ofind('a.bar', [('locals', locals())])
392 info = dict(found=False, isalias=False, ismagic=False,
412 info = dict(found=False, isalias=False, ismagic=False,
393 namespace=None, obj=None, parent=a)
413 namespace=None, obj=None, parent=a)
394 nt.assert_equal(found, info)
414 nt.assert_equal(found, info)
395
415
396 def test_ofind_prefers_property_to_instance_level_attribute(self):
416 def test_ofind_prefers_property_to_instance_level_attribute(self):
397 class A(object):
417 class A(object):
398 @property
418 @property
399 def foo(self):
419 def foo(self):
400 return 'bar'
420 return 'bar'
401 a = A()
421 a = A()
402 a.__dict__['foo'] = 'baz'
422 a.__dict__['foo'] = 'baz'
403 nt.assert_equal(a.foo, 'bar')
423 nt.assert_equal(a.foo, 'bar')
404 found = ip._ofind('a.foo', [('locals', locals())])
424 found = ip._ofind('a.foo', [('locals', locals())])
405 nt.assert_is(found['obj'], A.foo)
425 nt.assert_is(found['obj'], A.foo)
406
426
407 def test_custom_syntaxerror_exception(self):
427 def test_custom_syntaxerror_exception(self):
408 called = []
428 called = []
409 def my_handler(shell, etype, value, tb, tb_offset=None):
429 def my_handler(shell, etype, value, tb, tb_offset=None):
410 called.append(etype)
430 called.append(etype)
411 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
431 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
412
432
413 ip.set_custom_exc((SyntaxError,), my_handler)
433 ip.set_custom_exc((SyntaxError,), my_handler)
414 try:
434 try:
415 ip.run_cell("1f")
435 ip.run_cell("1f")
416 # Check that this was called, and only once.
436 # Check that this was called, and only once.
417 self.assertEqual(called, [SyntaxError])
437 self.assertEqual(called, [SyntaxError])
418 finally:
438 finally:
419 # Reset the custom exception hook
439 # Reset the custom exception hook
420 ip.set_custom_exc((), None)
440 ip.set_custom_exc((), None)
421
441
422 def test_custom_exception(self):
442 def test_custom_exception(self):
423 called = []
443 called = []
424 def my_handler(shell, etype, value, tb, tb_offset=None):
444 def my_handler(shell, etype, value, tb, tb_offset=None):
425 called.append(etype)
445 called.append(etype)
426 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
446 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
427
447
428 ip.set_custom_exc((ValueError,), my_handler)
448 ip.set_custom_exc((ValueError,), my_handler)
429 try:
449 try:
430 res = ip.run_cell("raise ValueError('test')")
450 res = ip.run_cell("raise ValueError('test')")
431 # Check that this was called, and only once.
451 # Check that this was called, and only once.
432 self.assertEqual(called, [ValueError])
452 self.assertEqual(called, [ValueError])
433 # Check that the error is on the result object
453 # Check that the error is on the result object
434 self.assertIsInstance(res.error_in_exec, ValueError)
454 self.assertIsInstance(res.error_in_exec, ValueError)
435 finally:
455 finally:
436 # Reset the custom exception hook
456 # Reset the custom exception hook
437 ip.set_custom_exc((), None)
457 ip.set_custom_exc((), None)
438
458
439 def test_mktempfile(self):
459 def test_mktempfile(self):
440 filename = ip.mktempfile()
460 filename = ip.mktempfile()
441 # Check that we can open the file again on Windows
461 # Check that we can open the file again on Windows
442 with open(filename, 'w') as f:
462 with open(filename, 'w') as f:
443 f.write('abc')
463 f.write('abc')
444
464
445 filename = ip.mktempfile(data='blah')
465 filename = ip.mktempfile(data='blah')
446 with open(filename, 'r') as f:
466 with open(filename, 'r') as f:
447 self.assertEqual(f.read(), 'blah')
467 self.assertEqual(f.read(), 'blah')
448
468
449 def test_new_main_mod(self):
469 def test_new_main_mod(self):
450 # Smoketest to check that this accepts a unicode module name
470 # Smoketest to check that this accepts a unicode module name
451 name = u'jiefmw'
471 name = u'jiefmw'
452 mod = ip.new_main_mod(u'%s.py' % name, name)
472 mod = ip.new_main_mod(u'%s.py' % name, name)
453 self.assertEqual(mod.__name__, name)
473 self.assertEqual(mod.__name__, name)
454
474
455 def test_get_exception_only(self):
475 def test_get_exception_only(self):
456 try:
476 try:
457 raise KeyboardInterrupt
477 raise KeyboardInterrupt
458 except KeyboardInterrupt:
478 except KeyboardInterrupt:
459 msg = ip.get_exception_only()
479 msg = ip.get_exception_only()
460 self.assertEqual(msg, 'KeyboardInterrupt\n')
480 self.assertEqual(msg, 'KeyboardInterrupt\n')
461
481
462 try:
482 try:
463 raise DerivedInterrupt("foo")
483 raise DerivedInterrupt("foo")
464 except KeyboardInterrupt:
484 except KeyboardInterrupt:
465 msg = ip.get_exception_only()
485 msg = ip.get_exception_only()
466 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
486 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
467
487
468 def test_inspect_text(self):
488 def test_inspect_text(self):
469 ip.run_cell('a = 5')
489 ip.run_cell('a = 5')
470 text = ip.object_inspect_text('a')
490 text = ip.object_inspect_text('a')
471 self.assertIsInstance(text, str)
491 self.assertIsInstance(text, str)
472
492
473 def test_last_execution_result(self):
493 def test_last_execution_result(self):
474 """ Check that last execution result gets set correctly (GH-10702) """
494 """ Check that last execution result gets set correctly (GH-10702) """
475 result = ip.run_cell('a = 5; a')
495 result = ip.run_cell('a = 5; a')
476 self.assertTrue(ip.last_execution_succeeded)
496 self.assertTrue(ip.last_execution_succeeded)
477 self.assertEqual(ip.last_execution_result.result, 5)
497 self.assertEqual(ip.last_execution_result.result, 5)
478
498
479 result = ip.run_cell('a = x_invalid_id_x')
499 result = ip.run_cell('a = x_invalid_id_x')
480 self.assertFalse(ip.last_execution_succeeded)
500 self.assertFalse(ip.last_execution_succeeded)
481 self.assertFalse(ip.last_execution_result.success)
501 self.assertFalse(ip.last_execution_result.success)
482 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
502 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
483
503
484
504
485 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
505 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
486
506
487 @onlyif_unicode_paths
507 @onlyif_unicode_paths
488 def setUp(self):
508 def setUp(self):
489 self.BASETESTDIR = tempfile.mkdtemp()
509 self.BASETESTDIR = tempfile.mkdtemp()
490 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
510 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
491 os.mkdir(self.TESTDIR)
511 os.mkdir(self.TESTDIR)
492 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
512 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
493 sfile.write("pass\n")
513 sfile.write("pass\n")
494 self.oldpath = os.getcwd()
514 self.oldpath = os.getcwd()
495 os.chdir(self.TESTDIR)
515 os.chdir(self.TESTDIR)
496 self.fname = u"Γ₯Àâtestscript.py"
516 self.fname = u"Γ₯Àâtestscript.py"
497
517
498 def tearDown(self):
518 def tearDown(self):
499 os.chdir(self.oldpath)
519 os.chdir(self.oldpath)
500 shutil.rmtree(self.BASETESTDIR)
520 shutil.rmtree(self.BASETESTDIR)
501
521
502 @onlyif_unicode_paths
522 @onlyif_unicode_paths
503 def test_1(self):
523 def test_1(self):
504 """Test safe_execfile with non-ascii path
524 """Test safe_execfile with non-ascii path
505 """
525 """
506 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
526 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
507
527
508 class ExitCodeChecks(tt.TempFileMixin):
528 class ExitCodeChecks(tt.TempFileMixin):
509 def test_exit_code_ok(self):
529 def test_exit_code_ok(self):
510 self.system('exit 0')
530 self.system('exit 0')
511 self.assertEqual(ip.user_ns['_exit_code'], 0)
531 self.assertEqual(ip.user_ns['_exit_code'], 0)
512
532
513 def test_exit_code_error(self):
533 def test_exit_code_error(self):
514 self.system('exit 1')
534 self.system('exit 1')
515 self.assertEqual(ip.user_ns['_exit_code'], 1)
535 self.assertEqual(ip.user_ns['_exit_code'], 1)
516
536
517 @skipif(not hasattr(signal, 'SIGALRM'))
537 @skipif(not hasattr(signal, 'SIGALRM'))
518 def test_exit_code_signal(self):
538 def test_exit_code_signal(self):
519 self.mktmp("import signal, time\n"
539 self.mktmp("import signal, time\n"
520 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
540 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
521 "time.sleep(1)\n")
541 "time.sleep(1)\n")
522 self.system("%s %s" % (sys.executable, self.fname))
542 self.system("%s %s" % (sys.executable, self.fname))
523 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
543 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
524
544
525 @onlyif_cmds_exist("csh")
545 @onlyif_cmds_exist("csh")
526 def test_exit_code_signal_csh(self):
546 def test_exit_code_signal_csh(self):
527 SHELL = os.environ.get('SHELL', None)
547 SHELL = os.environ.get('SHELL', None)
528 os.environ['SHELL'] = find_cmd("csh")
548 os.environ['SHELL'] = find_cmd("csh")
529 try:
549 try:
530 self.test_exit_code_signal()
550 self.test_exit_code_signal()
531 finally:
551 finally:
532 if SHELL is not None:
552 if SHELL is not None:
533 os.environ['SHELL'] = SHELL
553 os.environ['SHELL'] = SHELL
534 else:
554 else:
535 del os.environ['SHELL']
555 del os.environ['SHELL']
536
556
537 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
557 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
538 system = ip.system_raw
558 system = ip.system_raw
539
559
540 @onlyif_unicode_paths
560 @onlyif_unicode_paths
541 def test_1(self):
561 def test_1(self):
542 """Test system_raw with non-ascii cmd
562 """Test system_raw with non-ascii cmd
543 """
563 """
544 cmd = u'''python -c "'Γ₯Àâ'" '''
564 cmd = u'''python -c "'Γ₯Àâ'" '''
545 ip.system_raw(cmd)
565 ip.system_raw(cmd)
546
566
547 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
567 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
548 @mock.patch('os.system', side_effect=KeyboardInterrupt)
568 @mock.patch('os.system', side_effect=KeyboardInterrupt)
549 def test_control_c(self, *mocks):
569 def test_control_c(self, *mocks):
550 try:
570 try:
551 self.system("sleep 1 # wont happen")
571 self.system("sleep 1 # wont happen")
552 except KeyboardInterrupt:
572 except KeyboardInterrupt:
553 self.fail("system call should intercept "
573 self.fail("system call should intercept "
554 "keyboard interrupt from subprocess.call")
574 "keyboard interrupt from subprocess.call")
555 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
575 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
556
576
557 # TODO: Exit codes are currently ignored on Windows.
577 # TODO: Exit codes are currently ignored on Windows.
558 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
578 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
559 system = ip.system_piped
579 system = ip.system_piped
560
580
561 @skip_win32
581 @skip_win32
562 def test_exit_code_ok(self):
582 def test_exit_code_ok(self):
563 ExitCodeChecks.test_exit_code_ok(self)
583 ExitCodeChecks.test_exit_code_ok(self)
564
584
565 @skip_win32
585 @skip_win32
566 def test_exit_code_error(self):
586 def test_exit_code_error(self):
567 ExitCodeChecks.test_exit_code_error(self)
587 ExitCodeChecks.test_exit_code_error(self)
568
588
569 @skip_win32
589 @skip_win32
570 def test_exit_code_signal(self):
590 def test_exit_code_signal(self):
571 ExitCodeChecks.test_exit_code_signal(self)
591 ExitCodeChecks.test_exit_code_signal(self)
572
592
573 class TestModules(unittest.TestCase, tt.TempFileMixin):
593 class TestModules(unittest.TestCase, tt.TempFileMixin):
574 def test_extraneous_loads(self):
594 def test_extraneous_loads(self):
575 """Test we're not loading modules on startup that we shouldn't.
595 """Test we're not loading modules on startup that we shouldn't.
576 """
596 """
577 self.mktmp("import sys\n"
597 self.mktmp("import sys\n"
578 "print('numpy' in sys.modules)\n"
598 "print('numpy' in sys.modules)\n"
579 "print('ipyparallel' in sys.modules)\n"
599 "print('ipyparallel' in sys.modules)\n"
580 "print('ipykernel' in sys.modules)\n"
600 "print('ipykernel' in sys.modules)\n"
581 )
601 )
582 out = "False\nFalse\nFalse\n"
602 out = "False\nFalse\nFalse\n"
583 tt.ipexec_validate(self.fname, out)
603 tt.ipexec_validate(self.fname, out)
584
604
585 class Negator(ast.NodeTransformer):
605 class Negator(ast.NodeTransformer):
586 """Negates all number literals in an AST."""
606 """Negates all number literals in an AST."""
587 def visit_Num(self, node):
607 def visit_Num(self, node):
588 node.n = -node.n
608 node.n = -node.n
589 return node
609 return node
590
610
591 class TestAstTransform(unittest.TestCase):
611 class TestAstTransform(unittest.TestCase):
592 def setUp(self):
612 def setUp(self):
593 self.negator = Negator()
613 self.negator = Negator()
594 ip.ast_transformers.append(self.negator)
614 ip.ast_transformers.append(self.negator)
595
615
596 def tearDown(self):
616 def tearDown(self):
597 ip.ast_transformers.remove(self.negator)
617 ip.ast_transformers.remove(self.negator)
598
618
599 def test_run_cell(self):
619 def test_run_cell(self):
600 with tt.AssertPrints('-34'):
620 with tt.AssertPrints('-34'):
601 ip.run_cell('print (12 + 22)')
621 ip.run_cell('print (12 + 22)')
602
622
603 # A named reference to a number shouldn't be transformed.
623 # A named reference to a number shouldn't be transformed.
604 ip.user_ns['n'] = 55
624 ip.user_ns['n'] = 55
605 with tt.AssertNotPrints('-55'):
625 with tt.AssertNotPrints('-55'):
606 ip.run_cell('print (n)')
626 ip.run_cell('print (n)')
607
627
608 def test_timeit(self):
628 def test_timeit(self):
609 called = set()
629 called = set()
610 def f(x):
630 def f(x):
611 called.add(x)
631 called.add(x)
612 ip.push({'f':f})
632 ip.push({'f':f})
613
633
614 with tt.AssertPrints("std. dev. of"):
634 with tt.AssertPrints("std. dev. of"):
615 ip.run_line_magic("timeit", "-n1 f(1)")
635 ip.run_line_magic("timeit", "-n1 f(1)")
616 self.assertEqual(called, {-1})
636 self.assertEqual(called, {-1})
617 called.clear()
637 called.clear()
618
638
619 with tt.AssertPrints("std. dev. of"):
639 with tt.AssertPrints("std. dev. of"):
620 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
640 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
621 self.assertEqual(called, {-2, -3})
641 self.assertEqual(called, {-2, -3})
622
642
623 def test_time(self):
643 def test_time(self):
624 called = []
644 called = []
625 def f(x):
645 def f(x):
626 called.append(x)
646 called.append(x)
627 ip.push({'f':f})
647 ip.push({'f':f})
628
648
629 # Test with an expression
649 # Test with an expression
630 with tt.AssertPrints("Wall time: "):
650 with tt.AssertPrints("Wall time: "):
631 ip.run_line_magic("time", "f(5+9)")
651 ip.run_line_magic("time", "f(5+9)")
632 self.assertEqual(called, [-14])
652 self.assertEqual(called, [-14])
633 called[:] = []
653 called[:] = []
634
654
635 # Test with a statement (different code path)
655 # Test with a statement (different code path)
636 with tt.AssertPrints("Wall time: "):
656 with tt.AssertPrints("Wall time: "):
637 ip.run_line_magic("time", "a = f(-3 + -2)")
657 ip.run_line_magic("time", "a = f(-3 + -2)")
638 self.assertEqual(called, [5])
658 self.assertEqual(called, [5])
639
659
640 def test_macro(self):
660 def test_macro(self):
641 ip.push({'a':10})
661 ip.push({'a':10})
642 # The AST transformation makes this do a+=-1
662 # The AST transformation makes this do a+=-1
643 ip.define_macro("amacro", "a+=1\nprint(a)")
663 ip.define_macro("amacro", "a+=1\nprint(a)")
644
664
645 with tt.AssertPrints("9"):
665 with tt.AssertPrints("9"):
646 ip.run_cell("amacro")
666 ip.run_cell("amacro")
647 with tt.AssertPrints("8"):
667 with tt.AssertPrints("8"):
648 ip.run_cell("amacro")
668 ip.run_cell("amacro")
649
669
650 class IntegerWrapper(ast.NodeTransformer):
670 class IntegerWrapper(ast.NodeTransformer):
651 """Wraps all integers in a call to Integer()"""
671 """Wraps all integers in a call to Integer()"""
652 def visit_Num(self, node):
672 def visit_Num(self, node):
653 if isinstance(node.n, int):
673 if isinstance(node.n, int):
654 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
674 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
655 args=[node], keywords=[])
675 args=[node], keywords=[])
656 return node
676 return node
657
677
658 class TestAstTransform2(unittest.TestCase):
678 class TestAstTransform2(unittest.TestCase):
659 def setUp(self):
679 def setUp(self):
660 self.intwrapper = IntegerWrapper()
680 self.intwrapper = IntegerWrapper()
661 ip.ast_transformers.append(self.intwrapper)
681 ip.ast_transformers.append(self.intwrapper)
662
682
663 self.calls = []
683 self.calls = []
664 def Integer(*args):
684 def Integer(*args):
665 self.calls.append(args)
685 self.calls.append(args)
666 return args
686 return args
667 ip.push({"Integer": Integer})
687 ip.push({"Integer": Integer})
668
688
669 def tearDown(self):
689 def tearDown(self):
670 ip.ast_transformers.remove(self.intwrapper)
690 ip.ast_transformers.remove(self.intwrapper)
671 del ip.user_ns['Integer']
691 del ip.user_ns['Integer']
672
692
673 def test_run_cell(self):
693 def test_run_cell(self):
674 ip.run_cell("n = 2")
694 ip.run_cell("n = 2")
675 self.assertEqual(self.calls, [(2,)])
695 self.assertEqual(self.calls, [(2,)])
676
696
677 # This shouldn't throw an error
697 # This shouldn't throw an error
678 ip.run_cell("o = 2.0")
698 ip.run_cell("o = 2.0")
679 self.assertEqual(ip.user_ns['o'], 2.0)
699 self.assertEqual(ip.user_ns['o'], 2.0)
680
700
681 def test_timeit(self):
701 def test_timeit(self):
682 called = set()
702 called = set()
683 def f(x):
703 def f(x):
684 called.add(x)
704 called.add(x)
685 ip.push({'f':f})
705 ip.push({'f':f})
686
706
687 with tt.AssertPrints("std. dev. of"):
707 with tt.AssertPrints("std. dev. of"):
688 ip.run_line_magic("timeit", "-n1 f(1)")
708 ip.run_line_magic("timeit", "-n1 f(1)")
689 self.assertEqual(called, {(1,)})
709 self.assertEqual(called, {(1,)})
690 called.clear()
710 called.clear()
691
711
692 with tt.AssertPrints("std. dev. of"):
712 with tt.AssertPrints("std. dev. of"):
693 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
713 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
694 self.assertEqual(called, {(2,), (3,)})
714 self.assertEqual(called, {(2,), (3,)})
695
715
696 class ErrorTransformer(ast.NodeTransformer):
716 class ErrorTransformer(ast.NodeTransformer):
697 """Throws an error when it sees a number."""
717 """Throws an error when it sees a number."""
698 def visit_Num(self, node):
718 def visit_Num(self, node):
699 raise ValueError("test")
719 raise ValueError("test")
700
720
701 class TestAstTransformError(unittest.TestCase):
721 class TestAstTransformError(unittest.TestCase):
702 def test_unregistering(self):
722 def test_unregistering(self):
703 err_transformer = ErrorTransformer()
723 err_transformer = ErrorTransformer()
704 ip.ast_transformers.append(err_transformer)
724 ip.ast_transformers.append(err_transformer)
705
725
706 with tt.AssertPrints("unregister", channel='stderr'):
726 with tt.AssertPrints("unregister", channel='stderr'):
707 ip.run_cell("1 + 2")
727 ip.run_cell("1 + 2")
708
728
709 # This should have been removed.
729 # This should have been removed.
710 nt.assert_not_in(err_transformer, ip.ast_transformers)
730 nt.assert_not_in(err_transformer, ip.ast_transformers)
711
731
712
732
713 class StringRejector(ast.NodeTransformer):
733 class StringRejector(ast.NodeTransformer):
714 """Throws an InputRejected when it sees a string literal.
734 """Throws an InputRejected when it sees a string literal.
715
735
716 Used to verify that NodeTransformers can signal that a piece of code should
736 Used to verify that NodeTransformers can signal that a piece of code should
717 not be executed by throwing an InputRejected.
737 not be executed by throwing an InputRejected.
718 """
738 """
719
739
720 def visit_Str(self, node):
740 def visit_Str(self, node):
721 raise InputRejected("test")
741 raise InputRejected("test")
722
742
723
743
724 class TestAstTransformInputRejection(unittest.TestCase):
744 class TestAstTransformInputRejection(unittest.TestCase):
725
745
726 def setUp(self):
746 def setUp(self):
727 self.transformer = StringRejector()
747 self.transformer = StringRejector()
728 ip.ast_transformers.append(self.transformer)
748 ip.ast_transformers.append(self.transformer)
729
749
730 def tearDown(self):
750 def tearDown(self):
731 ip.ast_transformers.remove(self.transformer)
751 ip.ast_transformers.remove(self.transformer)
732
752
733 def test_input_rejection(self):
753 def test_input_rejection(self):
734 """Check that NodeTransformers can reject input."""
754 """Check that NodeTransformers can reject input."""
735
755
736 expect_exception_tb = tt.AssertPrints("InputRejected: test")
756 expect_exception_tb = tt.AssertPrints("InputRejected: test")
737 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
757 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
738
758
739 # Run the same check twice to verify that the transformer is not
759 # Run the same check twice to verify that the transformer is not
740 # disabled after raising.
760 # disabled after raising.
741 with expect_exception_tb, expect_no_cell_output:
761 with expect_exception_tb, expect_no_cell_output:
742 ip.run_cell("'unsafe'")
762 ip.run_cell("'unsafe'")
743
763
744 with expect_exception_tb, expect_no_cell_output:
764 with expect_exception_tb, expect_no_cell_output:
745 res = ip.run_cell("'unsafe'")
765 res = ip.run_cell("'unsafe'")
746
766
747 self.assertIsInstance(res.error_before_exec, InputRejected)
767 self.assertIsInstance(res.error_before_exec, InputRejected)
748
768
749 def test__IPYTHON__():
769 def test__IPYTHON__():
750 # This shouldn't raise a NameError, that's all
770 # This shouldn't raise a NameError, that's all
751 __IPYTHON__
771 __IPYTHON__
752
772
753
773
754 class DummyRepr(object):
774 class DummyRepr(object):
755 def __repr__(self):
775 def __repr__(self):
756 return "DummyRepr"
776 return "DummyRepr"
757
777
758 def _repr_html_(self):
778 def _repr_html_(self):
759 return "<b>dummy</b>"
779 return "<b>dummy</b>"
760
780
761 def _repr_javascript_(self):
781 def _repr_javascript_(self):
762 return "console.log('hi');", {'key': 'value'}
782 return "console.log('hi');", {'key': 'value'}
763
783
764
784
765 def test_user_variables():
785 def test_user_variables():
766 # enable all formatters
786 # enable all formatters
767 ip.display_formatter.active_types = ip.display_formatter.format_types
787 ip.display_formatter.active_types = ip.display_formatter.format_types
768
788
769 ip.user_ns['dummy'] = d = DummyRepr()
789 ip.user_ns['dummy'] = d = DummyRepr()
770 keys = {'dummy', 'doesnotexist'}
790 keys = {'dummy', 'doesnotexist'}
771 r = ip.user_expressions({ key:key for key in keys})
791 r = ip.user_expressions({ key:key for key in keys})
772
792
773 nt.assert_equal(keys, set(r.keys()))
793 nt.assert_equal(keys, set(r.keys()))
774 dummy = r['dummy']
794 dummy = r['dummy']
775 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
795 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
776 nt.assert_equal(dummy['status'], 'ok')
796 nt.assert_equal(dummy['status'], 'ok')
777 data = dummy['data']
797 data = dummy['data']
778 metadata = dummy['metadata']
798 metadata = dummy['metadata']
779 nt.assert_equal(data.get('text/html'), d._repr_html_())
799 nt.assert_equal(data.get('text/html'), d._repr_html_())
780 js, jsmd = d._repr_javascript_()
800 js, jsmd = d._repr_javascript_()
781 nt.assert_equal(data.get('application/javascript'), js)
801 nt.assert_equal(data.get('application/javascript'), js)
782 nt.assert_equal(metadata.get('application/javascript'), jsmd)
802 nt.assert_equal(metadata.get('application/javascript'), jsmd)
783
803
784 dne = r['doesnotexist']
804 dne = r['doesnotexist']
785 nt.assert_equal(dne['status'], 'error')
805 nt.assert_equal(dne['status'], 'error')
786 nt.assert_equal(dne['ename'], 'NameError')
806 nt.assert_equal(dne['ename'], 'NameError')
787
807
788 # back to text only
808 # back to text only
789 ip.display_formatter.active_types = ['text/plain']
809 ip.display_formatter.active_types = ['text/plain']
790
810
791 def test_user_expression():
811 def test_user_expression():
792 # enable all formatters
812 # enable all formatters
793 ip.display_formatter.active_types = ip.display_formatter.format_types
813 ip.display_formatter.active_types = ip.display_formatter.format_types
794 query = {
814 query = {
795 'a' : '1 + 2',
815 'a' : '1 + 2',
796 'b' : '1/0',
816 'b' : '1/0',
797 }
817 }
798 r = ip.user_expressions(query)
818 r = ip.user_expressions(query)
799 import pprint
819 import pprint
800 pprint.pprint(r)
820 pprint.pprint(r)
801 nt.assert_equal(set(r.keys()), set(query.keys()))
821 nt.assert_equal(set(r.keys()), set(query.keys()))
802 a = r['a']
822 a = r['a']
803 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
823 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
804 nt.assert_equal(a['status'], 'ok')
824 nt.assert_equal(a['status'], 'ok')
805 data = a['data']
825 data = a['data']
806 metadata = a['metadata']
826 metadata = a['metadata']
807 nt.assert_equal(data.get('text/plain'), '3')
827 nt.assert_equal(data.get('text/plain'), '3')
808
828
809 b = r['b']
829 b = r['b']
810 nt.assert_equal(b['status'], 'error')
830 nt.assert_equal(b['status'], 'error')
811 nt.assert_equal(b['ename'], 'ZeroDivisionError')
831 nt.assert_equal(b['ename'], 'ZeroDivisionError')
812
832
813 # back to text only
833 # back to text only
814 ip.display_formatter.active_types = ['text/plain']
834 ip.display_formatter.active_types = ['text/plain']
815
835
816
836
817
837
818
838
819
839
820 class TestSyntaxErrorTransformer(unittest.TestCase):
840 class TestSyntaxErrorTransformer(unittest.TestCase):
821 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
841 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
822
842
823 class SyntaxErrorTransformer(InputTransformer):
843 class SyntaxErrorTransformer(InputTransformer):
824
844
825 def push(self, line):
845 def push(self, line):
826 pos = line.find('syntaxerror')
846 pos = line.find('syntaxerror')
827 if pos >= 0:
847 if pos >= 0:
828 e = SyntaxError('input contains "syntaxerror"')
848 e = SyntaxError('input contains "syntaxerror"')
829 e.text = line
849 e.text = line
830 e.offset = pos + 1
850 e.offset = pos + 1
831 raise e
851 raise e
832 return line
852 return line
833
853
834 def reset(self):
854 def reset(self):
835 pass
855 pass
836
856
837 def setUp(self):
857 def setUp(self):
838 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
858 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
839 ip.input_splitter.python_line_transforms.append(self.transformer)
859 ip.input_splitter.python_line_transforms.append(self.transformer)
840 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
860 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
841
861
842 def tearDown(self):
862 def tearDown(self):
843 ip.input_splitter.python_line_transforms.remove(self.transformer)
863 ip.input_splitter.python_line_transforms.remove(self.transformer)
844 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
864 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
845
865
846 def test_syntaxerror_input_transformer(self):
866 def test_syntaxerror_input_transformer(self):
847 with tt.AssertPrints('1234'):
867 with tt.AssertPrints('1234'):
848 ip.run_cell('1234')
868 ip.run_cell('1234')
849 with tt.AssertPrints('SyntaxError: invalid syntax'):
869 with tt.AssertPrints('SyntaxError: invalid syntax'):
850 ip.run_cell('1 2 3') # plain python syntax error
870 ip.run_cell('1 2 3') # plain python syntax error
851 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
871 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
852 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
872 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
853 with tt.AssertPrints('3456'):
873 with tt.AssertPrints('3456'):
854 ip.run_cell('3456')
874 ip.run_cell('3456')
855
875
856
876
857
877
858 def test_warning_suppression():
878 def test_warning_suppression():
859 ip.run_cell("import warnings")
879 ip.run_cell("import warnings")
860 try:
880 try:
861 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
881 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
862 ip.run_cell("warnings.warn('asdf')")
882 ip.run_cell("warnings.warn('asdf')")
863 # Here's the real test -- if we run that again, we should get the
883 # Here's the real test -- if we run that again, we should get the
864 # warning again. Traditionally, each warning was only issued once per
884 # warning again. Traditionally, each warning was only issued once per
865 # IPython session (approximately), even if the user typed in new and
885 # IPython session (approximately), even if the user typed in new and
866 # different code that should have also triggered the warning, leading
886 # different code that should have also triggered the warning, leading
867 # to much confusion.
887 # to much confusion.
868 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
888 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
869 ip.run_cell("warnings.warn('asdf')")
889 ip.run_cell("warnings.warn('asdf')")
870 finally:
890 finally:
871 ip.run_cell("del warnings")
891 ip.run_cell("del warnings")
872
892
873
893
874 def test_deprecation_warning():
894 def test_deprecation_warning():
875 ip.run_cell("""
895 ip.run_cell("""
876 import warnings
896 import warnings
877 def wrn():
897 def wrn():
878 warnings.warn(
898 warnings.warn(
879 "I AM A WARNING",
899 "I AM A WARNING",
880 DeprecationWarning
900 DeprecationWarning
881 )
901 )
882 """)
902 """)
883 try:
903 try:
884 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
904 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
885 ip.run_cell("wrn()")
905 ip.run_cell("wrn()")
886 finally:
906 finally:
887 ip.run_cell("del warnings")
907 ip.run_cell("del warnings")
888 ip.run_cell("del wrn")
908 ip.run_cell("del wrn")
889
909
890
910
891 class TestImportNoDeprecate(tt.TempFileMixin):
911 class TestImportNoDeprecate(tt.TempFileMixin):
892
912
893 def setup(self):
913 def setup(self):
894 """Make a valid python temp file."""
914 """Make a valid python temp file."""
895 self.mktmp("""
915 self.mktmp("""
896 import warnings
916 import warnings
897 def wrn():
917 def wrn():
898 warnings.warn(
918 warnings.warn(
899 "I AM A WARNING",
919 "I AM A WARNING",
900 DeprecationWarning
920 DeprecationWarning
901 )
921 )
902 """)
922 """)
903
923
904 def test_no_dep(self):
924 def test_no_dep(self):
905 """
925 """
906 No deprecation warning should be raised from imported functions
926 No deprecation warning should be raised from imported functions
907 """
927 """
908 ip.run_cell("from {} import wrn".format(self.fname))
928 ip.run_cell("from {} import wrn".format(self.fname))
909
929
910 with tt.AssertNotPrints("I AM A WARNING"):
930 with tt.AssertNotPrints("I AM A WARNING"):
911 ip.run_cell("wrn()")
931 ip.run_cell("wrn()")
912 ip.run_cell("del wrn")
932 ip.run_cell("del wrn")
@@ -1,87 +1,110 b''
1 .. _events:
1 .. _events:
2 .. _callbacks:
2 .. _callbacks:
3
3
4 ==============
4 ==============
5 IPython Events
5 IPython Events
6 ==============
6 ==============
7
7
8 Extension code can register callbacks functions which will be called on specific
8 Extension code can register callbacks functions which will be called on specific
9 events within the IPython code. You can see the current list of available
9 events within the IPython code. You can see the current list of available
10 callbacks, and the parameters that will be passed with each, in the callback
10 callbacks, and the parameters that will be passed with each, in the callback
11 prototype functions defined in :mod:`IPython.core.callbacks`.
11 prototype functions defined in :mod:`IPython.core.callbacks`.
12
12
13 To register callbacks, use :meth:`IPython.core.events.EventManager.register`.
13 To register callbacks, use :meth:`IPython.core.events.EventManager.register`.
14 For example::
14 For example::
15
15
16 class VarWatcher(object):
16 class VarWatcher(object):
17 def __init__(self, ip):
17 def __init__(self, ip):
18 self.shell = ip
18 self.shell = ip
19 self.last_x = None
19 self.last_x = None
20
20
21 def pre_execute(self):
21 def pre_execute(self):
22 self.last_x = self.shell.user_ns.get('x', None)
22 self.last_x = self.shell.user_ns.get('x', None)
23
23
24 def post_execute(self):
24 def post_execute(self):
25 if self.shell.user_ns.get('x', None) != self.last_x:
25 if self.shell.user_ns.get('x', None) != self.last_x:
26 print("x changed!")
26 print("x changed!")
27
28 def finally_execute(self, result):
29 if result.error_before_exec:
30 print('Error before execution: %s' % result.error_before_exec)
31 else:
32 print('Execution result: %s', result.result)
27
33
28 def load_ipython_extension(ip):
34 def load_ipython_extension(ip):
29 vw = VarWatcher(ip)
35 vw = VarWatcher(ip)
30 ip.events.register('pre_execute', vw.pre_execute)
36 ip.events.register('pre_execute', vw.pre_execute)
31 ip.events.register('post_execute', vw.post_execute)
37 ip.events.register('post_execute', vw.post_execute)
38 ip.events.register('finally_execute', vw.finally_execute)
32
39
33
40
34 Events
41 Events
35 ======
42 ======
36
43
37 These are the events IPython will emit. Callbacks will be passed no arguments, unless otherwise specified.
44 These are the events IPython will emit. Callbacks will be passed no arguments, unless otherwise specified.
38
45
39 shell_initialized
46 shell_initialized
40 -----------------
47 -----------------
41
48
42 .. code-block:: python
49 .. code-block:: python
43
50
44 def shell_initialized(ipython):
51 def shell_initialized(ipython):
45 ...
52 ...
46
53
47 This event is triggered only once, at the end of setting up IPython.
54 This event is triggered only once, at the end of setting up IPython.
48 Extensions registered to load by default as part of configuration can use this to execute code to finalize setup.
55 Extensions registered to load by default as part of configuration can use this to execute code to finalize setup.
49 Callbacks will be passed the InteractiveShell instance.
56 Callbacks will be passed the InteractiveShell instance.
50
57
51 pre_run_cell
58 pre_run_cell
52 ------------
59 ------------
53
60
54 ``pre_run_cell`` fires prior to interactive execution (e.g. a cell in a notebook).
61 ``pre_run_cell`` fires prior to interactive execution (e.g. a cell in a notebook).
55 It can be used to note the state prior to execution, and keep track of changes.
62 It can be used to note the state prior to execution, and keep track of changes.
56
63
57 pre_execute
64 pre_execute
58 -----------
65 -----------
59
66
60 ``pre_execute`` is like ``pre_run_cell``, but is triggered prior to *any* execution.
67 ``pre_execute`` is like ``pre_run_cell``, but is triggered prior to *any* execution.
61 Sometimes code can be executed by libraries, etc. which
68 Sometimes code can be executed by libraries, etc. which
62 skipping the history/display mechanisms, in which cases ``pre_run_cell`` will not fire.
69 skipping the history/display mechanisms, in which cases ``pre_run_cell`` will not fire.
63
70
64 post_run_cell
71 post_run_cell
65 -------------
72 -------------
66
73
67 ``post_run_cell`` runs after interactive execution (e.g. a cell in a notebook).
74 ``post_run_cell`` runs after successful interactive execution (e.g. a cell in a
68 It can be used to cleanup or notify or perform operations on any side effects produced during execution.
75 notebook, but, for example, not when a ``SyntaxError`` was raised).
69 For instance, the inline matplotlib backend uses this event to display any figures created but not explicitly displayed during the course of the cell.
76 It can be used to cleanup or notify or perform operations on any side effects
70
77 produced during execution.
78 For instance, the inline matplotlib backend uses this event to display any
79 figures created but not explicitly displayed during the course of the cell.
71
80
72 post_execute
81 post_execute
73 ------------
82 ------------
74
83
75 The same as ``pre_execute``, ``post_execute`` is like ``post_run_cell``,
84 The same as ``pre_execute``, ``post_execute`` is like ``post_run_cell``,
76 but fires for *all* executions, not just interactive ones.
85 but fires for *all* successful executions, not just interactive ones.
86
87 finally_run_cell
88 -------------
89
90 ``finally_run_cell`` is like ``post_run_cell``, but fires after *all* executions
91 (even when, for example, a ``SyntaxError`` was raised).
92 Additionally, the execution result is provided as an argument.
93
94 finally_execute
95 ------------
96
97 ``finally_execute`` is like ``post_execute``, but fires after *all* executions
98 (even when, for example, a ``SyntaxError`` was raised).
99 Additionally, the execution result is provided as an argument.
77
100
78
101
79 .. seealso::
102 .. seealso::
80
103
81 Module :mod:`IPython.core.hooks`
104 Module :mod:`IPython.core.hooks`
82 The older 'hooks' system allows end users to customise some parts of
105 The older 'hooks' system allows end users to customise some parts of
83 IPython's behaviour.
106 IPython's behaviour.
84
107
85 :doc:`inputtransforms`
108 :doc:`inputtransforms`
86 By registering input transformers that don't change code, you can monitor
109 By registering input transformers that don't change code, you can monitor
87 what is being executed.
110 what is being executed.
@@ -1,64 +1,67 b''
1 .. _execution_semantics:
1 .. _execution_semantics:
2
2
3 Execution semantics in the IPython kernel
3 Execution semantics in the IPython kernel
4 =========================================
4 =========================================
5
5
6 The execution of user code consists of the following phases:
6 The execution of user code consists of the following phases:
7
7
8 1. Fire the ``pre_execute`` event.
8 1. Fire the ``pre_execute`` event.
9 2. Fire the ``pre_run_cell`` event unless silent is True.
9 2. Fire the ``pre_run_cell`` event unless silent is ``True``.
10 3. Execute the ``code`` field, see below for details.
10 3. Execute the ``code`` field, see below for details.
11 4. If execution succeeds, expressions in ``user_expressions`` are computed.
11 4. If execution succeeds, expressions in ``user_expressions`` are computed.
12 This ensures that any error in the expressions don't affect the main code execution.
12 This ensures that any error in the expressions don't affect the main code execution.
13 5. Fire the post_execute event.
13 5. Fire the ``post_execute`` event unless the execution failed.
14 6. Fire the ``post_run_cell`` event unless the execution failed or silent is ``True``.
15 7. Fire the ``finally_execute`` event.
16 8. Fire the ``finally_run_cell`` event unless silent is ``True``.
14
17
15 .. seealso::
18 .. seealso::
16
19
17 :doc:`/config/callbacks`
20 :doc:`/config/callbacks`
18
21
19
22
20 To understand how the ``code`` field is executed, one must know that Python
23 To understand how the ``code`` field is executed, one must know that Python
21 code can be compiled in one of three modes (controlled by the ``mode`` argument
24 code can be compiled in one of three modes (controlled by the ``mode`` argument
22 to the :func:`compile` builtin):
25 to the :func:`compile` builtin):
23
26
24 *single*
27 *single*
25 Valid for a single interactive statement (though the source can contain
28 Valid for a single interactive statement (though the source can contain
26 multiple lines, such as a for loop). When compiled in this mode, the
29 multiple lines, such as a for loop). When compiled in this mode, the
27 generated bytecode contains special instructions that trigger the calling of
30 generated bytecode contains special instructions that trigger the calling of
28 :func:`sys.displayhook` for any expression in the block that returns a value.
31 :func:`sys.displayhook` for any expression in the block that returns a value.
29 This means that a single statement can actually produce multiple calls to
32 This means that a single statement can actually produce multiple calls to
30 :func:`sys.displayhook`, if for example it contains a loop where each
33 :func:`sys.displayhook`, if for example it contains a loop where each
31 iteration computes an unassigned expression would generate 10 calls::
34 iteration computes an unassigned expression would generate 10 calls::
32
35
33 for i in range(10):
36 for i in range(10):
34 i**2
37 i**2
35
38
36 *exec*
39 *exec*
37 An arbitrary amount of source code, this is how modules are compiled.
40 An arbitrary amount of source code, this is how modules are compiled.
38 :func:`sys.displayhook` is *never* implicitly called.
41 :func:`sys.displayhook` is *never* implicitly called.
39
42
40 *eval*
43 *eval*
41 A single expression that returns a value. :func:`sys.displayhook` is *never*
44 A single expression that returns a value. :func:`sys.displayhook` is *never*
42 implicitly called.
45 implicitly called.
43
46
44
47
45 The ``code`` field is split into individual blocks each of which is valid for
48 The ``code`` field is split into individual blocks each of which is valid for
46 execution in 'single' mode, and then:
49 execution in 'single' mode, and then:
47
50
48 - If there is only a single block: it is executed in 'single' mode.
51 - If there is only a single block: it is executed in 'single' mode.
49
52
50 - If there is more than one block:
53 - If there is more than one block:
51
54
52 * if the last one is a single line long, run all but the last in 'exec' mode
55 * if the last one is a single line long, run all but the last in 'exec' mode
53 and the very last one in 'single' mode. This makes it easy to type simple
56 and the very last one in 'single' mode. This makes it easy to type simple
54 expressions at the end to see computed values.
57 expressions at the end to see computed values.
55
58
56 * if the last one is no more than two lines long, run all but the last in
59 * if the last one is no more than two lines long, run all but the last in
57 'exec' mode and the very last one in 'single' mode. This makes it easy to
60 'exec' mode and the very last one in 'single' mode. This makes it easy to
58 type simple expressions at the end to see computed values. - otherwise
61 type simple expressions at the end to see computed values. - otherwise
59 (last one is also multiline), run all in 'exec' mode
62 (last one is also multiline), run all in 'exec' mode
60
63
61 * otherwise (last one is also multiline), run all in 'exec' mode as a single
64 * otherwise (last one is also multiline), run all in 'exec' mode as a single
62 unit.
65 unit.
63
66
64
67
General Comments 0
You need to be logged in to leave comments. Login now