diff --git a/dyalog_kernel/kernel.py b/dyalog_kernel/kernel.py index 9395703..234fbae 100644 --- a/dyalog_kernel/kernel.py +++ b/dyalog_kernel/kernel.py @@ -9,8 +9,8 @@ from collections import deque - -from ipykernel.kernelbase import Kernel +from metakernel import MetaKernel +from IPython.display import HTML, Image from dyalog_kernel import __version__ if sys.platform.lower().startswith('win'): @@ -49,7 +49,7 @@ def writeln(s): sys.stdout = tmp_stdout -class DyalogKernel(Kernel): +class DyalogKernel(MetaKernel): implementation = 'Dyalog' implementation_version = __version__ @@ -72,60 +72,20 @@ class DyalogKernel(Kernel): dyalog_subprocess = None def out_error(self, s): - _content = { - 'output_type': 'stream', - 'name': 'stderr', # stdin or stderr - 'text': s - } - self.send_response(self.iopub_socket, 'stream', _content) + self.Error(s) def out_png(self, s): - _content = { - 'output_type': 'display_data', - 'data': { - #'text/plain' : ['multiline text data'], - 'image/png': s, - #'application/json':{ - # JSON data is included as-is - # 'json':'data', - #}, - }, - 'metadata': { - 'image/svg': { - 'width': 120, - 'height': 80, - }, - }, - } - self.send_response(self.iopub_socket, 'display_data', _content) + #width and height values are fixed to maintain consistency with original implementation + self.Display(Image(data=s, width=120, height=80)) def out_html(self, s): - _content = { - # 'output_type': 'display_data', - 'data': {'text/html': s}, - 'execution_count': self.execution_count, - 'metadata': '' - # 'transient': '' - } - self.send_response(self.iopub_socket, 'execute_result', _content) + self.Display(HTML(s)) def out_result(self, s): # injecting css: white-space:pre. Means no wrapping, RIDE SetPW will take care about line wrapping - html_start = '
'
         html_end = '
' - - _content = { - # 'output_type': 'display_data', - # 'data': {'text/plain': s}, - 'data': {'text/html': html_start + html.escape(s, False) + html_end}, - 'execution_count': self.execution_count, - 'metadata': {}, - - # 'transient': '' - } - - self.send_response(self.iopub_socket, 'execute_result', _content) + self.Display(HTML(html_start + html.escape(s, False) + html_end)) def out_stream(self, s): _content = { @@ -195,7 +155,7 @@ def __init__(self, **kwargs): #from ipykernel import get_connection_file #s = get_connection_file() # debug("########## " + str(s)) - + super(DyalogKernel, self).__init__(**kwargs) self._port = DYALOG_PORT # lets find first available port, starting from default DYALOG_PORT (:4502) # this makes sense only if Dyalog APL and Jupyter executables are on the same host (localhost) @@ -254,8 +214,6 @@ def __init__(self, **kwargs): self.dyalog_subprocess = subprocess.Popen([dyalog, '+s', '-q', os.path.dirname(os.path.abspath( __file__)) + '/init.dws'], stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=dyalog_env) - Kernel.__init__(self, **kwargs) - self.dyalog_ride_connect() def recv_all(self, msg_len): @@ -327,8 +285,9 @@ def ride_send(self, d): self.dyalogTCP.sendall(_data) debug("SEND " + _data[8:].decode("utf-8")) - - def do_execute(self, code, silent, store_history=True, user_expressions=None, + + #renamed to _do_execute to avoid overriding the do_execute method in MetaKernel + def _do_execute(self, code, silent=False, store_history=True, user_expressions=None, allow_stdin=True): global SUSPEND code = code.strip() @@ -355,7 +314,7 @@ def do_execute(self, code, silent, store_history=True, user_expressions=None, 'JUPYTER NOTEBOOK: UNDEFINED ARGUMENT TO %suspend, USE EITHER on OR off') lines = lines[1:] elif lines[0].lower() == ']dinput': - lines = lines[1:] + lines = lines[1:] try: # the windows interpreter can only handle ~125 chacaters at a time, so we do one line at a time pt = None @@ -452,6 +411,10 @@ def do_execute(self, code, silent, store_history=True, user_expressions=None, } return reply_content + + def do_execute_direct(self, code, silent=False): + #defers non-magic code execution to the do_execute function defined by Dyalog + return self._do_execute(code, silent) def execute_line(self, line): self.ride_send(["Execute", {"trace": 0, "text": line}]) @@ -463,7 +426,7 @@ def define_function(self, lines): self.execute_line("⎕SE.Dyalog.ipyFn,←⊂," + quoted + "\n") self.ride_receive_wait() dq.clear() - if re.match('^\\s*:namespace|:class|:interface',lines[0].lower()): + if re.match('^\\s*:namespace|:class|:interface',lines[0].lower()): self.execute_line("{0::'DEFN ERROR'⋄⎕FIX ⍵}⎕SE.Dyalog.ipyFn\n") else: self.execute_line("{''≢0⍴r←⎕FX ⍵:511 ⎕SIGNAL⍨'DEFN ERROR: Issue on line ',⍕r}⎕SE.Dyalog.ipyFn\n") diff --git a/setup.py b/setup.py index d53a3db..f39e6d5 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ ], python_requires=">=3.8", install_requires=[ - "ipykernel>=6.20.0", + "metakernel>=0.30.3" ], entry_points={ 'console_scripts': [