##// END OF EJS Templates
remove 'name' from inspect_reply
MinRK -
Show More
@@ -1,105 +1,104 b''
1 # Copyright (c) IPython Development Team.
1 # Copyright (c) IPython Development Team.
2 # Distributed under the terms of the Modified BSD License.
2 # Distributed under the terms of the Modified BSD License.
3
3
4 from __future__ import print_function
4 from __future__ import print_function
5
5
6 import unittest
6 import unittest
7
7
8 from IPython.kernel.inprocess.blocking import BlockingInProcessKernelClient
8 from IPython.kernel.inprocess.blocking import BlockingInProcessKernelClient
9 from IPython.kernel.inprocess.manager import InProcessKernelManager
9 from IPython.kernel.inprocess.manager import InProcessKernelManager
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Test case
12 # Test case
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 class InProcessKernelManagerTestCase(unittest.TestCase):
15 class InProcessKernelManagerTestCase(unittest.TestCase):
16
16
17 def test_interface(self):
17 def test_interface(self):
18 """ Does the in-process kernel manager implement the basic KM interface?
18 """ Does the in-process kernel manager implement the basic KM interface?
19 """
19 """
20 km = InProcessKernelManager()
20 km = InProcessKernelManager()
21 self.assert_(not km.has_kernel)
21 self.assert_(not km.has_kernel)
22
22
23 km.start_kernel()
23 km.start_kernel()
24 self.assert_(km.has_kernel)
24 self.assert_(km.has_kernel)
25 self.assert_(km.kernel is not None)
25 self.assert_(km.kernel is not None)
26
26
27 kc = BlockingInProcessKernelClient(kernel=km.kernel)
27 kc = BlockingInProcessKernelClient(kernel=km.kernel)
28 self.assert_(not kc.channels_running)
28 self.assert_(not kc.channels_running)
29
29
30 kc.start_channels()
30 kc.start_channels()
31 self.assert_(kc.channels_running)
31 self.assert_(kc.channels_running)
32
32
33 old_kernel = km.kernel
33 old_kernel = km.kernel
34 km.restart_kernel()
34 km.restart_kernel()
35 self.assert_(km.kernel is not None)
35 self.assert_(km.kernel is not None)
36 self.assertNotEquals(km.kernel, old_kernel)
36 self.assertNotEquals(km.kernel, old_kernel)
37
37
38 km.shutdown_kernel()
38 km.shutdown_kernel()
39 self.assert_(not km.has_kernel)
39 self.assert_(not km.has_kernel)
40
40
41 self.assertRaises(NotImplementedError, km.interrupt_kernel)
41 self.assertRaises(NotImplementedError, km.interrupt_kernel)
42 self.assertRaises(NotImplementedError, km.signal_kernel, 9)
42 self.assertRaises(NotImplementedError, km.signal_kernel, 9)
43
43
44 kc.stop_channels()
44 kc.stop_channels()
45 self.assert_(not kc.channels_running)
45 self.assert_(not kc.channels_running)
46
46
47 def test_execute(self):
47 def test_execute(self):
48 """ Does executing code in an in-process kernel work?
48 """ Does executing code in an in-process kernel work?
49 """
49 """
50 km = InProcessKernelManager()
50 km = InProcessKernelManager()
51 km.start_kernel()
51 km.start_kernel()
52 kc = BlockingInProcessKernelClient(kernel=km.kernel)
52 kc = BlockingInProcessKernelClient(kernel=km.kernel)
53 kc.start_channels()
53 kc.start_channels()
54 kc.execute('foo = 1')
54 kc.execute('foo = 1')
55 self.assertEquals(km.kernel.shell.user_ns['foo'], 1)
55 self.assertEquals(km.kernel.shell.user_ns['foo'], 1)
56
56
57 def test_complete(self):
57 def test_complete(self):
58 """ Does requesting completion from an in-process kernel work?
58 """ Does requesting completion from an in-process kernel work?
59 """
59 """
60 km = InProcessKernelManager()
60 km = InProcessKernelManager()
61 km.start_kernel()
61 km.start_kernel()
62 kc = BlockingInProcessKernelClient(kernel=km.kernel)
62 kc = BlockingInProcessKernelClient(kernel=km.kernel)
63 kc.start_channels()
63 kc.start_channels()
64 km.kernel.shell.push({'my_bar': 0, 'my_baz': 1})
64 km.kernel.shell.push({'my_bar': 0, 'my_baz': 1})
65 kc.complete('my_ba', 5)
65 kc.complete('my_ba', 5)
66 msg = kc.get_shell_msg()
66 msg = kc.get_shell_msg()
67 self.assertEqual(msg['header']['msg_type'], 'complete_reply')
67 self.assertEqual(msg['header']['msg_type'], 'complete_reply')
68 self.assertEqual(sorted(msg['content']['matches']),
68 self.assertEqual(sorted(msg['content']['matches']),
69 ['my_bar', 'my_baz'])
69 ['my_bar', 'my_baz'])
70
70
71 def test_inspect(self):
71 def test_inspect(self):
72 """ Does requesting object information from an in-process kernel work?
72 """ Does requesting object information from an in-process kernel work?
73 """
73 """
74 km = InProcessKernelManager()
74 km = InProcessKernelManager()
75 km.start_kernel()
75 km.start_kernel()
76 kc = BlockingInProcessKernelClient(kernel=km.kernel)
76 kc = BlockingInProcessKernelClient(kernel=km.kernel)
77 kc.start_channels()
77 kc.start_channels()
78 km.kernel.shell.user_ns['foo'] = 1
78 km.kernel.shell.user_ns['foo'] = 1
79 kc.inspect('foo')
79 kc.inspect('foo')
80 msg = kc.get_shell_msg()
80 msg = kc.get_shell_msg()
81 self.assertEqual(msg['header']['msg_type'], 'inspect_reply')
81 self.assertEqual(msg['header']['msg_type'], 'inspect_reply')
82 content = msg['content']
82 content = msg['content']
83 assert content['found']
83 assert content['found']
84 self.assertEqual(content['name'], 'foo')
85 text = content['data']['text/plain']
84 text = content['data']['text/plain']
86 self.assertIn('int', text)
85 self.assertIn('int', text)
87
86
88 def test_history(self):
87 def test_history(self):
89 """ Does requesting history from an in-process kernel work?
88 """ Does requesting history from an in-process kernel work?
90 """
89 """
91 km = InProcessKernelManager()
90 km = InProcessKernelManager()
92 km.start_kernel()
91 km.start_kernel()
93 kc = BlockingInProcessKernelClient(kernel=km.kernel)
92 kc = BlockingInProcessKernelClient(kernel=km.kernel)
94 kc.start_channels()
93 kc.start_channels()
95 kc.execute('%who')
94 kc.execute('%who')
96 kc.history(hist_access_type='tail', n=1)
95 kc.history(hist_access_type='tail', n=1)
97 msg = kc.shell_channel.get_msgs()[-1]
96 msg = kc.shell_channel.get_msgs()[-1]
98 self.assertEquals(msg['header']['msg_type'], 'history_reply')
97 self.assertEquals(msg['header']['msg_type'], 'history_reply')
99 history = msg['content']['history']
98 history = msg['content']['history']
100 self.assertEquals(len(history), 1)
99 self.assertEquals(len(history), 1)
101 self.assertEquals(history[0][2], '%who')
100 self.assertEquals(history[0][2], '%who')
102
101
103
102
104 if __name__ == '__main__':
103 if __name__ == '__main__':
105 unittest.main()
104 unittest.main()
@@ -1,404 +1,400 b''
1 """Test suite for our zeromq-based message specification."""
1 """Test suite for our zeromq-based message specification."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import re
6 import re
7 from distutils.version import LooseVersion as V
7 from distutils.version import LooseVersion as V
8 from subprocess import PIPE
8 from subprocess import PIPE
9 try:
9 try:
10 from queue import Empty # Py 3
10 from queue import Empty # Py 3
11 except ImportError:
11 except ImportError:
12 from Queue import Empty # Py 2
12 from Queue import Empty # Py 2
13
13
14 import nose.tools as nt
14 import nose.tools as nt
15
15
16 from IPython.kernel import KernelManager
16 from IPython.kernel import KernelManager
17
17
18 from IPython.utils.traitlets import (
18 from IPython.utils.traitlets import (
19 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
19 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
20 )
20 )
21 from IPython.utils.py3compat import string_types, iteritems
21 from IPython.utils.py3compat import string_types, iteritems
22
22
23 from .utils import TIMEOUT, start_global_kernel, flush_channels, execute
23 from .utils import TIMEOUT, start_global_kernel, flush_channels, execute
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Globals
26 # Globals
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 KC = None
28 KC = None
29
29
30 def setup():
30 def setup():
31 global KC
31 global KC
32 KC = start_global_kernel()
32 KC = start_global_kernel()
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Message Spec References
35 # Message Spec References
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 class Reference(HasTraits):
38 class Reference(HasTraits):
39
39
40 """
40 """
41 Base class for message spec specification testing.
41 Base class for message spec specification testing.
42
42
43 This class is the core of the message specification test. The
43 This class is the core of the message specification test. The
44 idea is that child classes implement trait attributes for each
44 idea is that child classes implement trait attributes for each
45 message keys, so that message keys can be tested against these
45 message keys, so that message keys can be tested against these
46 traits using :meth:`check` method.
46 traits using :meth:`check` method.
47
47
48 """
48 """
49
49
50 def check(self, d):
50 def check(self, d):
51 """validate a dict against our traits"""
51 """validate a dict against our traits"""
52 for key in self.trait_names():
52 for key in self.trait_names():
53 nt.assert_in(key, d)
53 nt.assert_in(key, d)
54 # FIXME: always allow None, probably not a good idea
54 # FIXME: always allow None, probably not a good idea
55 if d[key] is None:
55 if d[key] is None:
56 continue
56 continue
57 try:
57 try:
58 setattr(self, key, d[key])
58 setattr(self, key, d[key])
59 except TraitError as e:
59 except TraitError as e:
60 assert False, str(e)
60 assert False, str(e)
61
61
62 class Version(Unicode):
62 class Version(Unicode):
63 def validate(self, obj, value):
63 def validate(self, obj, value):
64 min_version = self.default_value
64 min_version = self.default_value
65 if V(value) < V(min_version):
65 if V(value) < V(min_version):
66 raise TraitError("bad version: %s < %s" % (value, min_version))
66 raise TraitError("bad version: %s < %s" % (value, min_version))
67
67
68 class RMessage(Reference):
68 class RMessage(Reference):
69 msg_id = Unicode()
69 msg_id = Unicode()
70 msg_type = Unicode()
70 msg_type = Unicode()
71 header = Dict()
71 header = Dict()
72 parent_header = Dict()
72 parent_header = Dict()
73 content = Dict()
73 content = Dict()
74
74
75 def check(self, d):
75 def check(self, d):
76 super(RMessage, self).check(d)
76 super(RMessage, self).check(d)
77 RHeader().check(self.header)
77 RHeader().check(self.header)
78 if self.parent_header:
78 if self.parent_header:
79 RHeader().check(self.parent_header)
79 RHeader().check(self.parent_header)
80
80
81 class RHeader(Reference):
81 class RHeader(Reference):
82 msg_id = Unicode()
82 msg_id = Unicode()
83 msg_type = Unicode()
83 msg_type = Unicode()
84 session = Unicode()
84 session = Unicode()
85 username = Unicode()
85 username = Unicode()
86 version = Version('5.0')
86 version = Version('5.0')
87
87
88 mime_pat = re.compile(r'\w+/\w+')
88 mime_pat = re.compile(r'\w+/\w+')
89
89
90 class MimeBundle(Reference):
90 class MimeBundle(Reference):
91 metadata = Dict()
91 metadata = Dict()
92 data = Dict()
92 data = Dict()
93 def _data_changed(self, name, old, new):
93 def _data_changed(self, name, old, new):
94 for k,v in iteritems(new):
94 for k,v in iteritems(new):
95 assert mime_pat.match(k)
95 assert mime_pat.match(k)
96 nt.assert_is_instance(v, string_types)
96 nt.assert_is_instance(v, string_types)
97
97
98 # shell replies
98 # shell replies
99
99
100 class ExecuteReply(Reference):
100 class ExecuteReply(Reference):
101 execution_count = Integer()
101 execution_count = Integer()
102 status = Enum((u'ok', u'error'))
102 status = Enum((u'ok', u'error'))
103
103
104 def check(self, d):
104 def check(self, d):
105 Reference.check(self, d)
105 Reference.check(self, d)
106 if d['status'] == 'ok':
106 if d['status'] == 'ok':
107 ExecuteReplyOkay().check(d)
107 ExecuteReplyOkay().check(d)
108 elif d['status'] == 'error':
108 elif d['status'] == 'error':
109 ExecuteReplyError().check(d)
109 ExecuteReplyError().check(d)
110
110
111
111
112 class ExecuteReplyOkay(Reference):
112 class ExecuteReplyOkay(Reference):
113 payload = List(Dict)
113 payload = List(Dict)
114 user_expressions = Dict()
114 user_expressions = Dict()
115
115
116
116
117 class ExecuteReplyError(Reference):
117 class ExecuteReplyError(Reference):
118 ename = Unicode()
118 ename = Unicode()
119 evalue = Unicode()
119 evalue = Unicode()
120 traceback = List(Unicode)
120 traceback = List(Unicode)
121
121
122
122
123 class InspectReply(MimeBundle):
123 class InspectReply(MimeBundle):
124 name = Unicode()
125 found = Bool()
124 found = Bool()
126
125
127
126
128 class ArgSpec(Reference):
127 class ArgSpec(Reference):
129 args = List(Unicode)
128 args = List(Unicode)
130 varargs = Unicode()
129 varargs = Unicode()
131 varkw = Unicode()
130 varkw = Unicode()
132 defaults = List()
131 defaults = List()
133
132
134
133
135 class Status(Reference):
134 class Status(Reference):
136 execution_state = Enum((u'busy', u'idle', u'starting'))
135 execution_state = Enum((u'busy', u'idle', u'starting'))
137
136
138
137
139 class CompleteReply(Reference):
138 class CompleteReply(Reference):
140 matches = List(Unicode)
139 matches = List(Unicode)
141 cursor_start = Integer()
140 cursor_start = Integer()
142 cursor_end = Integer()
141 cursor_end = Integer()
143 status = Unicode()
142 status = Unicode()
144
143
145
144
146 class KernelInfoReply(Reference):
145 class KernelInfoReply(Reference):
147 protocol_version = Version('5.0')
146 protocol_version = Version('5.0')
148 implementation = Unicode('ipython')
147 implementation = Unicode('ipython')
149 implementation_version = Version('2.1')
148 implementation_version = Version('2.1')
150 language_version = Version('2.7')
149 language_version = Version('2.7')
151 language = Unicode('python')
150 language = Unicode('python')
152 banner = Unicode()
151 banner = Unicode()
153
152
154
153
155 # IOPub messages
154 # IOPub messages
156
155
157 class ExecuteInput(Reference):
156 class ExecuteInput(Reference):
158 code = Unicode()
157 code = Unicode()
159 execution_count = Integer()
158 execution_count = Integer()
160
159
161
160
162 Error = ExecuteReplyError
161 Error = ExecuteReplyError
163
162
164
163
165 class Stream(Reference):
164 class Stream(Reference):
166 name = Enum((u'stdout', u'stderr'))
165 name = Enum((u'stdout', u'stderr'))
167 data = Unicode()
166 data = Unicode()
168
167
169
168
170 class DisplayData(MimeBundle):
169 class DisplayData(MimeBundle):
171 pass
170 pass
172
171
173
172
174 class ExecuteResult(MimeBundle):
173 class ExecuteResult(MimeBundle):
175 execution_count = Integer()
174 execution_count = Integer()
176
175
177
176
178 references = {
177 references = {
179 'execute_reply' : ExecuteReply(),
178 'execute_reply' : ExecuteReply(),
180 'inspect_reply' : InspectReply(),
179 'inspect_reply' : InspectReply(),
181 'status' : Status(),
180 'status' : Status(),
182 'complete_reply' : CompleteReply(),
181 'complete_reply' : CompleteReply(),
183 'kernel_info_reply': KernelInfoReply(),
182 'kernel_info_reply': KernelInfoReply(),
184 'execute_input' : ExecuteInput(),
183 'execute_input' : ExecuteInput(),
185 'execute_result' : ExecuteResult(),
184 'execute_result' : ExecuteResult(),
186 'error' : Error(),
185 'error' : Error(),
187 'stream' : Stream(),
186 'stream' : Stream(),
188 'display_data' : DisplayData(),
187 'display_data' : DisplayData(),
189 'header' : RHeader(),
188 'header' : RHeader(),
190 }
189 }
191 """
190 """
192 Specifications of `content` part of the reply messages.
191 Specifications of `content` part of the reply messages.
193 """
192 """
194
193
195
194
196 def validate_message(msg, msg_type=None, parent=None):
195 def validate_message(msg, msg_type=None, parent=None):
197 """validate a message
196 """validate a message
198
197
199 This is a generator, and must be iterated through to actually
198 This is a generator, and must be iterated through to actually
200 trigger each test.
199 trigger each test.
201
200
202 If msg_type and/or parent are given, the msg_type and/or parent msg_id
201 If msg_type and/or parent are given, the msg_type and/or parent msg_id
203 are compared with the given values.
202 are compared with the given values.
204 """
203 """
205 RMessage().check(msg)
204 RMessage().check(msg)
206 if msg_type:
205 if msg_type:
207 nt.assert_equal(msg['msg_type'], msg_type)
206 nt.assert_equal(msg['msg_type'], msg_type)
208 if parent:
207 if parent:
209 nt.assert_equal(msg['parent_header']['msg_id'], parent)
208 nt.assert_equal(msg['parent_header']['msg_id'], parent)
210 content = msg['content']
209 content = msg['content']
211 ref = references[msg['msg_type']]
210 ref = references[msg['msg_type']]
212 ref.check(content)
211 ref.check(content)
213
212
214
213
215 #-----------------------------------------------------------------------------
214 #-----------------------------------------------------------------------------
216 # Tests
215 # Tests
217 #-----------------------------------------------------------------------------
216 #-----------------------------------------------------------------------------
218
217
219 # Shell channel
218 # Shell channel
220
219
221 def test_execute():
220 def test_execute():
222 flush_channels()
221 flush_channels()
223
222
224 msg_id = KC.execute(code='x=1')
223 msg_id = KC.execute(code='x=1')
225 reply = KC.get_shell_msg(timeout=TIMEOUT)
224 reply = KC.get_shell_msg(timeout=TIMEOUT)
226 validate_message(reply, 'execute_reply', msg_id)
225 validate_message(reply, 'execute_reply', msg_id)
227
226
228
227
229 def test_execute_silent():
228 def test_execute_silent():
230 flush_channels()
229 flush_channels()
231 msg_id, reply = execute(code='x=1', silent=True)
230 msg_id, reply = execute(code='x=1', silent=True)
232
231
233 # flush status=idle
232 # flush status=idle
234 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
233 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
235 validate_message(status, 'status', msg_id)
234 validate_message(status, 'status', msg_id)
236 nt.assert_equal(status['content']['execution_state'], 'idle')
235 nt.assert_equal(status['content']['execution_state'], 'idle')
237
236
238 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
237 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
239 count = reply['execution_count']
238 count = reply['execution_count']
240
239
241 msg_id, reply = execute(code='x=2', silent=True)
240 msg_id, reply = execute(code='x=2', silent=True)
242
241
243 # flush status=idle
242 # flush status=idle
244 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
243 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
245 validate_message(status, 'status', msg_id)
244 validate_message(status, 'status', msg_id)
246 nt.assert_equal(status['content']['execution_state'], 'idle')
245 nt.assert_equal(status['content']['execution_state'], 'idle')
247
246
248 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
247 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
249 count_2 = reply['execution_count']
248 count_2 = reply['execution_count']
250 nt.assert_equal(count_2, count)
249 nt.assert_equal(count_2, count)
251
250
252
251
253 def test_execute_error():
252 def test_execute_error():
254 flush_channels()
253 flush_channels()
255
254
256 msg_id, reply = execute(code='1/0')
255 msg_id, reply = execute(code='1/0')
257 nt.assert_equal(reply['status'], 'error')
256 nt.assert_equal(reply['status'], 'error')
258 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
257 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
259
258
260 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
259 error = KC.iopub_channel.get_msg(timeout=TIMEOUT)
261 validate_message(error, 'error', msg_id)
260 validate_message(error, 'error', msg_id)
262
261
263
262
264 def test_execute_inc():
263 def test_execute_inc():
265 """execute request should increment execution_count"""
264 """execute request should increment execution_count"""
266 flush_channels()
265 flush_channels()
267
266
268 msg_id, reply = execute(code='x=1')
267 msg_id, reply = execute(code='x=1')
269 count = reply['execution_count']
268 count = reply['execution_count']
270
269
271 flush_channels()
270 flush_channels()
272
271
273 msg_id, reply = execute(code='x=2')
272 msg_id, reply = execute(code='x=2')
274 count_2 = reply['execution_count']
273 count_2 = reply['execution_count']
275 nt.assert_equal(count_2, count+1)
274 nt.assert_equal(count_2, count+1)
276
275
277
276
278 def test_user_expressions():
277 def test_user_expressions():
279 flush_channels()
278 flush_channels()
280
279
281 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
280 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
282 user_expressions = reply['user_expressions']
281 user_expressions = reply['user_expressions']
283 nt.assert_equal(user_expressions, {u'foo': {
282 nt.assert_equal(user_expressions, {u'foo': {
284 u'status': u'ok',
283 u'status': u'ok',
285 u'data': {u'text/plain': u'2'},
284 u'data': {u'text/plain': u'2'},
286 u'metadata': {},
285 u'metadata': {},
287 }})
286 }})
288
287
289
288
290 def test_user_expressions_fail():
289 def test_user_expressions_fail():
291 flush_channels()
290 flush_channels()
292
291
293 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
292 msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname'))
294 user_expressions = reply['user_expressions']
293 user_expressions = reply['user_expressions']
295 foo = user_expressions['foo']
294 foo = user_expressions['foo']
296 nt.assert_equal(foo['status'], 'error')
295 nt.assert_equal(foo['status'], 'error')
297 nt.assert_equal(foo['ename'], 'NameError')
296 nt.assert_equal(foo['ename'], 'NameError')
298
297
299
298
300 def test_oinfo():
299 def test_oinfo():
301 flush_channels()
300 flush_channels()
302
301
303 msg_id = KC.inspect('a')
302 msg_id = KC.inspect('a')
304 reply = KC.get_shell_msg(timeout=TIMEOUT)
303 reply = KC.get_shell_msg(timeout=TIMEOUT)
305 validate_message(reply, 'inspect_reply', msg_id)
304 validate_message(reply, 'inspect_reply', msg_id)
306
305
307
306
308 def test_oinfo_found():
307 def test_oinfo_found():
309 flush_channels()
308 flush_channels()
310
309
311 msg_id, reply = execute(code='a=5')
310 msg_id, reply = execute(code='a=5')
312
311
313 msg_id = KC.inspect('a')
312 msg_id = KC.inspect('a')
314 reply = KC.get_shell_msg(timeout=TIMEOUT)
313 reply = KC.get_shell_msg(timeout=TIMEOUT)
315 validate_message(reply, 'inspect_reply', msg_id)
314 validate_message(reply, 'inspect_reply', msg_id)
316 content = reply['content']
315 content = reply['content']
317 assert content['found']
316 assert content['found']
318 nt.assert_equal(content['name'], 'a')
319 text = content['data']['text/plain']
317 text = content['data']['text/plain']
320 nt.assert_in('Type:', text)
318 nt.assert_in('Type:', text)
321 nt.assert_in('Docstring:', text)
319 nt.assert_in('Docstring:', text)
322
320
323
321
324 def test_oinfo_detail():
322 def test_oinfo_detail():
325 flush_channels()
323 flush_channels()
326
324
327 msg_id, reply = execute(code='ip=get_ipython()')
325 msg_id, reply = execute(code='ip=get_ipython()')
328
326
329 msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1)
327 msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1)
330 reply = KC.get_shell_msg(timeout=TIMEOUT)
328 reply = KC.get_shell_msg(timeout=TIMEOUT)
331 validate_message(reply, 'inspect_reply', msg_id)
329 validate_message(reply, 'inspect_reply', msg_id)
332 content = reply['content']
330 content = reply['content']
333 assert content['found']
331 assert content['found']
334 nt.assert_equal(content['name'], 'ip.object_inspect')
335 text = content['data']['text/plain']
332 text = content['data']['text/plain']
336 nt.assert_in('Definition:', text)
333 nt.assert_in('Definition:', text)
337 nt.assert_in('Source:', text)
334 nt.assert_in('Source:', text)
338
335
339
336
340 def test_oinfo_not_found():
337 def test_oinfo_not_found():
341 flush_channels()
338 flush_channels()
342
339
343 msg_id = KC.inspect('dne')
340 msg_id = KC.inspect('dne')
344 reply = KC.get_shell_msg(timeout=TIMEOUT)
341 reply = KC.get_shell_msg(timeout=TIMEOUT)
345 validate_message(reply, 'inspect_reply', msg_id)
342 validate_message(reply, 'inspect_reply', msg_id)
346 content = reply['content']
343 content = reply['content']
347 nt.assert_false(content['found'])
344 nt.assert_false(content['found'])
348
345
349
346
350 def test_complete():
347 def test_complete():
351 flush_channels()
348 flush_channels()
352
349
353 msg_id, reply = execute(code="alpha = albert = 5")
350 msg_id, reply = execute(code="alpha = albert = 5")
354
351
355 msg_id = KC.complete('al', 2)
352 msg_id = KC.complete('al', 2)
356 reply = KC.get_shell_msg(timeout=TIMEOUT)
353 reply = KC.get_shell_msg(timeout=TIMEOUT)
357 validate_message(reply, 'complete_reply', msg_id)
354 validate_message(reply, 'complete_reply', msg_id)
358 matches = reply['content']['matches']
355 matches = reply['content']['matches']
359 for name in ('alpha', 'albert'):
356 for name in ('alpha', 'albert'):
360 nt.assert_in(name, matches)
357 nt.assert_in(name, matches)
361
358
362
359
363 def test_kernel_info_request():
360 def test_kernel_info_request():
364 flush_channels()
361 flush_channels()
365
362
366 msg_id = KC.kernel_info()
363 msg_id = KC.kernel_info()
367 reply = KC.get_shell_msg(timeout=TIMEOUT)
364 reply = KC.get_shell_msg(timeout=TIMEOUT)
368 validate_message(reply, 'kernel_info_reply', msg_id)
365 validate_message(reply, 'kernel_info_reply', msg_id)
369
366
370
367
371 def test_single_payload():
368 def test_single_payload():
372 flush_channels()
369 flush_channels()
373 msg_id, reply = execute(code="for i in range(3):\n"+
370 msg_id, reply = execute(code="for i in range(3):\n"+
374 " x=range?\n")
371 " x=range?\n")
375 payload = reply['payload']
372 payload = reply['payload']
376 next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"]
373 next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"]
377 nt.assert_equal(len(next_input_pls), 1)
374 nt.assert_equal(len(next_input_pls), 1)
378
375
379
376
380 # IOPub channel
377 # IOPub channel
381
378
382
379
383 def test_stream():
380 def test_stream():
384 flush_channels()
381 flush_channels()
385
382
386 msg_id, reply = execute("print('hi')")
383 msg_id, reply = execute("print('hi')")
387
384
388 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
385 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
389 validate_message(stdout, 'stream', msg_id)
386 validate_message(stdout, 'stream', msg_id)
390 content = stdout['content']
387 content = stdout['content']
391 nt.assert_equal(content['name'], u'stdout')
392 nt.assert_equal(content['data'], u'hi\n')
388 nt.assert_equal(content['data'], u'hi\n')
393
389
394
390
395 def test_display_data():
391 def test_display_data():
396 flush_channels()
392 flush_channels()
397
393
398 msg_id, reply = execute("from IPython.core.display import display; display(1)")
394 msg_id, reply = execute("from IPython.core.display import display; display(1)")
399
395
400 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
396 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
401 validate_message(display, 'display_data', parent=msg_id)
397 validate_message(display, 'display_data', parent=msg_id)
402 data = display['content']['data']
398 data = display['content']['data']
403 nt.assert_equal(data['text/plain'], u'1')
399 nt.assert_equal(data['text/plain'], u'1')
404
400
@@ -1,854 +1,853 b''
1 """An interactive kernel that talks to frontends over 0MQ."""
1 """An interactive kernel that talks to frontends over 0MQ."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from __future__ import print_function
6 from __future__ import print_function
7
7
8 import getpass
8 import getpass
9 import sys
9 import sys
10 import time
10 import time
11 import traceback
11 import traceback
12 import logging
12 import logging
13 import uuid
13 import uuid
14
14
15 from datetime import datetime
15 from datetime import datetime
16 from signal import (
16 from signal import (
17 signal, default_int_handler, SIGINT
17 signal, default_int_handler, SIGINT
18 )
18 )
19
19
20 import zmq
20 import zmq
21 from zmq.eventloop import ioloop
21 from zmq.eventloop import ioloop
22 from zmq.eventloop.zmqstream import ZMQStream
22 from zmq.eventloop.zmqstream import ZMQStream
23
23
24 from IPython.config.configurable import Configurable
24 from IPython.config.configurable import Configurable
25 from IPython.core.error import StdinNotImplementedError
25 from IPython.core.error import StdinNotImplementedError
26 from IPython.core import release
26 from IPython.core import release
27 from IPython.utils import py3compat
27 from IPython.utils import py3compat
28 from IPython.utils.py3compat import builtin_mod, unicode_type, string_types
28 from IPython.utils.py3compat import builtin_mod, unicode_type, string_types
29 from IPython.utils.jsonutil import json_clean
29 from IPython.utils.jsonutil import json_clean
30 from IPython.utils.tokenutil import token_at_cursor
30 from IPython.utils.tokenutil import token_at_cursor
31 from IPython.utils.traitlets import (
31 from IPython.utils.traitlets import (
32 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
32 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
33 Type, Bool,
33 Type, Bool,
34 )
34 )
35
35
36 from .serialize import serialize_object, unpack_apply_message
36 from .serialize import serialize_object, unpack_apply_message
37 from .session import Session
37 from .session import Session
38 from .zmqshell import ZMQInteractiveShell
38 from .zmqshell import ZMQInteractiveShell
39
39
40
40
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # Main kernel class
42 # Main kernel class
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44
44
45 protocol_version = release.kernel_protocol_version
45 protocol_version = release.kernel_protocol_version
46 ipython_version = release.version
46 ipython_version = release.version
47 language_version = sys.version.split()[0]
47 language_version = sys.version.split()[0]
48
48
49
49
50 class Kernel(Configurable):
50 class Kernel(Configurable):
51
51
52 #---------------------------------------------------------------------------
52 #---------------------------------------------------------------------------
53 # Kernel interface
53 # Kernel interface
54 #---------------------------------------------------------------------------
54 #---------------------------------------------------------------------------
55
55
56 # attribute to override with a GUI
56 # attribute to override with a GUI
57 eventloop = Any(None)
57 eventloop = Any(None)
58 def _eventloop_changed(self, name, old, new):
58 def _eventloop_changed(self, name, old, new):
59 """schedule call to eventloop from IOLoop"""
59 """schedule call to eventloop from IOLoop"""
60 loop = ioloop.IOLoop.instance()
60 loop = ioloop.IOLoop.instance()
61 loop.add_callback(self.enter_eventloop)
61 loop.add_callback(self.enter_eventloop)
62
62
63 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
63 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
64 shell_class = Type(ZMQInteractiveShell)
64 shell_class = Type(ZMQInteractiveShell)
65
65
66 session = Instance(Session)
66 session = Instance(Session)
67 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
67 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
68 shell_streams = List()
68 shell_streams = List()
69 control_stream = Instance(ZMQStream)
69 control_stream = Instance(ZMQStream)
70 iopub_socket = Instance(zmq.Socket)
70 iopub_socket = Instance(zmq.Socket)
71 stdin_socket = Instance(zmq.Socket)
71 stdin_socket = Instance(zmq.Socket)
72 log = Instance(logging.Logger)
72 log = Instance(logging.Logger)
73
73
74 user_module = Any()
74 user_module = Any()
75 def _user_module_changed(self, name, old, new):
75 def _user_module_changed(self, name, old, new):
76 if self.shell is not None:
76 if self.shell is not None:
77 self.shell.user_module = new
77 self.shell.user_module = new
78
78
79 user_ns = Instance(dict, args=None, allow_none=True)
79 user_ns = Instance(dict, args=None, allow_none=True)
80 def _user_ns_changed(self, name, old, new):
80 def _user_ns_changed(self, name, old, new):
81 if self.shell is not None:
81 if self.shell is not None:
82 self.shell.user_ns = new
82 self.shell.user_ns = new
83 self.shell.init_user_ns()
83 self.shell.init_user_ns()
84
84
85 # identities:
85 # identities:
86 int_id = Integer(-1)
86 int_id = Integer(-1)
87 ident = Unicode()
87 ident = Unicode()
88
88
89 def _ident_default(self):
89 def _ident_default(self):
90 return unicode_type(uuid.uuid4())
90 return unicode_type(uuid.uuid4())
91
91
92 # Private interface
92 # Private interface
93
93
94 _darwin_app_nap = Bool(True, config=True,
94 _darwin_app_nap = Bool(True, config=True,
95 help="""Whether to use appnope for compatiblity with OS X App Nap.
95 help="""Whether to use appnope for compatiblity with OS X App Nap.
96
96
97 Only affects OS X >= 10.9.
97 Only affects OS X >= 10.9.
98 """
98 """
99 )
99 )
100
100
101 # track associations with current request
101 # track associations with current request
102 _allow_stdin = Bool(False)
102 _allow_stdin = Bool(False)
103 _parent_header = Dict()
103 _parent_header = Dict()
104 _parent_ident = Any(b'')
104 _parent_ident = Any(b'')
105 # Time to sleep after flushing the stdout/err buffers in each execute
105 # Time to sleep after flushing the stdout/err buffers in each execute
106 # cycle. While this introduces a hard limit on the minimal latency of the
106 # cycle. While this introduces a hard limit on the minimal latency of the
107 # execute cycle, it helps prevent output synchronization problems for
107 # execute cycle, it helps prevent output synchronization problems for
108 # clients.
108 # clients.
109 # Units are in seconds. The minimum zmq latency on local host is probably
109 # Units are in seconds. The minimum zmq latency on local host is probably
110 # ~150 microseconds, set this to 500us for now. We may need to increase it
110 # ~150 microseconds, set this to 500us for now. We may need to increase it
111 # a little if it's not enough after more interactive testing.
111 # a little if it's not enough after more interactive testing.
112 _execute_sleep = Float(0.0005, config=True)
112 _execute_sleep = Float(0.0005, config=True)
113
113
114 # Frequency of the kernel's event loop.
114 # Frequency of the kernel's event loop.
115 # Units are in seconds, kernel subclasses for GUI toolkits may need to
115 # Units are in seconds, kernel subclasses for GUI toolkits may need to
116 # adapt to milliseconds.
116 # adapt to milliseconds.
117 _poll_interval = Float(0.05, config=True)
117 _poll_interval = Float(0.05, config=True)
118
118
119 # If the shutdown was requested over the network, we leave here the
119 # If the shutdown was requested over the network, we leave here the
120 # necessary reply message so it can be sent by our registered atexit
120 # necessary reply message so it can be sent by our registered atexit
121 # handler. This ensures that the reply is only sent to clients truly at
121 # handler. This ensures that the reply is only sent to clients truly at
122 # the end of our shutdown process (which happens after the underlying
122 # the end of our shutdown process (which happens after the underlying
123 # IPython shell's own shutdown).
123 # IPython shell's own shutdown).
124 _shutdown_message = None
124 _shutdown_message = None
125
125
126 # This is a dict of port number that the kernel is listening on. It is set
126 # This is a dict of port number that the kernel is listening on. It is set
127 # by record_ports and used by connect_request.
127 # by record_ports and used by connect_request.
128 _recorded_ports = Dict()
128 _recorded_ports = Dict()
129
129
130 # A reference to the Python builtin 'raw_input' function.
130 # A reference to the Python builtin 'raw_input' function.
131 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
131 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
132 _sys_raw_input = Any()
132 _sys_raw_input = Any()
133 _sys_eval_input = Any()
133 _sys_eval_input = Any()
134
134
135 # set of aborted msg_ids
135 # set of aborted msg_ids
136 aborted = Set()
136 aborted = Set()
137
137
138
138
139 def __init__(self, **kwargs):
139 def __init__(self, **kwargs):
140 super(Kernel, self).__init__(**kwargs)
140 super(Kernel, self).__init__(**kwargs)
141
141
142 # Initialize the InteractiveShell subclass
142 # Initialize the InteractiveShell subclass
143 self.shell = self.shell_class.instance(parent=self,
143 self.shell = self.shell_class.instance(parent=self,
144 profile_dir = self.profile_dir,
144 profile_dir = self.profile_dir,
145 user_module = self.user_module,
145 user_module = self.user_module,
146 user_ns = self.user_ns,
146 user_ns = self.user_ns,
147 kernel = self,
147 kernel = self,
148 )
148 )
149 self.shell.displayhook.session = self.session
149 self.shell.displayhook.session = self.session
150 self.shell.displayhook.pub_socket = self.iopub_socket
150 self.shell.displayhook.pub_socket = self.iopub_socket
151 self.shell.displayhook.topic = self._topic('execute_result')
151 self.shell.displayhook.topic = self._topic('execute_result')
152 self.shell.display_pub.session = self.session
152 self.shell.display_pub.session = self.session
153 self.shell.display_pub.pub_socket = self.iopub_socket
153 self.shell.display_pub.pub_socket = self.iopub_socket
154 self.shell.data_pub.session = self.session
154 self.shell.data_pub.session = self.session
155 self.shell.data_pub.pub_socket = self.iopub_socket
155 self.shell.data_pub.pub_socket = self.iopub_socket
156
156
157 # TMP - hack while developing
157 # TMP - hack while developing
158 self.shell._reply_content = None
158 self.shell._reply_content = None
159
159
160 # Build dict of handlers for message types
160 # Build dict of handlers for message types
161 msg_types = [ 'execute_request', 'complete_request',
161 msg_types = [ 'execute_request', 'complete_request',
162 'inspect_request', 'history_request',
162 'inspect_request', 'history_request',
163 'kernel_info_request',
163 'kernel_info_request',
164 'connect_request', 'shutdown_request',
164 'connect_request', 'shutdown_request',
165 'apply_request',
165 'apply_request',
166 ]
166 ]
167 self.shell_handlers = {}
167 self.shell_handlers = {}
168 for msg_type in msg_types:
168 for msg_type in msg_types:
169 self.shell_handlers[msg_type] = getattr(self, msg_type)
169 self.shell_handlers[msg_type] = getattr(self, msg_type)
170
170
171 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
171 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
172 comm_manager = self.shell.comm_manager
172 comm_manager = self.shell.comm_manager
173 for msg_type in comm_msg_types:
173 for msg_type in comm_msg_types:
174 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
174 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
175
175
176 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
176 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
177 self.control_handlers = {}
177 self.control_handlers = {}
178 for msg_type in control_msg_types:
178 for msg_type in control_msg_types:
179 self.control_handlers[msg_type] = getattr(self, msg_type)
179 self.control_handlers[msg_type] = getattr(self, msg_type)
180
180
181
181
182 def dispatch_control(self, msg):
182 def dispatch_control(self, msg):
183 """dispatch control requests"""
183 """dispatch control requests"""
184 idents,msg = self.session.feed_identities(msg, copy=False)
184 idents,msg = self.session.feed_identities(msg, copy=False)
185 try:
185 try:
186 msg = self.session.unserialize(msg, content=True, copy=False)
186 msg = self.session.unserialize(msg, content=True, copy=False)
187 except:
187 except:
188 self.log.error("Invalid Control Message", exc_info=True)
188 self.log.error("Invalid Control Message", exc_info=True)
189 return
189 return
190
190
191 self.log.debug("Control received: %s", msg)
191 self.log.debug("Control received: %s", msg)
192
192
193 header = msg['header']
193 header = msg['header']
194 msg_id = header['msg_id']
194 msg_id = header['msg_id']
195 msg_type = header['msg_type']
195 msg_type = header['msg_type']
196
196
197 handler = self.control_handlers.get(msg_type, None)
197 handler = self.control_handlers.get(msg_type, None)
198 if handler is None:
198 if handler is None:
199 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
199 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
200 else:
200 else:
201 try:
201 try:
202 handler(self.control_stream, idents, msg)
202 handler(self.control_stream, idents, msg)
203 except Exception:
203 except Exception:
204 self.log.error("Exception in control handler:", exc_info=True)
204 self.log.error("Exception in control handler:", exc_info=True)
205
205
206 def dispatch_shell(self, stream, msg):
206 def dispatch_shell(self, stream, msg):
207 """dispatch shell requests"""
207 """dispatch shell requests"""
208 # flush control requests first
208 # flush control requests first
209 if self.control_stream:
209 if self.control_stream:
210 self.control_stream.flush()
210 self.control_stream.flush()
211
211
212 idents,msg = self.session.feed_identities(msg, copy=False)
212 idents,msg = self.session.feed_identities(msg, copy=False)
213 try:
213 try:
214 msg = self.session.unserialize(msg, content=True, copy=False)
214 msg = self.session.unserialize(msg, content=True, copy=False)
215 except:
215 except:
216 self.log.error("Invalid Message", exc_info=True)
216 self.log.error("Invalid Message", exc_info=True)
217 return
217 return
218
218
219 header = msg['header']
219 header = msg['header']
220 msg_id = header['msg_id']
220 msg_id = header['msg_id']
221 msg_type = msg['header']['msg_type']
221 msg_type = msg['header']['msg_type']
222
222
223 # Print some info about this message and leave a '--->' marker, so it's
223 # Print some info about this message and leave a '--->' marker, so it's
224 # easier to trace visually the message chain when debugging. Each
224 # easier to trace visually the message chain when debugging. Each
225 # handler prints its message at the end.
225 # handler prints its message at the end.
226 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
226 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
227 self.log.debug(' Content: %s\n --->\n ', msg['content'])
227 self.log.debug(' Content: %s\n --->\n ', msg['content'])
228
228
229 if msg_id in self.aborted:
229 if msg_id in self.aborted:
230 self.aborted.remove(msg_id)
230 self.aborted.remove(msg_id)
231 # is it safe to assume a msg_id will not be resubmitted?
231 # is it safe to assume a msg_id will not be resubmitted?
232 reply_type = msg_type.split('_')[0] + '_reply'
232 reply_type = msg_type.split('_')[0] + '_reply'
233 status = {'status' : 'aborted'}
233 status = {'status' : 'aborted'}
234 md = {'engine' : self.ident}
234 md = {'engine' : self.ident}
235 md.update(status)
235 md.update(status)
236 reply_msg = self.session.send(stream, reply_type, metadata=md,
236 reply_msg = self.session.send(stream, reply_type, metadata=md,
237 content=status, parent=msg, ident=idents)
237 content=status, parent=msg, ident=idents)
238 return
238 return
239
239
240 handler = self.shell_handlers.get(msg_type, None)
240 handler = self.shell_handlers.get(msg_type, None)
241 if handler is None:
241 if handler is None:
242 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
242 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
243 else:
243 else:
244 # ensure default_int_handler during handler call
244 # ensure default_int_handler during handler call
245 sig = signal(SIGINT, default_int_handler)
245 sig = signal(SIGINT, default_int_handler)
246 self.log.debug("%s: %s", msg_type, msg)
246 self.log.debug("%s: %s", msg_type, msg)
247 try:
247 try:
248 handler(stream, idents, msg)
248 handler(stream, idents, msg)
249 except Exception:
249 except Exception:
250 self.log.error("Exception in message handler:", exc_info=True)
250 self.log.error("Exception in message handler:", exc_info=True)
251 finally:
251 finally:
252 signal(SIGINT, sig)
252 signal(SIGINT, sig)
253
253
254 def enter_eventloop(self):
254 def enter_eventloop(self):
255 """enter eventloop"""
255 """enter eventloop"""
256 self.log.info("entering eventloop %s", self.eventloop)
256 self.log.info("entering eventloop %s", self.eventloop)
257 for stream in self.shell_streams:
257 for stream in self.shell_streams:
258 # flush any pending replies,
258 # flush any pending replies,
259 # which may be skipped by entering the eventloop
259 # which may be skipped by entering the eventloop
260 stream.flush(zmq.POLLOUT)
260 stream.flush(zmq.POLLOUT)
261 # restore default_int_handler
261 # restore default_int_handler
262 signal(SIGINT, default_int_handler)
262 signal(SIGINT, default_int_handler)
263 while self.eventloop is not None:
263 while self.eventloop is not None:
264 try:
264 try:
265 self.eventloop(self)
265 self.eventloop(self)
266 except KeyboardInterrupt:
266 except KeyboardInterrupt:
267 # Ctrl-C shouldn't crash the kernel
267 # Ctrl-C shouldn't crash the kernel
268 self.log.error("KeyboardInterrupt caught in kernel")
268 self.log.error("KeyboardInterrupt caught in kernel")
269 continue
269 continue
270 else:
270 else:
271 # eventloop exited cleanly, this means we should stop (right?)
271 # eventloop exited cleanly, this means we should stop (right?)
272 self.eventloop = None
272 self.eventloop = None
273 break
273 break
274 self.log.info("exiting eventloop")
274 self.log.info("exiting eventloop")
275
275
276 def start(self):
276 def start(self):
277 """register dispatchers for streams"""
277 """register dispatchers for streams"""
278 self.shell.exit_now = False
278 self.shell.exit_now = False
279 if self.control_stream:
279 if self.control_stream:
280 self.control_stream.on_recv(self.dispatch_control, copy=False)
280 self.control_stream.on_recv(self.dispatch_control, copy=False)
281
281
282 def make_dispatcher(stream):
282 def make_dispatcher(stream):
283 def dispatcher(msg):
283 def dispatcher(msg):
284 return self.dispatch_shell(stream, msg)
284 return self.dispatch_shell(stream, msg)
285 return dispatcher
285 return dispatcher
286
286
287 for s in self.shell_streams:
287 for s in self.shell_streams:
288 s.on_recv(make_dispatcher(s), copy=False)
288 s.on_recv(make_dispatcher(s), copy=False)
289
289
290 # publish idle status
290 # publish idle status
291 self._publish_status('starting')
291 self._publish_status('starting')
292
292
293 def do_one_iteration(self):
293 def do_one_iteration(self):
294 """step eventloop just once"""
294 """step eventloop just once"""
295 if self.control_stream:
295 if self.control_stream:
296 self.control_stream.flush()
296 self.control_stream.flush()
297 for stream in self.shell_streams:
297 for stream in self.shell_streams:
298 # handle at most one request per iteration
298 # handle at most one request per iteration
299 stream.flush(zmq.POLLIN, 1)
299 stream.flush(zmq.POLLIN, 1)
300 stream.flush(zmq.POLLOUT)
300 stream.flush(zmq.POLLOUT)
301
301
302
302
303 def record_ports(self, ports):
303 def record_ports(self, ports):
304 """Record the ports that this kernel is using.
304 """Record the ports that this kernel is using.
305
305
306 The creator of the Kernel instance must call this methods if they
306 The creator of the Kernel instance must call this methods if they
307 want the :meth:`connect_request` method to return the port numbers.
307 want the :meth:`connect_request` method to return the port numbers.
308 """
308 """
309 self._recorded_ports = ports
309 self._recorded_ports = ports
310
310
311 #---------------------------------------------------------------------------
311 #---------------------------------------------------------------------------
312 # Kernel request handlers
312 # Kernel request handlers
313 #---------------------------------------------------------------------------
313 #---------------------------------------------------------------------------
314
314
315 def _make_metadata(self, other=None):
315 def _make_metadata(self, other=None):
316 """init metadata dict, for execute/apply_reply"""
316 """init metadata dict, for execute/apply_reply"""
317 new_md = {
317 new_md = {
318 'dependencies_met' : True,
318 'dependencies_met' : True,
319 'engine' : self.ident,
319 'engine' : self.ident,
320 'started': datetime.now(),
320 'started': datetime.now(),
321 }
321 }
322 if other:
322 if other:
323 new_md.update(other)
323 new_md.update(other)
324 return new_md
324 return new_md
325
325
326 def _publish_execute_input(self, code, parent, execution_count):
326 def _publish_execute_input(self, code, parent, execution_count):
327 """Publish the code request on the iopub stream."""
327 """Publish the code request on the iopub stream."""
328
328
329 self.session.send(self.iopub_socket, u'execute_input',
329 self.session.send(self.iopub_socket, u'execute_input',
330 {u'code':code, u'execution_count': execution_count},
330 {u'code':code, u'execution_count': execution_count},
331 parent=parent, ident=self._topic('execute_input')
331 parent=parent, ident=self._topic('execute_input')
332 )
332 )
333
333
334 def _publish_status(self, status, parent=None):
334 def _publish_status(self, status, parent=None):
335 """send status (busy/idle) on IOPub"""
335 """send status (busy/idle) on IOPub"""
336 self.session.send(self.iopub_socket,
336 self.session.send(self.iopub_socket,
337 u'status',
337 u'status',
338 {u'execution_state': status},
338 {u'execution_state': status},
339 parent=parent,
339 parent=parent,
340 ident=self._topic('status'),
340 ident=self._topic('status'),
341 )
341 )
342
342
343 def _forward_input(self, allow_stdin=False):
343 def _forward_input(self, allow_stdin=False):
344 """Forward raw_input and getpass to the current frontend.
344 """Forward raw_input and getpass to the current frontend.
345
345
346 via input_request
346 via input_request
347 """
347 """
348 self._allow_stdin = allow_stdin
348 self._allow_stdin = allow_stdin
349
349
350 if py3compat.PY3:
350 if py3compat.PY3:
351 self._sys_raw_input = builtin_mod.input
351 self._sys_raw_input = builtin_mod.input
352 builtin_mod.input = self.raw_input
352 builtin_mod.input = self.raw_input
353 else:
353 else:
354 self._sys_raw_input = builtin_mod.raw_input
354 self._sys_raw_input = builtin_mod.raw_input
355 self._sys_eval_input = builtin_mod.input
355 self._sys_eval_input = builtin_mod.input
356 builtin_mod.raw_input = self.raw_input
356 builtin_mod.raw_input = self.raw_input
357 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
357 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
358 self._save_getpass = getpass.getpass
358 self._save_getpass = getpass.getpass
359 getpass.getpass = self.getpass
359 getpass.getpass = self.getpass
360
360
361 def _restore_input(self):
361 def _restore_input(self):
362 """Restore raw_input, getpass"""
362 """Restore raw_input, getpass"""
363 if py3compat.PY3:
363 if py3compat.PY3:
364 builtin_mod.input = self._sys_raw_input
364 builtin_mod.input = self._sys_raw_input
365 else:
365 else:
366 builtin_mod.raw_input = self._sys_raw_input
366 builtin_mod.raw_input = self._sys_raw_input
367 builtin_mod.input = self._sys_eval_input
367 builtin_mod.input = self._sys_eval_input
368
368
369 getpass.getpass = self._save_getpass
369 getpass.getpass = self._save_getpass
370
370
371 def set_parent(self, ident, parent):
371 def set_parent(self, ident, parent):
372 """Record the parent state
372 """Record the parent state
373
373
374 For associating side effects with their requests.
374 For associating side effects with their requests.
375 """
375 """
376 self._parent_ident = ident
376 self._parent_ident = ident
377 self._parent_header = parent
377 self._parent_header = parent
378 self.shell.set_parent(parent)
378 self.shell.set_parent(parent)
379
379
380 def execute_request(self, stream, ident, parent):
380 def execute_request(self, stream, ident, parent):
381 """handle an execute_request"""
381 """handle an execute_request"""
382
382
383 self._publish_status(u'busy', parent)
383 self._publish_status(u'busy', parent)
384
384
385 try:
385 try:
386 content = parent[u'content']
386 content = parent[u'content']
387 code = py3compat.cast_unicode_py2(content[u'code'])
387 code = py3compat.cast_unicode_py2(content[u'code'])
388 silent = content[u'silent']
388 silent = content[u'silent']
389 store_history = content.get(u'store_history', not silent)
389 store_history = content.get(u'store_history', not silent)
390 except:
390 except:
391 self.log.error("Got bad msg: ")
391 self.log.error("Got bad msg: ")
392 self.log.error("%s", parent)
392 self.log.error("%s", parent)
393 return
393 return
394
394
395 md = self._make_metadata(parent['metadata'])
395 md = self._make_metadata(parent['metadata'])
396
396
397 shell = self.shell # we'll need this a lot here
397 shell = self.shell # we'll need this a lot here
398
398
399 self._forward_input(content.get('allow_stdin', False))
399 self._forward_input(content.get('allow_stdin', False))
400 # Set the parent message of the display hook and out streams.
400 # Set the parent message of the display hook and out streams.
401 self.set_parent(ident, parent)
401 self.set_parent(ident, parent)
402
402
403 # Re-broadcast our input for the benefit of listening clients, and
403 # Re-broadcast our input for the benefit of listening clients, and
404 # start computing output
404 # start computing output
405 if not silent:
405 if not silent:
406 self._publish_execute_input(code, parent, shell.execution_count)
406 self._publish_execute_input(code, parent, shell.execution_count)
407
407
408 reply_content = {}
408 reply_content = {}
409 # FIXME: the shell calls the exception handler itself.
409 # FIXME: the shell calls the exception handler itself.
410 shell._reply_content = None
410 shell._reply_content = None
411 try:
411 try:
412 shell.run_cell(code, store_history=store_history, silent=silent)
412 shell.run_cell(code, store_history=store_history, silent=silent)
413 except:
413 except:
414 status = u'error'
414 status = u'error'
415 # FIXME: this code right now isn't being used yet by default,
415 # FIXME: this code right now isn't being used yet by default,
416 # because the run_cell() call above directly fires off exception
416 # because the run_cell() call above directly fires off exception
417 # reporting. This code, therefore, is only active in the scenario
417 # reporting. This code, therefore, is only active in the scenario
418 # where runlines itself has an unhandled exception. We need to
418 # where runlines itself has an unhandled exception. We need to
419 # uniformize this, for all exception construction to come from a
419 # uniformize this, for all exception construction to come from a
420 # single location in the codbase.
420 # single location in the codbase.
421 etype, evalue, tb = sys.exc_info()
421 etype, evalue, tb = sys.exc_info()
422 tb_list = traceback.format_exception(etype, evalue, tb)
422 tb_list = traceback.format_exception(etype, evalue, tb)
423 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
423 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
424 else:
424 else:
425 status = u'ok'
425 status = u'ok'
426 finally:
426 finally:
427 self._restore_input()
427 self._restore_input()
428
428
429 reply_content[u'status'] = status
429 reply_content[u'status'] = status
430
430
431 # Return the execution counter so clients can display prompts
431 # Return the execution counter so clients can display prompts
432 reply_content['execution_count'] = shell.execution_count - 1
432 reply_content['execution_count'] = shell.execution_count - 1
433
433
434 # FIXME - fish exception info out of shell, possibly left there by
434 # FIXME - fish exception info out of shell, possibly left there by
435 # runlines. We'll need to clean up this logic later.
435 # runlines. We'll need to clean up this logic later.
436 if shell._reply_content is not None:
436 if shell._reply_content is not None:
437 reply_content.update(shell._reply_content)
437 reply_content.update(shell._reply_content)
438 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
438 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
439 reply_content['engine_info'] = e_info
439 reply_content['engine_info'] = e_info
440 # reset after use
440 # reset after use
441 shell._reply_content = None
441 shell._reply_content = None
442
442
443 if 'traceback' in reply_content:
443 if 'traceback' in reply_content:
444 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
444 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
445
445
446
446
447 # At this point, we can tell whether the main code execution succeeded
447 # At this point, we can tell whether the main code execution succeeded
448 # or not. If it did, we proceed to evaluate user_expressions
448 # or not. If it did, we proceed to evaluate user_expressions
449 if reply_content['status'] == 'ok':
449 if reply_content['status'] == 'ok':
450 reply_content[u'user_expressions'] = \
450 reply_content[u'user_expressions'] = \
451 shell.user_expressions(content.get(u'user_expressions', {}))
451 shell.user_expressions(content.get(u'user_expressions', {}))
452 else:
452 else:
453 # If there was an error, don't even try to compute expressions
453 # If there was an error, don't even try to compute expressions
454 reply_content[u'user_expressions'] = {}
454 reply_content[u'user_expressions'] = {}
455
455
456 # Payloads should be retrieved regardless of outcome, so we can both
456 # Payloads should be retrieved regardless of outcome, so we can both
457 # recover partial output (that could have been generated early in a
457 # recover partial output (that could have been generated early in a
458 # block, before an error) and clear the payload system always.
458 # block, before an error) and clear the payload system always.
459 reply_content[u'payload'] = shell.payload_manager.read_payload()
459 reply_content[u'payload'] = shell.payload_manager.read_payload()
460 # Be agressive about clearing the payload because we don't want
460 # Be agressive about clearing the payload because we don't want
461 # it to sit in memory until the next execute_request comes in.
461 # it to sit in memory until the next execute_request comes in.
462 shell.payload_manager.clear_payload()
462 shell.payload_manager.clear_payload()
463
463
464 # Flush output before sending the reply.
464 # Flush output before sending the reply.
465 sys.stdout.flush()
465 sys.stdout.flush()
466 sys.stderr.flush()
466 sys.stderr.flush()
467 # FIXME: on rare occasions, the flush doesn't seem to make it to the
467 # FIXME: on rare occasions, the flush doesn't seem to make it to the
468 # clients... This seems to mitigate the problem, but we definitely need
468 # clients... This seems to mitigate the problem, but we definitely need
469 # to better understand what's going on.
469 # to better understand what's going on.
470 if self._execute_sleep:
470 if self._execute_sleep:
471 time.sleep(self._execute_sleep)
471 time.sleep(self._execute_sleep)
472
472
473 # Send the reply.
473 # Send the reply.
474 reply_content = json_clean(reply_content)
474 reply_content = json_clean(reply_content)
475
475
476 md['status'] = reply_content['status']
476 md['status'] = reply_content['status']
477 if reply_content['status'] == 'error' and \
477 if reply_content['status'] == 'error' and \
478 reply_content['ename'] == 'UnmetDependency':
478 reply_content['ename'] == 'UnmetDependency':
479 md['dependencies_met'] = False
479 md['dependencies_met'] = False
480
480
481 reply_msg = self.session.send(stream, u'execute_reply',
481 reply_msg = self.session.send(stream, u'execute_reply',
482 reply_content, parent, metadata=md,
482 reply_content, parent, metadata=md,
483 ident=ident)
483 ident=ident)
484
484
485 self.log.debug("%s", reply_msg)
485 self.log.debug("%s", reply_msg)
486
486
487 if not silent and reply_msg['content']['status'] == u'error':
487 if not silent and reply_msg['content']['status'] == u'error':
488 self._abort_queues()
488 self._abort_queues()
489
489
490 self._publish_status(u'idle', parent)
490 self._publish_status(u'idle', parent)
491
491
492 def complete_request(self, stream, ident, parent):
492 def complete_request(self, stream, ident, parent):
493 content = parent['content']
493 content = parent['content']
494 code = content['code']
494 code = content['code']
495 cursor_pos = content['cursor_pos']
495 cursor_pos = content['cursor_pos']
496
496
497 txt, matches = self.shell.complete('', code, cursor_pos)
497 txt, matches = self.shell.complete('', code, cursor_pos)
498 matches = {'matches' : matches,
498 matches = {'matches' : matches,
499 'cursor_end' : cursor_pos,
499 'cursor_end' : cursor_pos,
500 'cursor_start' : cursor_pos - len(txt),
500 'cursor_start' : cursor_pos - len(txt),
501 'metadata' : {},
501 'metadata' : {},
502 'status' : 'ok'}
502 'status' : 'ok'}
503 matches = json_clean(matches)
503 matches = json_clean(matches)
504 completion_msg = self.session.send(stream, 'complete_reply',
504 completion_msg = self.session.send(stream, 'complete_reply',
505 matches, parent, ident)
505 matches, parent, ident)
506 self.log.debug("%s", completion_msg)
506 self.log.debug("%s", completion_msg)
507
507
508 def inspect_request(self, stream, ident, parent):
508 def inspect_request(self, stream, ident, parent):
509 content = parent['content']
509 content = parent['content']
510
510
511 name = token_at_cursor(content['code'], content['cursor_pos'])
511 name = token_at_cursor(content['code'], content['cursor_pos'])
512 info = self.shell.object_inspect(name)
512 info = self.shell.object_inspect(name)
513
513
514 reply_content = {'status' : 'ok'}
514 reply_content = {'status' : 'ok'}
515 reply_content['data'] = data = {}
515 reply_content['data'] = data = {}
516 reply_content['metadata'] = {}
516 reply_content['metadata'] = {}
517 reply_content['name'] = name
518 reply_content['found'] = info['found']
517 reply_content['found'] = info['found']
519 if info['found']:
518 if info['found']:
520 info_text = self.shell.object_inspect_text(
519 info_text = self.shell.object_inspect_text(
521 name,
520 name,
522 detail_level=content.get('detail_level', 0),
521 detail_level=content.get('detail_level', 0),
523 )
522 )
524 reply_content['data']['text/plain'] = info_text
523 reply_content['data']['text/plain'] = info_text
525 # Before we send this object over, we scrub it for JSON usage
524 # Before we send this object over, we scrub it for JSON usage
526 reply_content = json_clean(reply_content)
525 reply_content = json_clean(reply_content)
527 msg = self.session.send(stream, 'inspect_reply',
526 msg = self.session.send(stream, 'inspect_reply',
528 reply_content, parent, ident)
527 reply_content, parent, ident)
529 self.log.debug("%s", msg)
528 self.log.debug("%s", msg)
530
529
531 def history_request(self, stream, ident, parent):
530 def history_request(self, stream, ident, parent):
532 # We need to pull these out, as passing **kwargs doesn't work with
531 # We need to pull these out, as passing **kwargs doesn't work with
533 # unicode keys before Python 2.6.5.
532 # unicode keys before Python 2.6.5.
534 hist_access_type = parent['content']['hist_access_type']
533 hist_access_type = parent['content']['hist_access_type']
535 raw = parent['content']['raw']
534 raw = parent['content']['raw']
536 output = parent['content']['output']
535 output = parent['content']['output']
537 if hist_access_type == 'tail':
536 if hist_access_type == 'tail':
538 n = parent['content']['n']
537 n = parent['content']['n']
539 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
538 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
540 include_latest=True)
539 include_latest=True)
541
540
542 elif hist_access_type == 'range':
541 elif hist_access_type == 'range':
543 session = parent['content']['session']
542 session = parent['content']['session']
544 start = parent['content']['start']
543 start = parent['content']['start']
545 stop = parent['content']['stop']
544 stop = parent['content']['stop']
546 hist = self.shell.history_manager.get_range(session, start, stop,
545 hist = self.shell.history_manager.get_range(session, start, stop,
547 raw=raw, output=output)
546 raw=raw, output=output)
548
547
549 elif hist_access_type == 'search':
548 elif hist_access_type == 'search':
550 n = parent['content'].get('n')
549 n = parent['content'].get('n')
551 unique = parent['content'].get('unique', False)
550 unique = parent['content'].get('unique', False)
552 pattern = parent['content']['pattern']
551 pattern = parent['content']['pattern']
553 hist = self.shell.history_manager.search(
552 hist = self.shell.history_manager.search(
554 pattern, raw=raw, output=output, n=n, unique=unique)
553 pattern, raw=raw, output=output, n=n, unique=unique)
555
554
556 else:
555 else:
557 hist = []
556 hist = []
558 hist = list(hist)
557 hist = list(hist)
559 content = {'history' : hist}
558 content = {'history' : hist}
560 content = json_clean(content)
559 content = json_clean(content)
561 msg = self.session.send(stream, 'history_reply',
560 msg = self.session.send(stream, 'history_reply',
562 content, parent, ident)
561 content, parent, ident)
563 self.log.debug("Sending history reply with %i entries", len(hist))
562 self.log.debug("Sending history reply with %i entries", len(hist))
564
563
565 def connect_request(self, stream, ident, parent):
564 def connect_request(self, stream, ident, parent):
566 if self._recorded_ports is not None:
565 if self._recorded_ports is not None:
567 content = self._recorded_ports.copy()
566 content = self._recorded_ports.copy()
568 else:
567 else:
569 content = {}
568 content = {}
570 msg = self.session.send(stream, 'connect_reply',
569 msg = self.session.send(stream, 'connect_reply',
571 content, parent, ident)
570 content, parent, ident)
572 self.log.debug("%s", msg)
571 self.log.debug("%s", msg)
573
572
574 def kernel_info_request(self, stream, ident, parent):
573 def kernel_info_request(self, stream, ident, parent):
575 vinfo = {
574 vinfo = {
576 'protocol_version': protocol_version,
575 'protocol_version': protocol_version,
577 'implementation': 'ipython',
576 'implementation': 'ipython',
578 'implementation_version': ipython_version,
577 'implementation_version': ipython_version,
579 'language_version': language_version,
578 'language_version': language_version,
580 'language': 'python',
579 'language': 'python',
581 'banner': self.shell.banner,
580 'banner': self.shell.banner,
582 }
581 }
583 msg = self.session.send(stream, 'kernel_info_reply',
582 msg = self.session.send(stream, 'kernel_info_reply',
584 vinfo, parent, ident)
583 vinfo, parent, ident)
585 self.log.debug("%s", msg)
584 self.log.debug("%s", msg)
586
585
587 def shutdown_request(self, stream, ident, parent):
586 def shutdown_request(self, stream, ident, parent):
588 self.shell.exit_now = True
587 self.shell.exit_now = True
589 content = dict(status='ok')
588 content = dict(status='ok')
590 content.update(parent['content'])
589 content.update(parent['content'])
591 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
590 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
592 # same content, but different msg_id for broadcasting on IOPub
591 # same content, but different msg_id for broadcasting on IOPub
593 self._shutdown_message = self.session.msg(u'shutdown_reply',
592 self._shutdown_message = self.session.msg(u'shutdown_reply',
594 content, parent
593 content, parent
595 )
594 )
596
595
597 self._at_shutdown()
596 self._at_shutdown()
598 # call sys.exit after a short delay
597 # call sys.exit after a short delay
599 loop = ioloop.IOLoop.instance()
598 loop = ioloop.IOLoop.instance()
600 loop.add_timeout(time.time()+0.1, loop.stop)
599 loop.add_timeout(time.time()+0.1, loop.stop)
601
600
602 #---------------------------------------------------------------------------
601 #---------------------------------------------------------------------------
603 # Engine methods
602 # Engine methods
604 #---------------------------------------------------------------------------
603 #---------------------------------------------------------------------------
605
604
606 def apply_request(self, stream, ident, parent):
605 def apply_request(self, stream, ident, parent):
607 try:
606 try:
608 content = parent[u'content']
607 content = parent[u'content']
609 bufs = parent[u'buffers']
608 bufs = parent[u'buffers']
610 msg_id = parent['header']['msg_id']
609 msg_id = parent['header']['msg_id']
611 except:
610 except:
612 self.log.error("Got bad msg: %s", parent, exc_info=True)
611 self.log.error("Got bad msg: %s", parent, exc_info=True)
613 return
612 return
614
613
615 self._publish_status(u'busy', parent)
614 self._publish_status(u'busy', parent)
616
615
617 # Set the parent message of the display hook and out streams.
616 # Set the parent message of the display hook and out streams.
618 shell = self.shell
617 shell = self.shell
619 shell.set_parent(parent)
618 shell.set_parent(parent)
620
619
621 # execute_input_msg = self.session.msg(u'execute_input',{u'code':code}, parent=parent)
620 # execute_input_msg = self.session.msg(u'execute_input',{u'code':code}, parent=parent)
622 # self.iopub_socket.send(execute_input_msg)
621 # self.iopub_socket.send(execute_input_msg)
623 # self.session.send(self.iopub_socket, u'execute_input', {u'code':code},parent=parent)
622 # self.session.send(self.iopub_socket, u'execute_input', {u'code':code},parent=parent)
624 md = self._make_metadata(parent['metadata'])
623 md = self._make_metadata(parent['metadata'])
625 try:
624 try:
626 working = shell.user_ns
625 working = shell.user_ns
627
626
628 prefix = "_"+str(msg_id).replace("-","")+"_"
627 prefix = "_"+str(msg_id).replace("-","")+"_"
629
628
630 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
629 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
631
630
632 fname = getattr(f, '__name__', 'f')
631 fname = getattr(f, '__name__', 'f')
633
632
634 fname = prefix+"f"
633 fname = prefix+"f"
635 argname = prefix+"args"
634 argname = prefix+"args"
636 kwargname = prefix+"kwargs"
635 kwargname = prefix+"kwargs"
637 resultname = prefix+"result"
636 resultname = prefix+"result"
638
637
639 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
638 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
640 # print ns
639 # print ns
641 working.update(ns)
640 working.update(ns)
642 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
641 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
643 try:
642 try:
644 exec(code, shell.user_global_ns, shell.user_ns)
643 exec(code, shell.user_global_ns, shell.user_ns)
645 result = working.get(resultname)
644 result = working.get(resultname)
646 finally:
645 finally:
647 for key in ns:
646 for key in ns:
648 working.pop(key)
647 working.pop(key)
649
648
650 result_buf = serialize_object(result,
649 result_buf = serialize_object(result,
651 buffer_threshold=self.session.buffer_threshold,
650 buffer_threshold=self.session.buffer_threshold,
652 item_threshold=self.session.item_threshold,
651 item_threshold=self.session.item_threshold,
653 )
652 )
654
653
655 except:
654 except:
656 # invoke IPython traceback formatting
655 # invoke IPython traceback formatting
657 shell.showtraceback()
656 shell.showtraceback()
658 # FIXME - fish exception info out of shell, possibly left there by
657 # FIXME - fish exception info out of shell, possibly left there by
659 # run_code. We'll need to clean up this logic later.
658 # run_code. We'll need to clean up this logic later.
660 reply_content = {}
659 reply_content = {}
661 if shell._reply_content is not None:
660 if shell._reply_content is not None:
662 reply_content.update(shell._reply_content)
661 reply_content.update(shell._reply_content)
663 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
662 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
664 reply_content['engine_info'] = e_info
663 reply_content['engine_info'] = e_info
665 # reset after use
664 # reset after use
666 shell._reply_content = None
665 shell._reply_content = None
667
666
668 self.session.send(self.iopub_socket, u'error', reply_content, parent=parent,
667 self.session.send(self.iopub_socket, u'error', reply_content, parent=parent,
669 ident=self._topic('error'))
668 ident=self._topic('error'))
670 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
669 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
671 result_buf = []
670 result_buf = []
672
671
673 if reply_content['ename'] == 'UnmetDependency':
672 if reply_content['ename'] == 'UnmetDependency':
674 md['dependencies_met'] = False
673 md['dependencies_met'] = False
675 else:
674 else:
676 reply_content = {'status' : 'ok'}
675 reply_content = {'status' : 'ok'}
677
676
678 # put 'ok'/'error' status in header, for scheduler introspection:
677 # put 'ok'/'error' status in header, for scheduler introspection:
679 md['status'] = reply_content['status']
678 md['status'] = reply_content['status']
680
679
681 # flush i/o
680 # flush i/o
682 sys.stdout.flush()
681 sys.stdout.flush()
683 sys.stderr.flush()
682 sys.stderr.flush()
684
683
685 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
684 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
686 parent=parent, ident=ident,buffers=result_buf, metadata=md)
685 parent=parent, ident=ident,buffers=result_buf, metadata=md)
687
686
688 self._publish_status(u'idle', parent)
687 self._publish_status(u'idle', parent)
689
688
690 #---------------------------------------------------------------------------
689 #---------------------------------------------------------------------------
691 # Control messages
690 # Control messages
692 #---------------------------------------------------------------------------
691 #---------------------------------------------------------------------------
693
692
694 def abort_request(self, stream, ident, parent):
693 def abort_request(self, stream, ident, parent):
695 """abort a specifig msg by id"""
694 """abort a specifig msg by id"""
696 msg_ids = parent['content'].get('msg_ids', None)
695 msg_ids = parent['content'].get('msg_ids', None)
697 if isinstance(msg_ids, string_types):
696 if isinstance(msg_ids, string_types):
698 msg_ids = [msg_ids]
697 msg_ids = [msg_ids]
699 if not msg_ids:
698 if not msg_ids:
700 self.abort_queues()
699 self.abort_queues()
701 for mid in msg_ids:
700 for mid in msg_ids:
702 self.aborted.add(str(mid))
701 self.aborted.add(str(mid))
703
702
704 content = dict(status='ok')
703 content = dict(status='ok')
705 reply_msg = self.session.send(stream, 'abort_reply', content=content,
704 reply_msg = self.session.send(stream, 'abort_reply', content=content,
706 parent=parent, ident=ident)
705 parent=parent, ident=ident)
707 self.log.debug("%s", reply_msg)
706 self.log.debug("%s", reply_msg)
708
707
709 def clear_request(self, stream, idents, parent):
708 def clear_request(self, stream, idents, parent):
710 """Clear our namespace."""
709 """Clear our namespace."""
711 self.shell.reset(False)
710 self.shell.reset(False)
712 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
711 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
713 content = dict(status='ok'))
712 content = dict(status='ok'))
714
713
715
714
716 #---------------------------------------------------------------------------
715 #---------------------------------------------------------------------------
717 # Protected interface
716 # Protected interface
718 #---------------------------------------------------------------------------
717 #---------------------------------------------------------------------------
719
718
720 def _wrap_exception(self, method=None):
719 def _wrap_exception(self, method=None):
721 # import here, because _wrap_exception is only used in parallel,
720 # import here, because _wrap_exception is only used in parallel,
722 # and parallel has higher min pyzmq version
721 # and parallel has higher min pyzmq version
723 from IPython.parallel.error import wrap_exception
722 from IPython.parallel.error import wrap_exception
724 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
723 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
725 content = wrap_exception(e_info)
724 content = wrap_exception(e_info)
726 return content
725 return content
727
726
728 def _topic(self, topic):
727 def _topic(self, topic):
729 """prefixed topic for IOPub messages"""
728 """prefixed topic for IOPub messages"""
730 if self.int_id >= 0:
729 if self.int_id >= 0:
731 base = "engine.%i" % self.int_id
730 base = "engine.%i" % self.int_id
732 else:
731 else:
733 base = "kernel.%s" % self.ident
732 base = "kernel.%s" % self.ident
734
733
735 return py3compat.cast_bytes("%s.%s" % (base, topic))
734 return py3compat.cast_bytes("%s.%s" % (base, topic))
736
735
737 def _abort_queues(self):
736 def _abort_queues(self):
738 for stream in self.shell_streams:
737 for stream in self.shell_streams:
739 if stream:
738 if stream:
740 self._abort_queue(stream)
739 self._abort_queue(stream)
741
740
742 def _abort_queue(self, stream):
741 def _abort_queue(self, stream):
743 poller = zmq.Poller()
742 poller = zmq.Poller()
744 poller.register(stream.socket, zmq.POLLIN)
743 poller.register(stream.socket, zmq.POLLIN)
745 while True:
744 while True:
746 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
745 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
747 if msg is None:
746 if msg is None:
748 return
747 return
749
748
750 self.log.info("Aborting:")
749 self.log.info("Aborting:")
751 self.log.info("%s", msg)
750 self.log.info("%s", msg)
752 msg_type = msg['header']['msg_type']
751 msg_type = msg['header']['msg_type']
753 reply_type = msg_type.split('_')[0] + '_reply'
752 reply_type = msg_type.split('_')[0] + '_reply'
754
753
755 status = {'status' : 'aborted'}
754 status = {'status' : 'aborted'}
756 md = {'engine' : self.ident}
755 md = {'engine' : self.ident}
757 md.update(status)
756 md.update(status)
758 reply_msg = self.session.send(stream, reply_type, metadata=md,
757 reply_msg = self.session.send(stream, reply_type, metadata=md,
759 content=status, parent=msg, ident=idents)
758 content=status, parent=msg, ident=idents)
760 self.log.debug("%s", reply_msg)
759 self.log.debug("%s", reply_msg)
761 # We need to wait a bit for requests to come in. This can probably
760 # We need to wait a bit for requests to come in. This can probably
762 # be set shorter for true asynchronous clients.
761 # be set shorter for true asynchronous clients.
763 poller.poll(50)
762 poller.poll(50)
764
763
765
764
766 def _no_raw_input(self):
765 def _no_raw_input(self):
767 """Raise StdinNotImplentedError if active frontend doesn't support
766 """Raise StdinNotImplentedError if active frontend doesn't support
768 stdin."""
767 stdin."""
769 raise StdinNotImplementedError("raw_input was called, but this "
768 raise StdinNotImplementedError("raw_input was called, but this "
770 "frontend does not support stdin.")
769 "frontend does not support stdin.")
771
770
772 def getpass(self, prompt=''):
771 def getpass(self, prompt=''):
773 """Forward getpass to frontends
772 """Forward getpass to frontends
774
773
775 Raises
774 Raises
776 ------
775 ------
777 StdinNotImplentedError if active frontend doesn't support stdin.
776 StdinNotImplentedError if active frontend doesn't support stdin.
778 """
777 """
779 if not self._allow_stdin:
778 if not self._allow_stdin:
780 raise StdinNotImplementedError(
779 raise StdinNotImplementedError(
781 "getpass was called, but this frontend does not support input requests."
780 "getpass was called, but this frontend does not support input requests."
782 )
781 )
783 return self._input_request(prompt,
782 return self._input_request(prompt,
784 self._parent_ident,
783 self._parent_ident,
785 self._parent_header,
784 self._parent_header,
786 password=True,
785 password=True,
787 )
786 )
788
787
789 def raw_input(self, prompt=''):
788 def raw_input(self, prompt=''):
790 """Forward raw_input to frontends
789 """Forward raw_input to frontends
791
790
792 Raises
791 Raises
793 ------
792 ------
794 StdinNotImplentedError if active frontend doesn't support stdin.
793 StdinNotImplentedError if active frontend doesn't support stdin.
795 """
794 """
796 if not self._allow_stdin:
795 if not self._allow_stdin:
797 raise StdinNotImplementedError(
796 raise StdinNotImplementedError(
798 "raw_input was called, but this frontend does not support input requests."
797 "raw_input was called, but this frontend does not support input requests."
799 )
798 )
800 return self._input_request(prompt,
799 return self._input_request(prompt,
801 self._parent_ident,
800 self._parent_ident,
802 self._parent_header,
801 self._parent_header,
803 password=False,
802 password=False,
804 )
803 )
805
804
806 def _input_request(self, prompt, ident, parent, password=False):
805 def _input_request(self, prompt, ident, parent, password=False):
807 # Flush output before making the request.
806 # Flush output before making the request.
808 sys.stderr.flush()
807 sys.stderr.flush()
809 sys.stdout.flush()
808 sys.stdout.flush()
810 # flush the stdin socket, to purge stale replies
809 # flush the stdin socket, to purge stale replies
811 while True:
810 while True:
812 try:
811 try:
813 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
812 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
814 except zmq.ZMQError as e:
813 except zmq.ZMQError as e:
815 if e.errno == zmq.EAGAIN:
814 if e.errno == zmq.EAGAIN:
816 break
815 break
817 else:
816 else:
818 raise
817 raise
819
818
820 # Send the input request.
819 # Send the input request.
821 content = json_clean(dict(prompt=prompt, password=password))
820 content = json_clean(dict(prompt=prompt, password=password))
822 self.session.send(self.stdin_socket, u'input_request', content, parent,
821 self.session.send(self.stdin_socket, u'input_request', content, parent,
823 ident=ident)
822 ident=ident)
824
823
825 # Await a response.
824 # Await a response.
826 while True:
825 while True:
827 try:
826 try:
828 ident, reply = self.session.recv(self.stdin_socket, 0)
827 ident, reply = self.session.recv(self.stdin_socket, 0)
829 except Exception:
828 except Exception:
830 self.log.warn("Invalid Message:", exc_info=True)
829 self.log.warn("Invalid Message:", exc_info=True)
831 except KeyboardInterrupt:
830 except KeyboardInterrupt:
832 # re-raise KeyboardInterrupt, to truncate traceback
831 # re-raise KeyboardInterrupt, to truncate traceback
833 raise KeyboardInterrupt
832 raise KeyboardInterrupt
834 else:
833 else:
835 break
834 break
836 try:
835 try:
837 value = py3compat.unicode_to_str(reply['content']['value'])
836 value = py3compat.unicode_to_str(reply['content']['value'])
838 except:
837 except:
839 self.log.error("Bad input_reply: %s", parent)
838 self.log.error("Bad input_reply: %s", parent)
840 value = ''
839 value = ''
841 if value == '\x04':
840 if value == '\x04':
842 # EOF
841 # EOF
843 raise EOFError
842 raise EOFError
844 return value
843 return value
845
844
846 def _at_shutdown(self):
845 def _at_shutdown(self):
847 """Actions taken at shutdown by the kernel, called by python's atexit.
846 """Actions taken at shutdown by the kernel, called by python's atexit.
848 """
847 """
849 # io.rprint("Kernel at_shutdown") # dbg
848 # io.rprint("Kernel at_shutdown") # dbg
850 if self._shutdown_message is not None:
849 if self._shutdown_message is not None:
851 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
850 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
852 self.log.debug("%s", self._shutdown_message)
851 self.log.debug("%s", self._shutdown_message)
853 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
852 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
854
853
General Comments 0
You need to be logged in to leave comments. Login now