Boomerang update avoidance

Hi, I’m unclear on exactly what object to give the setter property of a widget when avoiding “boomerang” updates. In particular, I have a server-side function that checks on the state of a variable that is not part of the bokeh app and then uses the update method to synchronize the state of a toggle.

To avoid triggering my_toggle.on_change, I think I need to acquire my_server_session and use it in:

doc.add_next_tick_callback(lambda: my_toggle.update(active=False,setter=my_server_session))

Is that right? And if so, how is my_server_session acquired?

setter is a deep implementation detail and should really never be used or set by users directly. It may even go away in the future. This is why there essentially no documentation for it. Callbacks for widgets can be removed, so your best approach is probably to remove the callback that you don’t want to fire, make your update, and then add it back.

cc @mattpap in case there was some other mechanism for suppressing callback execution that was added at some point that I missed go by.

Thanks, @Bryan. I wonder if adding a context manager to bokeh like

with suspend_callbacks(my_widget):

would be a good idea?

where the context manager is defined by

class suspend_callbacks(object):
    def __init__(self, my_widget):
        self._widget = my_widget

    def __enter__(self):
        self._suspended_callbacks = self._widget._callbacks.copy() # not sure if copy required...
        self._widget._callbacks = {} #delete all callbacks from widget

    def __exit__(self,exc_type, exc_value, exc_traceback):
        self._widget._callbacks = self._suspended_callbacks.copy() # restore callbacks again, not sure re copy

I implemented this in my code and it seems to work, although it is accessing a private variable and I’m sure there’s a way to do it that’s more consistent with your user-facing api.

Context manager seems nice but I would try to use remove_on_change rather than mucking with _callbacks directly. Private attrs might be changed at any point in the future, so just FYI. If you need to use _callbacks for now I would advise to open a GitHub Issue so we can consider what minimal public supported API additions might be warranted to help with this situation.

Not sure if this would work in your use case, but I leveraged the tags property on a shared data source among widgets to provide conceptually similar control in one of my apps.

Here’s snippet of a relevant part of a callback in case it helps.

    def radial_eq_cb(self, attr, old, new):
        """Set radial equalizer
        _methodname = self.radial_eq_cb.__name__

        s = self.equalizer().ui.sources.radial

        if s.tags:
            s.tags = []

        s.tags = ['cb']

        if new:
            c = new[0] // self.equalizer().num_levels
            if c < self.equalizer().num_eq:
                # Set selected equalizer
                self.equalizer().sel0[c] = new[0]
                eq_index = new[0] % self.equalizer().num_levels
1 Like

Ok. Will do!

1 Like

Interesting idea. Thanks!