##// END OF EJS Templates
only use zmq.jsonapi when talking to zmq sockets...
MinRK -
Show More
@@ -1,134 +1,134 b''
1 """Tornado handlers for WebSocket <-> ZMQ sockets."""
1 """Tornado handlers for WebSocket <-> ZMQ sockets."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import json
7
6 try:
8 try:
7 from urllib.parse import urlparse # Py 3
9 from urllib.parse import urlparse # Py 3
8 except ImportError:
10 except ImportError:
9 from urlparse import urlparse # Py 2
11 from urlparse import urlparse # Py 2
10
12
11 try:
13 try:
12 from http.cookies import SimpleCookie # Py 3
14 from http.cookies import SimpleCookie # Py 3
13 except ImportError:
15 except ImportError:
14 from Cookie import SimpleCookie # Py 2
16 from Cookie import SimpleCookie # Py 2
15 import logging
17 import logging
16 from tornado import web
18 from tornado import web
17 from tornado import websocket
19 from tornado import websocket
18
20
19 from zmq.utils import jsonapi
20
21 from IPython.kernel.zmq.session import Session
21 from IPython.kernel.zmq.session import Session
22 from IPython.utils.jsonutil import date_default
22 from IPython.utils.jsonutil import date_default
23 from IPython.utils.py3compat import PY3, cast_unicode
23 from IPython.utils.py3compat import PY3, cast_unicode
24
24
25 from .handlers import IPythonHandler
25 from .handlers import IPythonHandler
26
26
27
27
28 class ZMQStreamHandler(websocket.WebSocketHandler):
28 class ZMQStreamHandler(websocket.WebSocketHandler):
29
29
30 def same_origin(self):
30 def same_origin(self):
31 """Check to see that origin and host match in the headers."""
31 """Check to see that origin and host match in the headers."""
32
32
33 # The difference between version 8 and 13 is that in 8 the
33 # The difference between version 8 and 13 is that in 8 the
34 # client sends a "Sec-Websocket-Origin" header and in 13 it's
34 # client sends a "Sec-Websocket-Origin" header and in 13 it's
35 # simply "Origin".
35 # simply "Origin".
36 if self.request.headers.get("Sec-WebSocket-Version") in ("7", "8"):
36 if self.request.headers.get("Sec-WebSocket-Version") in ("7", "8"):
37 origin_header = self.request.headers.get("Sec-Websocket-Origin")
37 origin_header = self.request.headers.get("Sec-Websocket-Origin")
38 else:
38 else:
39 origin_header = self.request.headers.get("Origin")
39 origin_header = self.request.headers.get("Origin")
40
40
41 host = self.request.headers.get("Host")
41 host = self.request.headers.get("Host")
42
42
43 # If no header is provided, assume we can't verify origin
43 # If no header is provided, assume we can't verify origin
44 if(origin_header is None or host is None):
44 if(origin_header is None or host is None):
45 return False
45 return False
46
46
47 parsed_origin = urlparse(origin_header)
47 parsed_origin = urlparse(origin_header)
48 origin = parsed_origin.netloc
48 origin = parsed_origin.netloc
49
49
50 # Check to see that origin matches host directly, including ports
50 # Check to see that origin matches host directly, including ports
51 return origin == host
51 return origin == host
52
52
53 def clear_cookie(self, *args, **kwargs):
53 def clear_cookie(self, *args, **kwargs):
54 """meaningless for websockets"""
54 """meaningless for websockets"""
55 pass
55 pass
56
56
57 def _reserialize_reply(self, msg_list):
57 def _reserialize_reply(self, msg_list):
58 """Reserialize a reply message using JSON.
58 """Reserialize a reply message using JSON.
59
59
60 This takes the msg list from the ZMQ socket, unserializes it using
60 This takes the msg list from the ZMQ socket, unserializes it using
61 self.session and then serializes the result using JSON. This method
61 self.session and then serializes the result using JSON. This method
62 should be used by self._on_zmq_reply to build messages that can
62 should be used by self._on_zmq_reply to build messages that can
63 be sent back to the browser.
63 be sent back to the browser.
64 """
64 """
65 idents, msg_list = self.session.feed_identities(msg_list)
65 idents, msg_list = self.session.feed_identities(msg_list)
66 msg = self.session.unserialize(msg_list)
66 msg = self.session.unserialize(msg_list)
67 try:
67 try:
68 msg['header'].pop('date')
68 msg['header'].pop('date')
69 except KeyError:
69 except KeyError:
70 pass
70 pass
71 try:
71 try:
72 msg['parent_header'].pop('date')
72 msg['parent_header'].pop('date')
73 except KeyError:
73 except KeyError:
74 pass
74 pass
75 msg.pop('buffers')
75 msg.pop('buffers')
76 return jsonapi.dumps(msg, default=date_default)
76 return json.dumps(msg, default=date_default)
77
77
78 def _on_zmq_reply(self, msg_list):
78 def _on_zmq_reply(self, msg_list):
79 # Sometimes this gets triggered when the on_close method is scheduled in the
79 # Sometimes this gets triggered when the on_close method is scheduled in the
80 # eventloop but hasn't been called.
80 # eventloop but hasn't been called.
81 if self.stream.closed(): return
81 if self.stream.closed(): return
82 try:
82 try:
83 msg = self._reserialize_reply(msg_list)
83 msg = self._reserialize_reply(msg_list)
84 except Exception:
84 except Exception:
85 self.log.critical("Malformed message: %r" % msg_list, exc_info=True)
85 self.log.critical("Malformed message: %r" % msg_list, exc_info=True)
86 else:
86 else:
87 self.write_message(msg)
87 self.write_message(msg)
88
88
89 def allow_draft76(self):
89 def allow_draft76(self):
90 """Allow draft 76, until browsers such as Safari update to RFC 6455.
90 """Allow draft 76, until browsers such as Safari update to RFC 6455.
91
91
92 This has been disabled by default in tornado in release 2.2.0, and
92 This has been disabled by default in tornado in release 2.2.0, and
93 support will be removed in later versions.
93 support will be removed in later versions.
94 """
94 """
95 return True
95 return True
96
96
97
97
98 class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler):
98 class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler):
99
99
100 def open(self, kernel_id):
100 def open(self, kernel_id):
101 self.kernel_id = cast_unicode(kernel_id, 'ascii')
101 self.kernel_id = cast_unicode(kernel_id, 'ascii')
102 # Check to see that origin matches host directly, including ports
102 # Check to see that origin matches host directly, including ports
103 if not self.same_origin():
103 if not self.same_origin():
104 self.log.warn("Cross Origin WebSocket Attempt.")
104 self.log.warn("Cross Origin WebSocket Attempt.")
105 raise web.HTTPError(404)
105 raise web.HTTPError(404)
106
106
107 self.session = Session(config=self.config)
107 self.session = Session(config=self.config)
108 self.save_on_message = self.on_message
108 self.save_on_message = self.on_message
109 self.on_message = self.on_first_message
109 self.on_message = self.on_first_message
110
110
111 def _inject_cookie_message(self, msg):
111 def _inject_cookie_message(self, msg):
112 """Inject the first message, which is the document cookie,
112 """Inject the first message, which is the document cookie,
113 for authentication."""
113 for authentication."""
114 if not PY3 and isinstance(msg, unicode):
114 if not PY3 and isinstance(msg, unicode):
115 # Cookie constructor doesn't accept unicode strings
115 # Cookie constructor doesn't accept unicode strings
116 # under Python 2.x for some reason
116 # under Python 2.x for some reason
117 msg = msg.encode('utf8', 'replace')
117 msg = msg.encode('utf8', 'replace')
118 try:
118 try:
119 identity, msg = msg.split(':', 1)
119 identity, msg = msg.split(':', 1)
120 self.session.session = cast_unicode(identity, 'ascii')
120 self.session.session = cast_unicode(identity, 'ascii')
121 except Exception:
121 except Exception:
122 logging.error("First ws message didn't have the form 'identity:[cookie]' - %r", msg)
122 logging.error("First ws message didn't have the form 'identity:[cookie]' - %r", msg)
123
123
124 try:
124 try:
125 self.request._cookies = SimpleCookie(msg)
125 self.request._cookies = SimpleCookie(msg)
126 except:
126 except:
127 self.log.warn("couldn't parse cookie string: %s",msg, exc_info=True)
127 self.log.warn("couldn't parse cookie string: %s",msg, exc_info=True)
128
128
129 def on_first_message(self, msg):
129 def on_first_message(self, msg):
130 self._inject_cookie_message(msg)
130 self._inject_cookie_message(msg)
131 if self.get_current_user() is None:
131 if self.get_current_user() is None:
132 self.log.warn("Couldn't authenticate WebSocket connection")
132 self.log.warn("Couldn't authenticate WebSocket connection")
133 raise web.HTTPError(403)
133 raise web.HTTPError(403)
134 self.on_message = self.save_on_message
134 self.on_message = self.save_on_message
@@ -1,72 +1,59 b''
1 """Tornado handlers for cluster web service.
1 """Tornado handlers for cluster web service."""
2
2
3 Authors:
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4
5
5 * Brian Granger
6 import json
6 """
7
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
7
19 from tornado import web
8 from tornado import web
20
9
21 from zmq.utils import jsonapi
22
23 from ...base.handlers import IPythonHandler
10 from ...base.handlers import IPythonHandler
24
11
25 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
26 # Cluster handlers
13 # Cluster handlers
27 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
28
15
29
16
30 class MainClusterHandler(IPythonHandler):
17 class MainClusterHandler(IPythonHandler):
31
18
32 @web.authenticated
19 @web.authenticated
33 def get(self):
20 def get(self):
34 self.finish(jsonapi.dumps(self.cluster_manager.list_profiles()))
21 self.finish(json.dumps(self.cluster_manager.list_profiles()))
35
22
36
23
37 class ClusterProfileHandler(IPythonHandler):
24 class ClusterProfileHandler(IPythonHandler):
38
25
39 @web.authenticated
26 @web.authenticated
40 def get(self, profile):
27 def get(self, profile):
41 self.finish(jsonapi.dumps(self.cluster_manager.profile_info(profile)))
28 self.finish(json.dumps(self.cluster_manager.profile_info(profile)))
42
29
43
30
44 class ClusterActionHandler(IPythonHandler):
31 class ClusterActionHandler(IPythonHandler):
45
32
46 @web.authenticated
33 @web.authenticated
47 def post(self, profile, action):
34 def post(self, profile, action):
48 cm = self.cluster_manager
35 cm = self.cluster_manager
49 if action == 'start':
36 if action == 'start':
50 n = self.get_argument('n', default=None)
37 n = self.get_argument('n', default=None)
51 if not n:
38 if not n:
52 data = cm.start_cluster(profile)
39 data = cm.start_cluster(profile)
53 else:
40 else:
54 data = cm.start_cluster(profile, int(n))
41 data = cm.start_cluster(profile, int(n))
55 if action == 'stop':
42 if action == 'stop':
56 data = cm.stop_cluster(profile)
43 data = cm.stop_cluster(profile)
57 self.finish(jsonapi.dumps(data))
44 self.finish(json.dumps(data))
58
45
59
46
60 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
61 # URL to handler mappings
48 # URL to handler mappings
62 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
63
50
64
51
65 _cluster_action_regex = r"(?P<action>start|stop)"
52 _cluster_action_regex = r"(?P<action>start|stop)"
66 _profile_regex = r"(?P<profile>[^\/]+)" # there is almost no text that is invalid
53 _profile_regex = r"(?P<profile>[^\/]+)" # there is almost no text that is invalid
67
54
68 default_handlers = [
55 default_handlers = [
69 (r"/clusters", MainClusterHandler),
56 (r"/clusters", MainClusterHandler),
70 (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
57 (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
71 (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
58 (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
72 ]
59 ]
@@ -1,218 +1,217 b''
1 """Tornado handlers for kernels."""
1 """Tornado handlers for kernels."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import json
6 import logging
7 import logging
7 from tornado import web
8 from tornado import web
8
9
9 from zmq.utils import jsonapi
10
11 from IPython.utils.jsonutil import date_default
10 from IPython.utils.jsonutil import date_default
12 from IPython.utils.py3compat import string_types
11 from IPython.utils.py3compat import string_types
13 from IPython.html.utils import url_path_join, url_escape
12 from IPython.html.utils import url_path_join, url_escape
14
13
15 from ...base.handlers import IPythonHandler, json_errors
14 from ...base.handlers import IPythonHandler, json_errors
16 from ...base.zmqhandlers import AuthenticatedZMQStreamHandler
15 from ...base.zmqhandlers import AuthenticatedZMQStreamHandler
17
16
18 from IPython.core.release import kernel_protocol_version
17 from IPython.core.release import kernel_protocol_version
19
18
20 class MainKernelHandler(IPythonHandler):
19 class MainKernelHandler(IPythonHandler):
21
20
22 @web.authenticated
21 @web.authenticated
23 @json_errors
22 @json_errors
24 def get(self):
23 def get(self):
25 km = self.kernel_manager
24 km = self.kernel_manager
26 self.finish(jsonapi.dumps(km.list_kernels()))
25 self.finish(json.dumps(km.list_kernels()))
27
26
28 @web.authenticated
27 @web.authenticated
29 @json_errors
28 @json_errors
30 def post(self):
29 def post(self):
31 km = self.kernel_manager
30 km = self.kernel_manager
32 kernel_id = km.start_kernel()
31 kernel_id = km.start_kernel()
33 model = km.kernel_model(kernel_id)
32 model = km.kernel_model(kernel_id)
34 location = url_path_join(self.base_url, 'api', 'kernels', kernel_id)
33 location = url_path_join(self.base_url, 'api', 'kernels', kernel_id)
35 self.set_header('Location', url_escape(location))
34 self.set_header('Location', url_escape(location))
36 self.set_status(201)
35 self.set_status(201)
37 self.finish(jsonapi.dumps(model))
36 self.finish(json.dumps(model))
38
37
39
38
40 class KernelHandler(IPythonHandler):
39 class KernelHandler(IPythonHandler):
41
40
42 SUPPORTED_METHODS = ('DELETE', 'GET')
41 SUPPORTED_METHODS = ('DELETE', 'GET')
43
42
44 @web.authenticated
43 @web.authenticated
45 @json_errors
44 @json_errors
46 def get(self, kernel_id):
45 def get(self, kernel_id):
47 km = self.kernel_manager
46 km = self.kernel_manager
48 km._check_kernel_id(kernel_id)
47 km._check_kernel_id(kernel_id)
49 model = km.kernel_model(kernel_id)
48 model = km.kernel_model(kernel_id)
50 self.finish(jsonapi.dumps(model))
49 self.finish(json.dumps(model))
51
50
52 @web.authenticated
51 @web.authenticated
53 @json_errors
52 @json_errors
54 def delete(self, kernel_id):
53 def delete(self, kernel_id):
55 km = self.kernel_manager
54 km = self.kernel_manager
56 km.shutdown_kernel(kernel_id)
55 km.shutdown_kernel(kernel_id)
57 self.set_status(204)
56 self.set_status(204)
58 self.finish()
57 self.finish()
59
58
60
59
61 class KernelActionHandler(IPythonHandler):
60 class KernelActionHandler(IPythonHandler):
62
61
63 @web.authenticated
62 @web.authenticated
64 @json_errors
63 @json_errors
65 def post(self, kernel_id, action):
64 def post(self, kernel_id, action):
66 km = self.kernel_manager
65 km = self.kernel_manager
67 if action == 'interrupt':
66 if action == 'interrupt':
68 km.interrupt_kernel(kernel_id)
67 km.interrupt_kernel(kernel_id)
69 self.set_status(204)
68 self.set_status(204)
70 if action == 'restart':
69 if action == 'restart':
71 km.restart_kernel(kernel_id)
70 km.restart_kernel(kernel_id)
72 model = km.kernel_model(kernel_id)
71 model = km.kernel_model(kernel_id)
73 self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_url, kernel_id))
72 self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_url, kernel_id))
74 self.write(jsonapi.dumps(model))
73 self.write(json.dumps(model))
75 self.finish()
74 self.finish()
76
75
77
76
78 class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
77 class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
79
78
80 def create_stream(self):
79 def create_stream(self):
81 km = self.kernel_manager
80 km = self.kernel_manager
82 meth = getattr(km, 'connect_%s' % self.channel)
81 meth = getattr(km, 'connect_%s' % self.channel)
83 self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
82 self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
84 # Create a kernel_info channel to query the kernel protocol version.
83 # Create a kernel_info channel to query the kernel protocol version.
85 # This channel will be closed after the kernel_info reply is received.
84 # This channel will be closed after the kernel_info reply is received.
86 self.kernel_info_channel = None
85 self.kernel_info_channel = None
87 self.kernel_info_channel = km.connect_shell(self.kernel_id)
86 self.kernel_info_channel = km.connect_shell(self.kernel_id)
88 self.kernel_info_channel.on_recv(self._handle_kernel_info_reply)
87 self.kernel_info_channel.on_recv(self._handle_kernel_info_reply)
89 self._request_kernel_info()
88 self._request_kernel_info()
90
89
91 def _request_kernel_info(self):
90 def _request_kernel_info(self):
92 """send a request for kernel_info"""
91 """send a request for kernel_info"""
93 self.log.debug("requesting kernel info")
92 self.log.debug("requesting kernel info")
94 self.session.send(self.kernel_info_channel, "kernel_info_request")
93 self.session.send(self.kernel_info_channel, "kernel_info_request")
95
94
96 def _handle_kernel_info_reply(self, msg):
95 def _handle_kernel_info_reply(self, msg):
97 """process the kernel_info_reply
96 """process the kernel_info_reply
98
97
99 enabling msg spec adaptation, if necessary
98 enabling msg spec adaptation, if necessary
100 """
99 """
101 idents,msg = self.session.feed_identities(msg)
100 idents,msg = self.session.feed_identities(msg)
102 try:
101 try:
103 msg = self.session.unserialize(msg)
102 msg = self.session.unserialize(msg)
104 except:
103 except:
105 self.log.error("Bad kernel_info reply", exc_info=True)
104 self.log.error("Bad kernel_info reply", exc_info=True)
106 self._request_kernel_info()
105 self._request_kernel_info()
107 return
106 return
108 else:
107 else:
109 if msg['msg_type'] != 'kernel_info_reply' or 'protocol_version' not in msg['content']:
108 if msg['msg_type'] != 'kernel_info_reply' or 'protocol_version' not in msg['content']:
110 self.log.error("Kernel info request failed, assuming current %s", msg['content'])
109 self.log.error("Kernel info request failed, assuming current %s", msg['content'])
111 else:
110 else:
112 protocol_version = msg['content']['protocol_version']
111 protocol_version = msg['content']['protocol_version']
113 if protocol_version != kernel_protocol_version:
112 if protocol_version != kernel_protocol_version:
114 self.session.adapt_version = int(protocol_version.split('.')[0])
113 self.session.adapt_version = int(protocol_version.split('.')[0])
115 self.log.info("adapting kernel to %s" % protocol_version)
114 self.log.info("adapting kernel to %s" % protocol_version)
116 self.kernel_info_channel.close()
115 self.kernel_info_channel.close()
117 self.kernel_info_channel = None
116 self.kernel_info_channel = None
118
117
119
118
120 def initialize(self, *args, **kwargs):
119 def initialize(self, *args, **kwargs):
121 self.zmq_stream = None
120 self.zmq_stream = None
122
121
123 def on_first_message(self, msg):
122 def on_first_message(self, msg):
124 try:
123 try:
125 super(ZMQChannelHandler, self).on_first_message(msg)
124 super(ZMQChannelHandler, self).on_first_message(msg)
126 except web.HTTPError:
125 except web.HTTPError:
127 self.close()
126 self.close()
128 return
127 return
129 try:
128 try:
130 self.create_stream()
129 self.create_stream()
131 except web.HTTPError:
130 except web.HTTPError:
132 # WebSockets don't response to traditional error codes so we
131 # WebSockets don't response to traditional error codes so we
133 # close the connection.
132 # close the connection.
134 if not self.stream.closed():
133 if not self.stream.closed():
135 self.stream.close()
134 self.stream.close()
136 self.close()
135 self.close()
137 else:
136 else:
138 self.zmq_stream.on_recv(self._on_zmq_reply)
137 self.zmq_stream.on_recv(self._on_zmq_reply)
139
138
140 def on_message(self, msg):
139 def on_message(self, msg):
141 msg = jsonapi.loads(msg)
140 msg = json.loads(msg)
142 self.session.send(self.zmq_stream, msg)
141 self.session.send(self.zmq_stream, msg)
143
142
144 def on_close(self):
143 def on_close(self):
145 # This method can be called twice, once by self.kernel_died and once
144 # This method can be called twice, once by self.kernel_died and once
146 # from the WebSocket close event. If the WebSocket connection is
145 # from the WebSocket close event. If the WebSocket connection is
147 # closed before the ZMQ streams are setup, they could be None.
146 # closed before the ZMQ streams are setup, they could be None.
148 if self.zmq_stream is not None and not self.zmq_stream.closed():
147 if self.zmq_stream is not None and not self.zmq_stream.closed():
149 self.zmq_stream.on_recv(None)
148 self.zmq_stream.on_recv(None)
150 # close the socket directly, don't wait for the stream
149 # close the socket directly, don't wait for the stream
151 socket = self.zmq_stream.socket
150 socket = self.zmq_stream.socket
152 self.zmq_stream.close()
151 self.zmq_stream.close()
153 socket.close()
152 socket.close()
154
153
155
154
156 class IOPubHandler(ZMQChannelHandler):
155 class IOPubHandler(ZMQChannelHandler):
157 channel = 'iopub'
156 channel = 'iopub'
158
157
159 def create_stream(self):
158 def create_stream(self):
160 super(IOPubHandler, self).create_stream()
159 super(IOPubHandler, self).create_stream()
161 km = self.kernel_manager
160 km = self.kernel_manager
162 km.add_restart_callback(self.kernel_id, self.on_kernel_restarted)
161 km.add_restart_callback(self.kernel_id, self.on_kernel_restarted)
163 km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead')
162 km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead')
164
163
165 def on_close(self):
164 def on_close(self):
166 km = self.kernel_manager
165 km = self.kernel_manager
167 if self.kernel_id in km:
166 if self.kernel_id in km:
168 km.remove_restart_callback(
167 km.remove_restart_callback(
169 self.kernel_id, self.on_kernel_restarted,
168 self.kernel_id, self.on_kernel_restarted,
170 )
169 )
171 km.remove_restart_callback(
170 km.remove_restart_callback(
172 self.kernel_id, self.on_restart_failed, 'dead',
171 self.kernel_id, self.on_restart_failed, 'dead',
173 )
172 )
174 super(IOPubHandler, self).on_close()
173 super(IOPubHandler, self).on_close()
175
174
176 def _send_status_message(self, status):
175 def _send_status_message(self, status):
177 msg = self.session.msg("status",
176 msg = self.session.msg("status",
178 {'execution_state': status}
177 {'execution_state': status}
179 )
178 )
180 self.write_message(jsonapi.dumps(msg, default=date_default))
179 self.write_message(json.dumps(msg, default=date_default))
181
180
182 def on_kernel_restarted(self):
181 def on_kernel_restarted(self):
183 logging.warn("kernel %s restarted", self.kernel_id)
182 logging.warn("kernel %s restarted", self.kernel_id)
184 self._send_status_message('restarting')
183 self._send_status_message('restarting')
185
184
186 def on_restart_failed(self):
185 def on_restart_failed(self):
187 logging.error("kernel %s restarted failed!", self.kernel_id)
186 logging.error("kernel %s restarted failed!", self.kernel_id)
188 self._send_status_message('dead')
187 self._send_status_message('dead')
189
188
190 def on_message(self, msg):
189 def on_message(self, msg):
191 """IOPub messages make no sense"""
190 """IOPub messages make no sense"""
192 pass
191 pass
193
192
194
193
195 class ShellHandler(ZMQChannelHandler):
194 class ShellHandler(ZMQChannelHandler):
196 channel = 'shell'
195 channel = 'shell'
197
196
198
197
199 class StdinHandler(ZMQChannelHandler):
198 class StdinHandler(ZMQChannelHandler):
200 channel = 'stdin'
199 channel = 'stdin'
201
200
202
201
203 #-----------------------------------------------------------------------------
202 #-----------------------------------------------------------------------------
204 # URL to handler mappings
203 # URL to handler mappings
205 #-----------------------------------------------------------------------------
204 #-----------------------------------------------------------------------------
206
205
207
206
208 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
207 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
209 _kernel_action_regex = r"(?P<action>restart|interrupt)"
208 _kernel_action_regex = r"(?P<action>restart|interrupt)"
210
209
211 default_handlers = [
210 default_handlers = [
212 (r"/api/kernels", MainKernelHandler),
211 (r"/api/kernels", MainKernelHandler),
213 (r"/api/kernels/%s" % _kernel_id_regex, KernelHandler),
212 (r"/api/kernels/%s" % _kernel_id_regex, KernelHandler),
214 (r"/api/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
213 (r"/api/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
215 (r"/api/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
214 (r"/api/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
216 (r"/api/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
215 (r"/api/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
217 (r"/api/kernels/%s/stdin" % _kernel_id_regex, StdinHandler)
216 (r"/api/kernels/%s/stdin" % _kernel_id_regex, StdinHandler)
218 ]
217 ]
General Comments 0
You need to be logged in to leave comments. Login now