Index: ports/ipython-ppapi/kernel.py |
diff --git a/ports/ipython-ppapi/kernel.py b/ports/ipython-ppapi/kernel.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..ffa057ea9a6061a134e20da5b7697d19d255a13d |
--- /dev/null |
+++ b/ports/ipython-ppapi/kernel.py |
@@ -0,0 +1,249 @@ |
+# Copyright (c) 2014 Google Inc. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+"""A simple shell that uses the IPython messaging system.""" |
+ |
+import json |
+import logging |
+import sys |
+ |
+import IPython |
+from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC |
+from IPython.utils.traitlets import Type, Dict, Instance |
+from IPython.core.displayhook import DisplayHook |
+from IPython.utils.jsonutil import json_clean, encode_images |
+from IPython.core.displaypub import DisplayPublisher |
+from IPython.config.configurable import Configurable |
+ |
+# module defined in shell.cc for communicating via pepper API |
+import ppmessage |
+ |
+def sendMessage(socket_name, msg_type, parent_header=None, content=None): |
+ if parent_header is None: |
+ parent_header = {} |
+ if content is None: |
+ content = {} |
+ msg = { |
+ 'header': {'msg_type': msg_type}, |
+ 'parent_header': parent_header, |
+ 'content': content |
+ } |
+ ppmessage._PostJSONMessage(socket_name, json.dumps(msg)) |
+ |
+class MsgOutStream(object): |
+ """Class to overrides stderr and stdout.""" |
+ |
+ def __init__(self, stream_name): |
+ self._stream_name = stream_name |
+ self._parent_header = {} |
+ |
+ def SetParentHeader(self, parent_header): |
+ self._parent_header = parent_header |
+ |
+ def close(self): |
+ pass |
+ |
+ def flush(self): |
+ pass |
+ |
+ def write(self, string): |
+ sendMessage('iopub', 'stream', parent_header=self._parent_header, |
+ content={'name': self._stream_name, 'data': string}) |
+ |
+ def writelines(self, sequence): |
+ for string in sequence: |
+ self.write(string) |
+ |
+# override sys.stdout and sys.stderr to broadcast on iopub |
+stdout_stream = MsgOutStream('stdout') |
+stderr_stream = MsgOutStream('stderr') |
+sys_stdout = sys.stdout |
+sys_stderr = sys.stderr |
+sys.stdout = stdout_stream |
+sys.stderr = stderr_stream |
+ |
+ |
+ |
+class PepperShellDisplayHook(DisplayHook): |
+ parent_header = Dict({}) |
+ |
+ def set_parent_header(self, parent_header): |
+ """Set the parent for outbound messages.""" |
+ self.parent_header = parent_header |
+ |
+ def start_displayhook(self): |
+ self.content = {} |
+ |
+ def write_output_prompt(self): |
+ self.content['execution_count'] = self.prompt_count |
+ |
+ def write_format_data(self, format_dict, md_dict=None): |
+ self.content['data'] = encode_images(format_dict) |
+ self.content['metadata'] = md_dict |
+ |
+ def finish_displayhook(self): |
+ sys.stdout.flush() |
+ sys.stderr.flush() |
+ sendMessage('iopub', 'pyout', parent_header=self.parent_header, |
+ content=self.content) |
+ self.content = None |
+ |
+ |
+class PepperDisplayPublisher(DisplayPublisher): |
+ parent_header = Dict({}) |
+ |
+ def set_parent_header(self, parent_header): |
+ self.parent_header = parent_header |
+ |
+ def _flush_streams(self): |
+ """flush IO Streams prior to display""" |
+ sys.stdout.flush() |
+ sys.stderr.flush() |
+ |
+ def publish(self, source, data, metadata=None): |
+ self._flush_streams() |
+ if metadata is None: |
+ metadata = {} |
+ self._validate_data(source, data, metadata) |
+ content = {} |
+ content['source'] = source |
+ content['data'] = encode_images(data) |
+ content['metadata'] = metadata |
+ sendMessage('iopub', 'display_data', content=json_clean(content), |
+ parent_header=self.parent_header) |
+ |
+ def clear_output(self, stdout=True, stderr=True, other=True): |
+ content = dict(stdout=stdout, stderr=stderr, other=other) |
+ |
+ if stdout: |
+ sys.stdout.write('\r') |
+ if stderr: |
+ sys.stderr.write('\r') |
+ |
+ self._flush_streams() |
+ sendMessage('iopub', 'clear_output', content=content, |
+ parent_header=self.parent_header) |
+ |
+ |
+class PepperInteractiveShell(InteractiveShell): |
+ """A subclass of InteractiveShell for the Pepper Messagin API.""" |
+ displayhook_class = Type(PepperShellDisplayHook) |
+ display_pub_class = Type(PepperDisplayPublisher) |
+ @staticmethod |
+ def enable_gui(gui): |
+ pass |
+ |
+ |
+InteractiveShellABC.register(PepperInteractiveShell) |
+ |
+class PepperKernel(Configurable): |
+ shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') |
+ shell_class = Type(PepperInteractiveShell) |
+ |
+ def __init__(self): |
+ self.shell = self.shell_class.instance(parent=self) |
+ self.shell.run_cell(""" |
+import os |
+matplotlib_config_dir = '/mplconfigdir' |
+os.environ['XDG_CONFIG_HOME'] = matplotlib_config_dir |
+os.environ['TMP'] = '' |
+import matplotlib |
+import matplotlib.cbook |
+""") |
+ |
+execution_count = 1 |
+ |
+shell = PepperKernel().shell |
+ |
+# Special message to indicate the NaCl kernel is ready. |
+sendMessage('iopub', 'status', content={'execution_state': 'nacl_ready'}) |
+ |
+while 1: |
+ sendMessage('iopub', 'status', content={'execution_state': 'idle'}) |
+ msg = json.loads(ppmessage._AcquireJSONMessageWait()) |
+ sendMessage('iopub', 'status', content={'execution_state': 'busy'}) |
+ |
+ if not 'header' in msg: |
+ continue |
+ request_header = msg['header'] |
+ if not 'msg_type' in request_header: |
+ continue |
+ msg_type = request_header['msg_type'] |
+ if msg_type == 'execute_request': |
+ try: |
+ content = msg[u'content'] |
+ code = content[u'code'] |
+ silent = content[u'silent'] |
+ store_history = content.get(u'store_history', not silent) |
+ except: |
+ self.log.error("Got bad msg: ") |
+ self.log.error("%s", msg) |
+ continue |
+ |
+ # Let output streams know which message the output is for |
+ stdout_stream.SetParentHeader(request_header) |
+ stderr_stream.SetParentHeader(request_header) |
+ shell.displayhook.set_parent_header(request_header) |
+ shell.display_pub.set_parent_header(request_header) |
+ |
+ status = 'ok' |
+ content = {} |
+ try: |
+ shell.run_cell(msg['content']['code'], |
+ store_history=store_history, |
+ silent=silent) |
+ except Exception, ex: |
+ status = 'error' |
+ logging.exception('Exception occured while running cell') |
+ |
+ content = {'status': status, |
+ 'execution_count': execution_count} |
+ |
+ if status == 'ok': |
+ content['payload'] = [] |
+ content['user_variables'] = {} |
+ content['user_expressions'] = {} |
+ elif status == 'error': |
+ content['ename'] = type(ex).__name__ |
+ content['evalue'] = str(ex) |
+ content['traceback'] = [] |
+ |
+ execution_count += 1 |
+ if status == 'error': |
+ sendMessage('iopub', 'pyerr', parent_header=request_header, |
+ content={ |
+ 'execution_count': execution_count, |
+ 'ename': type(ex).__name__, |
+ 'evalue': str(ex), |
+ 'traceback': [] |
+ } |
+ ) |
+ sendMessage('shell', 'execute_reply', parent_header=request_header, |
+ content=content) |
+ elif msg_type == 'complete_request': |
+ c = msg['content'] |
+ try: |
+ cpos = int(c['cursor_pos']) |
+ except: |
+ cpos = len(c['text']) |
+ if cpos == 0: |
+ cpos = len(c['line']) |
+ txt, matches = shell.complete(c['text'], c['line'], cpos) |
+ sendMessage('shell', 'complete_reply', |
+ parent_header = request_header, |
+ content = { |
+ 'matches': matches, |
+ 'matched_text': txt, |
+ 'status': 'ok' |
+ }) |
+ elif msg_type == 'restart': |
+ # break out of this loop, ending this program. |
+ # The main event loop in shell.cc will then |
+ # run this program again. |
+ break |
+ elif msg_type == 'kill': |
+ # Raise an exception so that the function |
+ # running this script will return -1, resulting |
+ # in no restart of this script. |
+ raise RuntimeError |