##// END OF EJS Templates
Remove EventManager reset methods, because they violate encapsulation....
Nathaniel J. Smith -
Show More
@@ -1,139 +1,131 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 from __future__ import print_function
15 from __future__ import print_function
16
16
17 class EventManager(object):
17 class EventManager(object):
18 """Manage a collection of events and a sequence of callbacks for each.
18 """Manage a collection of events and a sequence of callbacks for each.
19
19
20 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
20 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
21 instances as an ``events`` attribute.
21 instances as an ``events`` attribute.
22
22
23 .. note::
23 .. note::
24
24
25 This API is experimental in IPython 2.0, and may be revised in future versions.
25 This API is experimental in IPython 2.0, and may be revised in future versions.
26 """
26 """
27 def __init__(self, shell, available_events):
27 def __init__(self, shell, available_events):
28 """Initialise the :class:`CallbackManager`.
28 """Initialise the :class:`CallbackManager`.
29
29
30 Parameters
30 Parameters
31 ----------
31 ----------
32 shell
32 shell
33 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
33 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
34 available_callbacks
34 available_callbacks
35 An iterable of names for callback events.
35 An iterable of names for callback events.
36 """
36 """
37 self.shell = shell
37 self.shell = shell
38 self.callbacks = {n:[] for n in available_events}
38 self.callbacks = {n:[] for n in available_events}
39
39
40 def register(self, event, function):
40 def register(self, event, function):
41 """Register a new event callback
41 """Register a new event callback
42
42
43 Parameters
43 Parameters
44 ----------
44 ----------
45 event : str
45 event : str
46 The event for which to register this callback.
46 The event for which to register this callback.
47 function : callable
47 function : callable
48 A function to be called on the given event. It should take the same
48 A function to be called on the given event. It should take the same
49 parameters as the appropriate callback prototype.
49 parameters as the appropriate callback prototype.
50
50
51 Raises
51 Raises
52 ------
52 ------
53 TypeError
53 TypeError
54 If ``function`` is not callable.
54 If ``function`` is not callable.
55 KeyError
55 KeyError
56 If ``event`` is not one of the known events.
56 If ``event`` is not one of the known events.
57 """
57 """
58 if not callable(function):
58 if not callable(function):
59 raise TypeError('Need a callable, got %r' % function)
59 raise TypeError('Need a callable, got %r' % function)
60 self.callbacks[event].append(function)
60 self.callbacks[event].append(function)
61
61
62 def unregister(self, event, function):
62 def unregister(self, event, function):
63 """Remove a callback from the given event."""
63 """Remove a callback from the given event."""
64 self.callbacks[event].remove(function)
64 self.callbacks[event].remove(function)
65
65
66 def reset(self, event):
67 """Clear all callbacks for the given event."""
68 self.callbacks[event] = []
69
70 def reset_all(self):
71 """Clear all callbacks for all events."""
72 self.callbacks = {n:[] for n in self.callbacks}
73
74 def trigger(self, event, *args, **kwargs):
66 def trigger(self, event, *args, **kwargs):
75 """Call callbacks for ``event``.
67 """Call callbacks for ``event``.
76
68
77 Any additional arguments are passed to all callbacks registered for this
69 Any additional arguments are passed to all callbacks registered for this
78 event. Exceptions raised by callbacks are caught, and a message printed.
70 event. Exceptions raised by callbacks are caught, and a message printed.
79 """
71 """
80 for func in self.callbacks[event]:
72 for func in self.callbacks[event]:
81 try:
73 try:
82 func(*args, **kwargs)
74 func(*args, **kwargs)
83 except Exception:
75 except Exception:
84 print("Error in callback {} (for {}):".format(func, event))
76 print("Error in callback {} (for {}):".format(func, event))
85 self.shell.showtraceback()
77 self.shell.showtraceback()
86
78
87 # event_name -> prototype mapping
79 # event_name -> prototype mapping
88 available_events = {}
80 available_events = {}
89
81
90 def _define_event(callback_proto):
82 def _define_event(callback_proto):
91 available_events[callback_proto.__name__] = callback_proto
83 available_events[callback_proto.__name__] = callback_proto
92 return callback_proto
84 return callback_proto
93
85
94 # ------------------------------------------------------------------------------
86 # ------------------------------------------------------------------------------
95 # Callback prototypes
87 # Callback prototypes
96 #
88 #
97 # No-op functions which describe the names of available events and the
89 # No-op functions which describe the names of available events and the
98 # signatures of callbacks for those events.
90 # signatures of callbacks for those events.
99 # ------------------------------------------------------------------------------
91 # ------------------------------------------------------------------------------
100
92
101 @_define_event
93 @_define_event
102 def pre_execute():
94 def pre_execute():
103 """Fires before code is executed in response to user/frontend action.
95 """Fires before code is executed in response to user/frontend action.
104
96
105 This includes comm and widget messages and silent execution, as well as user
97 This includes comm and widget messages and silent execution, as well as user
106 code cells."""
98 code cells."""
107 pass
99 pass
108
100
109 @_define_event
101 @_define_event
110 def pre_run_cell():
102 def pre_run_cell():
111 """Fires before user-entered code runs."""
103 """Fires before user-entered code runs."""
112 pass
104 pass
113
105
114 @_define_event
106 @_define_event
115 def post_execute():
107 def post_execute():
116 """Fires after code is executed in response to user/frontend action.
108 """Fires after code is executed in response to user/frontend action.
117
109
118 This includes comm and widget messages and silent execution, as well as user
110 This includes comm and widget messages and silent execution, as well as user
119 code cells."""
111 code cells."""
120 pass
112 pass
121
113
122 @_define_event
114 @_define_event
123 def post_run_cell():
115 def post_run_cell():
124 """Fires after user-entered code runs."""
116 """Fires after user-entered code runs."""
125 pass
117 pass
126
118
127 @_define_event
119 @_define_event
128 def shell_initialized(ip):
120 def shell_initialized(ip):
129 """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
121 """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
130
122
131 This is before extensions and startup scripts are loaded, so it can only be
123 This is before extensions and startup scripts are loaded, so it can only be
132 set by subclassing.
124 set by subclassing.
133
125
134 Parameters
126 Parameters
135 ----------
127 ----------
136 ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
128 ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
137 The newly initialised shell.
129 The newly initialised shell.
138 """
130 """
139 pass
131 pass
@@ -1,46 +1,32 b''
1 import unittest
1 import unittest
2 try: # Python 3.3 +
2 try: # Python 3.3 +
3 from unittest.mock import Mock
3 from unittest.mock import Mock
4 except ImportError:
4 except ImportError:
5 from mock import Mock
5 from mock import Mock
6
6
7 from IPython.core import events
7 from IPython.core import events
8 import IPython.testing.tools as tt
8 import IPython.testing.tools as tt
9
9
10 def ping_received():
10 def ping_received():
11 pass
11 pass
12
12
13 class CallbackTests(unittest.TestCase):
13 class CallbackTests(unittest.TestCase):
14 def setUp(self):
14 def setUp(self):
15 self.em = events.EventManager(get_ipython(), {'ping_received': ping_received})
15 self.em = events.EventManager(get_ipython(), {'ping_received': ping_received})
16
16
17 def test_register_unregister(self):
17 def test_register_unregister(self):
18 cb = Mock()
18 cb = Mock()
19
19
20 self.em.register('ping_received', cb)
20 self.em.register('ping_received', cb)
21 self.em.trigger('ping_received')
21 self.em.trigger('ping_received')
22 self.assertEqual(cb.call_count, 1)
22 self.assertEqual(cb.call_count, 1)
23
23
24 self.em.unregister('ping_received', cb)
24 self.em.unregister('ping_received', cb)
25 self.em.trigger('ping_received')
25 self.em.trigger('ping_received')
26 self.assertEqual(cb.call_count, 1)
26 self.assertEqual(cb.call_count, 1)
27
27
28 def test_reset(self):
29 cb = Mock()
30 self.em.register('ping_received', cb)
31 self.em.reset('ping_received')
32 self.em.trigger('ping_received')
33 assert not cb.called
34
35 def test_reset_all(self):
36 cb = Mock()
37 self.em.register('ping_received', cb)
38 self.em.reset_all()
39 self.em.trigger('ping_received')
40 assert not cb.called
41
42 def test_cb_error(self):
28 def test_cb_error(self):
43 cb = Mock(side_effect=ValueError)
29 cb = Mock(side_effect=ValueError)
44 self.em.register('ping_received', cb)
30 self.em.register('ping_received', cb)
45 with tt.AssertPrints("Error in callback"):
31 with tt.AssertPrints("Error in callback"):
46 self.em.trigger('ping_received') No newline at end of file
32 self.em.trigger('ping_received')
@@ -1,842 +1,845 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 try:
19 try:
20 from unittest import mock
20 from unittest import mock
21 except ImportError:
21 except ImportError:
22 import mock
22 import mock
23 from os.path import join
23 from os.path import join
24
24
25 import nose.tools as nt
25 import nose.tools as nt
26
26
27 from IPython.core.error import InputRejected
27 from IPython.core.error import InputRejected
28 from IPython.core.inputtransformer import InputTransformer
28 from IPython.core.inputtransformer import InputTransformer
29 from IPython.testing.decorators import (
29 from IPython.testing.decorators import (
30 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
30 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
31 )
31 )
32 from IPython.testing import tools as tt
32 from IPython.testing import tools as tt
33 from IPython.utils import io
33 from IPython.utils import io
34 from IPython.utils.process import find_cmd
34 from IPython.utils.process import find_cmd
35 from IPython.utils import py3compat
35 from IPython.utils import py3compat
36 from IPython.utils.py3compat import unicode_type, PY3
36 from IPython.utils.py3compat import unicode_type, PY3
37
37
38 if PY3:
38 if PY3:
39 from io import StringIO
39 from io import StringIO
40 else:
40 else:
41 from StringIO import StringIO
41 from StringIO import StringIO
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Globals
44 # Globals
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # This is used by every single test, no point repeating it ad nauseam
46 # This is used by every single test, no point repeating it ad nauseam
47 ip = get_ipython()
47 ip = get_ipython()
48
48
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50 # Tests
50 # Tests
51 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
52
52
53 class InteractiveShellTestCase(unittest.TestCase):
53 class InteractiveShellTestCase(unittest.TestCase):
54 def test_naked_string_cells(self):
54 def test_naked_string_cells(self):
55 """Test that cells with only naked strings are fully executed"""
55 """Test that cells with only naked strings are fully executed"""
56 # First, single-line inputs
56 # First, single-line inputs
57 ip.run_cell('"a"\n')
57 ip.run_cell('"a"\n')
58 self.assertEqual(ip.user_ns['_'], 'a')
58 self.assertEqual(ip.user_ns['_'], 'a')
59 # And also multi-line cells
59 # And also multi-line cells
60 ip.run_cell('"""a\nb"""\n')
60 ip.run_cell('"""a\nb"""\n')
61 self.assertEqual(ip.user_ns['_'], 'a\nb')
61 self.assertEqual(ip.user_ns['_'], 'a\nb')
62
62
63 def test_run_empty_cell(self):
63 def test_run_empty_cell(self):
64 """Just make sure we don't get a horrible error with a blank
64 """Just make sure we don't get a horrible error with a blank
65 cell of input. Yes, I did overlook that."""
65 cell of input. Yes, I did overlook that."""
66 old_xc = ip.execution_count
66 old_xc = ip.execution_count
67 ip.run_cell('')
67 ip.run_cell('')
68 self.assertEqual(ip.execution_count, old_xc)
68 self.assertEqual(ip.execution_count, old_xc)
69
69
70 def test_run_cell_multiline(self):
70 def test_run_cell_multiline(self):
71 """Multi-block, multi-line cells must execute correctly.
71 """Multi-block, multi-line cells must execute correctly.
72 """
72 """
73 src = '\n'.join(["x=1",
73 src = '\n'.join(["x=1",
74 "y=2",
74 "y=2",
75 "if 1:",
75 "if 1:",
76 " x += 1",
76 " x += 1",
77 " y += 1",])
77 " y += 1",])
78 ip.run_cell(src)
78 ip.run_cell(src)
79 self.assertEqual(ip.user_ns['x'], 2)
79 self.assertEqual(ip.user_ns['x'], 2)
80 self.assertEqual(ip.user_ns['y'], 3)
80 self.assertEqual(ip.user_ns['y'], 3)
81
81
82 def test_multiline_string_cells(self):
82 def test_multiline_string_cells(self):
83 "Code sprinkled with multiline strings should execute (GH-306)"
83 "Code sprinkled with multiline strings should execute (GH-306)"
84 ip.run_cell('tmp=0')
84 ip.run_cell('tmp=0')
85 self.assertEqual(ip.user_ns['tmp'], 0)
85 self.assertEqual(ip.user_ns['tmp'], 0)
86 ip.run_cell('tmp=1;"""a\nb"""\n')
86 ip.run_cell('tmp=1;"""a\nb"""\n')
87 self.assertEqual(ip.user_ns['tmp'], 1)
87 self.assertEqual(ip.user_ns['tmp'], 1)
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 ip.run_cell(cell, store_history=True)
93 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 i = 0
96 i = 0
97 #also test the default caching behavior
97 #also test the default caching behavior
98 for cell in ['1', '1;1']:
98 for cell in ['1', '1;1']:
99 ip.run_cell(cell, store_history=True)
99 ip.run_cell(cell, store_history=True)
100 newlen = len(ip.user_ns['Out'])
100 newlen = len(ip.user_ns['Out'])
101 i += 1
101 i += 1
102 self.assertEqual(oldlen+i, newlen)
102 self.assertEqual(oldlen+i, newlen)
103
103
104 def test_In_variable(self):
104 def test_In_variable(self):
105 "Verify that In variable grows with user input (GH-284)"
105 "Verify that In variable grows with user input (GH-284)"
106 oldlen = len(ip.user_ns['In'])
106 oldlen = len(ip.user_ns['In'])
107 ip.run_cell('1;', store_history=True)
107 ip.run_cell('1;', store_history=True)
108 newlen = len(ip.user_ns['In'])
108 newlen = len(ip.user_ns['In'])
109 self.assertEqual(oldlen+1, newlen)
109 self.assertEqual(oldlen+1, newlen)
110 self.assertEqual(ip.user_ns['In'][-1],'1;')
110 self.assertEqual(ip.user_ns['In'][-1],'1;')
111
111
112 def test_magic_names_in_string(self):
112 def test_magic_names_in_string(self):
113 ip.run_cell('a = """\n%exit\n"""')
113 ip.run_cell('a = """\n%exit\n"""')
114 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
114 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
115
115
116 def test_trailing_newline(self):
116 def test_trailing_newline(self):
117 """test that running !(command) does not raise a SyntaxError"""
117 """test that running !(command) does not raise a SyntaxError"""
118 ip.run_cell('!(true)\n', False)
118 ip.run_cell('!(true)\n', False)
119 ip.run_cell('!(true)\n\n\n', False)
119 ip.run_cell('!(true)\n\n\n', False)
120
120
121 def test_gh_597(self):
121 def test_gh_597(self):
122 """Pretty-printing lists of objects with non-ascii reprs may cause
122 """Pretty-printing lists of objects with non-ascii reprs may cause
123 problems."""
123 problems."""
124 class Spam(object):
124 class Spam(object):
125 def __repr__(self):
125 def __repr__(self):
126 return "\xe9"*50
126 return "\xe9"*50
127 import IPython.core.formatters
127 import IPython.core.formatters
128 f = IPython.core.formatters.PlainTextFormatter()
128 f = IPython.core.formatters.PlainTextFormatter()
129 f([Spam(),Spam()])
129 f([Spam(),Spam()])
130
130
131
131
132 def test_future_flags(self):
132 def test_future_flags(self):
133 """Check that future flags are used for parsing code (gh-777)"""
133 """Check that future flags are used for parsing code (gh-777)"""
134 ip.run_cell('from __future__ import print_function')
134 ip.run_cell('from __future__ import print_function')
135 try:
135 try:
136 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
136 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
137 assert 'prfunc_return_val' in ip.user_ns
137 assert 'prfunc_return_val' in ip.user_ns
138 finally:
138 finally:
139 # Reset compiler flags so we don't mess up other tests.
139 # Reset compiler flags so we don't mess up other tests.
140 ip.compile.reset_compiler_flags()
140 ip.compile.reset_compiler_flags()
141
141
142 def test_future_unicode(self):
142 def test_future_unicode(self):
143 """Check that unicode_literals is imported from __future__ (gh #786)"""
143 """Check that unicode_literals is imported from __future__ (gh #786)"""
144 try:
144 try:
145 ip.run_cell(u'byte_str = "a"')
145 ip.run_cell(u'byte_str = "a"')
146 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
146 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
147 ip.run_cell('from __future__ import unicode_literals')
147 ip.run_cell('from __future__ import unicode_literals')
148 ip.run_cell(u'unicode_str = "a"')
148 ip.run_cell(u'unicode_str = "a"')
149 assert isinstance(ip.user_ns['unicode_str'], unicode_type) # strings literals are now unicode
149 assert isinstance(ip.user_ns['unicode_str'], unicode_type) # strings literals are now unicode
150 finally:
150 finally:
151 # Reset compiler flags so we don't mess up other tests.
151 # Reset compiler flags so we don't mess up other tests.
152 ip.compile.reset_compiler_flags()
152 ip.compile.reset_compiler_flags()
153
153
154 def test_can_pickle(self):
154 def test_can_pickle(self):
155 "Can we pickle objects defined interactively (GH-29)"
155 "Can we pickle objects defined interactively (GH-29)"
156 ip = get_ipython()
156 ip = get_ipython()
157 ip.reset()
157 ip.reset()
158 ip.run_cell(("class Mylist(list):\n"
158 ip.run_cell(("class Mylist(list):\n"
159 " def __init__(self,x=[]):\n"
159 " def __init__(self,x=[]):\n"
160 " list.__init__(self,x)"))
160 " list.__init__(self,x)"))
161 ip.run_cell("w=Mylist([1,2,3])")
161 ip.run_cell("w=Mylist([1,2,3])")
162
162
163 from pickle import dumps
163 from pickle import dumps
164
164
165 # We need to swap in our main module - this is only necessary
165 # We need to swap in our main module - this is only necessary
166 # inside the test framework, because IPython puts the interactive module
166 # inside the test framework, because IPython puts the interactive module
167 # in place (but the test framework undoes this).
167 # in place (but the test framework undoes this).
168 _main = sys.modules['__main__']
168 _main = sys.modules['__main__']
169 sys.modules['__main__'] = ip.user_module
169 sys.modules['__main__'] = ip.user_module
170 try:
170 try:
171 res = dumps(ip.user_ns["w"])
171 res = dumps(ip.user_ns["w"])
172 finally:
172 finally:
173 sys.modules['__main__'] = _main
173 sys.modules['__main__'] = _main
174 self.assertTrue(isinstance(res, bytes))
174 self.assertTrue(isinstance(res, bytes))
175
175
176 def test_global_ns(self):
176 def test_global_ns(self):
177 "Code in functions must be able to access variables outside them."
177 "Code in functions must be able to access variables outside them."
178 ip = get_ipython()
178 ip = get_ipython()
179 ip.run_cell("a = 10")
179 ip.run_cell("a = 10")
180 ip.run_cell(("def f(x):\n"
180 ip.run_cell(("def f(x):\n"
181 " return x + a"))
181 " return x + a"))
182 ip.run_cell("b = f(12)")
182 ip.run_cell("b = f(12)")
183 self.assertEqual(ip.user_ns["b"], 22)
183 self.assertEqual(ip.user_ns["b"], 22)
184
184
185 def test_bad_custom_tb(self):
185 def test_bad_custom_tb(self):
186 """Check that InteractiveShell is protected from bad custom exception handlers"""
186 """Check that InteractiveShell is protected from bad custom exception handlers"""
187 from IPython.utils import io
187 from IPython.utils import io
188 save_stderr = io.stderr
188 save_stderr = io.stderr
189 try:
189 try:
190 # capture stderr
190 # capture stderr
191 io.stderr = StringIO()
191 io.stderr = StringIO()
192 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
192 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
193 self.assertEqual(ip.custom_exceptions, (IOError,))
193 self.assertEqual(ip.custom_exceptions, (IOError,))
194 ip.run_cell(u'raise IOError("foo")')
194 ip.run_cell(u'raise IOError("foo")')
195 self.assertEqual(ip.custom_exceptions, ())
195 self.assertEqual(ip.custom_exceptions, ())
196 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
196 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
197 finally:
197 finally:
198 io.stderr = save_stderr
198 io.stderr = save_stderr
199
199
200 def test_bad_custom_tb_return(self):
200 def test_bad_custom_tb_return(self):
201 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
201 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
202 from IPython.utils import io
202 from IPython.utils import io
203 save_stderr = io.stderr
203 save_stderr = io.stderr
204 try:
204 try:
205 # capture stderr
205 # capture stderr
206 io.stderr = StringIO()
206 io.stderr = StringIO()
207 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
207 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
208 self.assertEqual(ip.custom_exceptions, (NameError,))
208 self.assertEqual(ip.custom_exceptions, (NameError,))
209 ip.run_cell(u'a=abracadabra')
209 ip.run_cell(u'a=abracadabra')
210 self.assertEqual(ip.custom_exceptions, ())
210 self.assertEqual(ip.custom_exceptions, ())
211 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
211 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
212 finally:
212 finally:
213 io.stderr = save_stderr
213 io.stderr = save_stderr
214
214
215 def test_drop_by_id(self):
215 def test_drop_by_id(self):
216 myvars = {"a":object(), "b":object(), "c": object()}
216 myvars = {"a":object(), "b":object(), "c": object()}
217 ip.push(myvars, interactive=False)
217 ip.push(myvars, interactive=False)
218 for name in myvars:
218 for name in myvars:
219 assert name in ip.user_ns, name
219 assert name in ip.user_ns, name
220 assert name in ip.user_ns_hidden, name
220 assert name in ip.user_ns_hidden, name
221 ip.user_ns['b'] = 12
221 ip.user_ns['b'] = 12
222 ip.drop_by_id(myvars)
222 ip.drop_by_id(myvars)
223 for name in ["a", "c"]:
223 for name in ["a", "c"]:
224 assert name not in ip.user_ns, name
224 assert name not in ip.user_ns, name
225 assert name not in ip.user_ns_hidden, name
225 assert name not in ip.user_ns_hidden, name
226 assert ip.user_ns['b'] == 12
226 assert ip.user_ns['b'] == 12
227 ip.reset()
227 ip.reset()
228
228
229 def test_var_expand(self):
229 def test_var_expand(self):
230 ip.user_ns['f'] = u'Ca\xf1o'
230 ip.user_ns['f'] = u'Ca\xf1o'
231 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
231 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
232 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
232 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
233 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
233 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
234 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
234 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
235
235
236 ip.user_ns['f'] = b'Ca\xc3\xb1o'
236 ip.user_ns['f'] = b'Ca\xc3\xb1o'
237 # This should not raise any exception:
237 # This should not raise any exception:
238 ip.var_expand(u'echo $f')
238 ip.var_expand(u'echo $f')
239
239
240 def test_var_expand_local(self):
240 def test_var_expand_local(self):
241 """Test local variable expansion in !system and %magic calls"""
241 """Test local variable expansion in !system and %magic calls"""
242 # !system
242 # !system
243 ip.run_cell('def test():\n'
243 ip.run_cell('def test():\n'
244 ' lvar = "ttt"\n'
244 ' lvar = "ttt"\n'
245 ' ret = !echo {lvar}\n'
245 ' ret = !echo {lvar}\n'
246 ' return ret[0]\n')
246 ' return ret[0]\n')
247 res = ip.user_ns['test']()
247 res = ip.user_ns['test']()
248 nt.assert_in('ttt', res)
248 nt.assert_in('ttt', res)
249
249
250 # %magic
250 # %magic
251 ip.run_cell('def makemacro():\n'
251 ip.run_cell('def makemacro():\n'
252 ' macroname = "macro_var_expand_locals"\n'
252 ' macroname = "macro_var_expand_locals"\n'
253 ' %macro {macroname} codestr\n')
253 ' %macro {macroname} codestr\n')
254 ip.user_ns['codestr'] = "str(12)"
254 ip.user_ns['codestr'] = "str(12)"
255 ip.run_cell('makemacro()')
255 ip.run_cell('makemacro()')
256 nt.assert_in('macro_var_expand_locals', ip.user_ns)
256 nt.assert_in('macro_var_expand_locals', ip.user_ns)
257
257
258 def test_var_expand_self(self):
258 def test_var_expand_self(self):
259 """Test variable expansion with the name 'self', which was failing.
259 """Test variable expansion with the name 'self', which was failing.
260
260
261 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
261 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
262 """
262 """
263 ip.run_cell('class cTest:\n'
263 ip.run_cell('class cTest:\n'
264 ' classvar="see me"\n'
264 ' classvar="see me"\n'
265 ' def test(self):\n'
265 ' def test(self):\n'
266 ' res = !echo Variable: {self.classvar}\n'
266 ' res = !echo Variable: {self.classvar}\n'
267 ' return res[0]\n')
267 ' return res[0]\n')
268 nt.assert_in('see me', ip.user_ns['cTest']().test())
268 nt.assert_in('see me', ip.user_ns['cTest']().test())
269
269
270 def test_bad_var_expand(self):
270 def test_bad_var_expand(self):
271 """var_expand on invalid formats shouldn't raise"""
271 """var_expand on invalid formats shouldn't raise"""
272 # SyntaxError
272 # SyntaxError
273 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
273 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
274 # NameError
274 # NameError
275 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
275 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
276 # ZeroDivisionError
276 # ZeroDivisionError
277 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
277 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
278
278
279 def test_silent_postexec(self):
279 def test_silent_postexec(self):
280 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
280 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
281 pre_explicit = mock.Mock()
281 pre_explicit = mock.Mock()
282 pre_always = mock.Mock()
282 pre_always = mock.Mock()
283 post_explicit = mock.Mock()
283 post_explicit = mock.Mock()
284 post_always = mock.Mock()
284 post_always = mock.Mock()
285
285
286 ip.events.register('pre_run_cell', pre_explicit)
286 ip.events.register('pre_run_cell', pre_explicit)
287 ip.events.register('pre_execute', pre_always)
287 ip.events.register('pre_execute', pre_always)
288 ip.events.register('post_run_cell', post_explicit)
288 ip.events.register('post_run_cell', post_explicit)
289 ip.events.register('post_execute', post_always)
289 ip.events.register('post_execute', post_always)
290
290
291 try:
291 try:
292 ip.run_cell("1", silent=True)
292 ip.run_cell("1", silent=True)
293 assert pre_always.called
293 assert pre_always.called
294 assert not pre_explicit.called
294 assert not pre_explicit.called
295 assert post_always.called
295 assert post_always.called
296 assert not post_explicit.called
296 assert not post_explicit.called
297 # double-check that non-silent exec did what we expected
297 # double-check that non-silent exec did what we expected
298 # silent to avoid
298 # silent to avoid
299 ip.run_cell("1")
299 ip.run_cell("1")
300 assert pre_explicit.called
300 assert pre_explicit.called
301 assert post_explicit.called
301 assert post_explicit.called
302 finally:
302 finally:
303 # remove post-exec
303 # remove post-exec
304 ip.events.reset_all()
304 ip.events.unregister('pre_run_cell', pre_explicit)
305 ip.events.unregister('pre_execute', pre_always)
306 ip.events.unregister('post_run_cell', post_explicit)
307 ip.events.unregister('post_execute', post_always)
305
308
306 def test_silent_noadvance(self):
309 def test_silent_noadvance(self):
307 """run_cell(silent=True) doesn't advance execution_count"""
310 """run_cell(silent=True) doesn't advance execution_count"""
308 ec = ip.execution_count
311 ec = ip.execution_count
309 # silent should force store_history=False
312 # silent should force store_history=False
310 ip.run_cell("1", store_history=True, silent=True)
313 ip.run_cell("1", store_history=True, silent=True)
311
314
312 self.assertEqual(ec, ip.execution_count)
315 self.assertEqual(ec, ip.execution_count)
313 # double-check that non-silent exec did what we expected
316 # double-check that non-silent exec did what we expected
314 # silent to avoid
317 # silent to avoid
315 ip.run_cell("1", store_history=True)
318 ip.run_cell("1", store_history=True)
316 self.assertEqual(ec+1, ip.execution_count)
319 self.assertEqual(ec+1, ip.execution_count)
317
320
318 def test_silent_nodisplayhook(self):
321 def test_silent_nodisplayhook(self):
319 """run_cell(silent=True) doesn't trigger displayhook"""
322 """run_cell(silent=True) doesn't trigger displayhook"""
320 d = dict(called=False)
323 d = dict(called=False)
321
324
322 trap = ip.display_trap
325 trap = ip.display_trap
323 save_hook = trap.hook
326 save_hook = trap.hook
324
327
325 def failing_hook(*args, **kwargs):
328 def failing_hook(*args, **kwargs):
326 d['called'] = True
329 d['called'] = True
327
330
328 try:
331 try:
329 trap.hook = failing_hook
332 trap.hook = failing_hook
330 ip.run_cell("1", silent=True)
333 ip.run_cell("1", silent=True)
331 self.assertFalse(d['called'])
334 self.assertFalse(d['called'])
332 # double-check that non-silent exec did what we expected
335 # double-check that non-silent exec did what we expected
333 # silent to avoid
336 # silent to avoid
334 ip.run_cell("1")
337 ip.run_cell("1")
335 self.assertTrue(d['called'])
338 self.assertTrue(d['called'])
336 finally:
339 finally:
337 trap.hook = save_hook
340 trap.hook = save_hook
338
341
339 @skipif(sys.version_info[0] >= 3, "softspace removed in py3")
342 @skipif(sys.version_info[0] >= 3, "softspace removed in py3")
340 def test_print_softspace(self):
343 def test_print_softspace(self):
341 """Verify that softspace is handled correctly when executing multiple
344 """Verify that softspace is handled correctly when executing multiple
342 statements.
345 statements.
343
346
344 In [1]: print 1; print 2
347 In [1]: print 1; print 2
345 1
348 1
346 2
349 2
347
350
348 In [2]: print 1,; print 2
351 In [2]: print 1,; print 2
349 1 2
352 1 2
350 """
353 """
351
354
352 def test_ofind_line_magic(self):
355 def test_ofind_line_magic(self):
353 from IPython.core.magic import register_line_magic
356 from IPython.core.magic import register_line_magic
354
357
355 @register_line_magic
358 @register_line_magic
356 def lmagic(line):
359 def lmagic(line):
357 "A line magic"
360 "A line magic"
358
361
359 # Get info on line magic
362 # Get info on line magic
360 lfind = ip._ofind('lmagic')
363 lfind = ip._ofind('lmagic')
361 info = dict(found=True, isalias=False, ismagic=True,
364 info = dict(found=True, isalias=False, ismagic=True,
362 namespace = 'IPython internal', obj= lmagic.__wrapped__,
365 namespace = 'IPython internal', obj= lmagic.__wrapped__,
363 parent = None)
366 parent = None)
364 nt.assert_equal(lfind, info)
367 nt.assert_equal(lfind, info)
365
368
366 def test_ofind_cell_magic(self):
369 def test_ofind_cell_magic(self):
367 from IPython.core.magic import register_cell_magic
370 from IPython.core.magic import register_cell_magic
368
371
369 @register_cell_magic
372 @register_cell_magic
370 def cmagic(line, cell):
373 def cmagic(line, cell):
371 "A cell magic"
374 "A cell magic"
372
375
373 # Get info on cell magic
376 # Get info on cell magic
374 find = ip._ofind('cmagic')
377 find = ip._ofind('cmagic')
375 info = dict(found=True, isalias=False, ismagic=True,
378 info = dict(found=True, isalias=False, ismagic=True,
376 namespace = 'IPython internal', obj= cmagic.__wrapped__,
379 namespace = 'IPython internal', obj= cmagic.__wrapped__,
377 parent = None)
380 parent = None)
378 nt.assert_equal(find, info)
381 nt.assert_equal(find, info)
379
382
380 def test_ofind_property_with_error(self):
383 def test_ofind_property_with_error(self):
381 class A(object):
384 class A(object):
382 @property
385 @property
383 def foo(self):
386 def foo(self):
384 raise NotImplementedError()
387 raise NotImplementedError()
385 a = A()
388 a = A()
386
389
387 found = ip._ofind('a.foo', [('locals', locals())])
390 found = ip._ofind('a.foo', [('locals', locals())])
388 info = dict(found=True, isalias=False, ismagic=False,
391 info = dict(found=True, isalias=False, ismagic=False,
389 namespace='locals', obj=A.foo, parent=a)
392 namespace='locals', obj=A.foo, parent=a)
390 nt.assert_equal(found, info)
393 nt.assert_equal(found, info)
391
394
392 def test_ofind_multiple_attribute_lookups(self):
395 def test_ofind_multiple_attribute_lookups(self):
393 class A(object):
396 class A(object):
394 @property
397 @property
395 def foo(self):
398 def foo(self):
396 raise NotImplementedError()
399 raise NotImplementedError()
397
400
398 a = A()
401 a = A()
399 a.a = A()
402 a.a = A()
400 a.a.a = A()
403 a.a.a = A()
401
404
402 found = ip._ofind('a.a.a.foo', [('locals', locals())])
405 found = ip._ofind('a.a.a.foo', [('locals', locals())])
403 info = dict(found=True, isalias=False, ismagic=False,
406 info = dict(found=True, isalias=False, ismagic=False,
404 namespace='locals', obj=A.foo, parent=a.a.a)
407 namespace='locals', obj=A.foo, parent=a.a.a)
405 nt.assert_equal(found, info)
408 nt.assert_equal(found, info)
406
409
407 def test_ofind_slotted_attributes(self):
410 def test_ofind_slotted_attributes(self):
408 class A(object):
411 class A(object):
409 __slots__ = ['foo']
412 __slots__ = ['foo']
410 def __init__(self):
413 def __init__(self):
411 self.foo = 'bar'
414 self.foo = 'bar'
412
415
413 a = A()
416 a = A()
414 found = ip._ofind('a.foo', [('locals', locals())])
417 found = ip._ofind('a.foo', [('locals', locals())])
415 info = dict(found=True, isalias=False, ismagic=False,
418 info = dict(found=True, isalias=False, ismagic=False,
416 namespace='locals', obj=a.foo, parent=a)
419 namespace='locals', obj=a.foo, parent=a)
417 nt.assert_equal(found, info)
420 nt.assert_equal(found, info)
418
421
419 found = ip._ofind('a.bar', [('locals', locals())])
422 found = ip._ofind('a.bar', [('locals', locals())])
420 info = dict(found=False, isalias=False, ismagic=False,
423 info = dict(found=False, isalias=False, ismagic=False,
421 namespace=None, obj=None, parent=a)
424 namespace=None, obj=None, parent=a)
422 nt.assert_equal(found, info)
425 nt.assert_equal(found, info)
423
426
424 def test_ofind_prefers_property_to_instance_level_attribute(self):
427 def test_ofind_prefers_property_to_instance_level_attribute(self):
425 class A(object):
428 class A(object):
426 @property
429 @property
427 def foo(self):
430 def foo(self):
428 return 'bar'
431 return 'bar'
429 a = A()
432 a = A()
430 a.__dict__['foo'] = 'baz'
433 a.__dict__['foo'] = 'baz'
431 nt.assert_equal(a.foo, 'bar')
434 nt.assert_equal(a.foo, 'bar')
432 found = ip._ofind('a.foo', [('locals', locals())])
435 found = ip._ofind('a.foo', [('locals', locals())])
433 nt.assert_is(found['obj'], A.foo)
436 nt.assert_is(found['obj'], A.foo)
434
437
435 def test_custom_exception(self):
438 def test_custom_exception(self):
436 called = []
439 called = []
437 def my_handler(shell, etype, value, tb, tb_offset=None):
440 def my_handler(shell, etype, value, tb, tb_offset=None):
438 called.append(etype)
441 called.append(etype)
439 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
442 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
440
443
441 ip.set_custom_exc((ValueError,), my_handler)
444 ip.set_custom_exc((ValueError,), my_handler)
442 try:
445 try:
443 ip.run_cell("raise ValueError('test')")
446 ip.run_cell("raise ValueError('test')")
444 # Check that this was called, and only once.
447 # Check that this was called, and only once.
445 self.assertEqual(called, [ValueError])
448 self.assertEqual(called, [ValueError])
446 finally:
449 finally:
447 # Reset the custom exception hook
450 # Reset the custom exception hook
448 ip.set_custom_exc((), None)
451 ip.set_custom_exc((), None)
449
452
450 @skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
453 @skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
451 def test_future_environment(self):
454 def test_future_environment(self):
452 "Can we run code with & without the shell's __future__ imports?"
455 "Can we run code with & without the shell's __future__ imports?"
453 ip.run_cell("from __future__ import division")
456 ip.run_cell("from __future__ import division")
454 ip.run_cell("a = 1/2", shell_futures=True)
457 ip.run_cell("a = 1/2", shell_futures=True)
455 self.assertEqual(ip.user_ns['a'], 0.5)
458 self.assertEqual(ip.user_ns['a'], 0.5)
456 ip.run_cell("b = 1/2", shell_futures=False)
459 ip.run_cell("b = 1/2", shell_futures=False)
457 self.assertEqual(ip.user_ns['b'], 0)
460 self.assertEqual(ip.user_ns['b'], 0)
458
461
459 ip.compile.reset_compiler_flags()
462 ip.compile.reset_compiler_flags()
460 # This shouldn't leak to the shell's compiler
463 # This shouldn't leak to the shell's compiler
461 ip.run_cell("from __future__ import division \nc=1/2", shell_futures=False)
464 ip.run_cell("from __future__ import division \nc=1/2", shell_futures=False)
462 self.assertEqual(ip.user_ns['c'], 0.5)
465 self.assertEqual(ip.user_ns['c'], 0.5)
463 ip.run_cell("d = 1/2", shell_futures=True)
466 ip.run_cell("d = 1/2", shell_futures=True)
464 self.assertEqual(ip.user_ns['d'], 0)
467 self.assertEqual(ip.user_ns['d'], 0)
465
468
466 def test_mktempfile(self):
469 def test_mktempfile(self):
467 filename = ip.mktempfile()
470 filename = ip.mktempfile()
468 # Check that we can open the file again on Windows
471 # Check that we can open the file again on Windows
469 with open(filename, 'w') as f:
472 with open(filename, 'w') as f:
470 f.write('abc')
473 f.write('abc')
471
474
472 filename = ip.mktempfile(data='blah')
475 filename = ip.mktempfile(data='blah')
473 with open(filename, 'r') as f:
476 with open(filename, 'r') as f:
474 self.assertEqual(f.read(), 'blah')
477 self.assertEqual(f.read(), 'blah')
475
478
476 def test_new_main_mod(self):
479 def test_new_main_mod(self):
477 # Smoketest to check that this accepts a unicode module name
480 # Smoketest to check that this accepts a unicode module name
478 name = u'jiefmw'
481 name = u'jiefmw'
479 mod = ip.new_main_mod(u'%s.py' % name, name)
482 mod = ip.new_main_mod(u'%s.py' % name, name)
480 self.assertEqual(mod.__name__, name)
483 self.assertEqual(mod.__name__, name)
481
484
482 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
485 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
483
486
484 @onlyif_unicode_paths
487 @onlyif_unicode_paths
485 def setUp(self):
488 def setUp(self):
486 self.BASETESTDIR = tempfile.mkdtemp()
489 self.BASETESTDIR = tempfile.mkdtemp()
487 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
490 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
488 os.mkdir(self.TESTDIR)
491 os.mkdir(self.TESTDIR)
489 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
492 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
490 sfile.write("pass\n")
493 sfile.write("pass\n")
491 self.oldpath = py3compat.getcwd()
494 self.oldpath = py3compat.getcwd()
492 os.chdir(self.TESTDIR)
495 os.chdir(self.TESTDIR)
493 self.fname = u"Γ₯Àâtestscript.py"
496 self.fname = u"Γ₯Àâtestscript.py"
494
497
495 def tearDown(self):
498 def tearDown(self):
496 os.chdir(self.oldpath)
499 os.chdir(self.oldpath)
497 shutil.rmtree(self.BASETESTDIR)
500 shutil.rmtree(self.BASETESTDIR)
498
501
499 @onlyif_unicode_paths
502 @onlyif_unicode_paths
500 def test_1(self):
503 def test_1(self):
501 """Test safe_execfile with non-ascii path
504 """Test safe_execfile with non-ascii path
502 """
505 """
503 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
506 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
504
507
505 class ExitCodeChecks(tt.TempFileMixin):
508 class ExitCodeChecks(tt.TempFileMixin):
506 def test_exit_code_ok(self):
509 def test_exit_code_ok(self):
507 self.system('exit 0')
510 self.system('exit 0')
508 self.assertEqual(ip.user_ns['_exit_code'], 0)
511 self.assertEqual(ip.user_ns['_exit_code'], 0)
509
512
510 def test_exit_code_error(self):
513 def test_exit_code_error(self):
511 self.system('exit 1')
514 self.system('exit 1')
512 self.assertEqual(ip.user_ns['_exit_code'], 1)
515 self.assertEqual(ip.user_ns['_exit_code'], 1)
513
516
514 @skipif(not hasattr(signal, 'SIGALRM'))
517 @skipif(not hasattr(signal, 'SIGALRM'))
515 def test_exit_code_signal(self):
518 def test_exit_code_signal(self):
516 self.mktmp("import signal, time\n"
519 self.mktmp("import signal, time\n"
517 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
520 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
518 "time.sleep(1)\n")
521 "time.sleep(1)\n")
519 self.system("%s %s" % (sys.executable, self.fname))
522 self.system("%s %s" % (sys.executable, self.fname))
520 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
523 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
521
524
522 @onlyif_cmds_exist("csh")
525 @onlyif_cmds_exist("csh")
523 def test_exit_code_signal_csh(self):
526 def test_exit_code_signal_csh(self):
524 SHELL = os.environ.get('SHELL', None)
527 SHELL = os.environ.get('SHELL', None)
525 os.environ['SHELL'] = find_cmd("csh")
528 os.environ['SHELL'] = find_cmd("csh")
526 try:
529 try:
527 self.test_exit_code_signal()
530 self.test_exit_code_signal()
528 finally:
531 finally:
529 if SHELL is not None:
532 if SHELL is not None:
530 os.environ['SHELL'] = SHELL
533 os.environ['SHELL'] = SHELL
531 else:
534 else:
532 del os.environ['SHELL']
535 del os.environ['SHELL']
533
536
534 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
537 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
535 system = ip.system_raw
538 system = ip.system_raw
536
539
537 @onlyif_unicode_paths
540 @onlyif_unicode_paths
538 def test_1(self):
541 def test_1(self):
539 """Test system_raw with non-ascii cmd
542 """Test system_raw with non-ascii cmd
540 """
543 """
541 cmd = u'''python -c "'Γ₯Àâ'" '''
544 cmd = u'''python -c "'Γ₯Àâ'" '''
542 ip.system_raw(cmd)
545 ip.system_raw(cmd)
543
546
544 # TODO: Exit codes are currently ignored on Windows.
547 # TODO: Exit codes are currently ignored on Windows.
545 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
548 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
546 system = ip.system_piped
549 system = ip.system_piped
547
550
548 @skip_win32
551 @skip_win32
549 def test_exit_code_ok(self):
552 def test_exit_code_ok(self):
550 ExitCodeChecks.test_exit_code_ok(self)
553 ExitCodeChecks.test_exit_code_ok(self)
551
554
552 @skip_win32
555 @skip_win32
553 def test_exit_code_error(self):
556 def test_exit_code_error(self):
554 ExitCodeChecks.test_exit_code_error(self)
557 ExitCodeChecks.test_exit_code_error(self)
555
558
556 @skip_win32
559 @skip_win32
557 def test_exit_code_signal(self):
560 def test_exit_code_signal(self):
558 ExitCodeChecks.test_exit_code_signal(self)
561 ExitCodeChecks.test_exit_code_signal(self)
559
562
560 class TestModules(unittest.TestCase, tt.TempFileMixin):
563 class TestModules(unittest.TestCase, tt.TempFileMixin):
561 def test_extraneous_loads(self):
564 def test_extraneous_loads(self):
562 """Test we're not loading modules on startup that we shouldn't.
565 """Test we're not loading modules on startup that we shouldn't.
563 """
566 """
564 self.mktmp("import sys\n"
567 self.mktmp("import sys\n"
565 "print('numpy' in sys.modules)\n"
568 "print('numpy' in sys.modules)\n"
566 "print('IPython.parallel' in sys.modules)\n"
569 "print('IPython.parallel' in sys.modules)\n"
567 "print('IPython.kernel.zmq' in sys.modules)\n"
570 "print('IPython.kernel.zmq' in sys.modules)\n"
568 )
571 )
569 out = "False\nFalse\nFalse\n"
572 out = "False\nFalse\nFalse\n"
570 tt.ipexec_validate(self.fname, out)
573 tt.ipexec_validate(self.fname, out)
571
574
572 class Negator(ast.NodeTransformer):
575 class Negator(ast.NodeTransformer):
573 """Negates all number literals in an AST."""
576 """Negates all number literals in an AST."""
574 def visit_Num(self, node):
577 def visit_Num(self, node):
575 node.n = -node.n
578 node.n = -node.n
576 return node
579 return node
577
580
578 class TestAstTransform(unittest.TestCase):
581 class TestAstTransform(unittest.TestCase):
579 def setUp(self):
582 def setUp(self):
580 self.negator = Negator()
583 self.negator = Negator()
581 ip.ast_transformers.append(self.negator)
584 ip.ast_transformers.append(self.negator)
582
585
583 def tearDown(self):
586 def tearDown(self):
584 ip.ast_transformers.remove(self.negator)
587 ip.ast_transformers.remove(self.negator)
585
588
586 def test_run_cell(self):
589 def test_run_cell(self):
587 with tt.AssertPrints('-34'):
590 with tt.AssertPrints('-34'):
588 ip.run_cell('print (12 + 22)')
591 ip.run_cell('print (12 + 22)')
589
592
590 # A named reference to a number shouldn't be transformed.
593 # A named reference to a number shouldn't be transformed.
591 ip.user_ns['n'] = 55
594 ip.user_ns['n'] = 55
592 with tt.AssertNotPrints('-55'):
595 with tt.AssertNotPrints('-55'):
593 ip.run_cell('print (n)')
596 ip.run_cell('print (n)')
594
597
595 def test_timeit(self):
598 def test_timeit(self):
596 called = set()
599 called = set()
597 def f(x):
600 def f(x):
598 called.add(x)
601 called.add(x)
599 ip.push({'f':f})
602 ip.push({'f':f})
600
603
601 with tt.AssertPrints("best of "):
604 with tt.AssertPrints("best of "):
602 ip.run_line_magic("timeit", "-n1 f(1)")
605 ip.run_line_magic("timeit", "-n1 f(1)")
603 self.assertEqual(called, set([-1]))
606 self.assertEqual(called, set([-1]))
604 called.clear()
607 called.clear()
605
608
606 with tt.AssertPrints("best of "):
609 with tt.AssertPrints("best of "):
607 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
610 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
608 self.assertEqual(called, set([-2, -3]))
611 self.assertEqual(called, set([-2, -3]))
609
612
610 def test_time(self):
613 def test_time(self):
611 called = []
614 called = []
612 def f(x):
615 def f(x):
613 called.append(x)
616 called.append(x)
614 ip.push({'f':f})
617 ip.push({'f':f})
615
618
616 # Test with an expression
619 # Test with an expression
617 with tt.AssertPrints("Wall time: "):
620 with tt.AssertPrints("Wall time: "):
618 ip.run_line_magic("time", "f(5+9)")
621 ip.run_line_magic("time", "f(5+9)")
619 self.assertEqual(called, [-14])
622 self.assertEqual(called, [-14])
620 called[:] = []
623 called[:] = []
621
624
622 # Test with a statement (different code path)
625 # Test with a statement (different code path)
623 with tt.AssertPrints("Wall time: "):
626 with tt.AssertPrints("Wall time: "):
624 ip.run_line_magic("time", "a = f(-3 + -2)")
627 ip.run_line_magic("time", "a = f(-3 + -2)")
625 self.assertEqual(called, [5])
628 self.assertEqual(called, [5])
626
629
627 def test_macro(self):
630 def test_macro(self):
628 ip.push({'a':10})
631 ip.push({'a':10})
629 # The AST transformation makes this do a+=-1
632 # The AST transformation makes this do a+=-1
630 ip.define_macro("amacro", "a+=1\nprint(a)")
633 ip.define_macro("amacro", "a+=1\nprint(a)")
631
634
632 with tt.AssertPrints("9"):
635 with tt.AssertPrints("9"):
633 ip.run_cell("amacro")
636 ip.run_cell("amacro")
634 with tt.AssertPrints("8"):
637 with tt.AssertPrints("8"):
635 ip.run_cell("amacro")
638 ip.run_cell("amacro")
636
639
637 class IntegerWrapper(ast.NodeTransformer):
640 class IntegerWrapper(ast.NodeTransformer):
638 """Wraps all integers in a call to Integer()"""
641 """Wraps all integers in a call to Integer()"""
639 def visit_Num(self, node):
642 def visit_Num(self, node):
640 if isinstance(node.n, int):
643 if isinstance(node.n, int):
641 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
644 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
642 args=[node], keywords=[])
645 args=[node], keywords=[])
643 return node
646 return node
644
647
645 class TestAstTransform2(unittest.TestCase):
648 class TestAstTransform2(unittest.TestCase):
646 def setUp(self):
649 def setUp(self):
647 self.intwrapper = IntegerWrapper()
650 self.intwrapper = IntegerWrapper()
648 ip.ast_transformers.append(self.intwrapper)
651 ip.ast_transformers.append(self.intwrapper)
649
652
650 self.calls = []
653 self.calls = []
651 def Integer(*args):
654 def Integer(*args):
652 self.calls.append(args)
655 self.calls.append(args)
653 return args
656 return args
654 ip.push({"Integer": Integer})
657 ip.push({"Integer": Integer})
655
658
656 def tearDown(self):
659 def tearDown(self):
657 ip.ast_transformers.remove(self.intwrapper)
660 ip.ast_transformers.remove(self.intwrapper)
658 del ip.user_ns['Integer']
661 del ip.user_ns['Integer']
659
662
660 def test_run_cell(self):
663 def test_run_cell(self):
661 ip.run_cell("n = 2")
664 ip.run_cell("n = 2")
662 self.assertEqual(self.calls, [(2,)])
665 self.assertEqual(self.calls, [(2,)])
663
666
664 # This shouldn't throw an error
667 # This shouldn't throw an error
665 ip.run_cell("o = 2.0")
668 ip.run_cell("o = 2.0")
666 self.assertEqual(ip.user_ns['o'], 2.0)
669 self.assertEqual(ip.user_ns['o'], 2.0)
667
670
668 def test_timeit(self):
671 def test_timeit(self):
669 called = set()
672 called = set()
670 def f(x):
673 def f(x):
671 called.add(x)
674 called.add(x)
672 ip.push({'f':f})
675 ip.push({'f':f})
673
676
674 with tt.AssertPrints("best of "):
677 with tt.AssertPrints("best of "):
675 ip.run_line_magic("timeit", "-n1 f(1)")
678 ip.run_line_magic("timeit", "-n1 f(1)")
676 self.assertEqual(called, set([(1,)]))
679 self.assertEqual(called, set([(1,)]))
677 called.clear()
680 called.clear()
678
681
679 with tt.AssertPrints("best of "):
682 with tt.AssertPrints("best of "):
680 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
683 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
681 self.assertEqual(called, set([(2,), (3,)]))
684 self.assertEqual(called, set([(2,), (3,)]))
682
685
683 class ErrorTransformer(ast.NodeTransformer):
686 class ErrorTransformer(ast.NodeTransformer):
684 """Throws an error when it sees a number."""
687 """Throws an error when it sees a number."""
685 def visit_Num(self, node):
688 def visit_Num(self, node):
686 raise ValueError("test")
689 raise ValueError("test")
687
690
688 class TestAstTransformError(unittest.TestCase):
691 class TestAstTransformError(unittest.TestCase):
689 def test_unregistering(self):
692 def test_unregistering(self):
690 err_transformer = ErrorTransformer()
693 err_transformer = ErrorTransformer()
691 ip.ast_transformers.append(err_transformer)
694 ip.ast_transformers.append(err_transformer)
692
695
693 with tt.AssertPrints("unregister", channel='stderr'):
696 with tt.AssertPrints("unregister", channel='stderr'):
694 ip.run_cell("1 + 2")
697 ip.run_cell("1 + 2")
695
698
696 # This should have been removed.
699 # This should have been removed.
697 nt.assert_not_in(err_transformer, ip.ast_transformers)
700 nt.assert_not_in(err_transformer, ip.ast_transformers)
698
701
699
702
700 class StringRejector(ast.NodeTransformer):
703 class StringRejector(ast.NodeTransformer):
701 """Throws an InputRejected when it sees a string literal.
704 """Throws an InputRejected when it sees a string literal.
702
705
703 Used to verify that NodeTransformers can signal that a piece of code should
706 Used to verify that NodeTransformers can signal that a piece of code should
704 not be executed by throwing an InputRejected.
707 not be executed by throwing an InputRejected.
705 """
708 """
706
709
707 def visit_Str(self, node):
710 def visit_Str(self, node):
708 raise InputRejected("test")
711 raise InputRejected("test")
709
712
710
713
711 class TestAstTransformInputRejection(unittest.TestCase):
714 class TestAstTransformInputRejection(unittest.TestCase):
712
715
713 def setUp(self):
716 def setUp(self):
714 self.transformer = StringRejector()
717 self.transformer = StringRejector()
715 ip.ast_transformers.append(self.transformer)
718 ip.ast_transformers.append(self.transformer)
716
719
717 def tearDown(self):
720 def tearDown(self):
718 ip.ast_transformers.remove(self.transformer)
721 ip.ast_transformers.remove(self.transformer)
719
722
720 def test_input_rejection(self):
723 def test_input_rejection(self):
721 """Check that NodeTransformers can reject input."""
724 """Check that NodeTransformers can reject input."""
722
725
723 expect_exception_tb = tt.AssertPrints("InputRejected: test")
726 expect_exception_tb = tt.AssertPrints("InputRejected: test")
724 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
727 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
725
728
726 # Run the same check twice to verify that the transformer is not
729 # Run the same check twice to verify that the transformer is not
727 # disabled after raising.
730 # disabled after raising.
728 with expect_exception_tb, expect_no_cell_output:
731 with expect_exception_tb, expect_no_cell_output:
729 ip.run_cell("'unsafe'")
732 ip.run_cell("'unsafe'")
730
733
731 with expect_exception_tb, expect_no_cell_output:
734 with expect_exception_tb, expect_no_cell_output:
732 ip.run_cell("'unsafe'")
735 ip.run_cell("'unsafe'")
733
736
734 def test__IPYTHON__():
737 def test__IPYTHON__():
735 # This shouldn't raise a NameError, that's all
738 # This shouldn't raise a NameError, that's all
736 __IPYTHON__
739 __IPYTHON__
737
740
738
741
739 class DummyRepr(object):
742 class DummyRepr(object):
740 def __repr__(self):
743 def __repr__(self):
741 return "DummyRepr"
744 return "DummyRepr"
742
745
743 def _repr_html_(self):
746 def _repr_html_(self):
744 return "<b>dummy</b>"
747 return "<b>dummy</b>"
745
748
746 def _repr_javascript_(self):
749 def _repr_javascript_(self):
747 return "console.log('hi');", {'key': 'value'}
750 return "console.log('hi');", {'key': 'value'}
748
751
749
752
750 def test_user_variables():
753 def test_user_variables():
751 # enable all formatters
754 # enable all formatters
752 ip.display_formatter.active_types = ip.display_formatter.format_types
755 ip.display_formatter.active_types = ip.display_formatter.format_types
753
756
754 ip.user_ns['dummy'] = d = DummyRepr()
757 ip.user_ns['dummy'] = d = DummyRepr()
755 keys = set(['dummy', 'doesnotexist'])
758 keys = set(['dummy', 'doesnotexist'])
756 r = ip.user_expressions({ key:key for key in keys})
759 r = ip.user_expressions({ key:key for key in keys})
757
760
758 nt.assert_equal(keys, set(r.keys()))
761 nt.assert_equal(keys, set(r.keys()))
759 dummy = r['dummy']
762 dummy = r['dummy']
760 nt.assert_equal(set(['status', 'data', 'metadata']), set(dummy.keys()))
763 nt.assert_equal(set(['status', 'data', 'metadata']), set(dummy.keys()))
761 nt.assert_equal(dummy['status'], 'ok')
764 nt.assert_equal(dummy['status'], 'ok')
762 data = dummy['data']
765 data = dummy['data']
763 metadata = dummy['metadata']
766 metadata = dummy['metadata']
764 nt.assert_equal(data.get('text/html'), d._repr_html_())
767 nt.assert_equal(data.get('text/html'), d._repr_html_())
765 js, jsmd = d._repr_javascript_()
768 js, jsmd = d._repr_javascript_()
766 nt.assert_equal(data.get('application/javascript'), js)
769 nt.assert_equal(data.get('application/javascript'), js)
767 nt.assert_equal(metadata.get('application/javascript'), jsmd)
770 nt.assert_equal(metadata.get('application/javascript'), jsmd)
768
771
769 dne = r['doesnotexist']
772 dne = r['doesnotexist']
770 nt.assert_equal(dne['status'], 'error')
773 nt.assert_equal(dne['status'], 'error')
771 nt.assert_equal(dne['ename'], 'NameError')
774 nt.assert_equal(dne['ename'], 'NameError')
772
775
773 # back to text only
776 # back to text only
774 ip.display_formatter.active_types = ['text/plain']
777 ip.display_formatter.active_types = ['text/plain']
775
778
776 def test_user_expression():
779 def test_user_expression():
777 # enable all formatters
780 # enable all formatters
778 ip.display_formatter.active_types = ip.display_formatter.format_types
781 ip.display_formatter.active_types = ip.display_formatter.format_types
779 query = {
782 query = {
780 'a' : '1 + 2',
783 'a' : '1 + 2',
781 'b' : '1/0',
784 'b' : '1/0',
782 }
785 }
783 r = ip.user_expressions(query)
786 r = ip.user_expressions(query)
784 import pprint
787 import pprint
785 pprint.pprint(r)
788 pprint.pprint(r)
786 nt.assert_equal(set(r.keys()), set(query.keys()))
789 nt.assert_equal(set(r.keys()), set(query.keys()))
787 a = r['a']
790 a = r['a']
788 nt.assert_equal(set(['status', 'data', 'metadata']), set(a.keys()))
791 nt.assert_equal(set(['status', 'data', 'metadata']), set(a.keys()))
789 nt.assert_equal(a['status'], 'ok')
792 nt.assert_equal(a['status'], 'ok')
790 data = a['data']
793 data = a['data']
791 metadata = a['metadata']
794 metadata = a['metadata']
792 nt.assert_equal(data.get('text/plain'), '3')
795 nt.assert_equal(data.get('text/plain'), '3')
793
796
794 b = r['b']
797 b = r['b']
795 nt.assert_equal(b['status'], 'error')
798 nt.assert_equal(b['status'], 'error')
796 nt.assert_equal(b['ename'], 'ZeroDivisionError')
799 nt.assert_equal(b['ename'], 'ZeroDivisionError')
797
800
798 # back to text only
801 # back to text only
799 ip.display_formatter.active_types = ['text/plain']
802 ip.display_formatter.active_types = ['text/plain']
800
803
801
804
802
805
803
806
804
807
805 class TestSyntaxErrorTransformer(unittest.TestCase):
808 class TestSyntaxErrorTransformer(unittest.TestCase):
806 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
809 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
807
810
808 class SyntaxErrorTransformer(InputTransformer):
811 class SyntaxErrorTransformer(InputTransformer):
809
812
810 def push(self, line):
813 def push(self, line):
811 pos = line.find('syntaxerror')
814 pos = line.find('syntaxerror')
812 if pos >= 0:
815 if pos >= 0:
813 e = SyntaxError('input contains "syntaxerror"')
816 e = SyntaxError('input contains "syntaxerror"')
814 e.text = line
817 e.text = line
815 e.offset = pos + 1
818 e.offset = pos + 1
816 raise e
819 raise e
817 return line
820 return line
818
821
819 def reset(self):
822 def reset(self):
820 pass
823 pass
821
824
822 def setUp(self):
825 def setUp(self):
823 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
826 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
824 ip.input_splitter.python_line_transforms.append(self.transformer)
827 ip.input_splitter.python_line_transforms.append(self.transformer)
825 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
828 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
826
829
827 def tearDown(self):
830 def tearDown(self):
828 ip.input_splitter.python_line_transforms.remove(self.transformer)
831 ip.input_splitter.python_line_transforms.remove(self.transformer)
829 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
832 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
830
833
831 def test_syntaxerror_input_transformer(self):
834 def test_syntaxerror_input_transformer(self):
832 with tt.AssertPrints('1234'):
835 with tt.AssertPrints('1234'):
833 ip.run_cell('1234')
836 ip.run_cell('1234')
834 with tt.AssertPrints('SyntaxError: invalid syntax'):
837 with tt.AssertPrints('SyntaxError: invalid syntax'):
835 ip.run_cell('1 2 3') # plain python syntax error
838 ip.run_cell('1 2 3') # plain python syntax error
836 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
839 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
837 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
840 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
838 with tt.AssertPrints('3456'):
841 with tt.AssertPrints('3456'):
839 ip.run_cell('3456')
842 ip.run_cell('3456')
840
843
841
844
842
845
General Comments 0
You need to be logged in to leave comments. Login now