forked from jupyter/nbclient
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement Output widget that mimics a frontend
This is a port of voila-dashboards/voila#91 and subsequent fixes.
- Loading branch information
1 parent
6510bd9
commit 11f381e
Showing
3 changed files
with
910 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
from ipykernel.jsonutil import json_clean | ||
from nbformat.v4 import output_from_msg | ||
|
||
|
||
class OutputWidget: | ||
"""This class mimics a front end output widget""" | ||
def __init__(self, comm_id, state, kernel_client, executor): | ||
self.comm_id = comm_id | ||
self.state = state | ||
self.kernel_client = kernel_client | ||
self.executor = executor | ||
self.topic = ('comm-%s' % self.comm_id).encode('ascii') | ||
self.outputs = self.state['outputs'] | ||
self.clear_before_next_output = False | ||
|
||
def clear_output(self, outs, msg, cell_index): | ||
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): | ||
state = {'outputs': self.outputs} | ||
msg = {'method': 'update', 'state': state, 'buffer_paths': []} | ||
self.send(msg) | ||
|
||
def _publish_msg(self, msg_type, data=None, metadata=None, buffers=None, **keys): | ||
"""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=None, metadata=None, buffers=None): | ||
self._publish_msg('comm_msg', data=data, metadata=metadata, buffers=buffers) | ||
|
||
def output(self, outs, msg, display_id, cell_index): | ||
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): | ||
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 |
Oops, something went wrong.