##// END OF EJS Templates
Separate IPython kernel implementation from kernel machinery
Thomas Kluyver -
Show More
@@ -1,4 +1,286 b''
1 """The IPython kernel implementation"""
2
3 import getpass
4 import sys
5 import traceback
6
7 from IPython.core import release
8 from IPython.utils.py3compat import builtin_mod, PY3
9 from IPython.utils.tokenutil import token_at_cursor
10 from IPython.utils.traitlets import Instance, Type, Any
11
1 12 from .kernelbase import KernelBase
13 from .serialize import serialize_object, unpack_apply_message
14 from .zmqshell import ZMQInteractiveShell
2 15
3 16 class Kernel(KernelBase):
17 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
18 shell_class = Type(ZMQInteractiveShell)
19
20 user_module = Any()
21 def _user_module_changed(self, name, old, new):
22 if self.shell is not None:
23 self.shell.user_module = new
24
25 user_ns = Instance(dict, args=None, allow_none=True)
26 def _user_ns_changed(self, name, old, new):
27 if self.shell is not None:
28 self.shell.user_ns = new
29 self.shell.init_user_ns()
30
31 # A reference to the Python builtin 'raw_input' function.
32 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
33 _sys_raw_input = Any()
34 _sys_eval_input = Any()
35
36 def __init__(self, **kwargs):
37 super(Kernel, self).__init__(**kwargs)
38
39 # Initialize the InteractiveShell subclass
40 self.shell = self.shell_class.instance(parent=self,
41 profile_dir = self.profile_dir,
42 user_module = self.user_module,
43 user_ns = self.user_ns,
44 kernel = self,
45 )
46 self.shell.displayhook.session = self.session
47 self.shell.displayhook.pub_socket = self.iopub_socket
48 self.shell.displayhook.topic = self._topic('execute_result')
49 self.shell.display_pub.session = self.session
50 self.shell.display_pub.pub_socket = self.iopub_socket
51 self.shell.data_pub.session = self.session
52 self.shell.data_pub.pub_socket = self.iopub_socket
53
54 # TMP - hack while developing
55 self.shell._reply_content = None
56
57 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
58 comm_manager = self.shell.comm_manager
59 for msg_type in comm_msg_types:
60 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
61
62 # Kernel info fields
63 implementation = 'ipython'
64 implementation_version = release.version
65 language = 'python'
66 language_version = sys.version.split()[0]
67 @property
68 def banner(self):
69 return self.shell.banner
70
71 def set_parent(self, ident, parent):
72 """Overridden from parent to tell the display hook and output streams
73 about the parent message.
74 """
75 super(Kernel, self).set_parent(ident, parent)
76 self.shell.set_parent(parent)
77
78 def _forward_input(self, allow_stdin=False):
79 """Forward raw_input and getpass to the current frontend.
80
81 via input_request
82 """
83 self._allow_stdin = allow_stdin
84
85 if PY3:
86 self._sys_raw_input = builtin_mod.input
87 builtin_mod.input = self.raw_input
88 else:
89 self._sys_raw_input = builtin_mod.raw_input
90 self._sys_eval_input = builtin_mod.input
91 builtin_mod.raw_input = self.raw_input
92 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
93 self._save_getpass = getpass.getpass
94 getpass.getpass = self.getpass
95
96 def _restore_input(self):
97 """Restore raw_input, getpass"""
98 if PY3:
99 builtin_mod.input = self._sys_raw_input
100 else:
101 builtin_mod.raw_input = self._sys_raw_input
102 builtin_mod.input = self._sys_eval_input
103
104 getpass.getpass = self._save_getpass
105
106 @property
107 def execution_count(self):
108 return self.shell.execution_count
109
110 @execution_count.setter
111 def execution_count(self, value):
112 # Ignore the incrememnting done by KernelBase, in favour of our shell's
113 # execution counter.
4 114 pass
115
116 def do_execute(self, code, silent, store_history=True,
117 user_expressions=None, allow_stdin=False):
118 shell = self.shell # we'll need this a lot here
119
120 self._forward_input(allow_stdin)
121
122 reply_content = {}
123 # FIXME: the shell calls the exception handler itself.
124 shell._reply_content = None
125 try:
126 shell.run_cell(code, store_history=store_history, silent=silent)
127 except:
128 status = u'error'
129 # FIXME: this code right now isn't being used yet by default,
130 # because the run_cell() call above directly fires off exception
131 # reporting. This code, therefore, is only active in the scenario
132 # where runlines itself has an unhandled exception. We need to
133 # uniformize this, for all exception construction to come from a
134 # single location in the codbase.
135 etype, evalue, tb = sys.exc_info()
136 tb_list = traceback.format_exception(etype, evalue, tb)
137 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
138 else:
139 status = u'ok'
140 finally:
141 self._restore_input()
142
143 reply_content[u'status'] = status
144
145 # Return the execution counter so clients can display prompts
146 reply_content['execution_count'] = shell.execution_count - 1
147
148 # FIXME - fish exception info out of shell, possibly left there by
149 # runlines. We'll need to clean up this logic later.
150 if shell._reply_content is not None:
151 reply_content.update(shell._reply_content)
152 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
153 reply_content['engine_info'] = e_info
154 # reset after use
155 shell._reply_content = None
156
157 if 'traceback' in reply_content:
158 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
159
160
161 # At this point, we can tell whether the main code execution succeeded
162 # or not. If it did, we proceed to evaluate user_expressions
163 if reply_content['status'] == 'ok':
164 reply_content[u'user_expressions'] = \
165 shell.user_expressions(user_expressions or {})
166 else:
167 # If there was an error, don't even try to compute expressions
168 reply_content[u'user_expressions'] = {}
169
170 # Payloads should be retrieved regardless of outcome, so we can both
171 # recover partial output (that could have been generated early in a
172 # block, before an error) and clear the payload system always.
173 reply_content[u'payload'] = shell.payload_manager.read_payload()
174 # Be agressive about clearing the payload because we don't want
175 # it to sit in memory until the next execute_request comes in.
176 shell.payload_manager.clear_payload()
177
178 return reply_content
179
180 def do_complete(self, code, cursor_pos):
181 txt, matches = self.shell.complete('', code, cursor_pos)
182 return {'matches' : matches,
183 'cursor_end' : cursor_pos,
184 'cursor_start' : cursor_pos - len(txt),
185 'metadata' : {},
186 'status' : 'ok'}
187
188 def do_inspect(self, code, cursor_pos, detail_level=0):
189 name = token_at_cursor(code, cursor_pos)
190 info = self.shell.object_inspect(name)
191
192 reply_content = {'status' : 'ok'}
193 reply_content['data'] = data = {}
194 reply_content['metadata'] = {}
195 reply_content['found'] = info['found']
196 if info['found']:
197 info_text = self.shell.object_inspect_text(
198 name,
199 detail_level=detail_level,
200 )
201 data['text/plain'] = info_text
202
203 return reply_content
204
205 def do_history(self, hist_access_type, output, raw, session=None, start=None,
206 stop=None, n=None, pattern=None, unique=False):
207 if hist_access_type == 'tail':
208 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
209 include_latest=True)
210
211 elif hist_access_type == 'range':
212 hist = self.shell.history_manager.get_range(session, start, stop,
213 raw=raw, output=output)
214
215 elif hist_access_type == 'search':
216 hist = self.shell.history_manager.search(
217 pattern, raw=raw, output=output, n=n, unique=unique)
218 else:
219 hist = []
220
221 return {'history' : list(hist)}
222
223 def do_shutdown(self, restart):
224 self.shell.exit_now = True
225 return dict(status='ok', restart=restart)
226
227 def do_apply(self, content, bufs, msg_id, reply_metadata):
228 shell = self.shell
229 try:
230 working = shell.user_ns
231
232 prefix = "_"+str(msg_id).replace("-","")+"_"
233
234 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
235
236 fname = getattr(f, '__name__', 'f')
237
238 fname = prefix+"f"
239 argname = prefix+"args"
240 kwargname = prefix+"kwargs"
241 resultname = prefix+"result"
242
243 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
244 # print ns
245 working.update(ns)
246 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
247 try:
248 exec(code, shell.user_global_ns, shell.user_ns)
249 result = working.get(resultname)
250 finally:
251 for key in ns:
252 working.pop(key)
253
254 result_buf = serialize_object(result,
255 buffer_threshold=self.session.buffer_threshold,
256 item_threshold=self.session.item_threshold,
257 )
258
259 except:
260 # invoke IPython traceback formatting
261 shell.showtraceback()
262 # FIXME - fish exception info out of shell, possibly left there by
263 # run_code. We'll need to clean up this logic later.
264 reply_content = {}
265 if shell._reply_content is not None:
266 reply_content.update(shell._reply_content)
267 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
268 reply_content['engine_info'] = e_info
269 # reset after use
270 shell._reply_content = None
271
272 self.send_response(self.iopub_socket, u'error', reply_content,
273 ident=self._topic('error'))
274 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
275 result_buf = []
276
277 if reply_content['ename'] == 'UnmetDependency':
278 reply_metadata['dependencies_met'] = False
279 else:
280 reply_content = {'status' : 'ok'}
281
282 return reply_content, result_buf
283
284 def do_clear(self):
285 self.shell.reset(False)
286 return dict(status='ok')
@@ -1,14 +1,12 b''
1 """An interactive kernel that talks to frontends over 0MQ."""
1 """Base class for a kernel that talks to frontends over 0MQ."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 from __future__ import print_function
7 7
8 import getpass
9 8 import sys
10 9 import time
11 import traceback
12 10 import logging
13 11 import uuid
14 12
@@ -25,28 +23,19 b' from IPython.config.configurable import Configurable'
25 23 from IPython.core.error import StdinNotImplementedError
26 24 from IPython.core import release
27 25 from IPython.utils import py3compat
28 from IPython.utils.py3compat import builtin_mod, unicode_type, string_types
26 from IPython.utils.py3compat import unicode_type, string_types
29 27 from IPython.utils.jsonutil import json_clean
30 from IPython.utils.tokenutil import token_at_cursor
31 28 from IPython.utils.traitlets import (
32 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
33 Type, Bool,
29 Any, Instance, Float, Dict, List, Set, Integer, Unicode, Bool,
34 30 )
35 31
36 from .serialize import serialize_object, unpack_apply_message
37 32 from .session import Session
38 from .zmqshell import ZMQInteractiveShell
39 33
40 34
41 35 #-----------------------------------------------------------------------------
42 36 # Main kernel class
43 37 #-----------------------------------------------------------------------------
44 38
45 protocol_version = release.kernel_protocol_version
46 ipython_version = release.version
47 language_version = sys.version.split()[0]
48
49
50 39 class KernelBase(Configurable):
51 40
52 41 #---------------------------------------------------------------------------
@@ -60,9 +49,6 b' class KernelBase(Configurable):'
60 49 loop = ioloop.IOLoop.instance()
61 50 loop.add_callback(self.enter_eventloop)
62 51
63 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
64 shell_class = Type(ZMQInteractiveShell)
65
66 52 session = Instance(Session)
67 53 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
68 54 shell_streams = List()
@@ -71,17 +57,6 b' class KernelBase(Configurable):'
71 57 stdin_socket = Instance(zmq.Socket)
72 58 log = Instance(logging.Logger)
73 59
74 user_module = Any()
75 def _user_module_changed(self, name, old, new):
76 if self.shell is not None:
77 self.shell.user_module = new
78
79 user_ns = Instance(dict, args=None, allow_none=True)
80 def _user_ns_changed(self, name, old, new):
81 if self.shell is not None:
82 self.shell.user_ns = new
83 self.shell.init_user_ns()
84
85 60 # identities:
86 61 int_id = Integer(-1)
87 62 ident = Unicode()
@@ -127,36 +102,17 b' class KernelBase(Configurable):'
127 102 # by record_ports and used by connect_request.
128 103 _recorded_ports = Dict()
129 104
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)
132 _sys_raw_input = Any()
133 _sys_eval_input = Any()
134
135 105 # set of aborted msg_ids
136 106 aborted = Set()
137 107
108 # Track execution count here. For IPython, we override this to use the
109 # execution count we store in the shell.
110 execution_count = 0
111
138 112
139 113 def __init__(self, **kwargs):
140 114 super(KernelBase, self).__init__(**kwargs)
141 115
142 # Initialize the InteractiveShell subclass
143 self.shell = self.shell_class.instance(parent=self,
144 profile_dir = self.profile_dir,
145 user_module = self.user_module,
146 user_ns = self.user_ns,
147 kernel = self,
148 )
149 self.shell.displayhook.session = self.session
150 self.shell.displayhook.pub_socket = self.iopub_socket
151 self.shell.displayhook.topic = self._topic('execute_result')
152 self.shell.display_pub.session = self.session
153 self.shell.display_pub.pub_socket = self.iopub_socket
154 self.shell.data_pub.session = self.session
155 self.shell.data_pub.pub_socket = self.iopub_socket
156
157 # TMP - hack while developing
158 self.shell._reply_content = None
159
160 116 # Build dict of handlers for message types
161 117 msg_types = [ 'execute_request', 'complete_request',
162 118 'inspect_request', 'history_request',
@@ -168,11 +124,6 b' class KernelBase(Configurable):'
168 124 for msg_type in msg_types:
169 125 self.shell_handlers[msg_type] = getattr(self, msg_type)
170 126
171 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
172 comm_manager = self.shell.comm_manager
173 for msg_type in comm_msg_types:
174 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
175
176 127 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
177 128 self.control_handlers = {}
178 129 for msg_type in control_msg_types:
@@ -191,7 +142,6 b' class KernelBase(Configurable):'
191 142 self.log.debug("Control received: %s", msg)
192 143
193 144 header = msg['header']
194 msg_id = header['msg_id']
195 145 msg_type = header['msg_type']
196 146
197 147 handler = self.control_handlers.get(msg_type, None)
@@ -233,7 +183,7 b' class KernelBase(Configurable):'
233 183 status = {'status' : 'aborted'}
234 184 md = {'engine' : self.ident}
235 185 md.update(status)
236 reply_msg = self.session.send(stream, reply_type, metadata=md,
186 self.session.send(stream, reply_type, metadata=md,
237 187 content=status, parent=msg, ident=idents)
238 188 return
239 189
@@ -340,34 +290,6 b' class KernelBase(Configurable):'
340 290 ident=self._topic('status'),
341 291 )
342 292
343 def _forward_input(self, allow_stdin=False):
344 """Forward raw_input and getpass to the current frontend.
345
346 via input_request
347 """
348 self._allow_stdin = allow_stdin
349
350 if py3compat.PY3:
351 self._sys_raw_input = builtin_mod.input
352 builtin_mod.input = self.raw_input
353 else:
354 self._sys_raw_input = builtin_mod.raw_input
355 self._sys_eval_input = builtin_mod.input
356 builtin_mod.raw_input = self.raw_input
357 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
358 self._save_getpass = getpass.getpass
359 getpass.getpass = self.getpass
360
361 def _restore_input(self):
362 """Restore raw_input, getpass"""
363 if py3compat.PY3:
364 builtin_mod.input = self._sys_raw_input
365 else:
366 builtin_mod.raw_input = self._sys_raw_input
367 builtin_mod.input = self._sys_eval_input
368
369 getpass.getpass = self._save_getpass
370
371 293 def set_parent(self, ident, parent):
372 294 """Set the current parent_header
373 295
@@ -379,7 +301,19 b' class KernelBase(Configurable):'
379 301 """
380 302 self._parent_ident = ident
381 303 self._parent_header = parent
382 self.shell.set_parent(parent)
304
305 def send_response(self, stream, msg_or_type, content=None, ident=None,
306 buffers=None, track=False, header=None, metadata=None):
307 """Send a response to the message we're currently processing.
308
309 This accepts all the parameters of :meth:`IPython.kernel.zmq.session.Session.send`
310 except ``parent``.
311
312 This relies on :meth:`set_parent` having been called for the current
313 message.
314 """
315 return self.session.send(stream, msg_or_type, content, self._parent_header,
316 ident, buffers, track, header, metadata)
383 317
384 318 def execute_request(self, stream, ident, parent):
385 319 """handle an execute_request"""
@@ -391,6 +325,8 b' class KernelBase(Configurable):'
391 325 code = py3compat.cast_unicode_py2(content[u'code'])
392 326 silent = content[u'silent']
393 327 store_history = content.get(u'store_history', not silent)
328 user_expressions = content.get('user_expressions', {})
329 allow_stdin = content.get('allow_stdin', False)
394 330 except:
395 331 self.log.error("Got bad msg: ")
396 332 self.log.error("%s", parent)
@@ -398,72 +334,17 b' class KernelBase(Configurable):'
398 334
399 335 md = self._make_metadata(parent['metadata'])
400 336
401 shell = self.shell # we'll need this a lot here
402
403 self._forward_input(content.get('allow_stdin', False))
404 337 # Set the parent message of the display hook and out streams.
405 338 self.set_parent(ident, parent)
406 339
407 340 # Re-broadcast our input for the benefit of listening clients, and
408 341 # start computing output
409 342 if not silent:
410 self._publish_execute_input(code, parent, shell.execution_count)
411
412 reply_content = {}
413 # FIXME: the shell calls the exception handler itself.
414 shell._reply_content = None
415 try:
416 shell.run_cell(code, store_history=store_history, silent=silent)
417 except:
418 status = u'error'
419 # FIXME: this code right now isn't being used yet by default,
420 # because the run_cell() call above directly fires off exception
421 # reporting. This code, therefore, is only active in the scenario
422 # where runlines itself has an unhandled exception. We need to
423 # uniformize this, for all exception construction to come from a
424 # single location in the codbase.
425 etype, evalue, tb = sys.exc_info()
426 tb_list = traceback.format_exception(etype, evalue, tb)
427 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
428 else:
429 status = u'ok'
430 finally:
431 self._restore_input()
432
433 reply_content[u'status'] = status
434
435 # Return the execution counter so clients can display prompts
436 reply_content['execution_count'] = shell.execution_count - 1
437
438 # FIXME - fish exception info out of shell, possibly left there by
439 # runlines. We'll need to clean up this logic later.
440 if shell._reply_content is not None:
441 reply_content.update(shell._reply_content)
442 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
443 reply_content['engine_info'] = e_info
444 # reset after use
445 shell._reply_content = None
343 self.execution_count += 1
344 self._publish_execute_input(code, parent, self.execution_count)
446 345
447 if 'traceback' in reply_content:
448 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
449
450
451 # At this point, we can tell whether the main code execution succeeded
452 # or not. If it did, we proceed to evaluate user_expressions
453 if reply_content['status'] == 'ok':
454 reply_content[u'user_expressions'] = \
455 shell.user_expressions(content.get(u'user_expressions', {}))
456 else:
457 # If there was an error, don't even try to compute expressions
458 reply_content[u'user_expressions'] = {}
459
460 # Payloads should be retrieved regardless of outcome, so we can both
461 # recover partial output (that could have been generated early in a
462 # block, before an error) and clear the payload system always.
463 reply_content[u'payload'] = shell.payload_manager.read_payload()
464 # Be agressive about clearing the payload because we don't want
465 # it to sit in memory until the next execute_request comes in.
466 shell.payload_manager.clear_payload()
346 reply_content = self.do_execute(code, silent, store_history,
347 user_expressions, allow_stdin)
467 348
468 349 # Flush output before sending the reply.
469 350 sys.stdout.flush()
@@ -498,12 +379,7 b' class KernelBase(Configurable):'
498 379 code = content['code']
499 380 cursor_pos = content['cursor_pos']
500 381
501 txt, matches = self.shell.complete('', code, cursor_pos)
502 matches = {'matches' : matches,
503 'cursor_end' : cursor_pos,
504 'cursor_start' : cursor_pos - len(txt),
505 'metadata' : {},
506 'status' : 'ok'}
382 matches = self.do_complete(code, cursor_pos)
507 383 matches = json_clean(matches)
508 384 completion_msg = self.session.send(stream, 'complete_reply',
509 385 matches, parent, ident)
@@ -512,19 +388,8 b' class KernelBase(Configurable):'
512 388 def inspect_request(self, stream, ident, parent):
513 389 content = parent['content']
514 390
515 name = token_at_cursor(content['code'], content['cursor_pos'])
516 info = self.shell.object_inspect(name)
517
518 reply_content = {'status' : 'ok'}
519 reply_content['data'] = data = {}
520 reply_content['metadata'] = {}
521 reply_content['found'] = info['found']
522 if info['found']:
523 info_text = self.shell.object_inspect_text(
524 name,
525 detail_level=content.get('detail_level', 0),
526 )
527 reply_content['data']['text/plain'] = info_text
391 reply_content = self.do_inspect(content['code'], content['cursor_pos'],
392 content.get('detail_level', 0))
528 393 # Before we send this object over, we scrub it for JSON usage
529 394 reply_content = json_clean(reply_content)
530 395 msg = self.session.send(stream, 'inspect_reply',
@@ -534,36 +399,22 b' class KernelBase(Configurable):'
534 399 def history_request(self, stream, ident, parent):
535 400 # We need to pull these out, as passing **kwargs doesn't work with
536 401 # unicode keys before Python 2.6.5.
537 hist_access_type = parent['content']['hist_access_type']
538 raw = parent['content']['raw']
539 output = parent['content']['output']
540 if hist_access_type == 'tail':
541 n = parent['content']['n']
542 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
543 include_latest=True)
544
545 elif hist_access_type == 'range':
546 session = parent['content']['session']
547 start = parent['content']['start']
548 stop = parent['content']['stop']
549 hist = self.shell.history_manager.get_range(session, start, stop,
550 raw=raw, output=output)
551
552 elif hist_access_type == 'search':
553 n = parent['content'].get('n')
554 unique = parent['content'].get('unique', False)
555 pattern = parent['content']['pattern']
556 hist = self.shell.history_manager.search(
557 pattern, raw=raw, output=output, n=n, unique=unique)
402 content = parent['content']
558 403
559 else:
560 hist = []
561 hist = list(hist)
562 content = {'history' : hist}
563 content = json_clean(content)
404 reply_content = self.do_history(content['hist_access_type'],
405 content['output'], content['raw'],
406 content.get('session', None),
407 content.get('start', None),
408 content.get('stop', None),
409 content.get('n', None),
410 content.get('pattern', None),
411 content.get('unique', False),
412 )
413
414 reply_content = json_clean(reply_content)
564 415 msg = self.session.send(stream, 'history_reply',
565 content, parent, ident)
566 self.log.debug("Sending history reply with %i entries", len(hist))
416 reply_content, parent, ident)
417 self.log.debug("%s", msg)
567 418
568 419 def connect_request(self, stream, ident, parent):
569 420 if self._recorded_ports is not None:
@@ -574,23 +425,24 b' class KernelBase(Configurable):'
574 425 content, parent, ident)
575 426 self.log.debug("%s", msg)
576 427
577 def kernel_info_request(self, stream, ident, parent):
578 vinfo = {
579 'protocol_version': protocol_version,
580 'implementation': 'ipython',
581 'implementation_version': ipython_version,
582 'language_version': language_version,
583 'language': 'python',
584 'banner': self.shell.banner,
428 @property
429 def kernel_info(self):
430 return {
431 'protocol_version': release.kernel_protocol_version,
432 'implementation': self.implementation,
433 'implementation_version': self.implementation_version,
434 'language': self.language,
435 'language_version': self.language_version,
436 'banner': self.banner,
585 437 }
438
439 def kernel_info_request(self, stream, ident, parent):
586 440 msg = self.session.send(stream, 'kernel_info_reply',
587 vinfo, parent, ident)
441 self.kernel_info, parent, ident)
588 442 self.log.debug("%s", msg)
589 443
590 444 def shutdown_request(self, stream, ident, parent):
591 self.shell.exit_now = True
592 content = dict(status='ok')
593 content.update(parent['content'])
445 content = self.do_shutdown(parent['content']['restart'])
594 446 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
595 447 # same content, but different msg_id for broadcasting on IOPub
596 448 self._shutdown_message = self.session.msg(u'shutdown_reply',
@@ -618,62 +470,11 b' class KernelBase(Configurable):'
618 470 self._publish_status(u'busy', parent)
619 471
620 472 # Set the parent message of the display hook and out streams.
621 shell = self.shell
622 shell.set_parent(parent)
473 self.set_parent(ident, parent)
623 474
624 475 md = self._make_metadata(parent['metadata'])
625 try:
626 working = shell.user_ns
627
628 prefix = "_"+str(msg_id).replace("-","")+"_"
629
630 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
631
632 fname = getattr(f, '__name__', 'f')
633
634 fname = prefix+"f"
635 argname = prefix+"args"
636 kwargname = prefix+"kwargs"
637 resultname = prefix+"result"
638
639 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
640 # print ns
641 working.update(ns)
642 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
643 try:
644 exec(code, shell.user_global_ns, shell.user_ns)
645 result = working.get(resultname)
646 finally:
647 for key in ns:
648 working.pop(key)
649
650 result_buf = serialize_object(result,
651 buffer_threshold=self.session.buffer_threshold,
652 item_threshold=self.session.item_threshold,
653 )
654 476
655 except:
656 # invoke IPython traceback formatting
657 shell.showtraceback()
658 # FIXME - fish exception info out of shell, possibly left there by
659 # run_code. We'll need to clean up this logic later.
660 reply_content = {}
661 if shell._reply_content is not None:
662 reply_content.update(shell._reply_content)
663 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
664 reply_content['engine_info'] = e_info
665 # reset after use
666 shell._reply_content = None
667
668 self.session.send(self.iopub_socket, u'error', reply_content, parent=parent,
669 ident=self._topic('error'))
670 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
671 result_buf = []
672
673 if reply_content['ename'] == 'UnmetDependency':
674 md['dependencies_met'] = False
675 else:
676 reply_content = {'status' : 'ok'}
477 reply_content, result_buf = self.do_apply(content, bufs, msg_id, md)
677 478
678 479 # put 'ok'/'error' status in header, for scheduler introspection:
679 480 md['status'] = reply_content['status']
@@ -682,7 +483,7 b' class KernelBase(Configurable):'
682 483 sys.stdout.flush()
683 484 sys.stderr.flush()
684 485
685 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
486 self.session.send(stream, u'apply_reply', reply_content,
686 487 parent=parent, ident=ident,buffers=result_buf, metadata=md)
687 488
688 489 self._publish_status(u'idle', parent)
@@ -708,23 +509,14 b' class KernelBase(Configurable):'
708 509
709 510 def clear_request(self, stream, idents, parent):
710 511 """Clear our namespace."""
711 self.shell.reset(False)
712 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
713 content = dict(status='ok'))
714
512 content = self.do_clear()
513 self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
514 content = content)
715 515
716 516 #---------------------------------------------------------------------------
717 517 # Protected interface
718 518 #---------------------------------------------------------------------------
719 519
720 def _wrap_exception(self, method=None):
721 # import here, because _wrap_exception is only used in parallel,
722 # and parallel has higher min pyzmq version
723 from IPython.parallel.error import wrap_exception
724 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
725 content = wrap_exception(e_info)
726 return content
727
728 520 def _topic(self, topic):
729 521 """prefixed topic for IOPub messages"""
730 522 if self.int_id >= 0:
@@ -188,8 +188,8 b' class AsyncResultTest(ClusterTestCase):'
188 188 ar = v.apply_async(time.sleep, 0.25)
189 189 while not ar.ready():
190 190 time.sleep(0.01)
191 self.assertTrue(ar.elapsed < 1)
192 self.assertTrue(ar.elapsed < 1)
191 self.assertLess(ar.elapsed, 1)
192 self.assertLess(ar.elapsed, 1)
193 193 ar.get(2)
194 194
195 195 def test_hubresult_timestamps(self):
General Comments 0
You need to be logged in to leave comments. Login now