##// END OF EJS Templates
make js / Python widgets symmetrical...
MinRK -
Show More
@@ -1,121 +1,148 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2013 The IPython Development Team
2 // Copyright (C) 2013 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Widget and WidgetManager bases
9 // Widget and WidgetManager bases
10 //============================================================================
10 //============================================================================
11 /**
11 /**
12 * Base Widget classes
12 * Base Widget classes
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule widget
15 * @submodule widget
16 */
16 */
17
17
18 var IPython = (function (IPython) {
18 var IPython = (function (IPython) {
19 "use strict";
19 "use strict";
20
20
21 //-----------------------------------------------------------------------
21 //-----------------------------------------------------------------------
22 // WidgetManager class
22 // WidgetManager class
23 //-----------------------------------------------------------------------
23 //-----------------------------------------------------------------------
24
24
25 var WidgetManager = function (kernel) {
25 var WidgetManager = function (kernel) {
26 this.widgets = {};
26 this.widgets = {};
27 this.widget_types = {widget : Widget};
27 this.widget_types = {widget : Widget};
28 if (kernel !== undefined) {
28 if (kernel !== undefined) {
29 this.init_kernel(kernel);
29 this.init_kernel(kernel);
30 }
30 }
31 };
31 };
32
32
33 WidgetManager.prototype.init_kernel = function (kernel) {
33 WidgetManager.prototype.init_kernel = function (kernel) {
34 // connect the kernel, and register message handlers
34 this.kernel = kernel;
35 this.kernel = kernel;
35 var msg_types = ['widget_create', 'widget_destroy', 'widget_update'];
36 var msg_types = ['widget_create', 'widget_destroy', 'widget_update'];
36 for (var i = 0; i < msg_types.length; i++) {
37 for (var i = 0; i < msg_types.length; i++) {
37 var msg_type = msg_types[i];
38 var msg_type = msg_types[i];
38 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
39 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
39 }
40 }
40 };
41 };
41
42
42 WidgetManager.prototype.register_widget_type = function (widget_type, constructor) {
43 WidgetManager.prototype.register_widget_type = function (widget_type, constructor) {
43 // Register a constructor for a given widget type name
44 // Register a constructor for a given widget type name
44 this.widget_types[widget_type] = constructor;
45 this.widget_types[widget_type] = constructor;
45 };
46 };
46
47
48 WidgetManager.prototype.register_widget = function (widget) {
49 // Register a widget in the mapping
50 this.widgets[widget.widget_id] = widget;
51 widget.kernel = this.kernel;
52 return widget.widget_id;
53 };
54
55 WidgetManager.prototype.unregister_widget = function (widget_id) {
56 // Remove a widget from the mapping
57 delete this.widgets[widget_id];
58 };
59
60 // widget message handlers
61
47 WidgetManager.prototype.widget_create = function (msg) {
62 WidgetManager.prototype.widget_create = function (msg) {
48 var content = msg.content;
63 var content = msg.content;
49 var constructor = this.widget_types[content.widget_type];
64 var constructor = this.widget_types[content.widget_type];
50 if (constructor === undefined) {
65 if (constructor === undefined) {
51 console.log("No such widget type registered: ", content.widget_type);
66 console.log("No such widget type registered: ", content.widget_type);
52 console.log("Available widget types are: ", this.widget_types);
67 console.log("Available widget types are: ", this.widget_types);
53 return;
68 return;
54 }
69 }
55 var widget = new constructor(this.kernel, content);
70 var widget = new constructor(content.widget_id);
71 this.register_widget(widget);
72 widget.handle_create(content.data);
73
56 this.widgets[content.widget_id] = widget;
74 this.widgets[content.widget_id] = widget;
57 };
75 };
58
76
59 WidgetManager.prototype.widget_destroy = function (msg) {
77 WidgetManager.prototype.widget_destroy = function (msg) {
60 var content = msg.content;
78 var content = msg.content;
61 var widget = this.widgets[content.widget_id];
79 var widget = this.widgets[content.widget_id];
62 if (widget === undefined) {
80 if (widget === undefined) {
63 return;
81 return;
64 }
82 }
65 delete this.widgets[content.widget_id];
83 delete this.widgets[content.widget_id];
66 widget.handle_destroy(content.data);
84 widget.handle_destroy(content.data);
67 };
85 };
68
86
69 WidgetManager.prototype.widget_update = function (msg) {
87 WidgetManager.prototype.widget_update = function (msg) {
70 var content = msg.content;
88 var content = msg.content;
71 var widget = this.widgets[content.widget_id];
89 var widget = this.widgets[content.widget_id];
72 if (widget === undefined) {
90 if (widget === undefined) {
73 return;
91 return;
74 }
92 }
75 widget.handle_update(content.data);
93 widget.handle_update(content.data);
76 };
94 };
77
95
78 //-----------------------------------------------------------------------
96 //-----------------------------------------------------------------------
79 // Widget base class
97 // Widget base class
80 //-----------------------------------------------------------------------
98 //-----------------------------------------------------------------------
81
99
82 var Widget = function (kernel, content) {
100 var Widget = function (widget_id) {
83 this.kernel = kernel;
101 this.widget_id = widget_id;
84 if (!content) return;
102 this.widget_type = 'widget';
85 this.widget_id = content.widget_id;
86 this.handle_create(content.data);
87 };
88
89 Widget.prototype.handle_create = function (data) {
90 };
103 };
91
104
92 Widget.prototype.handle_update = function (data) {
105 // methods for sending messages
93 };
106 Widget.prototype.create = function (data) {
94
107 var content = {
95 Widget.prototype.handle_destroy = function (data) {
108 widget_id : this.widget_id,
109 widget_type : this.widget_type,
110 data : data || {},
111 };
112 this.kernel.send_shell_message("widget_create", content);
96 };
113 };
97
114
98 Widget.prototype.update = function (data) {
115 Widget.prototype.update = function (data) {
99 var content = {
116 var content = {
100 widget_id : this.widget_id,
117 widget_id : this.widget_id,
101 data : data,
118 data : data || {},
102 };
119 };
103 this.kernel.send_shell_message("widget_update", content);
120 this.kernel.send_shell_message("widget_update", content);
104 };
121 };
105
122
106
107 Widget.prototype.destroy = function (data) {
123 Widget.prototype.destroy = function (data) {
108 var content = {
124 var content = {
109 widget_id : this.widget_id,
125 widget_id : this.widget_id,
110 data : data,
126 data : data || {},
111 };
127 };
112 this.kernel.send_shell_message("widget_destroy", content);
128 this.kernel.send_shell_message("widget_destroy", content);
113 };
129 };
114
130
131 // methods for handling incoming messages
132
133 Widget.prototype.handle_create = function (data) {
134 };
135
136 Widget.prototype.handle_update = function (data) {
137 };
138
139 Widget.prototype.handle_destroy = function (data) {
140 };
141
115 IPython.WidgetManager = WidgetManager;
142 IPython.WidgetManager = WidgetManager;
116 IPython.Widget = Widget;
143 IPython.Widget = Widget;
117
144
118 return IPython;
145 return IPython;
119
146
120 }(IPython));
147 }(IPython));
121
148
@@ -1,115 +1,134 b''
1 """Base class to manage widgets"""
1 """Base class to manage widgets"""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
4 # Copyright (C) 2013 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 from weakref import ref
15
16 from IPython.config import LoggingConfigurable
14 from IPython.config import LoggingConfigurable
17 from IPython.core.prompts import LazyEvaluate
15 from IPython.core.prompts import LazyEvaluate
18 from IPython.core.getipython import get_ipython
16 from IPython.core.getipython import get_ipython
17
18 from IPython.utils.importstring import import_item
19 from IPython.utils.traitlets import Instance, Unicode, Dict, Any
19 from IPython.utils.traitlets import Instance, Unicode, Dict, Any
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Code
22 # Code
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 def lazy_keys(dikt):
25 def lazy_keys(dikt):
26 """Return lazy-evaluated string representation of a dictionary's keys
26 """Return lazy-evaluated string representation of a dictionary's keys
27
27
28 Key list is only constructed if it will actually be used.
28 Key list is only constructed if it will actually be used.
29 Used for debug-logging.
29 Used for debug-logging.
30 """
30 """
31 return LazyEvaluate(lambda d: list(d.keys()))
31 return LazyEvaluate(lambda d: list(d.keys()))
32
32
33
33 class WidgetManager(LoggingConfigurable):
34 class WidgetManager(LoggingConfigurable):
34 """Manager for Widgets in the Kernel"""
35 """Manager for Widgets in the Kernel"""
35
36
36 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
37 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
37 def _shell_default(self):
38 def _shell_default(self):
38 return get_ipython()
39 return get_ipython()
39 iopub_socket = Any()
40 iopub_socket = Any()
40 def _iopub_socket_default(self):
41 def _iopub_socket_default(self):
41 return self.shell.parent.iopub_socket
42 return self.shell.parent.iopub_socket
42 session = Instance('IPython.kernel.zmq.session.Session')
43 session = Instance('IPython.kernel.zmq.session.Session')
43 def _session_default(self):
44 def _session_default(self):
44 if self.shell is None:
45 if self.shell is None:
45 return
46 return
46 return self.shell.parent.session
47 return self.shell.parent.session
47
48
48 widgets = Dict()
49 widgets = Dict()
50 widget_types = Dict()
49
51
50 # Public APIs
52 # Public APIs
51
53
54 def register_widget_type(self, widget_type, constructor):
55 """Register a constructor for a given widget_type
56
57 constructor can be a Widget class or an importstring for a Widget class.
58 """
59 if isinstance(constructor, basestring):
60 constructor = import_item(constructor)
61
62 self.widget_types[widget_type] = constructor
63
52 def register_widget(self, widget):
64 def register_widget(self, widget):
53 """Register a new widget"""
65 """Register a new widget"""
54 self.widgets[widget.widget_id] = ref(widget)
66 widget_id = widget.widget_id
55 widget.shell = self.shell
67 widget.shell = self.shell
56 widget.iopub_socket = self.iopub_socket
68 widget.iopub_socket = self.iopub_socket
57 widget.create()
69 self.widgets[widget_id] = widget
58 return widget.widget_id
70 return widget_id
59
71
60 def unregister_widget(self, widget_id):
72 def unregister_widget(self, widget_id):
61 """Unregister a widget, and destroy its counterpart"""
73 """Unregister a widget, and destroy its counterpart"""
62 # unlike get_widget, this should raise a KeyError
74 # unlike get_widget, this should raise a KeyError
63 widget_ref = self.widgets.pop(widget_id)
75 widget = self.widgets.pop(widget_id)
64 widget = widget_ref()
65 if widget is None:
66 # already destroyed, nothing to do
67 return
68 widget.destroy()
76 widget.destroy()
69
77
70 def get_widget(self, widget_id):
78 def get_widget(self, widget_id):
71 """Get a widget with a particular id
79 """Get a widget with a particular id
72
80
73 Returns the widget if found, otherwise None.
81 Returns the widget if found, otherwise None.
74
82
75 This will not raise an error,
83 This will not raise an error,
76 it will log messages if the widget cannot be found.
84 it will log messages if the widget cannot be found.
77 """
85 """
78 if widget_id not in self.widgets:
86 if widget_id not in self.widgets:
79 self.log.error("No such widget: %s", widget_id)
87 self.log.error("No such widget: %s", widget_id)
80 self.log.debug("Current widgets: %s", lazy_keys(self.widgets))
88 self.log.debug("Current widgets: %s", lazy_keys(self.widgets))
81 return
89 return
82 # call, because we store weakrefs
90 # call, because we store weakrefs
83 widget = self.widgets[widget_id]()
91 widget = self.widgets[widget_id]
84 if widget is None:
85 self.log.error("Widget %s has been removed", widget_id)
86 del self.widgets[widget_id]
87 self.log.debug("Current widgets: %s", lazy_keys(self.widgets))
88 return
89 return widget
92 return widget
90
93
91 # Message handlers
94 # Message handlers
92
95
96 def widget_create(self, stream, ident, msg):
97 """Handler for widget_update messages"""
98 content = msg['content']
99 widget_id = content['widget_id']
100 widget_type = content['widget_type']
101 constructor = self.widget_types.get(widget_type, None)
102 if constructor is None:
103 self.log.error("No such widget_type registered: %s", widget_type)
104 return
105 widget = constructor(widget_id=widget_id,
106 shell=self.shell,
107 iopub_socket=self.iopub_socket,
108 _create_data=content['data'],
109 )
110 self.register_widget(widget)
111
93 def widget_update(self, stream, ident, msg):
112 def widget_update(self, stream, ident, msg):
94 """Handler for widget_update messages"""
113 """Handler for widget_update messages"""
95 content = msg['content']
114 content = msg['content']
96 widget_id = content['widget_id']
115 widget_id = content['widget_id']
97 widget = self.get_widget(widget_id)
116 widget = self.get_widget(widget_id)
98 if widget is None:
117 if widget is None:
99 # no such widget
118 # no such widget
100 return
119 return
101 widget.handle_update(content['data'])
120 widget.handle_update(content['data'])
102
121
103 def widget_destroy(self, stream, ident, msg):
122 def widget_destroy(self, stream, ident, msg):
104 """Handler for widget_destroy messages"""
123 """Handler for widget_destroy messages"""
105 content = msg['content']
124 content = msg['content']
106 widget_id = content['widget_id']
125 widget_id = content['widget_id']
107 widget = self.get_widget(widget_id)
126 widget = self.get_widget(widget_id)
108 if widget is None:
127 if widget is None:
109 # no such widget
128 # no such widget
110 return
129 return
111 widget.handle_destroy(content['data'])
130 widget.handle_destroy(content['data'])
112 del self.widgets[widget_id]
131 del self.widgets[widget_id]
113
132
114
133
115 __all__ = ['WidgetManager']
134 __all__ = ['WidgetManager']
@@ -1,92 +1,113 b''
1 """Base class for a Widget"""
1 """Base class for a Widget"""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
4 # Copyright (C) 2013 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 import uuid
14 import uuid
15
15
16 from IPython.core.getipython import get_ipython
16 from IPython.config import LoggingConfigurable
17 from IPython.config import LoggingConfigurable
17 from IPython.utils.traitlets import Instance, Unicode, Bytes, Bool, Dict, Any
18 from IPython.utils.traitlets import Instance, Unicode, Bytes, Bool, Dict, Any
18
19
19 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
20 # Code
21 # Code
21 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
22
23
23 class Widget(LoggingConfigurable):
24 class Widget(LoggingConfigurable):
24
25
25 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
26 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
26 def _shell_default(self):
27 def _shell_default(self):
27 return get_ipython()
28 return get_ipython()
28 iopub_socket = Any()
29 iopub_socket = Any()
29 def _iopub_socket_default(self):
30 def _iopub_socket_default(self):
30 return self.shell.parent.iopub_socket
31 return self.shell.parent.iopub_socket
31 session = Instance('IPython.kernel.zmq.session.Session')
32 session = Instance('IPython.kernel.zmq.session.Session')
32 def _session_default(self):
33 def _session_default(self):
33 if self.shell is None:
34 if self.shell is None:
34 return
35 return
35 return self.shell.parent.session
36 return self.shell.parent.session
36
37
37 topic = Bytes()
38 topic = Bytes()
38 def _topic_default(self):
39 def _topic_default(self):
39 return ('widget-%s' % self.widget_id).encode('ascii')
40 return ('widget-%s' % self.widget_id).encode('ascii')
40
41
41 _destroy_data = Dict(help="data dict, if any, to be included in widget_destroy")
42 _destroy_data = Dict(help="data dict, if any, to be included in widget_destroy")
42 _create_data = Dict(help="data dict, if any, to be included in widget_create")
43 _create_data = Dict(help="data dict, if any, to be included in widget_create")
43
44
44 _destroyed = Bool(False)
45 _destroyed = Bool(False)
45 widget_type = Unicode('widget')
46 widget_type = Unicode('widget')
46 widget_id = Unicode()
47 widget_id = Unicode()
47 def _widget_id_default(self):
48 def _widget_id_default(self):
48 return uuid.uuid4().hex
49 return uuid.uuid4().hex
49
50
51 primary = Bool(False, help="Am I the primary or secondary Widget?")
52
53 def __init__(self, **kwargs):
54 super(Widget, self).__init__(**kwargs)
55 get_ipython().widget_manager.register_widget(self)
56 if self.primary:
57 # I am primary, create my peer
58 self.create()
59 else:
60 # I am secondary, handle creation
61 self.handle_create(self._create_data)
62
50 def _publish_msg(self, msg_type, data=None, **keys):
63 def _publish_msg(self, msg_type, data=None, **keys):
51 """Helper for sending a widget message on IOPub"""
64 """Helper for sending a widget message on IOPub"""
52 data = {} if data is None else data
65 data = {} if data is None else data
53 self.session.send(self.iopub_socket, msg_type,
66 self.session.send(self.iopub_socket, msg_type,
54 dict(data=data, widget_id=self.widget_id, **keys),
67 dict(data=data, widget_id=self.widget_id, **keys),
55 ident=self.topic,
68 ident=self.topic,
56 )
69 )
57
70
58 def __del__(self):
71 def __del__(self):
59 """trigger destroy on gc"""
72 """trigger destroy on gc"""
60 self.destroy()
73 self.destroy()
61
74
62 # publishing messages
75 # publishing messages
63
76
64 def create(self):
77 def create(self, data=None):
65 """Create the frontend-side version of this widget"""
78 """Create the frontend-side version of this widget"""
66 self._publish_msg('widget_create', self._create_data, widget_type = self.widget_type)
79 if data is None:
80 data = self._create_data
81 self._publish_msg('widget_create', data, widget_type=self.widget_type)
67
82
68 def destroy(self):
83 def destroy(self, data=None):
69 """Destroy the frontend-side version of this widget"""
84 """Destroy the frontend-side version of this widget"""
70 if self._destroyed:
85 if self._destroyed:
71 # only destroy once
86 # only destroy once
72 return
87 return
73 self._publish_msg('widget_destroy', self._destroy_data)
88 if data is None:
89 data = self._destroy_data
90 self._publish_msg('widget_destroy', data)
74 self._destroyed = True
91 self._destroyed = True
75
92
76 def update(self, data=None):
93 def update(self, data=None):
77 """Update the frontend-side version of this widget"""
94 """Update the frontend-side version of this widget"""
78 self._publish_msg('widget_update', data)
95 self._publish_msg('widget_update', data)
79
96
80 # handling of incoming messages
97 # handling of incoming messages
81
98
99 def handle_create(self, data):
100 """Handle a widget_create message"""
101 self.log.debug("handle_create %s", data)
102
82 def handle_destroy(self, data):
103 def handle_destroy(self, data):
83 """Handle a widget_destroy message"""
104 """Handle a widget_destroy message"""
84 self.log.debug("handle_destroy %s", data)
105 self.log.debug("handle_destroy %s", data)
85
106
86 def handle_update(self, data):
107 def handle_update(self, data):
87 """Handle a widget_update message"""
108 """Handle a widget_update message"""
88 self.log.debug("handle_update %s", data)
109 self.log.debug("handle_update %s", data)
89 self.update_data = data
110 self.update_data = data
90
111
91
112
92 __all__ = ['Widget']
113 __all__ = ['Widget']
@@ -1,817 +1,817 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
3
4 Things to do:
4 Things to do:
5
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
8 * Implement random port and security key logic.
9 * Implement control messages.
9 * Implement control messages.
10 * Implement event loop and poll version.
10 * Implement event loop and poll version.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Standard library imports
18 # Standard library imports
19 import __builtin__
19 import __builtin__
20 import sys
20 import sys
21 import time
21 import time
22 import traceback
22 import traceback
23 import logging
23 import logging
24 import uuid
24 import uuid
25
25
26 from datetime import datetime
26 from datetime import datetime
27 from signal import (
27 from signal import (
28 signal, default_int_handler, SIGINT
28 signal, default_int_handler, SIGINT
29 )
29 )
30
30
31 # System library imports
31 # System library imports
32 import zmq
32 import zmq
33 from zmq.eventloop import ioloop
33 from zmq.eventloop import ioloop
34 from zmq.eventloop.zmqstream import ZMQStream
34 from zmq.eventloop.zmqstream import ZMQStream
35
35
36 # Local imports
36 # Local imports
37 from IPython.config.configurable import Configurable
37 from IPython.config.configurable import Configurable
38 from IPython.core.error import StdinNotImplementedError
38 from IPython.core.error import StdinNotImplementedError
39 from IPython.core import release
39 from IPython.core import release
40 from IPython.utils import py3compat
40 from IPython.utils import py3compat
41 from IPython.utils.jsonutil import json_clean
41 from IPython.utils.jsonutil import json_clean
42 from IPython.utils.traitlets import (
42 from IPython.utils.traitlets import (
43 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
43 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
44 Type
44 Type
45 )
45 )
46
46
47 from serialize import serialize_object, unpack_apply_message
47 from serialize import serialize_object, unpack_apply_message
48 from session import Session
48 from session import Session
49 from zmqshell import ZMQInteractiveShell
49 from zmqshell import ZMQInteractiveShell
50
50
51
51
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53 # Main kernel class
53 # Main kernel class
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55
55
56 protocol_version = list(release.kernel_protocol_version_info)
56 protocol_version = list(release.kernel_protocol_version_info)
57 ipython_version = list(release.version_info)
57 ipython_version = list(release.version_info)
58 language_version = list(sys.version_info[:3])
58 language_version = list(sys.version_info[:3])
59
59
60
60
61 class Kernel(Configurable):
61 class Kernel(Configurable):
62
62
63 #---------------------------------------------------------------------------
63 #---------------------------------------------------------------------------
64 # Kernel interface
64 # Kernel interface
65 #---------------------------------------------------------------------------
65 #---------------------------------------------------------------------------
66
66
67 # attribute to override with a GUI
67 # attribute to override with a GUI
68 eventloop = Any(None)
68 eventloop = Any(None)
69 def _eventloop_changed(self, name, old, new):
69 def _eventloop_changed(self, name, old, new):
70 """schedule call to eventloop from IOLoop"""
70 """schedule call to eventloop from IOLoop"""
71 loop = ioloop.IOLoop.instance()
71 loop = ioloop.IOLoop.instance()
72 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
72 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
73
73
74 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
74 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
75 shell_class = Type(ZMQInteractiveShell)
75 shell_class = Type(ZMQInteractiveShell)
76
76
77 session = Instance(Session)
77 session = Instance(Session)
78 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
78 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
79 shell_streams = List()
79 shell_streams = List()
80 control_stream = Instance(ZMQStream)
80 control_stream = Instance(ZMQStream)
81 iopub_socket = Instance(zmq.Socket)
81 iopub_socket = Instance(zmq.Socket)
82 stdin_socket = Instance(zmq.Socket)
82 stdin_socket = Instance(zmq.Socket)
83 log = Instance(logging.Logger)
83 log = Instance(logging.Logger)
84
84
85 user_module = Any()
85 user_module = Any()
86 def _user_module_changed(self, name, old, new):
86 def _user_module_changed(self, name, old, new):
87 if self.shell is not None:
87 if self.shell is not None:
88 self.shell.user_module = new
88 self.shell.user_module = new
89
89
90 user_ns = Instance(dict, args=None, allow_none=True)
90 user_ns = Instance(dict, args=None, allow_none=True)
91 def _user_ns_changed(self, name, old, new):
91 def _user_ns_changed(self, name, old, new):
92 if self.shell is not None:
92 if self.shell is not None:
93 self.shell.user_ns = new
93 self.shell.user_ns = new
94 self.shell.init_user_ns()
94 self.shell.init_user_ns()
95
95
96 # identities:
96 # identities:
97 int_id = Integer(-1)
97 int_id = Integer(-1)
98 ident = Unicode()
98 ident = Unicode()
99
99
100 def _ident_default(self):
100 def _ident_default(self):
101 return unicode(uuid.uuid4())
101 return unicode(uuid.uuid4())
102
102
103
103
104 # Private interface
104 # Private interface
105
105
106 # Time to sleep after flushing the stdout/err buffers in each execute
106 # Time to sleep after flushing the stdout/err buffers in each execute
107 # cycle. While this introduces a hard limit on the minimal latency of the
107 # cycle. While this introduces a hard limit on the minimal latency of the
108 # execute cycle, it helps prevent output synchronization problems for
108 # execute cycle, it helps prevent output synchronization problems for
109 # clients.
109 # clients.
110 # Units are in seconds. The minimum zmq latency on local host is probably
110 # Units are in seconds. The minimum zmq latency on local host is probably
111 # ~150 microseconds, set this to 500us for now. We may need to increase it
111 # ~150 microseconds, set this to 500us for now. We may need to increase it
112 # a little if it's not enough after more interactive testing.
112 # a little if it's not enough after more interactive testing.
113 _execute_sleep = Float(0.0005, config=True)
113 _execute_sleep = Float(0.0005, config=True)
114
114
115 # Frequency of the kernel's event loop.
115 # Frequency of the kernel's event loop.
116 # Units are in seconds, kernel subclasses for GUI toolkits may need to
116 # Units are in seconds, kernel subclasses for GUI toolkits may need to
117 # adapt to milliseconds.
117 # adapt to milliseconds.
118 _poll_interval = Float(0.05, config=True)
118 _poll_interval = Float(0.05, config=True)
119
119
120 # If the shutdown was requested over the network, we leave here the
120 # If the shutdown was requested over the network, we leave here the
121 # necessary reply message so it can be sent by our registered atexit
121 # necessary reply message so it can be sent by our registered atexit
122 # handler. This ensures that the reply is only sent to clients truly at
122 # handler. This ensures that the reply is only sent to clients truly at
123 # the end of our shutdown process (which happens after the underlying
123 # the end of our shutdown process (which happens after the underlying
124 # IPython shell's own shutdown).
124 # IPython shell's own shutdown).
125 _shutdown_message = None
125 _shutdown_message = None
126
126
127 # This is a dict of port number that the kernel is listening on. It is set
127 # This is a dict of port number that the kernel is listening on. It is set
128 # by record_ports and used by connect_request.
128 # by record_ports and used by connect_request.
129 _recorded_ports = Dict()
129 _recorded_ports = Dict()
130
130
131 # A reference to the Python builtin 'raw_input' function.
131 # A reference to the Python builtin 'raw_input' function.
132 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
132 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
133 _sys_raw_input = Any()
133 _sys_raw_input = Any()
134 _sys_eval_input = Any()
134 _sys_eval_input = Any()
135
135
136 # set of aborted msg_ids
136 # set of aborted msg_ids
137 aborted = Set()
137 aborted = Set()
138
138
139
139
140 def __init__(self, **kwargs):
140 def __init__(self, **kwargs):
141 super(Kernel, self).__init__(**kwargs)
141 super(Kernel, self).__init__(**kwargs)
142
142
143 # Initialize the InteractiveShell subclass
143 # Initialize the InteractiveShell subclass
144 self.shell = self.shell_class.instance(parent=self,
144 self.shell = self.shell_class.instance(parent=self,
145 profile_dir = self.profile_dir,
145 profile_dir = self.profile_dir,
146 user_module = self.user_module,
146 user_module = self.user_module,
147 user_ns = self.user_ns,
147 user_ns = self.user_ns,
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('pyout')
151 self.shell.displayhook.topic = self._topic('pyout')
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 'object_info_request', 'history_request',
162 'object_info_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 widget_msg_types = [ 'widget_update', 'widget_destroy' ]
171 widget_msg_types = [ 'widget_create', 'widget_update', 'widget_destroy' ]
172 widget_manager = self.shell.widget_manager
172 widget_manager = self.shell.widget_manager
173 for msg_type in widget_msg_types:
173 for msg_type in widget_msg_types:
174 self.shell_handlers[msg_type] = getattr(widget_manager, msg_type)
174 self.shell_handlers[msg_type] = getattr(widget_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 try:
246 try:
247 handler(stream, idents, msg)
247 handler(stream, idents, msg)
248 except Exception:
248 except Exception:
249 self.log.error("Exception in message handler:", exc_info=True)
249 self.log.error("Exception in message handler:", exc_info=True)
250 finally:
250 finally:
251 signal(SIGINT, sig)
251 signal(SIGINT, sig)
252
252
253 def enter_eventloop(self):
253 def enter_eventloop(self):
254 """enter eventloop"""
254 """enter eventloop"""
255 self.log.info("entering eventloop")
255 self.log.info("entering eventloop")
256 # restore default_int_handler
256 # restore default_int_handler
257 signal(SIGINT, default_int_handler)
257 signal(SIGINT, default_int_handler)
258 while self.eventloop is not None:
258 while self.eventloop is not None:
259 try:
259 try:
260 self.eventloop(self)
260 self.eventloop(self)
261 except KeyboardInterrupt:
261 except KeyboardInterrupt:
262 # Ctrl-C shouldn't crash the kernel
262 # Ctrl-C shouldn't crash the kernel
263 self.log.error("KeyboardInterrupt caught in kernel")
263 self.log.error("KeyboardInterrupt caught in kernel")
264 continue
264 continue
265 else:
265 else:
266 # eventloop exited cleanly, this means we should stop (right?)
266 # eventloop exited cleanly, this means we should stop (right?)
267 self.eventloop = None
267 self.eventloop = None
268 break
268 break
269 self.log.info("exiting eventloop")
269 self.log.info("exiting eventloop")
270
270
271 def start(self):
271 def start(self):
272 """register dispatchers for streams"""
272 """register dispatchers for streams"""
273 self.shell.exit_now = False
273 self.shell.exit_now = False
274 if self.control_stream:
274 if self.control_stream:
275 self.control_stream.on_recv(self.dispatch_control, copy=False)
275 self.control_stream.on_recv(self.dispatch_control, copy=False)
276
276
277 def make_dispatcher(stream):
277 def make_dispatcher(stream):
278 def dispatcher(msg):
278 def dispatcher(msg):
279 return self.dispatch_shell(stream, msg)
279 return self.dispatch_shell(stream, msg)
280 return dispatcher
280 return dispatcher
281
281
282 for s in self.shell_streams:
282 for s in self.shell_streams:
283 s.on_recv(make_dispatcher(s), copy=False)
283 s.on_recv(make_dispatcher(s), copy=False)
284
284
285 # publish idle status
285 # publish idle status
286 self._publish_status('starting')
286 self._publish_status('starting')
287
287
288 def do_one_iteration(self):
288 def do_one_iteration(self):
289 """step eventloop just once"""
289 """step eventloop just once"""
290 if self.control_stream:
290 if self.control_stream:
291 self.control_stream.flush()
291 self.control_stream.flush()
292 for stream in self.shell_streams:
292 for stream in self.shell_streams:
293 # handle at most one request per iteration
293 # handle at most one request per iteration
294 stream.flush(zmq.POLLIN, 1)
294 stream.flush(zmq.POLLIN, 1)
295 stream.flush(zmq.POLLOUT)
295 stream.flush(zmq.POLLOUT)
296
296
297
297
298 def record_ports(self, ports):
298 def record_ports(self, ports):
299 """Record the ports that this kernel is using.
299 """Record the ports that this kernel is using.
300
300
301 The creator of the Kernel instance must call this methods if they
301 The creator of the Kernel instance must call this methods if they
302 want the :meth:`connect_request` method to return the port numbers.
302 want the :meth:`connect_request` method to return the port numbers.
303 """
303 """
304 self._recorded_ports = ports
304 self._recorded_ports = ports
305
305
306 #---------------------------------------------------------------------------
306 #---------------------------------------------------------------------------
307 # Kernel request handlers
307 # Kernel request handlers
308 #---------------------------------------------------------------------------
308 #---------------------------------------------------------------------------
309
309
310 def _make_metadata(self, other=None):
310 def _make_metadata(self, other=None):
311 """init metadata dict, for execute/apply_reply"""
311 """init metadata dict, for execute/apply_reply"""
312 new_md = {
312 new_md = {
313 'dependencies_met' : True,
313 'dependencies_met' : True,
314 'engine' : self.ident,
314 'engine' : self.ident,
315 'started': datetime.now(),
315 'started': datetime.now(),
316 }
316 }
317 if other:
317 if other:
318 new_md.update(other)
318 new_md.update(other)
319 return new_md
319 return new_md
320
320
321 def _publish_pyin(self, code, parent, execution_count):
321 def _publish_pyin(self, code, parent, execution_count):
322 """Publish the code request on the pyin stream."""
322 """Publish the code request on the pyin stream."""
323
323
324 self.session.send(self.iopub_socket, u'pyin',
324 self.session.send(self.iopub_socket, u'pyin',
325 {u'code':code, u'execution_count': execution_count},
325 {u'code':code, u'execution_count': execution_count},
326 parent=parent, ident=self._topic('pyin')
326 parent=parent, ident=self._topic('pyin')
327 )
327 )
328
328
329 def _publish_status(self, status, parent=None):
329 def _publish_status(self, status, parent=None):
330 """send status (busy/idle) on IOPub"""
330 """send status (busy/idle) on IOPub"""
331 self.session.send(self.iopub_socket,
331 self.session.send(self.iopub_socket,
332 u'status',
332 u'status',
333 {u'execution_state': status},
333 {u'execution_state': status},
334 parent=parent,
334 parent=parent,
335 ident=self._topic('status'),
335 ident=self._topic('status'),
336 )
336 )
337
337
338
338
339 def execute_request(self, stream, ident, parent):
339 def execute_request(self, stream, ident, parent):
340 """handle an execute_request"""
340 """handle an execute_request"""
341
341
342 self._publish_status(u'busy', parent)
342 self._publish_status(u'busy', parent)
343
343
344 try:
344 try:
345 content = parent[u'content']
345 content = parent[u'content']
346 code = content[u'code']
346 code = content[u'code']
347 silent = content[u'silent']
347 silent = content[u'silent']
348 store_history = content.get(u'store_history', not silent)
348 store_history = content.get(u'store_history', not silent)
349 except:
349 except:
350 self.log.error("Got bad msg: ")
350 self.log.error("Got bad msg: ")
351 self.log.error("%s", parent)
351 self.log.error("%s", parent)
352 return
352 return
353
353
354 md = self._make_metadata(parent['metadata'])
354 md = self._make_metadata(parent['metadata'])
355
355
356 shell = self.shell # we'll need this a lot here
356 shell = self.shell # we'll need this a lot here
357
357
358 # Replace raw_input. Note that is not sufficient to replace
358 # Replace raw_input. Note that is not sufficient to replace
359 # raw_input in the user namespace.
359 # raw_input in the user namespace.
360 if content.get('allow_stdin', False):
360 if content.get('allow_stdin', False):
361 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
361 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
362 input = lambda prompt='': eval(raw_input(prompt))
362 input = lambda prompt='': eval(raw_input(prompt))
363 else:
363 else:
364 raw_input = input = lambda prompt='' : self._no_raw_input()
364 raw_input = input = lambda prompt='' : self._no_raw_input()
365
365
366 if py3compat.PY3:
366 if py3compat.PY3:
367 self._sys_raw_input = __builtin__.input
367 self._sys_raw_input = __builtin__.input
368 __builtin__.input = raw_input
368 __builtin__.input = raw_input
369 else:
369 else:
370 self._sys_raw_input = __builtin__.raw_input
370 self._sys_raw_input = __builtin__.raw_input
371 self._sys_eval_input = __builtin__.input
371 self._sys_eval_input = __builtin__.input
372 __builtin__.raw_input = raw_input
372 __builtin__.raw_input = raw_input
373 __builtin__.input = input
373 __builtin__.input = input
374
374
375 # Set the parent message of the display hook and out streams.
375 # Set the parent message of the display hook and out streams.
376 shell.displayhook.set_parent(parent)
376 shell.displayhook.set_parent(parent)
377 shell.display_pub.set_parent(parent)
377 shell.display_pub.set_parent(parent)
378 shell.data_pub.set_parent(parent)
378 shell.data_pub.set_parent(parent)
379 try:
379 try:
380 sys.stdout.set_parent(parent)
380 sys.stdout.set_parent(parent)
381 except AttributeError:
381 except AttributeError:
382 pass
382 pass
383 try:
383 try:
384 sys.stderr.set_parent(parent)
384 sys.stderr.set_parent(parent)
385 except AttributeError:
385 except AttributeError:
386 pass
386 pass
387
387
388 # Re-broadcast our input for the benefit of listening clients, and
388 # Re-broadcast our input for the benefit of listening clients, and
389 # start computing output
389 # start computing output
390 if not silent:
390 if not silent:
391 self._publish_pyin(code, parent, shell.execution_count)
391 self._publish_pyin(code, parent, shell.execution_count)
392
392
393 reply_content = {}
393 reply_content = {}
394 try:
394 try:
395 # FIXME: the shell calls the exception handler itself.
395 # FIXME: the shell calls the exception handler itself.
396 shell.run_cell(code, store_history=store_history, silent=silent)
396 shell.run_cell(code, store_history=store_history, silent=silent)
397 except:
397 except:
398 status = u'error'
398 status = u'error'
399 # FIXME: this code right now isn't being used yet by default,
399 # FIXME: this code right now isn't being used yet by default,
400 # because the run_cell() call above directly fires off exception
400 # because the run_cell() call above directly fires off exception
401 # reporting. This code, therefore, is only active in the scenario
401 # reporting. This code, therefore, is only active in the scenario
402 # where runlines itself has an unhandled exception. We need to
402 # where runlines itself has an unhandled exception. We need to
403 # uniformize this, for all exception construction to come from a
403 # uniformize this, for all exception construction to come from a
404 # single location in the codbase.
404 # single location in the codbase.
405 etype, evalue, tb = sys.exc_info()
405 etype, evalue, tb = sys.exc_info()
406 tb_list = traceback.format_exception(etype, evalue, tb)
406 tb_list = traceback.format_exception(etype, evalue, tb)
407 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
407 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
408 else:
408 else:
409 status = u'ok'
409 status = u'ok'
410 finally:
410 finally:
411 # Restore raw_input.
411 # Restore raw_input.
412 if py3compat.PY3:
412 if py3compat.PY3:
413 __builtin__.input = self._sys_raw_input
413 __builtin__.input = self._sys_raw_input
414 else:
414 else:
415 __builtin__.raw_input = self._sys_raw_input
415 __builtin__.raw_input = self._sys_raw_input
416 __builtin__.input = self._sys_eval_input
416 __builtin__.input = self._sys_eval_input
417
417
418 reply_content[u'status'] = status
418 reply_content[u'status'] = status
419
419
420 # Return the execution counter so clients can display prompts
420 # Return the execution counter so clients can display prompts
421 reply_content['execution_count'] = shell.execution_count - 1
421 reply_content['execution_count'] = shell.execution_count - 1
422
422
423 # FIXME - fish exception info out of shell, possibly left there by
423 # FIXME - fish exception info out of shell, possibly left there by
424 # runlines. We'll need to clean up this logic later.
424 # runlines. We'll need to clean up this logic later.
425 if shell._reply_content is not None:
425 if shell._reply_content is not None:
426 reply_content.update(shell._reply_content)
426 reply_content.update(shell._reply_content)
427 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
427 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
428 reply_content['engine_info'] = e_info
428 reply_content['engine_info'] = e_info
429 # reset after use
429 # reset after use
430 shell._reply_content = None
430 shell._reply_content = None
431
431
432 if 'traceback' in reply_content:
432 if 'traceback' in reply_content:
433 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
433 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
434
434
435
435
436 # At this point, we can tell whether the main code execution succeeded
436 # At this point, we can tell whether the main code execution succeeded
437 # or not. If it did, we proceed to evaluate user_variables/expressions
437 # or not. If it did, we proceed to evaluate user_variables/expressions
438 if reply_content['status'] == 'ok':
438 if reply_content['status'] == 'ok':
439 reply_content[u'user_variables'] = \
439 reply_content[u'user_variables'] = \
440 shell.user_variables(content.get(u'user_variables', []))
440 shell.user_variables(content.get(u'user_variables', []))
441 reply_content[u'user_expressions'] = \
441 reply_content[u'user_expressions'] = \
442 shell.user_expressions(content.get(u'user_expressions', {}))
442 shell.user_expressions(content.get(u'user_expressions', {}))
443 else:
443 else:
444 # If there was an error, don't even try to compute variables or
444 # If there was an error, don't even try to compute variables or
445 # expressions
445 # expressions
446 reply_content[u'user_variables'] = {}
446 reply_content[u'user_variables'] = {}
447 reply_content[u'user_expressions'] = {}
447 reply_content[u'user_expressions'] = {}
448
448
449 # Payloads should be retrieved regardless of outcome, so we can both
449 # Payloads should be retrieved regardless of outcome, so we can both
450 # recover partial output (that could have been generated early in a
450 # recover partial output (that could have been generated early in a
451 # block, before an error) and clear the payload system always.
451 # block, before an error) and clear the payload system always.
452 reply_content[u'payload'] = shell.payload_manager.read_payload()
452 reply_content[u'payload'] = shell.payload_manager.read_payload()
453 # Be agressive about clearing the payload because we don't want
453 # Be agressive about clearing the payload because we don't want
454 # it to sit in memory until the next execute_request comes in.
454 # it to sit in memory until the next execute_request comes in.
455 shell.payload_manager.clear_payload()
455 shell.payload_manager.clear_payload()
456
456
457 # Flush output before sending the reply.
457 # Flush output before sending the reply.
458 sys.stdout.flush()
458 sys.stdout.flush()
459 sys.stderr.flush()
459 sys.stderr.flush()
460 # FIXME: on rare occasions, the flush doesn't seem to make it to the
460 # FIXME: on rare occasions, the flush doesn't seem to make it to the
461 # clients... This seems to mitigate the problem, but we definitely need
461 # clients... This seems to mitigate the problem, but we definitely need
462 # to better understand what's going on.
462 # to better understand what's going on.
463 if self._execute_sleep:
463 if self._execute_sleep:
464 time.sleep(self._execute_sleep)
464 time.sleep(self._execute_sleep)
465
465
466 # Send the reply.
466 # Send the reply.
467 reply_content = json_clean(reply_content)
467 reply_content = json_clean(reply_content)
468
468
469 md['status'] = reply_content['status']
469 md['status'] = reply_content['status']
470 if reply_content['status'] == 'error' and \
470 if reply_content['status'] == 'error' and \
471 reply_content['ename'] == 'UnmetDependency':
471 reply_content['ename'] == 'UnmetDependency':
472 md['dependencies_met'] = False
472 md['dependencies_met'] = False
473
473
474 reply_msg = self.session.send(stream, u'execute_reply',
474 reply_msg = self.session.send(stream, u'execute_reply',
475 reply_content, parent, metadata=md,
475 reply_content, parent, metadata=md,
476 ident=ident)
476 ident=ident)
477
477
478 self.log.debug("%s", reply_msg)
478 self.log.debug("%s", reply_msg)
479
479
480 if not silent and reply_msg['content']['status'] == u'error':
480 if not silent and reply_msg['content']['status'] == u'error':
481 self._abort_queues()
481 self._abort_queues()
482
482
483 self._publish_status(u'idle', parent)
483 self._publish_status(u'idle', parent)
484
484
485 def complete_request(self, stream, ident, parent):
485 def complete_request(self, stream, ident, parent):
486 txt, matches = self._complete(parent)
486 txt, matches = self._complete(parent)
487 matches = {'matches' : matches,
487 matches = {'matches' : matches,
488 'matched_text' : txt,
488 'matched_text' : txt,
489 'status' : 'ok'}
489 'status' : 'ok'}
490 matches = json_clean(matches)
490 matches = json_clean(matches)
491 completion_msg = self.session.send(stream, 'complete_reply',
491 completion_msg = self.session.send(stream, 'complete_reply',
492 matches, parent, ident)
492 matches, parent, ident)
493 self.log.debug("%s", completion_msg)
493 self.log.debug("%s", completion_msg)
494
494
495 def object_info_request(self, stream, ident, parent):
495 def object_info_request(self, stream, ident, parent):
496 content = parent['content']
496 content = parent['content']
497 object_info = self.shell.object_inspect(content['oname'],
497 object_info = self.shell.object_inspect(content['oname'],
498 detail_level = content.get('detail_level', 0)
498 detail_level = content.get('detail_level', 0)
499 )
499 )
500 # Before we send this object over, we scrub it for JSON usage
500 # Before we send this object over, we scrub it for JSON usage
501 oinfo = json_clean(object_info)
501 oinfo = json_clean(object_info)
502 msg = self.session.send(stream, 'object_info_reply',
502 msg = self.session.send(stream, 'object_info_reply',
503 oinfo, parent, ident)
503 oinfo, parent, ident)
504 self.log.debug("%s", msg)
504 self.log.debug("%s", msg)
505
505
506 def history_request(self, stream, ident, parent):
506 def history_request(self, stream, ident, parent):
507 # We need to pull these out, as passing **kwargs doesn't work with
507 # We need to pull these out, as passing **kwargs doesn't work with
508 # unicode keys before Python 2.6.5.
508 # unicode keys before Python 2.6.5.
509 hist_access_type = parent['content']['hist_access_type']
509 hist_access_type = parent['content']['hist_access_type']
510 raw = parent['content']['raw']
510 raw = parent['content']['raw']
511 output = parent['content']['output']
511 output = parent['content']['output']
512 if hist_access_type == 'tail':
512 if hist_access_type == 'tail':
513 n = parent['content']['n']
513 n = parent['content']['n']
514 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
514 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
515 include_latest=True)
515 include_latest=True)
516
516
517 elif hist_access_type == 'range':
517 elif hist_access_type == 'range':
518 session = parent['content']['session']
518 session = parent['content']['session']
519 start = parent['content']['start']
519 start = parent['content']['start']
520 stop = parent['content']['stop']
520 stop = parent['content']['stop']
521 hist = self.shell.history_manager.get_range(session, start, stop,
521 hist = self.shell.history_manager.get_range(session, start, stop,
522 raw=raw, output=output)
522 raw=raw, output=output)
523
523
524 elif hist_access_type == 'search':
524 elif hist_access_type == 'search':
525 n = parent['content'].get('n')
525 n = parent['content'].get('n')
526 unique = parent['content'].get('unique', False)
526 unique = parent['content'].get('unique', False)
527 pattern = parent['content']['pattern']
527 pattern = parent['content']['pattern']
528 hist = self.shell.history_manager.search(
528 hist = self.shell.history_manager.search(
529 pattern, raw=raw, output=output, n=n, unique=unique)
529 pattern, raw=raw, output=output, n=n, unique=unique)
530
530
531 else:
531 else:
532 hist = []
532 hist = []
533 hist = list(hist)
533 hist = list(hist)
534 content = {'history' : hist}
534 content = {'history' : hist}
535 content = json_clean(content)
535 content = json_clean(content)
536 msg = self.session.send(stream, 'history_reply',
536 msg = self.session.send(stream, 'history_reply',
537 content, parent, ident)
537 content, parent, ident)
538 self.log.debug("Sending history reply with %i entries", len(hist))
538 self.log.debug("Sending history reply with %i entries", len(hist))
539
539
540 def connect_request(self, stream, ident, parent):
540 def connect_request(self, stream, ident, parent):
541 if self._recorded_ports is not None:
541 if self._recorded_ports is not None:
542 content = self._recorded_ports.copy()
542 content = self._recorded_ports.copy()
543 else:
543 else:
544 content = {}
544 content = {}
545 msg = self.session.send(stream, 'connect_reply',
545 msg = self.session.send(stream, 'connect_reply',
546 content, parent, ident)
546 content, parent, ident)
547 self.log.debug("%s", msg)
547 self.log.debug("%s", msg)
548
548
549 def kernel_info_request(self, stream, ident, parent):
549 def kernel_info_request(self, stream, ident, parent):
550 vinfo = {
550 vinfo = {
551 'protocol_version': protocol_version,
551 'protocol_version': protocol_version,
552 'ipython_version': ipython_version,
552 'ipython_version': ipython_version,
553 'language_version': language_version,
553 'language_version': language_version,
554 'language': 'python',
554 'language': 'python',
555 }
555 }
556 msg = self.session.send(stream, 'kernel_info_reply',
556 msg = self.session.send(stream, 'kernel_info_reply',
557 vinfo, parent, ident)
557 vinfo, parent, ident)
558 self.log.debug("%s", msg)
558 self.log.debug("%s", msg)
559
559
560 def shutdown_request(self, stream, ident, parent):
560 def shutdown_request(self, stream, ident, parent):
561 self.shell.exit_now = True
561 self.shell.exit_now = True
562 content = dict(status='ok')
562 content = dict(status='ok')
563 content.update(parent['content'])
563 content.update(parent['content'])
564 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
564 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
565 # same content, but different msg_id for broadcasting on IOPub
565 # same content, but different msg_id for broadcasting on IOPub
566 self._shutdown_message = self.session.msg(u'shutdown_reply',
566 self._shutdown_message = self.session.msg(u'shutdown_reply',
567 content, parent
567 content, parent
568 )
568 )
569
569
570 self._at_shutdown()
570 self._at_shutdown()
571 # call sys.exit after a short delay
571 # call sys.exit after a short delay
572 loop = ioloop.IOLoop.instance()
572 loop = ioloop.IOLoop.instance()
573 loop.add_timeout(time.time()+0.1, loop.stop)
573 loop.add_timeout(time.time()+0.1, loop.stop)
574
574
575 #---------------------------------------------------------------------------
575 #---------------------------------------------------------------------------
576 # Engine methods
576 # Engine methods
577 #---------------------------------------------------------------------------
577 #---------------------------------------------------------------------------
578
578
579 def apply_request(self, stream, ident, parent):
579 def apply_request(self, stream, ident, parent):
580 try:
580 try:
581 content = parent[u'content']
581 content = parent[u'content']
582 bufs = parent[u'buffers']
582 bufs = parent[u'buffers']
583 msg_id = parent['header']['msg_id']
583 msg_id = parent['header']['msg_id']
584 except:
584 except:
585 self.log.error("Got bad msg: %s", parent, exc_info=True)
585 self.log.error("Got bad msg: %s", parent, exc_info=True)
586 return
586 return
587
587
588 self._publish_status(u'busy', parent)
588 self._publish_status(u'busy', parent)
589
589
590 # Set the parent message of the display hook and out streams.
590 # Set the parent message of the display hook and out streams.
591 shell = self.shell
591 shell = self.shell
592 shell.displayhook.set_parent(parent)
592 shell.displayhook.set_parent(parent)
593 shell.display_pub.set_parent(parent)
593 shell.display_pub.set_parent(parent)
594 shell.data_pub.set_parent(parent)
594 shell.data_pub.set_parent(parent)
595 try:
595 try:
596 sys.stdout.set_parent(parent)
596 sys.stdout.set_parent(parent)
597 except AttributeError:
597 except AttributeError:
598 pass
598 pass
599 try:
599 try:
600 sys.stderr.set_parent(parent)
600 sys.stderr.set_parent(parent)
601 except AttributeError:
601 except AttributeError:
602 pass
602 pass
603
603
604 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
604 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
605 # self.iopub_socket.send(pyin_msg)
605 # self.iopub_socket.send(pyin_msg)
606 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
606 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
607 md = self._make_metadata(parent['metadata'])
607 md = self._make_metadata(parent['metadata'])
608 try:
608 try:
609 working = shell.user_ns
609 working = shell.user_ns
610
610
611 prefix = "_"+str(msg_id).replace("-","")+"_"
611 prefix = "_"+str(msg_id).replace("-","")+"_"
612
612
613 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
613 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
614
614
615 fname = getattr(f, '__name__', 'f')
615 fname = getattr(f, '__name__', 'f')
616
616
617 fname = prefix+"f"
617 fname = prefix+"f"
618 argname = prefix+"args"
618 argname = prefix+"args"
619 kwargname = prefix+"kwargs"
619 kwargname = prefix+"kwargs"
620 resultname = prefix+"result"
620 resultname = prefix+"result"
621
621
622 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
622 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
623 # print ns
623 # print ns
624 working.update(ns)
624 working.update(ns)
625 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
625 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
626 try:
626 try:
627 exec code in shell.user_global_ns, shell.user_ns
627 exec code in shell.user_global_ns, shell.user_ns
628 result = working.get(resultname)
628 result = working.get(resultname)
629 finally:
629 finally:
630 for key in ns.iterkeys():
630 for key in ns.iterkeys():
631 working.pop(key)
631 working.pop(key)
632
632
633 result_buf = serialize_object(result,
633 result_buf = serialize_object(result,
634 buffer_threshold=self.session.buffer_threshold,
634 buffer_threshold=self.session.buffer_threshold,
635 item_threshold=self.session.item_threshold,
635 item_threshold=self.session.item_threshold,
636 )
636 )
637
637
638 except:
638 except:
639 # invoke IPython traceback formatting
639 # invoke IPython traceback formatting
640 shell.showtraceback()
640 shell.showtraceback()
641 # FIXME - fish exception info out of shell, possibly left there by
641 # FIXME - fish exception info out of shell, possibly left there by
642 # run_code. We'll need to clean up this logic later.
642 # run_code. We'll need to clean up this logic later.
643 reply_content = {}
643 reply_content = {}
644 if shell._reply_content is not None:
644 if shell._reply_content is not None:
645 reply_content.update(shell._reply_content)
645 reply_content.update(shell._reply_content)
646 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
646 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
647 reply_content['engine_info'] = e_info
647 reply_content['engine_info'] = e_info
648 # reset after use
648 # reset after use
649 shell._reply_content = None
649 shell._reply_content = None
650
650
651 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
651 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
652 ident=self._topic('pyerr'))
652 ident=self._topic('pyerr'))
653 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
653 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
654 result_buf = []
654 result_buf = []
655
655
656 if reply_content['ename'] == 'UnmetDependency':
656 if reply_content['ename'] == 'UnmetDependency':
657 md['dependencies_met'] = False
657 md['dependencies_met'] = False
658 else:
658 else:
659 reply_content = {'status' : 'ok'}
659 reply_content = {'status' : 'ok'}
660
660
661 # put 'ok'/'error' status in header, for scheduler introspection:
661 # put 'ok'/'error' status in header, for scheduler introspection:
662 md['status'] = reply_content['status']
662 md['status'] = reply_content['status']
663
663
664 # flush i/o
664 # flush i/o
665 sys.stdout.flush()
665 sys.stdout.flush()
666 sys.stderr.flush()
666 sys.stderr.flush()
667
667
668 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
668 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
669 parent=parent, ident=ident,buffers=result_buf, metadata=md)
669 parent=parent, ident=ident,buffers=result_buf, metadata=md)
670
670
671 self._publish_status(u'idle', parent)
671 self._publish_status(u'idle', parent)
672
672
673 #---------------------------------------------------------------------------
673 #---------------------------------------------------------------------------
674 # Control messages
674 # Control messages
675 #---------------------------------------------------------------------------
675 #---------------------------------------------------------------------------
676
676
677 def abort_request(self, stream, ident, parent):
677 def abort_request(self, stream, ident, parent):
678 """abort a specifig msg by id"""
678 """abort a specifig msg by id"""
679 msg_ids = parent['content'].get('msg_ids', None)
679 msg_ids = parent['content'].get('msg_ids', None)
680 if isinstance(msg_ids, basestring):
680 if isinstance(msg_ids, basestring):
681 msg_ids = [msg_ids]
681 msg_ids = [msg_ids]
682 if not msg_ids:
682 if not msg_ids:
683 self.abort_queues()
683 self.abort_queues()
684 for mid in msg_ids:
684 for mid in msg_ids:
685 self.aborted.add(str(mid))
685 self.aborted.add(str(mid))
686
686
687 content = dict(status='ok')
687 content = dict(status='ok')
688 reply_msg = self.session.send(stream, 'abort_reply', content=content,
688 reply_msg = self.session.send(stream, 'abort_reply', content=content,
689 parent=parent, ident=ident)
689 parent=parent, ident=ident)
690 self.log.debug("%s", reply_msg)
690 self.log.debug("%s", reply_msg)
691
691
692 def clear_request(self, stream, idents, parent):
692 def clear_request(self, stream, idents, parent):
693 """Clear our namespace."""
693 """Clear our namespace."""
694 self.shell.reset(False)
694 self.shell.reset(False)
695 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
695 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
696 content = dict(status='ok'))
696 content = dict(status='ok'))
697
697
698
698
699 #---------------------------------------------------------------------------
699 #---------------------------------------------------------------------------
700 # Protected interface
700 # Protected interface
701 #---------------------------------------------------------------------------
701 #---------------------------------------------------------------------------
702
702
703 def _wrap_exception(self, method=None):
703 def _wrap_exception(self, method=None):
704 # import here, because _wrap_exception is only used in parallel,
704 # import here, because _wrap_exception is only used in parallel,
705 # and parallel has higher min pyzmq version
705 # and parallel has higher min pyzmq version
706 from IPython.parallel.error import wrap_exception
706 from IPython.parallel.error import wrap_exception
707 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
707 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
708 content = wrap_exception(e_info)
708 content = wrap_exception(e_info)
709 return content
709 return content
710
710
711 def _topic(self, topic):
711 def _topic(self, topic):
712 """prefixed topic for IOPub messages"""
712 """prefixed topic for IOPub messages"""
713 if self.int_id >= 0:
713 if self.int_id >= 0:
714 base = "engine.%i" % self.int_id
714 base = "engine.%i" % self.int_id
715 else:
715 else:
716 base = "kernel.%s" % self.ident
716 base = "kernel.%s" % self.ident
717
717
718 return py3compat.cast_bytes("%s.%s" % (base, topic))
718 return py3compat.cast_bytes("%s.%s" % (base, topic))
719
719
720 def _abort_queues(self):
720 def _abort_queues(self):
721 for stream in self.shell_streams:
721 for stream in self.shell_streams:
722 if stream:
722 if stream:
723 self._abort_queue(stream)
723 self._abort_queue(stream)
724
724
725 def _abort_queue(self, stream):
725 def _abort_queue(self, stream):
726 poller = zmq.Poller()
726 poller = zmq.Poller()
727 poller.register(stream.socket, zmq.POLLIN)
727 poller.register(stream.socket, zmq.POLLIN)
728 while True:
728 while True:
729 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
729 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
730 if msg is None:
730 if msg is None:
731 return
731 return
732
732
733 self.log.info("Aborting:")
733 self.log.info("Aborting:")
734 self.log.info("%s", msg)
734 self.log.info("%s", msg)
735 msg_type = msg['header']['msg_type']
735 msg_type = msg['header']['msg_type']
736 reply_type = msg_type.split('_')[0] + '_reply'
736 reply_type = msg_type.split('_')[0] + '_reply'
737
737
738 status = {'status' : 'aborted'}
738 status = {'status' : 'aborted'}
739 md = {'engine' : self.ident}
739 md = {'engine' : self.ident}
740 md.update(status)
740 md.update(status)
741 reply_msg = self.session.send(stream, reply_type, metadata=md,
741 reply_msg = self.session.send(stream, reply_type, metadata=md,
742 content=status, parent=msg, ident=idents)
742 content=status, parent=msg, ident=idents)
743 self.log.debug("%s", reply_msg)
743 self.log.debug("%s", reply_msg)
744 # We need to wait a bit for requests to come in. This can probably
744 # We need to wait a bit for requests to come in. This can probably
745 # be set shorter for true asynchronous clients.
745 # be set shorter for true asynchronous clients.
746 poller.poll(50)
746 poller.poll(50)
747
747
748
748
749 def _no_raw_input(self):
749 def _no_raw_input(self):
750 """Raise StdinNotImplentedError if active frontend doesn't support
750 """Raise StdinNotImplentedError if active frontend doesn't support
751 stdin."""
751 stdin."""
752 raise StdinNotImplementedError("raw_input was called, but this "
752 raise StdinNotImplementedError("raw_input was called, but this "
753 "frontend does not support stdin.")
753 "frontend does not support stdin.")
754
754
755 def _raw_input(self, prompt, ident, parent):
755 def _raw_input(self, prompt, ident, parent):
756 # Flush output before making the request.
756 # Flush output before making the request.
757 sys.stderr.flush()
757 sys.stderr.flush()
758 sys.stdout.flush()
758 sys.stdout.flush()
759 # flush the stdin socket, to purge stale replies
759 # flush the stdin socket, to purge stale replies
760 while True:
760 while True:
761 try:
761 try:
762 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
762 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
763 except zmq.ZMQError as e:
763 except zmq.ZMQError as e:
764 if e.errno == zmq.EAGAIN:
764 if e.errno == zmq.EAGAIN:
765 break
765 break
766 else:
766 else:
767 raise
767 raise
768
768
769 # Send the input request.
769 # Send the input request.
770 content = json_clean(dict(prompt=prompt))
770 content = json_clean(dict(prompt=prompt))
771 self.session.send(self.stdin_socket, u'input_request', content, parent,
771 self.session.send(self.stdin_socket, u'input_request', content, parent,
772 ident=ident)
772 ident=ident)
773
773
774 # Await a response.
774 # Await a response.
775 while True:
775 while True:
776 try:
776 try:
777 ident, reply = self.session.recv(self.stdin_socket, 0)
777 ident, reply = self.session.recv(self.stdin_socket, 0)
778 except Exception:
778 except Exception:
779 self.log.warn("Invalid Message:", exc_info=True)
779 self.log.warn("Invalid Message:", exc_info=True)
780 except KeyboardInterrupt:
780 except KeyboardInterrupt:
781 # re-raise KeyboardInterrupt, to truncate traceback
781 # re-raise KeyboardInterrupt, to truncate traceback
782 raise KeyboardInterrupt
782 raise KeyboardInterrupt
783 else:
783 else:
784 break
784 break
785 try:
785 try:
786 value = py3compat.unicode_to_str(reply['content']['value'])
786 value = py3compat.unicode_to_str(reply['content']['value'])
787 except:
787 except:
788 self.log.error("Got bad raw_input reply: ")
788 self.log.error("Got bad raw_input reply: ")
789 self.log.error("%s", parent)
789 self.log.error("%s", parent)
790 value = ''
790 value = ''
791 if value == '\x04':
791 if value == '\x04':
792 # EOF
792 # EOF
793 raise EOFError
793 raise EOFError
794 return value
794 return value
795
795
796 def _complete(self, msg):
796 def _complete(self, msg):
797 c = msg['content']
797 c = msg['content']
798 try:
798 try:
799 cpos = int(c['cursor_pos'])
799 cpos = int(c['cursor_pos'])
800 except:
800 except:
801 # If we don't get something that we can convert to an integer, at
801 # If we don't get something that we can convert to an integer, at
802 # least attempt the completion guessing the cursor is at the end of
802 # least attempt the completion guessing the cursor is at the end of
803 # the text, if there's any, and otherwise of the line
803 # the text, if there's any, and otherwise of the line
804 cpos = len(c['text'])
804 cpos = len(c['text'])
805 if cpos==0:
805 if cpos==0:
806 cpos = len(c['line'])
806 cpos = len(c['line'])
807 return self.shell.complete(c['text'], c['line'], cpos)
807 return self.shell.complete(c['text'], c['line'], cpos)
808
808
809 def _at_shutdown(self):
809 def _at_shutdown(self):
810 """Actions taken at shutdown by the kernel, called by python's atexit.
810 """Actions taken at shutdown by the kernel, called by python's atexit.
811 """
811 """
812 # io.rprint("Kernel at_shutdown") # dbg
812 # io.rprint("Kernel at_shutdown") # dbg
813 if self._shutdown_message is not None:
813 if self._shutdown_message is not None:
814 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
814 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
815 self.log.debug("%s", self._shutdown_message)
815 self.log.debug("%s", self._shutdown_message)
816 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
816 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
817
817
General Comments 0
You need to be logged in to leave comments. Login now