from .jsonutil import json_clean from nbformat.v4 import output_from_msg from typing import Dict, List, Any, Optional from jupyter_client.client import KernelClient class OutputWidget: """This class mimics a front end output widget""" def __init__( self, comm_id: str, state: Dict[str, Any], kernel_client: KernelClient, executor) -> None: self.comm_id: str = comm_id self.state: Dict[str, Any] = state self.kernel_client: KernelClient = kernel_client self.executor = executor self.topic: bytes = ('comm-%s' % self.comm_id).encode('ascii') self.outputs: List = self.state['outputs'] self.clear_before_next_output: bool = False def clear_output( self, outs: List, msg: Dict, cell_index: int) -> None: self.parent_header = msg['parent_header'] content = msg['content'] if content.get('wait'): self.clear_before_next_output = True else: self.outputs = [] # sync back the state to the kernel self.sync_state() if hasattr(self.executor, 'widget_state'): # sync the state to the nbconvert state as well, since that is used for testing self.executor.widget_state[self.comm_id]['outputs'] = self.outputs def sync_state(self) -> None: state = {'outputs': self.outputs} msg = {'method': 'update', 'state': state, 'buffer_paths': []} self.send(msg) def _publish_msg( self, msg_type: str, data: Optional[Dict] = None, metadata: Optional[Dict] = None, buffers: Optional[List] = None, **keys) -> None: """Helper for sending a comm message on IOPub""" data = {} if data is None else data metadata = {} if metadata is None else metadata content = json_clean(dict(data=data, comm_id=self.comm_id, **keys)) msg = self.kernel_client.session.msg(msg_type, content=content, parent=self.parent_header, metadata=metadata) self.kernel_client.shell_channel.send(msg) def send( self, data: Optional[Dict] = None, metadata: Optional[Dict] = None, buffers: Optional[List] = None) -> None: self._publish_msg('comm_msg', data=data, metadata=metadata, buffers=buffers) def output( self, outs: List, msg: Dict, display_id: str, cell_index: int) -> None: if self.clear_before_next_output: self.outputs = [] self.clear_before_next_output = False self.parent_header = msg['parent_header'] output = output_from_msg(msg) if self.outputs: # try to coalesce/merge output text last_output = self.outputs[-1] if (last_output['output_type'] == 'stream' and output['output_type'] == 'stream' and last_output['name'] == output['name']): last_output['text'] += output['text'] else: self.outputs.append(output) else: self.outputs.append(output) self.sync_state() if hasattr(self.executor, 'widget_state'): # sync the state to the nbconvert state as well, since that is used for testing self.executor.widget_state[self.comm_id]['outputs'] = self.outputs def set_state(self, state: Dict) -> None: if 'msg_id' in state: msg_id = state.get('msg_id') if msg_id: self.executor.register_output_hook(msg_id, self) self.msg_id = msg_id else: self.executor.remove_output_hook(self.msg_id, self) self.msg_id = msg_id def handle_msg(self, msg: Dict) -> None: content = msg['content'] comm_id = content['comm_id'] assert comm_id == self.comm_id data = content['data'] if 'state' in data: self.set_state(data['state'])