import ipykernel.kernelbase import sys import traceback import cadabra2 from cadabra2_jupyter.context import SandboxContext from cadabra2_jupyter.server import Server from cadabra2_jupyter.completer import CodeCompleter from cadabra2_jupyter import __version__ # This is a simple 'Python wrapper kernel' along the lines of # https://jupyter-protocol.readthedocs.io/en/latest/wrapperkernels.html class CadabraJupyterKernel(ipykernel.kernelbase.Kernel): implementation = "cadabra_kernel" implementation_version = __version__ language_info = { "name": "cadabra2", "codemirror_mode": "cadabra", "pygments_lexer": "cadabra", "mimetype": "text/cadabra", "file_extension": ".ipynb", } @property def banner(self): return "Cadabra (C) 2001-2023 Kasper Peeters\nJupyter kernel by Fergus Baker and Kasper Peeters\nMore info at https://cadabra.science/\nAvailable under the terms of the GNU General Public License v3" def __init__(self, **kwargs): ipykernel.kernelbase.Kernel.__init__(self, **kwargs) self._parse_cadabra = cadabra2.cdb2python_string # attach the server class for callbacks self._cdb_server = Server(self) # init the sandbox self._sandbox_context = SandboxContext(self) # init code completion self._completer = CodeCompleter(self) def do_execute( self, code, silent, store_history=True, user_expressions=None, allow_stdin=False ): """ callback for iPython kernel: code execution """ self.silent = silent # check for blank input if not code.strip(): return self._status_ok interrupted = False try: #  main execution calls pycode = self._parse_cadabra(code, True) self._execute_python(pycode) except KeyboardInterrupt: interrupted = True except Exception as e: # get traceback; not massively informative but can be useful err_str = traceback.format_exc() self._send_error(err_str) if interrupted: return {"status": "abort", "execution_count": self.execution_count} else: return self._status_ok def do_complete(self, code, cursor_pos): """ callback for iPython kernel: code completion """ # if no code, or last character is whitespace if not code or code[-1] not in self._completer.triggers: return self._default_complete(cursor_pos) # sandbox namespace namespace = self._sandbox_context.namespace options, rewind = self._completer(code, cursor_pos, namespace) return { "matches": sorted(options), "cursor_start": cursor_pos - rewind, "cursor_end": cursor_pos, "status": "ok", "metadata": dict(), } @property def _status_ok(self): return { "status": "ok", "execution_count": self.execution_count, "payload": [], "user_expressions": {}, } def _default_complete(self, cursor_pos): return { "matches": [], "cursor_start": 0, "cursor_end": cursor_pos, "metadata": dict(), "status": "ok", } def _execute_python(self, pycode): """ executes python code in the cadabra context """ self._sandbox_context(pycode) def _send_result(self, res_str): self.send_response( self.iopub_socket, "display_data", {"data": {"text/markdown": "{}".format(res_str)}, "metadata": {}}, ) def _send_image(self, img): self.send_response( self.iopub_socket, "display_data", {"data": {"image/png": "{}".format(img.decode("utf-8"))}, "metadata": {}}, ) def _send_code(self, res_str): self.send_response( self.iopub_socket, "stream", {"name": "stdout", "text": res_str}, ) def _send_error(self, err_str): self.send_response( self.iopub_socket, "stream", {"name": "stderr", "text": err_str} )