cling/tools/Jupyter/kernel/clingkernel.py

180 lines
5.5 KiB
Python
Raw Normal View History

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
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()