Skip to content

Commit 877b551

Browse files
authored
Reuse an established WebSocket connection to Jupyter kernel. (#29)
* Reuse an established WebSocket connection to Jupyter kernel. This should solve some performance issues.
1 parent ccb2af8 commit 877b551

File tree

3 files changed

+37
-17
lines changed

3 files changed

+37
-17
lines changed

hermes.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from .kernel import KernelConnection
1919

2020
import requests
21-
from websocket import WebSocketTimeoutException
2221

2322
from .utils import chain_callbacks
2423

@@ -895,5 +894,5 @@ def on_query_completions(
895894
(completion + "\tHermes", completion)
896895
for completion
897896
in kernel.get_complete(code, col, timeout)]
898-
except (KeyError, WebSocketTimeoutException):
897+
except Exception:
899898
return None

kernel.py

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
33
KernelConnection class provides interaction with Jupyter kernels.
44
5-
by NEGORO Tetsuya, 2017
6-
This code is under GPL2 License.
7-
All rights are reserved.
5+
Copyright (c) 2017, NEGORO Tetsuya (https://github.com/ngr-t)
86
"""
97

108
from threading import Thread
@@ -294,13 +292,28 @@ def repr(self):
294292
lang=self.lang,
295293
kernel_id=self.kernel_id)
296294

297-
def _create_connection(self, connect_kwargs=dict()):
295+
def _ping(self) -> bool:
296+
try:
297+
self.sock.ping()
298+
frame = self.sock.recv_frame()
299+
if frame.opcode == websocket.ABNF.OPCODE_PONG:
300+
return True
301+
return False
302+
except Exception as ex:
303+
return False
304+
305+
def _establish_ws_connection(self, connect_kwargs: dict=dict()) -> None:
306+
# Send ping and check if connection is alive.
307+
if self._ping():
308+
return
309+
else:
310+
self.sock = None
298311
try:
299312
response = self.manager.get_request(self._http_url)
300313
if response['id'] != self.kernel_id:
301-
return None
314+
return
302315
except requests.RequestException:
303-
return None
316+
return
304317
if self._auth_type == "no_auth":
305318
sock = websocket.create_connection(
306319
self._ws_url,
@@ -317,7 +330,12 @@ def _create_connection(self, connect_kwargs=dict()):
317330
sock = websocket.create_connection(
318331
self._ws_url,
319332
header=header)
320-
return sock
333+
self.sock = sock
334+
if not self._ping():
335+
# Connection can't be established (ex. when the kernel is dead)
336+
# Should we show some message in this case,
337+
# and let users to make a new connection?
338+
self.sock = None
321339

322340
def _communicate(self, message, timeout=None) -> JupyterReply:
323341
"""Send `message` to the kernel and return `reply` for it."""
@@ -326,18 +344,18 @@ def _communicate(self, message, timeout=None) -> JupyterReply:
326344
connect_kwargs = dict(timeout=timeout)
327345
else:
328346
connect_kwargs = dict()
329-
sock = self._create_connection(connect_kwargs)
330-
if sock is None:
347+
self._establish_ws_connection(connect_kwargs)
348+
if self.sock is None:
331349
return None
332-
sock.send(json.dumps(message).encode())
350+
self.sock.send(json.dumps(message).encode())
333351
replies = []
334352
replied = False
335353
while True:
336354
# The code here requires refactoring.
337355
# The code to interpret reply messages is devided into here and `JupyterReply` class.
338356
# Maybe it's better choice to remove `JupyterReply` class and
339357
# let all message interpretation processed here.
340-
reply = json.loads(sock.recv())
358+
reply = json.loads(self.sock.recv())
341359
replies.append(reply)
342360
self._logger.info(reply)
343361
msg_type = get_msg_type(reply)
@@ -361,7 +379,7 @@ def send_input(value):
361379
channel='stdin',
362380
metadata={},
363381
buffers={})
364-
sock.send(json.dumps(input_reply).encode())
382+
self.sock.send(json.dumps(input_reply).encode())
365383

366384
prompt = content["prompt"]
367385

@@ -563,9 +581,8 @@ def callback(reply):
563581
def is_alive(self):
564582
"""Return True if kernel is alive."""
565583
try:
566-
sock = self._create_connection()
567-
if sock is not None:
568-
sock.close()
584+
self._establish_ws_connection()
585+
if self.sock is not None:
569586
return True
570587
else:
571588
self._execution_state = 'dead'

messages/0.4.3.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Update in 0.4.3
2+
---------------
3+
4+
Fix to reuse an established WebSocket connection to Jupyter kernel. This should solve some performance issues.

0 commit comments

Comments
 (0)