2015-05-19 13:34:15 -07:00
#!/usr/bin/env python
2015-12-09 19:00:47 +01:00
"""
Cling Kernel for Jupyter
Talks to Cling via ctypes
"""
2015-05-19 13:34:15 -07:00
from __future__ import print_function
2015-12-09 19:00:47 +01:00
__version__ = ' 0.0.2 '
import ctypes
from contextlib import contextmanager
from fcntl import fcntl , F_GETFL , F_SETFL
2015-05-19 13:37:50 -07:00
import os
2015-12-10 11:43:47 +01:00
import shutil
2015-12-09 19:00:47 +01:00
import select
2015-05-19 13:34:15 -07:00
import sys
2015-12-09 19:00:47 +01:00
import threading
2015-05-19 13:34:15 -07:00
2015-12-10 11:02:36 +01:00
from traitlets import Unicode , Float
from ipykernel . kernelapp import IPKernelApp
from ipykernel . kernelbase import Kernel
2015-05-19 13:34:15 -07:00
2015-12-09 19:00:47 +01:00
libc = ctypes . CDLL ( None )
2015-06-16 12:12:02 -07:00
try :
2015-12-09 19:00:47 +01:00
c_stdout_p = ctypes . c_void_p . in_dll ( libc , ' stdout ' )
2015-12-10 11:42:43 +01:00
c_stderr_p = ctypes . c_void_p . in_dll ( libc , ' stderr ' )
2015-12-09 19:00:47 +01:00
except ValueError :
# libc.stdout is has a funny name on OS X
c_stdout_p = ctypes . c_void_p . in_dll ( libc , ' __stdoutp ' )
2015-12-10 11:42:43 +01:00
c_stderr_p = ctypes . c_void_p . in_dll ( libc , ' __stderrp ' )
2015-06-16 12:12:11 -07:00
2015-05-19 13:34:15 -07:00
class ClingKernel ( Kernel ) :
2015-12-09 19:00:47 +01:00
""" Cling Kernel for Jupyter """
2015-05-19 13:34:15 -07:00
implementation = ' cling_kernel '
implementation_version = __version__
2015-12-09 19:00:47 +01:00
language_version = ' X '
2015-05-19 13:34:15 -07:00
banner = Unicode ( )
def _banner_default ( self ) :
return ' cling- %s ' % self . language_version
return self . _banner
2015-12-10 15:47:34 +01:00
# codemirror_mode='clike' *should* work but doesn't, using the mimetype instead
2015-06-08 11:52:54 -05:00
language_info = { ' name ' : ' c++ ' ,
2015-09-09 17:18:50 +01:00
' codemirror_mode ' : ' text/x-c++src ' ,
2015-05-19 13:34:15 -07:00
' mimetype ' : ' text/x-c++src ' ,
' file_extension ' : ' .c++ ' }
2015-12-09 19:00:47 +01:00
flush_interval = Float ( 0.25 , config = True )
2015-12-10 11:43:47 +01:00
def __init__ ( self , * * kwargs ) :
super ( ClingKernel , self ) . __init__ ( * * kwargs )
whichCling = shutil . which ( ' cling ' )
if whichCling :
clingInstDir = os . path . dirname ( os . path . dirname ( whichCling ) )
else :
2015-12-10 15:47:34 +01:00
#clingInstDir = '/Users/axel/build/cling/cling-all-in-one/clion-inst'
2015-12-10 11:43:47 +01:00
clingInstDir = ' /Users/axel/Library/Caches/CLion12/cmake/generated/e0f22745/e0f22745/Debug '
self . libclingJupyter = ctypes . CDLL ( clingInstDir + " /lib/libclingJupyter.dylib " , mode = ctypes . RTLD_GLOBAL )
self . libclingJupyter . cling_create . restype = ctypes . c_void_p
strarr = ctypes . c_char_p * 4
argv = strarr ( b " clingJupyter " , b " " , b " " , b " " )
2015-12-10 15:45:04 +01:00
llvmresourcedir = ctypes . c_char_p ( clingInstDir . encode ( ' utf8 ' ) )
self . interp = ctypes . c_void_p ( self . libclingJupyter . cling_create ( 4 , argv , llvmresourcedir ) )
2015-12-10 11:43:47 +01:00
2015-12-09 19:00:47 +01:00
@contextmanager
2015-12-10 11:42:43 +01:00
def forward_stream ( self , name ) :
2015-12-09 19:00:47 +01:00
""" Capture stdout and forward it as stream messages """
# create pipe for stdout
2015-12-10 11:42:43 +01:00
if name == ' stdout ' :
c_flush_p = c_stdout_p
elif name == ' stderr ' :
c_flush_p = c_stderr_p
else :
raise ValueError ( " Name must be stdout or stderr, not %r " % name )
real_fd = getattr ( sys , ' __ %s __ ' % name ) . fileno ( )
save_fd = os . dup ( real_fd )
2015-12-09 19:00:47 +01:00
pipe_out , pipe_in = os . pipe ( )
2015-12-10 11:42:43 +01:00
os . dup2 ( pipe_in , real_fd )
2015-12-09 19:00:47 +01:00
os . close ( pipe_in )
2015-05-19 13:34:15 -07:00
2015-12-09 19:00:47 +01:00
# make pipe_out non-blocking
flags = fcntl ( pipe_out , F_GETFL )
fcntl ( pipe_out , F_SETFL , flags | os . O_NONBLOCK )
2015-12-10 11:42:43 +01:00
def forwarder ( pipe ) :
2015-12-09 19:00:47 +01:00
""" Forward bytes on a pipe to stream messages """
while True :
r , w , x = select . select ( [ pipe ] , [ ] , [ ] , self . flush_interval )
if not r :
# nothing to read, flush libc's stdout and check again
2015-12-10 11:42:43 +01:00
libc . fflush ( c_flush_p )
2015-12-09 19:00:47 +01:00
continue
data = os . read ( pipe , 1024 )
if not data :
# pipe closed, we are done
break
# send output
self . session . send ( self . iopub_socket , ' stream ' , {
' name ' : name ,
' text ' : data . decode ( ' utf8 ' , ' replace ' ) ,
} , parent = self . _parent_header )
t = threading . Thread ( target = forwarder , args = ( pipe_out , ) )
t . start ( )
2015-05-19 13:34:15 -07:00
try :
2015-12-09 19:00:47 +01:00
yield
2015-05-19 13:34:15 -07:00
finally :
2015-12-09 19:00:47 +01:00
# flush the pipe
2015-12-10 11:42:43 +01:00
libc . fflush ( c_flush_p )
os . close ( real_fd )
2015-12-09 19:00:47 +01:00
t . join ( )
# and restore original stdout
os . close ( pipe_out )
2015-12-10 11:42:43 +01:00
os . dup2 ( save_fd , real_fd )
os . close ( save_fd )
2015-12-09 19:00:47 +01:00
def run_cell ( self , code , silent = False ) :
""" Dummy run cell while waiting for cling ctypes API """
2015-12-10 11:43:47 +01:00
self . libclingJupyter . cling_eval ( self . interp , ctypes . c_char_p ( code . encode ( ' utf8 ' ) ) )
2015-05-19 13:34:15 -07:00
def do_execute ( self , code , silent , store_history = True ,
user_expressions = None , allow_stdin = False ) :
if not code . strip ( ) :
return {
' status ' : ' ok ' ,
' execution_count ' : self . execution_count ,
' payload ' : [ ] ,
' user_expressions ' : { } ,
}
status = ' ok '
2015-12-10 11:42:43 +01:00
with self . forward_stream ( ' stdout ' ) , self . forward_stream ( ' stderr ' ) :
2015-12-09 19:00:47 +01:00
self . run_cell ( code , silent )
2015-05-19 13:34:15 -07:00
reply = {
' status ' : status ,
' execution_count ' : self . execution_count ,
}
2015-12-09 19:00:47 +01:00
if status == ' error ' :
2015-05-19 13:34:15 -07:00
err = {
' ename ' : ' ename ' ,
' evalue ' : ' evalue ' ,
2015-12-09 19:00:47 +01:00
' traceback ' : [ ] ,
2015-05-19 13:34:15 -07:00
}
self . send_response ( self . iopub_socket , ' error ' , err )
reply . update ( err )
elif status == ' ok ' :
reply . update ( {
' payload ' : [ ] ,
' user_expressions ' : { } ,
} )
else :
raise ValueError ( " Invalid status: %r " % status )
return reply
2015-12-09 19:00:47 +01:00
class ClingKernelApp ( IPKernelApp ) :
kernel_class = ClingKernel
def init_io ( self ) :
# disable io forwarding
pass
2015-05-19 13:34:15 -07:00
def main ( ) :
""" launch a cling kernel """
2015-12-09 19:00:47 +01:00
ClingKernelApp . launch_instance ( )
2015-05-19 13:34:15 -07:00
if __name__ == ' __main__ ' :
main ( )