diff --git a/nbclient/client.py b/nbclient/client.py index 5f148f06..bf3c2f0a 100644 --- a/nbclient/client.py +++ b/nbclient/client.py @@ -1,5 +1,6 @@ -import datetime import base64 +import collections +import datetime from textwrap import dedent from async_generator import asynccontextmanager @@ -22,6 +23,7 @@ CellExecutionError ) from .util import run_sync, ensure_async +from .output_widget import OutputWidget def timestamp(): @@ -307,6 +309,11 @@ def reset_execution_trackers(self): self._display_id_map = {} self.widget_state = {} self.widget_buffers = {} + # maps to list of hooks, where the last is used, this is used + # to support nested use of output widgets. + self.output_hook_stack = collections.defaultdict(list) + # our front-end mimicing Output widgets + self.output_widget_objects = {} def start_kernel_manager(self): """Creates a new kernel manager. @@ -787,6 +794,14 @@ def process_message(self, msg, cell, cell_index): def output(self, outs, msg, display_id, cell_index): msg_type = msg['msg_type'] + parent_msg_id = msg['parent_header'].get('msg_id') + if self.output_hook_stack[parent_msg_id]: + # if we have a hook registered, it will overrride our + # default output behaviour (e.g. OutputWidget) + hook = self.output_hook_stack[parent_msg_id][-1] + hook.output(outs, msg, display_id, cell_index) + return + try: out = output_from_msg(msg) except ValueError: @@ -812,6 +827,15 @@ def output(self, outs, msg, display_id, cell_index): def clear_output(self, outs, msg, cell_index): content = msg['content'] + + parent_msg_id = msg['parent_header'].get('msg_id') + if self.output_hook_stack[parent_msg_id]: + # if we have a hook registered, it will overrride our + # default clear_output behaviour (e.g. OutputWidget) + hook = self.output_hook_stack[parent_msg_id][-1] + hook.clear_output(outs, msg, cell_index) + return + if content.get('wait'): self.log.debug('Wait to clear output') self.clear_before_next_output = True @@ -832,6 +856,23 @@ def handle_comm_msg(self, outs, msg, cell_index): self.widget_state.setdefault(content['comm_id'], {}).update(data['state']) if 'buffer_paths' in data and data['buffer_paths']: self.widget_buffers[content['comm_id']] = self._get_buffer_data(msg) + # There are cases where we need to mimic a frontend, to get similar behaviour as + # when using the Output widget from Jupyter lab/notebook + if msg['msg_type'] == 'comm_open' and msg['content'].get('target_name') == 'jupyter.widget': + content = msg['content'] + data = content['data'] + state = data['state'] + comm_id = msg['content']['comm_id'] + if state['_model_module'] == '@jupyter-widgets/output' and state['_model_name'] == 'OutputModel': + self.output_widget_objects[comm_id] = OutputWidget(comm_id, state, self.kc, self) + elif msg['msg_type'] == 'comm_msg': + content = msg['content'] + data = content['data'] + if 'state' in data: + state = data['state'] + comm_id = msg['content']['comm_id'] + if comm_id in self.output_widget_objects: + self.output_widget_objects[comm_id].set_state(state) def _serialize_widget_state(self, state): """Serialize a widget state, following format in @jupyter-widgets/schema.""" @@ -856,6 +897,22 @@ def _get_buffer_data(self, msg): ) return encoded_buffers + def register_output_hook(self, msg_id, hook): + """Registers an override object that handles output/clear_output instead. + + Multiple hooks can be registered, where the last one will be used (stack based) + """ + # mimics + # https://jupyterlab.github.io/jupyterlab/services/interfaces/kernel.ikernelconnection.html#registermessagehook + self.output_hook_stack[msg_id].append(hook) + + def remove_output_hook(self, msg_id, hook): + """Unregisters an override object that handles output/clear_output instead""" + # mimics + # https://jupyterlab.github.io/jupyterlab/services/interfaces/kernel.ikernelconnection.html#removemessagehook + removed_hook = self.output_hook_stack[msg_id].pop() + assert removed_hook == hook + def execute(nb, cwd=None, km=None, **kwargs): """Execute a notebook's code, updating outputs within the notebook object. diff --git a/nbclient/output_widget.py b/nbclient/output_widget.py new file mode 100644 index 00000000..64141872 --- /dev/null +++ b/nbclient/output_widget.py @@ -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 \ No newline at end of file diff --git a/nbclient/tests/files/Output.ipynb b/nbclient/tests/files/Output.ipynb new file mode 100644 index 00000000..8c8851a3 --- /dev/null +++ b/nbclient/tests/files/Output.ipynb @@ -0,0 +1,776 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e152547dd69d46fcbcb602cf9f92e50b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import ipywidgets as widgets\n", + "from IPython.display import clear_output\n", + "output1 = widgets.Output()\n", + "output1" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hi\n" + ] + } + ], + "source": [ + "print(\"hi\")\n", + "with output1:\n", + " print(\"in output\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "with output1:\n", + " raise ValueError(\"trigger msg_type=error\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "44dc393cd7c6461a8c4901f85becfc0e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import ipywidgets as widgets\n", + "output2 = widgets.Output()\n", + "output2" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hi2\n" + ] + } + ], + "source": [ + "print(\"hi2\")\n", + "with output2:\n", + " print(\"in output2\")\n", + " clear_output(wait=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d6cd7a1de3494d2daff23c6d4ffe42ee", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import ipywidgets as widgets\n", + "output3 = widgets.Output()\n", + "output3" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hi3\n" + ] + } + ], + "source": [ + "print(\"hi3\")\n", + "with output3:\n", + " print(\"hello\")\n", + " clear_output(wait=True)\n", + " print(\"world\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "10517a9d5b1d4ea386945642894dd898", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import ipywidgets as widgets\n", + "output4 = widgets.Output()\n", + "output4" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hi4\n" + ] + } + ], + "source": [ + "print(\"hi4\")\n", + "with output4:\n", + " print(\"hello world\")\n", + " clear_output()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "37f7ba6a9ecc4c19b519e718cd12aafe", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import ipywidgets as widgets\n", + "output5 = widgets.Output()\n", + "output5" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"hi5\")\n", + "with output5:\n", + " display(\"hello world\") # this is not a stream but plain text\n", + "clear_output()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4fb0ee7e557440109c08547514f03c7b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import ipywidgets as widgets\n", + "output_outer = widgets.Output()\n", + "output_inner = widgets.Output()\n", + "output_inner" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "01ea355e26484c13b1caaaf6d29ac0f2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "output_outer" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "with output_inner:\n", + " print('in inner')\n", + " with output_outer:\n", + " print('in outer')\n", + " print('also in inner')" + ] + } + ], + "metadata": { + "kernelspec": { + "language": "python" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": { + "01ea355e26484c13b1caaaf6d29ac0f2": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_7213e178683c4d0682b3c848a2452cf1", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in outer\n" + } + ] + } + }, + "025929abe8a143a08ad23de9e99c610f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "03c04d8645a74c4dac2e08e2142122a6": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "091f6e59c48442b1bdb13320b4f6605d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "10517a9d5b1d4ea386945642894dd898": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_2c67de94f62d4887866d22abca7f6f13" + } + }, + "106de0ded502439c873de5449248b00c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "1b9529b98aaf40ccbbf38e178796be88": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "22592f3cb7674cb79cc60def5e8bc060": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "2468aac6020349139ee6236b5dde0310": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_d5e88b6a26114d6da0b7af215aa2c3bb" + } + }, + "2955dc9c531c4c6b80086da240d0df13": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_1b9529b98aaf40ccbbf38e178796be88", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "world\n" + } + ] + } + }, + "2c67de94f62d4887866d22abca7f6f13": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "37f7ba6a9ecc4c19b519e718cd12aafe": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_03c04d8645a74c4dac2e08e2142122a6", + "outputs": [ + { + "data": { + "text/plain": "'hello world'" + }, + "metadata": {}, + "output_type": "display_data" + } + ] + } + }, + "3945ce528fbf40dc830767281892ea56": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "3c6bb7a6fd4f4f8786d30ef7b2c7c050": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "3e0e8f5d18fe4992b11e1d5c13faecdf": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "44dc393cd7c6461a8c4901f85becfc0e": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_3c6bb7a6fd4f4f8786d30ef7b2c7c050", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in output2\n" + } + ] + } + }, + "45823daa739447a6ba5393e45204ec8e": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_3e0e8f5d18fe4992b11e1d5c13faecdf", + "outputs": [ + { + "data": { + "text/plain": "'hello world'" + }, + "metadata": {}, + "output_type": "display_data" + } + ] + } + }, + "4fa2d1a41bd64017a20e358526ad9cf3": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_6490daaa1d2e42a0aef909e7b8c8eff4", + "outputs": [ + { + "data": { + "text/plain": "'hello world'" + }, + "metadata": {}, + "output_type": "display_data" + } + ] + } + }, + "4fb0ee7e557440109c08547514f03c7b": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_dbf140d66ba247b7847c0f5642b7f607", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in inner\nalso in inner\n" + } + ] + } + }, + "55aff5c4b53f440a868919f042cf9c14": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_a14653416772496aabed04b4719268ef", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in inner\nalso in inner\n" + } + ] + } + }, + "5747ce87279c44519b9df62799e25e6f": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_6ef78dc31eec422ab2afce4be129836f", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in output2\n" + } + ] + } + }, + "6490daaa1d2e42a0aef909e7b8c8eff4": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "6ef78dc31eec422ab2afce4be129836f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "7134e81fdb364a738c1e58b26ec0d008": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_025929abe8a143a08ad23de9e99c610f", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in inner\nalso in inner\n" + } + ] + } + }, + "7213e178683c4d0682b3c848a2452cf1": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "804b6628ca0a48dfbad930615626b1fb": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "a14653416772496aabed04b4719268ef": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "a32671b19b814cf5bd964c36368f9f79": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_c843c22ff72e4983984ca4d62ce68e2b", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in outer\n" + } + ] + } + }, + "aaf673ac9c774aaba4f751db2f3dd6c5": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_106de0ded502439c873de5449248b00c", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in output2\n" + } + ] + } + }, + "bc3d9af2591e4a52af73921f46d79efa": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_22592f3cb7674cb79cc60def5e8bc060" + } + }, + "c843c22ff72e4983984ca4d62ce68e2b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "cc022dc8b5584570a04facf68f9bdf0b": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_3945ce528fbf40dc830767281892ea56", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in outer\n" + } + ] + } + }, + "d0cb56db68f2485480da1b2a43ad3c02": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_df4468e2240a430599a01e731472c319", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in output\n" + }, + { + "ename": "ValueError", + "evalue": "trigger msg_type=error", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0moutput1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"trigger msg_type=error\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: trigger msg_type=error" + ] + } + ] + } + }, + "d314a6ef74d947f3a2149bdf9b8b57a3": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_804b6628ca0a48dfbad930615626b1fb", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in output\n" + } + ] + } + }, + "d5e88b6a26114d6da0b7af215aa2c3bb": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "d6cd7a1de3494d2daff23c6d4ffe42ee": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_091f6e59c48442b1bdb13320b4f6605d", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "world\n" + } + ] + } + }, + "dbf140d66ba247b7847c0f5642b7f607": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "de7ba4c0eed941a3b52fa940387d1415": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "df4468e2240a430599a01e731472c319": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "e152547dd69d46fcbcb602cf9f92e50b": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_de7ba4c0eed941a3b52fa940387d1415", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "in output\n" + }, + { + "ename": "ValueError", + "evalue": "trigger msg_type=error", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0moutput1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"trigger msg_type=error\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: trigger msg_type=error" + ] + } + ] + } + }, + "e27795e5a4f14450b8c9590cac51cb6b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": {} + }, + "e3e20af587534a9bb3fa413951ceb28d": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "layout": "IPY_MODEL_e27795e5a4f14450b8c9590cac51cb6b", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": "world\n" + } + ] + } + } + }, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}