4545 BaseException = Exception
4646
4747try :
48- from Queue import Empty # Python 2
48+ from Queue import Empty , Queue # Python 2
4949except ImportError :
50- from queue import Empty # Python 3
50+ from queue import Empty , Queue # Python 3
5151
5252DEBUG = os .environ .get ('DEBUG_DJAYAMANNE_IPYTHON' ) is not None
5353TEST = os .environ .get ('PYTHON_DONJAYAMANNE_TEST' ) is not None
@@ -128,12 +128,18 @@ def release(self):
128128- Not the best, but that's how it will have to be done
129129- http://www.xavierdupre.fr/app/pyquickhelper/helpsphinx/_modules/pyquickhelper/ipythonhelper/notebook_runner.html
130130"""
131+
132+
131133class iPythonKernelResponseMonitor (object ):
132- def __init__ (self , kernelUUID , socketConnection ):
134+
135+ def __init__ (self , kernelUUID , socketConnection , send_lock , shell_channel , iopub_channel ):
133136 import threading
134137 self .kernel = multiKernelManager .get_kernel (kernelUUID )
135138 self .conn = socketConnection
136139 self .is_stop_requested = False
140+ self .shell_channel = shell_channel
141+ self .iopub_channel = iopub_channel
142+ self .send_lock = send_lock
137143 thread .start_new_thread (self .start_processing , ())
138144
139145 def stop (self ):
@@ -142,6 +148,14 @@ def stop(self):
142148 def check_for_exit_socket_loop (self ):
143149 return self .is_stop_requested
144150
151+ def _populateErrorContents (self , sourceContents , targetContents ):
152+ try :
153+ targetContents .ename = sourceContents .ename
154+ targetContents .evalue = sourceContents .evalue
155+ targetContents .traceback = sourceContents .traceback
156+ except AttributeError :
157+ pass
158+
145159 def start_processing (self ):
146160 """loop to read the io ports/messages"""
147161
@@ -151,6 +165,31 @@ def start_processing(self):
151165 if self .check_for_exit_socket_loop ():
152166 break
153167
168+ try :
169+ # We can ignore msgtype=execute_request
170+
171+ exe_result = self .shell_channel .get_shell_msg (timeout = 1 )
172+ # message can be JSON, but not always
173+ # (http://jupyter-client.readthedocs.io/en/latest/messaging.html)
174+ # assume for now that dates are the only crappy (non JSONable stuff sent)
175+ json_to_send = json .dumps (exe_result , default = str )
176+ with self .send_lock :
177+ _debug_write ('shell_result' )
178+ write_bytes (self .conn , iPythonSocketServer ._SHEL )
179+ write_string (self .conn , json_to_send )
180+ except Empty :
181+ pass
182+
183+ try :
184+ msg = self .iopub_channel .get_iopub_msg (timeout = 10 )
185+ json_to_send = json .dumps (msg , default = str )
186+ with self .send_lock :
187+ _debug_write ('iopub_msg' )
188+ write_bytes (self .conn , iPythonSocketServer ._IOPB )
189+ write_string (self .conn , json_to_send )
190+ except Empty :
191+ pass
192+
154193 except IPythonExitException :
155194 _debug_write ('IPythonExitException' )
156195 _debug_write (traceback .format_exc ())
@@ -185,6 +224,9 @@ class iPythonSocketServer(object):
185224 _STPK = to_bytes ('STPK' )
186225 _RSTK = to_bytes ('RSTK' )
187226 _ITPK = to_bytes ('ITPK' )
227+ _RUN = to_bytes ('RUN ' )
228+ _SHEL = to_bytes ('SHEL' )
229+ _IOPB = to_bytes ('IOPB' )
188230
189231 def __init__ (self ):
190232 import threading
@@ -199,6 +241,8 @@ def __init__(self):
199241 self .execute_item_lock = threading .Lock ()
200242 # lock starts acquired (we use it like manual reset event)
201243 self .execute_item_lock .acquire ()
244+ self .kernelMonitor = None
245+ self .shell_channel = None
202246
203247 def connect (self , port ):
204248 # start a new thread for communicating w/ the remote process
@@ -332,14 +376,15 @@ def _cmd_strk(self, id):
332376 except socket .timeout :
333377 pass
334378 kernelUUID = multiKernelManager .start_kernel (kernel_name = kernelName )
379+ self ._postStartKernel (kernelUUID )
380+
335381 # get the config and the connection FileExistsError
336- kernel = multiKernelManager .get_kernel (kernelUUID )
337382 try :
338- config = kernel .config
383+ config = kernel_manager .config
339384 except :
340385 config = {}
341386 try :
342- connection_file = kernel .connection_file
387+ connection_file = kernel_manager .connection_file
343388 except :
344389 connection_file = ""
345390
@@ -351,6 +396,28 @@ def _cmd_strk(self, id):
351396 write_string (self .conn , json .dumps (config ))
352397 write_string (self .conn , connection_file )
353398
399+ def _postStartKernel (self , kernelUUID ):
400+ kernel_manager = multiKernelManager .get_kernel (kernelUUID )
401+ kernel_client = kernel_manager .client ()
402+ kernel_client .start_channels ()
403+
404+ try :
405+ # IPython 3.x
406+ kernel_client .wait_for_ready ()
407+ iopub = kernel_client
408+ shell = kernel_client
409+ except AttributeError :
410+ # Ipython 2.x
411+ # Based on https://github.com/paulgb/runipy/pull/49/files
412+ iopub = kernel_client .iopub_channel
413+ shell = kernel_client .shell_channel
414+ shell .get_shell_msg = shell .get_msg
415+ iopub .get_iopub_msg = iopub .get_msg
416+
417+ self .shell_channel = shell
418+ self .kernelMonitor = iPythonKernelResponseMonitor (
419+ kernelUUID , self .conn , self .send_lock , shell , iopub )
420+
354421 def _cmd_stpk (self , id ):
355422 """Shutdown a kernel by UUID"""
356423 while True :
@@ -359,12 +426,28 @@ def _cmd_stpk(self, id):
359426 break
360427 except socket .timeout :
361428 pass
429+
362430 try :
363- kernel = multiKernelManager .get_kernel (kernelUUID )
364- kernel .shutdown_kernel ()
431+ if self .kernelMonitor is not None :
432+ self .kernelMonitor .stop ()
433+ finally :
434+ pass
435+
436+ try :
437+ kernel_manager = multiKernelManager .get_kernel (kernelUUID )
438+ kernel_client = kernel_manager .client ()
439+ kernel_client .stop_channels ()
440+ finally :
441+ pass
442+
443+ try :
444+ kernel_manager = multiKernelManager .get_kernel (kernelUUID )
445+ kernel_manager .shutdown_kernel ()
365446 except :
366447 pass
367448 finally :
449+ self .shell_channel = None
450+ self .kernelMonitor = None
368451 with self .send_lock :
369452 write_bytes (self .conn , iPythonSocketServer ._STPK )
370453 write_string (self .conn , id )
@@ -377,8 +460,18 @@ def _cmd_rstk(self, id):
377460 break
378461 except socket .timeout :
379462 pass
380- kernel = multiKernelManager .get_kernel (kernelUUID )
381- kernel .restart_kernel (now = True )
463+
464+ try :
465+ if self .kernelMonitor is not None :
466+ self .kernelMonitor .stop ()
467+ finally :
468+ self .kernelMonitor = None
469+
470+ kernel_manager = multiKernelManager .get_kernel (kernelUUID )
471+ kernel_manager .restart_kernel (now = True )
472+
473+ self ._postStartKernel (kernelUUID )
474+
382475 with self .send_lock :
383476 write_bytes (self .conn , iPythonSocketServer ._RSTK )
384477 write_string (self .conn , id )
@@ -391,16 +484,25 @@ def _cmd_itpk(self, id):
391484 break
392485 except socket .timeout :
393486 pass
394- kernel = multiKernelManager .get_kernel (kernelUUID )
395- kernel .interrupt_kernel ()
487+ kernel_manager = multiKernelManager .get_kernel (kernelUUID )
488+ kernel_manager .interrupt_kernel ()
396489 with self .send_lock :
397490 write_bytes (self .conn , iPythonSocketServer ._ITPK )
398491 write_string (self .conn , id )
399492
400- def _cmd_run (self ):
401- """runs the received snippet of code"""
402- # self.run_command(read_string(self.conn))
403- pass
493+ def _cmd_run (self , id ):
494+ """runs the received snippet of code (kernel is expected to have been started)"""
495+ while True :
496+ try :
497+ code = read_string (self .conn )
498+ break
499+ except socket .timeout :
500+ pass
501+ msg_id = self .shell_channel .execute (code )
502+ with self .send_lock :
503+ write_bytes (self .conn , iPythonSocketServer ._RUN )
504+ write_string (self .conn , id )
505+ write_string (self .conn , msg_id )
404506
405507 def _cmd_abrt (self ):
406508 """aborts the current running command"""
@@ -501,6 +603,7 @@ def flush(self):
501603 to_bytes ('stpk' ): True ,
502604 to_bytes ('rstk' ): True ,
503605 to_bytes ('itpk' ): True ,
606+ to_bytes ('run ' ): True ,
504607 }
505608
506609
0 commit comments