Skip to content

Commit 92d5a50

Browse files
refactor: make comm_open handler extensible
1 parent c8049a1 commit 92d5a50

File tree

2 files changed

+32
-15
lines changed

2 files changed

+32
-15
lines changed

nbclient/client.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,10 @@ def __init__(self, nb, km=None, **kw):
306306
'OutputModel': OutputWidget
307307
}
308308
}
309+
# comm_open_handlers should return an object with a .handle_msg(msg) method or None
310+
self.comm_open_handlers = {
311+
'jupyter.widget': self.on_comm_open_jupyter_widget
312+
}
309313

310314
def reset_execution_trackers(self):
311315
"""Resets any per-execution trackers.
@@ -318,7 +322,7 @@ def reset_execution_trackers(self):
318322
# to support nested use of output widgets.
319323
self.output_hook_stack = collections.defaultdict(list)
320324
# our front-end mimicing Output widgets
321-
self.widget_objects = {}
325+
self.comm_objects = {}
322326

323327
def start_kernel_manager(self):
324328
"""Creates a new kernel manager.
@@ -863,23 +867,17 @@ def handle_comm_msg(self, outs, msg, cell_index):
863867
self.widget_buffers[content['comm_id']] = self._get_buffer_data(msg)
864868
# There are cases where we need to mimic a frontend, to get similar behaviour as
865869
# when using the Output widget from Jupyter lab/notebook
866-
if msg['msg_type'] == 'comm_open' and msg['content'].get('target_name') == 'jupyter.widget':
867-
content = msg['content']
868-
data = content['data']
869-
state = data['state']
870+
if msg['msg_type'] == 'comm_open':
871+
handler = self.comm_open_handlers.get(msg['content'].get('target_name'))
870872
comm_id = msg['content']['comm_id']
871-
module = self.widget_registry.get(state['_model_module'])
872-
if module:
873-
widget_class = module.get(state['_model_name'])
874-
self.widget_objects[comm_id] = widget_class(comm_id, state, self.kc, self)
873+
comm_object = handler(msg)
874+
if comm_object:
875+
self.comm_objects[comm_id] = comm_object
875876
elif msg['msg_type'] == 'comm_msg':
876877
content = msg['content']
877-
data = content['data']
878-
if 'state' in data:
879-
state = data['state']
880-
comm_id = msg['content']['comm_id']
881-
if comm_id in self.widget_objects:
882-
self.widget_objects[comm_id].set_state(state)
878+
comm_id = msg['content']['comm_id']
879+
if comm_id in self.comm_objects:
880+
self.comm_objects[comm_id].handle_msg(msg)
883881

884882
def _serialize_widget_state(self, state):
885883
"""Serialize a widget state, following format in @jupyter-widgets/schema."""
@@ -920,6 +918,17 @@ def remove_output_hook(self, msg_id, hook):
920918
removed_hook = self.output_hook_stack[msg_id].pop()
921919
assert removed_hook == hook
922920

921+
def on_comm_open_jupyter_widget(self, msg):
922+
content = msg['content']
923+
data = content['data']
924+
state = data['state']
925+
comm_id = msg['content']['comm_id']
926+
module = self.widget_registry.get(state['_model_module'])
927+
if module:
928+
widget_class = module.get(state['_model_name'])
929+
if widget_class:
930+
return widget_class(comm_id, state, self.kc, self)
931+
923932

924933
def execute(nb, cwd=None, km=None, **kwargs):
925934
"""Execute a notebook's code, updating outputs within the notebook object.

nbclient/output_widget.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,11 @@ def set_state(self, state):
7575
else:
7676
self.executor.remove_output_hook(self.msg_id, self)
7777
self.msg_id = msg_id
78+
79+
def handle_msg(self, msg):
80+
content = msg['content']
81+
comm_id = content['comm_id']
82+
assert comm_id == self.comm_id
83+
data = content['data']
84+
if 'state' in data:
85+
self.set_state(data['state'])

0 commit comments

Comments
 (0)