Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bbfe2c7

Browse files
committed
Add joined IOPubAndShellHandler
Now only one SockJS connection is needed in the browser for both channels. The previous single-channel connections remain available.
1 parent e2f8f50 commit bbfe2c7

File tree

3 files changed

+65
-55
lines changed

3 files changed

+65
-55
lines changed

IPython/frontend/html/notebook/handlers.py

Lines changed: 50 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ def application(self):
384384
def request(self):
385385
return self.session.handler.request
386386

387-
def _reserialize_reply(self, msg_list):
387+
def _reserialize_reply(self, msg_list, channel=None):
388388
"""Reserialize a reply message using JSON.
389389
390390
This takes the msg list from the ZMQ socket, unserializes it using
@@ -394,20 +394,13 @@ def _reserialize_reply(self, msg_list):
394394
"""
395395
idents, msg_list = self.ip_session.feed_identities(msg_list)
396396
msg = self.ip_session.unserialize(msg_list)
397-
try:
398-
msg['header'].pop('date')
399-
except KeyError:
400-
pass
401-
try:
402-
msg['parent_header'].pop('date')
403-
except KeyError:
404-
pass
405-
msg.pop('buffers')
397+
msg.pop('buffers', None)
398+
msg['channel'] = channel
406399
return jsonapi.dumps(msg, default=date_default)
407400

408-
def _on_zmq_reply(self, msg_list):
401+
def _on_zmq_reply(self, msg_list, channel=None):
409402
try:
410-
msg = self._reserialize_reply(msg_list)
403+
msg = self._reserialize_reply(msg_list, channel)
411404
except Exception:
412405
self.application.log.critical("Malformed message: %r" % msg_list, exc_info=True)
413406
else:
@@ -440,6 +433,12 @@ def on_open(self, info):
440433
self.ip_session = Session(config=cfg)
441434
self.save_on_message = self.on_message
442435
self.on_message = self.on_first_message
436+
437+
self.initialize()
438+
439+
def initialize(self):
440+
"""override in subclasses"""
441+
pass
443442

444443
def get_current_user(self):
445444
handler = self.session.handler
@@ -465,6 +464,11 @@ def on_first_message(self, cookie):
465464
logging.warn("Couldn't authenticate WebSocket connection")
466465
raise web.HTTPError(403)
467466
self.on_message = self.save_on_message
467+
self.create_streams()
468+
469+
def create_streams(self):
470+
"""override in subclass"""
471+
pass
468472

469473

470474
class IOPubHandler(AuthenticatedZMQStreamHandler):
@@ -473,28 +477,23 @@ class IOPubHandler(AuthenticatedZMQStreamHandler):
473477
_beating = False
474478
iopub_stream = None
475479
hb_stream = None
476-
477-
def on_first_message(self, msg):
478-
try:
479-
super(IOPubHandler, self).on_first_message(msg)
480-
except web.HTTPError:
481-
self.close()
482-
return
480+
481+
def initialize(self):
483482
km = self.application.kernel_manager
484483
self.time_to_dead = km.time_to_dead
485484
self.first_beat = km.first_beat
485+
486+
def create_streams(self):
487+
km = self.application.kernel_manager
486488
kernel_id = self.kernel_id
487489
try:
488490
self.iopub_stream = km.create_iopub_stream(kernel_id)
489491
self.hb_stream = km.create_hb_stream(kernel_id)
490-
except web.HTTPError:
491-
# WebSockets don't response to traditional error codes so we
492-
# close the connection.
493-
if not self.stream.closed():
494-
self.stream.close()
492+
except Exception:
493+
logging.error("Couldn't create streams", exc_info=True)
495494
self.close()
496495
else:
497-
self.iopub_stream.on_recv(self._on_zmq_reply)
496+
self.iopub_stream.on_recv(lambda msg: self._on_zmq_reply(msg, 'iopub'))
498497
self.start_hb(self.kernel_died)
499498

500499
def on_message(self, msg):
@@ -562,36 +561,32 @@ def kernel_died(self):
562561
self.send(json.dumps(
563562
{'header': {'msg_type': 'status'},
564563
'parent_header': {},
565-
'content': {'execution_state':'dead'}
564+
'content': {'execution_state':'dead'},
565+
'channel': 'iopub',
566566
}
567567
))
568568
self.on_close()
569569

570570

571571
class ShellHandler(AuthenticatedZMQStreamHandler):
572572

573-
def initialize(self, *args, **kwargs):
574-
self.shell_stream = None
575-
576-
def on_first_message(self, msg):
577-
try:
578-
super(ShellHandler, self).on_first_message(msg)
579-
except web.HTTPError:
580-
self.close()
581-
return
573+
shell_stream = None
574+
max_msg_size = 65535
575+
576+
def initialize(self):
582577
km = self.application.kernel_manager
583578
self.max_msg_size = km.max_msg_size
579+
580+
def create_streams(self):
581+
km = self.application.kernel_manager
584582
kernel_id = self.kernel_id
585583
try:
586584
self.shell_stream = km.create_shell_stream(kernel_id)
587-
except web.HTTPError:
588-
# WebSockets don't response to traditional error codes so we
589-
# close the connection.
590-
if not self.stream.closed():
591-
self.stream.close()
585+
except Exception:
586+
logging.error("Couldn't create shell stream", exc_info=True)
592587
self.close()
593588
else:
594-
self.shell_stream.on_recv(self._on_zmq_reply)
589+
self.shell_stream.on_recv(lambda msg: self._on_zmq_reply(msg, 'shell'))
595590

596591
def on_message(self, msg):
597592
if len(msg) < self.max_msg_size:
@@ -603,6 +598,20 @@ def on_close(self):
603598
if self.shell_stream is not None and not self.shell_stream.closed():
604599
self.shell_stream.close()
605600

601+
class IOPubAndShellHandler(ShellHandler, IOPubHandler):
602+
"""single SockJS handler for IOPub + Shell zmq channels"""
603+
def on_close(self):
604+
ShellHandler.on_close(self)
605+
IOPubHandler.on_close(self)
606+
607+
def initialize(self):
608+
ShellHandler.initialize(self)
609+
IOPubHandler.initialize(self)
610+
611+
def create_streams(self):
612+
ShellHandler.create_streams(self)
613+
IOPubHandler.create_streams(self)
614+
606615

607616
#-----------------------------------------------------------------------------
608617
# Notebook web service handlers

IPython/frontend/html/notebook/notebookapp.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
5353
RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler,
5454
MainClusterHandler, ClusterProfileHandler, ClusterActionHandler,
55-
FileFindHandler,
55+
FileFindHandler, IOPubAndShellHandler,
5656
)
5757
from .nbmanager import NotebookManager
5858
from .filenbmanager import FileNotebookManager
@@ -152,9 +152,11 @@ def __init__(self, ipython_app, kernel_manager, notebook_manager,
152152

153153
iopub_router = SockJSRouter(IOPubHandler, r"/kernels/%s/iopub" % _uuid_regex)
154154
shell_router = SockJSRouter(ShellHandler, r"/kernels/%s/shell" % _uuid_regex)
155+
sock_router = SockJSRouter(IOPubAndShellHandler, r"/kernels/%s/sock" % _uuid_regex)
155156

156157
handlers += shell_router.urls
157158
handlers += iopub_router.urls
159+
handlers += sock_router.urls
158160

159161
# Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
160162
# base_project_url will always be unicode, which will in turn

IPython/frontend/html/notebook/static/js/kernel.js

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ var IPython = (function (IPython) {
1818
var Kernel = function (base_url) {
1919
this.kernel_id = null;
2020
this.shell_channel = null;
21-
this.iopub_channel = null;
2221
this.base_url = base_url;
2322
this.running = false;
2423
this.username = "username";
@@ -71,13 +70,22 @@ var IPython = (function (IPython) {
7170

7271
Kernel.prototype._kernel_started = function (json) {
7372
console.log("Kernel started: ", json.kernel_id);
73+
var that = this
7474
this.running = true;
7575
this.kernel_id = json.kernel_id;
7676
this.ws_url = json.ws_url;
7777
this.kernel_url = this.base_url + "/" + this.kernel_id;
7878
this.start_channels();
79-
this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
80-
this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this);
79+
this.shell_channel.onmessage = function(e) {
80+
var msg = $.parseJSON(e.data);
81+
if (msg['channel'] == 'shell'){
82+
that._handle_shell_reply(e);
83+
} else if (msg['channel'] == 'iopub') {
84+
that._handle_iopub_reply(e);
85+
} else {
86+
console.log("Bad message", e);
87+
}
88+
};
8189
};
8290

8391

@@ -118,8 +126,7 @@ var IPython = (function (IPython) {
118126
this.stop_channels();
119127
var ws_url = this.ws_url + this.kernel_url;
120128
console.log("Starting SockJS:", ws_url);
121-
this.shell_channel = new SockJS(ws_url + "/shell");
122-
this.iopub_channel = new SockJS(ws_url + "/iopub");
129+
this.shell_channel = new SockJS(ws_url + "/sock");
123130
send_cookie = function(){
124131
this.send(document.cookie);
125132
};
@@ -144,12 +151,9 @@ var IPython = (function (IPython) {
144151
};
145152
this.shell_channel.onopen = send_cookie;
146153
this.shell_channel.onclose = ws_closed_early;
147-
this.iopub_channel.onopen = send_cookie;
148-
this.iopub_channel.onclose = ws_closed_early;
149154
// switch from early-close to late-close message after 1s
150155
setTimeout(function(){
151156
that.shell_channel.onclose = ws_closed_late;
152-
that.iopub_channel.onclose = ws_closed_late;
153157
}, 1000);
154158
};
155159

@@ -160,11 +164,6 @@ var IPython = (function (IPython) {
160164
this.shell_channel.close();
161165
this.shell_channel = null;
162166
};
163-
if (this.iopub_channel !== null) {
164-
this.iopub_channel.onclose = function (evt) {};
165-
this.iopub_channel.close();
166-
this.iopub_channel = null;
167-
};
168167
};
169168

170169
// Main public methods.

0 commit comments

Comments
 (0)