Traverse the tree of sub models?

I’d like to traverse the tree of sub models of a bokeh model. Is there a natural way to do this using existing methods?

Not really. There are not uniformly named accessors at every level. E.g. a Plot has a list of .renderers. Some of those might be a LinearAxis, and an axis does not have a .renderers list, but it does have a reference to a .formatter and depending on the formatter there might be yet arbitrary references to any other models, e.g. the args property of FuncTickFormatter (that no other tick formatters have, because they don’t need to).

Are there ways to go about this digging in to the internal guts of the property system? Sure. But before diving in to any of that, it would be very helpful to know what it is you actually want to accomplish, in case there is simply some altogether different better approach. Also important to know if you want to do this on the Python side, or the JavaScript side?

The question grows out of this project:
BokehModelBuilder
which is still in an early stage (though it basically works).

I want to be able to “disassemble” a model in a systematic way and pull out the styling options
to be manipulated in the graphical way you can see in that code.

Currently I’ve reverse-engineered things “by hand” for a simple plot structure.

And this is on the python side.

Thanks for any help or suggestions.

Ah, sure that’s neat use case. So I think your best bet is the properties_with_refs method on Bokeh models. It will tell you the names of any properties whose type is either Instance (of some Bokeh model) or is a container of Instance of some sort:

In [6]: p = figure()

In [7]: p.properties_with_refs()
Out[7]:
{'above',
 'below',
 'center',
 'extra_x_ranges',
 'extra_y_ranges',
 'js_event_callbacks',
 'js_property_callbacks',
 'left',
 'renderers',
 'right',
 'title',
 'toolbar',
 'x_range',
 'x_scale',
 'y_range',
 'y_scale'}

In [8]: p.left
Out[8]: [LinearAxis(id='1044', ...)]

In [9]: p.left[0].properties_with_refs()
Out[9]: {'formatter', 'js_event_callbacks', 'js_property_callbacks', 'ticker'}

In [10]: p.left[0].ticker
Out[10]: BasicTicker(id='1045', ...)

In [11]: p.left[0].ticker.properties_with_refs()
Out[11]: {'js_event_callbacks', 'js_property_callbacks'}

Note that in your tool you might want to screen out/ignore js_event_callbacks and js_event_callbacks. There’s just not yet any notion of “private” or “internal” properties. If there was, those two would definitely be made such.

Thanks! Yes, I agree that the js_callbacks are appropriate for screening out. I have a few other ideas about how to move forward with this, and I imagine I’ll have other questions as I move ahead.

1 Like