Modifying (server) plot in JupyterNotebook throws error

Hi there,

I am starting to fit a decently complex plot into JupyterNotebook.
I do the usual yadda-yadda to get it to show up (output_notebook, show(create_figure)) and that works fine.

The server-side logic works, too, e.g., I drag a slider => plot changes.
Now, I would like to manipulate the plot I am displaying in the notebook directly (with code) (I saved a reference to it beforehand).

When I call the manipulating function of the plot I get an error, though, which I think is helpful but I do not know how to avoid it.

ERROR
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-9-2d4198469974> in <module>
----> 1 a.on_dim_change(0,2)

c:\users\jannik\desktop\xxxxxxxxx\xxxxxx\xxxx\xxxxxx\xxxxx\xxxx\images.py in on_dim_change(self, dim_index, new)
     54         new_image = self.handler.get_data()
---> 55         self.img_src.data['image'] = [new_image]
     56 
     57         sel_vals = self.img.get_selected_values()

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\core\property\wrappers.py in __setitem__(self, i, y)
    342     # don't wrap with notify_owner --- notifies owners explicitly
    343     def __setitem__(self, i, y):
--> 344         return self.update([(i, y)])
    345 
    346     def __copy__(self):

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\core\property\wrappers.py in update(self, *args, **kwargs)
    376         for (owner, descriptor) in self._owners:
    377             hint = ColumnDataChangedEvent(owner.document, owner, cols=list(cols))
--> 378             descriptor._notify_mutated(owner, old, hint=hint)
    379 
    380         return result

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\core\property\descriptors.py in _notify_mutated(self, obj, old, hint)
    884         value = self.property.prepare_value(obj, self.name, value)
    885 
--> 886         self._real_set(obj, old, value, hint=hint)
    887 
    888     def _trigger(self, obj, old, value, hint=None, setter=None):

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\core\property\descriptors.py in _real_set(self, obj, old, value, hint, setter)
    847 
    848         # for notification purposes, "old" should be the logical old
--> 849         self._trigger(obj, old, value, hint=hint, setter=setter)
    850 
    851     # called when a container is mutated "behind our back" and

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\core\property\descriptors.py in _trigger(self, obj, old, value, hint, setter)
    924         """
    925         if hasattr(obj, 'trigger'):
--> 926             obj.trigger(self.name, old, value, hint, setter)
    927 
    928 

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\model.py in trigger(self, attr, old, new, hint, setter)
    680         # chain up to invoke callbacks
    681         descriptor = self.lookup(attr)
--> 682         super().trigger(descriptor.name, old, new, hint=hint, setter=setter)
    683 
    684     def _attach_document(self, doc):

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\util\callback_manager.py in trigger(self, attr, old, new, hint, setter)
    161                     callback(attr, old, new)
    162         if hasattr(self, '_document') and self._document is not None:
--> 163             self._document._notify_change(self, attr, old, new, hint, setter, invoke)
    164         else:
    165             invoke()

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\document\document.py in _notify_change(self, model, attr, old, new, hint, setter, callback_invoker)
   1088 
   1089         event = ModelChangedEvent(self, model, attr, old, new, serializable_new, hint, setter, callback_invoker)
-> 1090         self._trigger_on_change(event)
   1091 
   1092     def _push_all_models_freeze(self):

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\document\document.py in _trigger_on_change(self, event)
   1183             for cb in self._callbacks.values():
   1184                 cb(event)
-> 1185         self._with_self_as_curdoc(invoke_callbacks)
   1186 
   1187     def _with_self_as_curdoc(self, f):

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\document\document.py in _with_self_as_curdoc(self, f)
   1196             else:
   1197                 set_curdoc(self)
-> 1198             return f()
   1199         finally:
   1200             set_curdoc(old_doc)

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\document\document.py in invoke_callbacks()
   1182         def invoke_callbacks():
   1183             for cb in self._callbacks.values():
-> 1184                 cb(event)
   1185         self._with_self_as_curdoc(invoke_callbacks)
   1186 

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\document\document.py in <lambda>(event)
    722     def on_change_dispatch_to(self, receiver):
    723         if not receiver in self._callbacks:
--> 724             self._callbacks[receiver] = lambda event: event.dispatch(receiver)
    725 
    726     def on_session_destroyed(self, *callbacks):

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\document\events.py in dispatch(self, receiver)
    267 
    268         '''
--> 269         super().dispatch(receiver)
    270         if hasattr(receiver, '_document_model_changed'):
    271             receiver._document_model_changed(self)

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\document\events.py in dispatch(self, receiver)
    122         super().dispatch(receiver)
    123         if hasattr(receiver, '_document_patched'):
--> 124             receiver._document_patched(self)
    125 
    126     def generate(self, references, buffers):

c:\users\jannik\envs\bokehtest\lib\site-packages\bokeh\server\session.py in _document_patched(self, event)
    216 
    217         if self._pending_writes is None:
--> 218             raise RuntimeError("_pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes")
    219 
    220         # TODO (havocp): our "change sync" protocol is flawed because if both

RuntimeError: _pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes

The function in question changes values and updates other figures all over the place.
Usually I’m using the app without JupyterNotebook; do I have to use add_next_tick_callbacks to be compatible with the notebook?

If necessary I can compile an MRE but I was hoping I was just doing something obvious wrong.

This error typically only shows up if you are updating a Bokeh document from another thread without acquiring the necessary document lock first. If you are updating a document from “outside” a callback (either property change, event, or next tick callback) that would probably explain it, but an MRE would be needed to speculate more.

Wrapping the manipulating function in a doc.add_next_tick_callback helped indeed.
Thank you very much