OLD | NEW |
(Empty) | |
| 1 # Copyright (c) 2014 Google Inc. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 """A simple shell that uses the IPython messaging system.""" |
| 6 |
| 7 import json |
| 8 import logging |
| 9 import sys |
| 10 |
| 11 import IPython |
| 12 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC |
| 13 from IPython.utils.traitlets import Type, Dict, Instance |
| 14 from IPython.core.displayhook import DisplayHook |
| 15 from IPython.utils.jsonutil import json_clean, encode_images |
| 16 from IPython.core.displaypub import DisplayPublisher |
| 17 from IPython.config.configurable import Configurable |
| 18 |
| 19 # module defined in shell.cc for communicating via pepper API |
| 20 import ppmessage |
| 21 |
| 22 def sendMessage(socket_name, msg_type, parent_header=None, content=None): |
| 23 if parent_header is None: |
| 24 parent_header = {} |
| 25 if content is None: |
| 26 content = {} |
| 27 msg = { |
| 28 'header': {'msg_type': msg_type}, |
| 29 'parent_header': parent_header, |
| 30 'content': content |
| 31 } |
| 32 ppmessage._PostJSONMessage(socket_name, json.dumps(msg)) |
| 33 |
| 34 class MsgOutStream(object): |
| 35 """Class to overrides stderr and stdout.""" |
| 36 |
| 37 def __init__(self, stream_name): |
| 38 self._stream_name = stream_name |
| 39 self._parent_header = {} |
| 40 |
| 41 def SetParentHeader(self, parent_header): |
| 42 self._parent_header = parent_header |
| 43 |
| 44 def close(self): |
| 45 pass |
| 46 |
| 47 def flush(self): |
| 48 pass |
| 49 |
| 50 def write(self, string): |
| 51 sendMessage('iopub', 'stream', parent_header=self._parent_header, |
| 52 content={'name': self._stream_name, 'data': string}) |
| 53 |
| 54 def writelines(self, sequence): |
| 55 for string in sequence: |
| 56 self.write(string) |
| 57 |
| 58 # override sys.stdout and sys.stderr to broadcast on iopub |
| 59 stdout_stream = MsgOutStream('stdout') |
| 60 stderr_stream = MsgOutStream('stderr') |
| 61 sys_stdout = sys.stdout |
| 62 sys_stderr = sys.stderr |
| 63 sys.stdout = stdout_stream |
| 64 sys.stderr = stderr_stream |
| 65 |
| 66 |
| 67 |
| 68 class PepperShellDisplayHook(DisplayHook): |
| 69 parent_header = Dict({}) |
| 70 |
| 71 def set_parent_header(self, parent_header): |
| 72 """Set the parent for outbound messages.""" |
| 73 self.parent_header = parent_header |
| 74 |
| 75 def start_displayhook(self): |
| 76 self.content = {} |
| 77 |
| 78 def write_output_prompt(self): |
| 79 self.content['execution_count'] = self.prompt_count |
| 80 |
| 81 def write_format_data(self, format_dict, md_dict=None): |
| 82 self.content['data'] = encode_images(format_dict) |
| 83 self.content['metadata'] = md_dict |
| 84 |
| 85 def finish_displayhook(self): |
| 86 sys.stdout.flush() |
| 87 sys.stderr.flush() |
| 88 sendMessage('iopub', 'pyout', parent_header=self.parent_header, |
| 89 content=self.content) |
| 90 self.content = None |
| 91 |
| 92 |
| 93 class PepperDisplayPublisher(DisplayPublisher): |
| 94 parent_header = Dict({}) |
| 95 |
| 96 def set_parent_header(self, parent_header): |
| 97 self.parent_header = parent_header |
| 98 |
| 99 def _flush_streams(self): |
| 100 """flush IO Streams prior to display""" |
| 101 sys.stdout.flush() |
| 102 sys.stderr.flush() |
| 103 |
| 104 def publish(self, source, data, metadata=None): |
| 105 self._flush_streams() |
| 106 if metadata is None: |
| 107 metadata = {} |
| 108 self._validate_data(source, data, metadata) |
| 109 content = {} |
| 110 content['source'] = source |
| 111 content['data'] = encode_images(data) |
| 112 content['metadata'] = metadata |
| 113 sendMessage('iopub', 'display_data', content=json_clean(content), |
| 114 parent_header=self.parent_header) |
| 115 |
| 116 def clear_output(self, stdout=True, stderr=True, other=True): |
| 117 content = dict(stdout=stdout, stderr=stderr, other=other) |
| 118 |
| 119 if stdout: |
| 120 sys.stdout.write('\r') |
| 121 if stderr: |
| 122 sys.stderr.write('\r') |
| 123 |
| 124 self._flush_streams() |
| 125 sendMessage('iopub', 'clear_output', content=content, |
| 126 parent_header=self.parent_header) |
| 127 |
| 128 |
| 129 class PepperInteractiveShell(InteractiveShell): |
| 130 """A subclass of InteractiveShell for the Pepper Messagin API.""" |
| 131 displayhook_class = Type(PepperShellDisplayHook) |
| 132 display_pub_class = Type(PepperDisplayPublisher) |
| 133 @staticmethod |
| 134 def enable_gui(gui): |
| 135 pass |
| 136 |
| 137 |
| 138 InteractiveShellABC.register(PepperInteractiveShell) |
| 139 |
| 140 class PepperKernel(Configurable): |
| 141 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') |
| 142 shell_class = Type(PepperInteractiveShell) |
| 143 |
| 144 def __init__(self): |
| 145 self.shell = self.shell_class.instance(parent=self) |
| 146 self.shell.run_cell(""" |
| 147 import os |
| 148 matplotlib_config_dir = '/mplconfigdir' |
| 149 os.environ['XDG_CONFIG_HOME'] = matplotlib_config_dir |
| 150 os.environ['TMP'] = '' |
| 151 import matplotlib |
| 152 import matplotlib.cbook |
| 153 """) |
| 154 |
| 155 execution_count = 1 |
| 156 |
| 157 shell = PepperKernel().shell |
| 158 |
| 159 # Special message to indicate the NaCl kernel is ready. |
| 160 sendMessage('iopub', 'status', content={'execution_state': 'nacl_ready'}) |
| 161 |
| 162 while 1: |
| 163 sendMessage('iopub', 'status', content={'execution_state': 'idle'}) |
| 164 msg = json.loads(ppmessage._AcquireJSONMessageWait()) |
| 165 sendMessage('iopub', 'status', content={'execution_state': 'busy'}) |
| 166 |
| 167 if not 'header' in msg: |
| 168 continue |
| 169 request_header = msg['header'] |
| 170 if not 'msg_type' in request_header: |
| 171 continue |
| 172 msg_type = request_header['msg_type'] |
| 173 if msg_type == 'execute_request': |
| 174 try: |
| 175 content = msg[u'content'] |
| 176 code = content[u'code'] |
| 177 silent = content[u'silent'] |
| 178 store_history = content.get(u'store_history', not silent) |
| 179 except: |
| 180 self.log.error("Got bad msg: ") |
| 181 self.log.error("%s", msg) |
| 182 continue |
| 183 |
| 184 # Let output streams know which message the output is for |
| 185 stdout_stream.SetParentHeader(request_header) |
| 186 stderr_stream.SetParentHeader(request_header) |
| 187 shell.displayhook.set_parent_header(request_header) |
| 188 shell.display_pub.set_parent_header(request_header) |
| 189 |
| 190 status = 'ok' |
| 191 content = {} |
| 192 try: |
| 193 shell.run_cell(msg['content']['code'], |
| 194 store_history=store_history, |
| 195 silent=silent) |
| 196 except Exception, ex: |
| 197 status = 'error' |
| 198 logging.exception('Exception occured while running cell') |
| 199 |
| 200 content = {'status': status, |
| 201 'execution_count': execution_count} |
| 202 |
| 203 if status == 'ok': |
| 204 content['payload'] = [] |
| 205 content['user_variables'] = {} |
| 206 content['user_expressions'] = {} |
| 207 elif status == 'error': |
| 208 content['ename'] = type(ex).__name__ |
| 209 content['evalue'] = str(ex) |
| 210 content['traceback'] = [] |
| 211 |
| 212 execution_count += 1 |
| 213 if status == 'error': |
| 214 sendMessage('iopub', 'pyerr', parent_header=request_header, |
| 215 content={ |
| 216 'execution_count': execution_count, |
| 217 'ename': type(ex).__name__, |
| 218 'evalue': str(ex), |
| 219 'traceback': [] |
| 220 } |
| 221 ) |
| 222 sendMessage('shell', 'execute_reply', parent_header=request_header, |
| 223 content=content) |
| 224 elif msg_type == 'complete_request': |
| 225 c = msg['content'] |
| 226 try: |
| 227 cpos = int(c['cursor_pos']) |
| 228 except: |
| 229 cpos = len(c['text']) |
| 230 if cpos == 0: |
| 231 cpos = len(c['line']) |
| 232 txt, matches = shell.complete(c['text'], c['line'], cpos) |
| 233 sendMessage('shell', 'complete_reply', |
| 234 parent_header = request_header, |
| 235 content = { |
| 236 'matches': matches, |
| 237 'matched_text': txt, |
| 238 'status': 'ok' |
| 239 }) |
| 240 elif msg_type == 'restart': |
| 241 # break out of this loop, ending this program. |
| 242 # The main event loop in shell.cc will then |
| 243 # run this program again. |
| 244 break |
| 245 elif msg_type == 'kill': |
| 246 # Raise an exception so that the function |
| 247 # running this script will return -1, resulting |
| 248 # in no restart of this script. |
| 249 raise RuntimeError |
OLD | NEW |