The current callback API forces one to register callbacks on their senders. As a consequence, and also because callbacks rely on side-effects, intended receivers are not always obvious, particularly when the callback code is long. I propose an alternative, receiver-centric API. Here's a possible implementation for illustration:
<pre>
from bokeh.layouts import column
from bokeh.core.has_props import HasProps
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import curdoc, figure
from inspect import signature
# this logic is only here for the purpose of illustration
def lift(receiver, prop, cb):
# noinspection PyUnusedLocal
def callback(attr, old, new):
setattr(receiver, prop, cb(new))
return callback
bindings = {}
def bind(self, attr, cb):
sig = signature(cb)
params = sig.parameters
for param in params.keys():
bindings.setdefault(param, ).append(lift(self, attr, cb))
setattr(self, attr, cb())
HasProps.bind = bind # monkey patch
# example begins proper
def data(power=0.1):
"""More pythonic callback (returns value instead of side-effect)"""
xs = [x * 0.005 for x in range(200)]
ys = [x ** power for x in xs]
return dict(x=xs, y=ys)
source = ColumnDataSource()
source.bind('data', data) # binds to target instead of sources
plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
slider = Slider(start=0., end=4., value=.1, step=.1, title='power', name='power')
curdoc().add_root(column(slider, plot))
# Now bind callback to receivers, would move to a utility routine
for sender_name, callbacks in bindings.items():
sender = curdoc().get_model_by_name(sender_name)
sender.on_change('value', *callbacks)
</pre>
As you can see, source.bind replaces slider.on_change. The implementation identifies sender(s) by inspecting the callback signature.
Please share your thoughts on this idea. If there's enough interest, I will submit a PR. Folks more familiar with bokeh internals should feel free to suggest better implementation ideas.