├── .gitignore ├── LICENSE ├── README.md ├── cling.ipynb ├── cling └── kernel.json ├── clingkernel.py ├── scripts └── jupyter-cling-kernel └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | .ipynb_checkpoints 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Min RK 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of clingkernel nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cling Kernel 2 | 3 | C++ Kernel for Jupyter with Cling. 4 | 5 | ## Merged into cling 6 | 7 | **This repo is no longer maintained, as it has been [merged into cling itself](https://github.com/root-mirror/cling#jupyter)** 8 | 9 | ## Old stuff: 10 | 11 | You will probably need to specify the path to cling with the `CLING_EXE` env variable. 12 | 13 | **Note:** This currently requires master of everything IPython and Jupyter because that's what I use, 14 | but I'll clean that up so it works on 3.x. 15 | 16 | ## Install 17 | 18 | To install the kernel: 19 | 20 | jupyter kernelspec install cling 21 | 22 | or for IPython/Jupyter < 4: 23 | 24 | ipython kernelspec install cling 25 | -------------------------------------------------------------------------------- /cling.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# C++ kernel with Cling\n", 8 | "\n", 9 | "[Cling](https://root.cern.ch/drupal/content/cling) is an interpreter for C++.\n", 10 | "\n", 11 | "Yup, this is a thing." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 1, 17 | "metadata": { 18 | "collapsed": false 19 | }, 20 | "outputs": [ 21 | { 22 | "name": "stdout", 23 | "output_type": "stream", 24 | "text": [] 25 | } 26 | ], 27 | "source": [ 28 | ".rawInput\n", 29 | "class Rectangle {\n", 30 | " private:\n", 31 | " double w;\n", 32 | " double h;\n", 33 | "\n", 34 | " public:\n", 35 | " \n", 36 | " Rectangle(double w_, double h_) {\n", 37 | " w = w_;\n", 38 | " h = h_;\n", 39 | " }\n", 40 | " double area(void) {\n", 41 | " return w * h;\n", 42 | " }\n", 43 | " double perimiter(void) {\n", 44 | " return 2 * (w + h);\n", 45 | " }\n", 46 | "};\n", 47 | ".rawInput" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": 2, 53 | "metadata": { 54 | "collapsed": false 55 | }, 56 | "outputs": [ 57 | { 58 | "name": "stdout", 59 | "output_type": "stream", 60 | "text": [ 61 | "(double) 2.000000e+01\r\n" 62 | ] 63 | } 64 | ], 65 | "source": [ 66 | "Rectangle r = Rectangle(5, 4);\n", 67 | "r.area()" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 3, 73 | "metadata": { 74 | "collapsed": false 75 | }, 76 | "outputs": [ 77 | { 78 | "name": "stdout", 79 | "output_type": "stream", 80 | "text": [] 81 | }, 82 | { 83 | "ename": "ename", 84 | "evalue": "evalue", 85 | "output_type": "error", 86 | "traceback": [ 87 | "\u001b[1minput_line_6:2:4: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1mno member named 'methodmissing' in 'Rectangle'\u001b[0m", 88 | " r.methodmissing()", 89 | "\u001b[0;1;32m ~ ^", 90 | "\u001b[0m" 91 | ] 92 | } 93 | ], 94 | "source": [ 95 | "r.methodmissing()" 96 | ] 97 | } 98 | ], 99 | "metadata": { 100 | "kernelspec": { 101 | "display_name": "C++", 102 | "language": "", 103 | "name": "cling" 104 | }, 105 | "language_info": { 106 | "codemirror_mode": "c++", 107 | "file_extension": ".c++", 108 | "mimetype": " text/x-c++src", 109 | "name": "c++" 110 | } 111 | }, 112 | "nbformat": 4, 113 | "nbformat_minor": 0 114 | } 115 | -------------------------------------------------------------------------------- /cling/kernel.json: -------------------------------------------------------------------------------- 1 | { 2 | "display_name": "C++", 3 | "argv": [ 4 | "jupyter-cling-kernel", 5 | "-f", 6 | "{connection_file}" 7 | ] 8 | } -------------------------------------------------------------------------------- /clingkernel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import print_function 4 | 5 | import os 6 | from pipes import quote 7 | import re 8 | import signal 9 | import sys 10 | 11 | from tornado.ioloop import IOLoop 12 | 13 | try: 14 | from ipykernel.kernelbase import Kernel 15 | except ImportError: 16 | from IPython.kernel.zmq.kernelbase import Kernel 17 | 18 | from pexpect import replwrap, EOF 19 | 20 | __version__ = '0.0.1' 21 | 22 | try: 23 | from traitlets import Unicode 24 | except ImportError: 25 | from IPython.utils.traitlets import Unicode 26 | 27 | class ClingError(Exception): 28 | def __init__(self, buf): 29 | self.buf = buf 30 | 31 | PY2 = sys.version_info[0] < 3 32 | 33 | class ClingInterpreter(replwrap.REPLWrapper): 34 | 35 | prompt_pat = re.compile(r'\[cling\][\$\!\?]\s+') 36 | 37 | def __init__(self, cmd, **kw): 38 | self.buffer = [] 39 | self.output = '' 40 | if PY2 and isinstance(cmd, unicode): 41 | cmd = cmd.encode('utf8') 42 | 43 | super(ClingInterpreter, self).__init__( 44 | cmd, '[cling]', None, **kw) 45 | 46 | def run_command(self, command, timeout=-1): 47 | self.buffer = [] 48 | self.output = '' 49 | try: 50 | super(ClingInterpreter, self).run_command(command, timeout) 51 | finally: 52 | self.output = ''.join(self._squash_raw_input(self.buffer)) 53 | self.buffer = [] 54 | return self.output 55 | 56 | def _squash_raw_input(self, buf): 57 | """kill raw input toggle output""" 58 | in_raw = False 59 | for line in buf: 60 | if in_raw: 61 | if line.strip() == 'Not using raw input': 62 | in_raw = False 63 | elif line.strip() == 'Using raw input': 64 | in_raw = True 65 | else: 66 | yield line 67 | 68 | def _no_echo(self, buf): 69 | """Filter out cling's input-echo""" 70 | lines = [ line for line in buf.splitlines(True) if '\x1b[D' not in line ] 71 | return ''.join(lines) 72 | 73 | def _expect_prompt(self, timeout=-1): 74 | try: 75 | self.child.expect(self.prompt_pat, timeout) 76 | finally: 77 | if self.child.match and self.child.match.group() == '[cling]! ' and self.child.buffer.startswith('? '): 78 | self.child.buffer = self.child.buffer[2:].lstrip() 79 | 80 | buf = self._no_echo(self.child.before) 81 | 82 | if '\x1b[0m\x1b[0;1;31merror:' in buf: 83 | raise ClingError(buf) 84 | elif buf: 85 | self.buffer.append(buf) 86 | 87 | 88 | class ClingKernel(Kernel): 89 | implementation = 'cling_kernel' 90 | implementation_version = __version__ 91 | 92 | banner = Unicode() 93 | def _banner_default(self): 94 | return 'cling-%s' % self.language_version 95 | return self._banner 96 | 97 | # codemirror_mode='clike' *should* work but doesn't, using the mimetype instead 98 | language_info = {'name': 'c++', 99 | 'codemirror_mode': 'text/x-c++src', 100 | 'mimetype': ' text/x-c++src', 101 | 'file_extension': '.c++'} 102 | 103 | cling = Unicode(config=True, 104 | help="Path to cling if not on your PATH." 105 | ) 106 | def _cling_default(self): 107 | return os.environ.get('CLING_EXE') or 'cling' 108 | 109 | def __init__(self, **kwargs): 110 | 111 | Kernel.__init__(self, **kwargs) 112 | self._start_interpreter() 113 | 114 | def _start_interpreter(self): 115 | # Signal handlers are inherited by forked processes, and we can't easily 116 | # reset it from the subprocess. Since kernelapp ignores SIGINT except in 117 | # message handlers, we need to temporarily reset the SIGINT handler here 118 | # so that bash and its children are interruptible. 119 | sig = signal.signal(signal.SIGINT, signal.SIG_DFL) 120 | try: 121 | self.interpreter = ClingInterpreter( 122 | '%s --nologo --version' % quote(self.cling) 123 | ) 124 | finally: 125 | signal.signal(signal.SIGINT, sig) 126 | self.language_version = self.interpreter.child.before.strip() 127 | 128 | def do_execute(self, code, silent, store_history=True, 129 | user_expressions=None, allow_stdin=False): 130 | if not code.strip(): 131 | return { 132 | 'status': 'ok', 133 | 'execution_count': self.execution_count, 134 | 'payload': [], 135 | 'user_expressions': {}, 136 | } 137 | 138 | status = 'ok' 139 | traceback = None 140 | try: 141 | # if not clingy: 142 | # self.interpreter.run_command('.rawInput') 143 | output = self.interpreter.run_command(code, timeout=None) 144 | # if not clingy: 145 | # self.interpreter.run_command('.rawInput') 146 | except KeyboardInterrupt: 147 | self.interpreter.child.sendintr() 148 | status = 'interrupted' 149 | self.interpreter._expect_prompt() 150 | output = self.interpreter.output 151 | except EOF: 152 | # output = self.interpreter._filter_buf(self.interpr) 153 | output = self.interpreter.output + ' Restarting Cling' 154 | self._start_interpreter() 155 | except EOF: 156 | status = 'error' 157 | traceback = [] 158 | except ClingError as e: 159 | status = 'error' 160 | traceback = e.buf.splitlines() 161 | output = self.interpreter.output 162 | if not self.interpreter.child.isalive(): 163 | self.log.error("Cling interpreter died") 164 | loop = IOLoop.current() 165 | loop.add_callback(loop.stop) 166 | 167 | # print('out: %r' % output, file=sys.__stderr__) 168 | # print('tb: %r' % traceback, file=sys.__stderr__) 169 | 170 | if not silent: 171 | # Send output on stdout 172 | stream_content = {'name': 'stdout', 'text': output} 173 | self.send_response(self.iopub_socket, 'stream', stream_content) 174 | 175 | reply = { 176 | 'status': status, 177 | 'execution_count': self.execution_count, 178 | } 179 | 180 | if status == 'interrupted': 181 | pass 182 | elif status == 'error': 183 | err = { 184 | 'ename': 'ename', 185 | 'evalue': 'evalue', 186 | 'traceback': traceback, 187 | } 188 | self.send_response(self.iopub_socket, 'error', err) 189 | reply.update(err) 190 | elif status == 'ok': 191 | reply.update({ 192 | 'payload': [], 193 | 'user_expressions': {}, 194 | }) 195 | else: 196 | raise ValueError("Invalid status: %r" % status) 197 | 198 | return reply 199 | 200 | def main(): 201 | """launch a cling kernel""" 202 | try: 203 | from ipykernel.kernelapp import IPKernelApp 204 | except ImportError: 205 | from IPython.kernel.zmq.kernelapp import IPKernelApp 206 | IPKernelApp.launch_instance(kernel_class=ClingKernel) 207 | 208 | 209 | if __name__ == '__main__': 210 | main() 211 | -------------------------------------------------------------------------------- /scripts/jupyter-cling-kernel: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from clingkernel import main 4 | main() -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) Min RK 5 | # Distributed under the terms of the Modified BSD License. 6 | 7 | from __future__ import print_function 8 | 9 | # the name of the project 10 | name = 'clingkernel' 11 | 12 | #----------------------------------------------------------------------------- 13 | # Minimal Python version sanity check 14 | #----------------------------------------------------------------------------- 15 | 16 | import sys 17 | 18 | v = sys.version_info 19 | if v[:2] < (2,7) or (v[0] >= 3 and v[:2] < (3,3)): 20 | error = "ERROR: %s requires Python version 2.7 or 3.3 or above." % name 21 | print(error, file=sys.stderr) 22 | sys.exit(1) 23 | 24 | PY3 = (sys.version_info[0] >= 3) 25 | 26 | #----------------------------------------------------------------------------- 27 | # get on with it 28 | #----------------------------------------------------------------------------- 29 | 30 | import os 31 | from glob import glob 32 | 33 | from distutils.core import setup 34 | 35 | pjoin = os.path.join 36 | here = os.path.abspath(os.path.dirname(__file__)) 37 | pkg_root = pjoin(here, name) 38 | 39 | 40 | setup_args = dict( 41 | name = name, 42 | version = '0.0.1', 43 | py_modules = ['clingkernel'], 44 | scripts = glob(pjoin('scripts', '*')), 45 | description = "C++ Kernel for Jupyter with Cling", 46 | author = 'Min RK', 47 | author_email = 'benjaminrk@gmail.com', 48 | url = 'https://github.com/minrk/clingkernel', 49 | license = 'BSD', 50 | platforms = "Linux, Mac OS X", 51 | keywords = ['Interactive', 'Interpreter', 'Shell', 'Web'], 52 | classifiers = [ 53 | 'Intended Audience :: Developers', 54 | 'Intended Audience :: System Administrators', 55 | 'Intended Audience :: Science/Research', 56 | 'License :: OSI Approved :: BSD License', 57 | 'Programming Language :: Python', 58 | 'Programming Language :: Python :: 3', 59 | 'Programming Language :: Python :: 3.3', 60 | ], 61 | ) 62 | 63 | if 'develop' in sys.argv or any(a.startswith('bdist') for a in sys.argv): 64 | import setuptools 65 | 66 | setuptools_args = {} 67 | install_requires = setuptools_args['install_requires'] = [ 68 | 'ipython', 69 | 'pyzmq', 70 | 'tornado', 71 | 'pexpect>=3.3', 72 | ] 73 | 74 | if 'setuptools' in sys.modules: 75 | setup_args.update(setuptools_args) 76 | 77 | if __name__ == '__main__': 78 | setup(**setup_args) 79 | --------------------------------------------------------------------------------