22
33KernelConnection 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
108from 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'
0 commit comments