Callback property initialization in custom model

I’m trying to add a callback property to a custom model derived from ColumnDataSource. The python portion is fairly straight forward

class MyDataSource(ColumnDataSource):
    [...]
    callback = Instance(Callback)
    init = String( )

    __implementation__ = TypeScript("")  # inside preloaded ...-min.js

    def __init__( self, *args, **kwargs ):
        super( ).__init__( *args, **kwargs )

What I want to do is allow the user to specify the callback as a CustomJS function which is executed in the constructor of MyDataSource:

export namespace MyDataSource {
  export type Attrs = p.AttrsOf<Props>

  export type Props = ColumnDataSource.Props & {
      callback: p.Property<CallbackLike0<ColumnDataSource> | null>;
      init: p.Property<String>
      //callback: p.Property<any>
      //callback: p.Property<CustomJS>
      //callback: p.Property<Callback>
  }
}

export interface MyDataSource extends MyDataSource.Attrs {}

export class MyDataSource extends ColumnDataSource {
    properties: MyDataSource.Props

    constructor(attrs?: Partial<MyDataSource.Attrs>) {
        super(attrs);
//      const execute = () => this.callback!.execute( this, { num: 99 } )
//      execute( )
        eval( this.init.toString( ) )
    }
    initialize(): void {
        super.initialize();
    }

    static init_MyDataSource( ): void {
        //this.define<MyDataSource.Props>(({ Nullable, Any, Tuple, Number, Ref, String }) => ({
        //this.define<MyDataSource.Props>(({ Tuple, Number, String, Ref }) => ({
        this.define<MyDataSource.Props>(({ Tuple, Number, String, Ref, Any }) => ({
            //callback: [ Ref(Callback) ],
            //callback: [ Callback ],
            callback: [ Any ],
            //callback: [Nullable(Any /*TODO*/)],
            //callback: [ Any ],
            //callback: [ Ref(CustomJS) ],
            //callback: [ Ref(Callback) ],
        }));
    }
}

However, when the constructor is called callback only contains something like { id: "1003" }, and the execute function cannot be invoked. However, later on in subsequent CustomJS code which uses the MyDataSource object the callback property has been filled and execute can be called.

How should CustomJS properties be created (I tried a number of different options, see comments, and all seem to work but none result in initialization before use)?

Is there a way to force callback property to be initialized before the constructor code that I supply is executed?

Is there a function I can call to fully initialize property?

I have been using the work-around of specifying an init string property, but I cannot capture context (other Python variables) as CustomJS supports, and it is not clear how to fish this context out of the document:

self._my_source = myDataSource( ... ,
                                callback=CustomJS( args=dict( annotations=self._annotations ),
                                 code="""console.log('HERE WE ARE, FOLKS!!!!!!!!')"""),
                                 init='''...
                                         annotations[index]
                                         ...''' )

This class is compiled into a JavaScript library and imported along with the bokehjs library… so this may be a factor in the problem…

Thanks in advance for any help!

Bokeh models can have references to other models [1], so initialization has to occur in two passes: a first pass to rehydrate all the individual models, and a second pass to update references to be the actual models they refer to. The constructor is called during the first pass. There is absolutely no way to guarantee that the callback object even exists at the point the CDS is constructed, much less that the callback reference has been fixed up. Executing a configurable callback in the constructor is a complete non-starter, full stop.

However, Bokeh models define their own initialize method that BokehJS will invoke during the second pass, when references are guaranteed to be ready. You can see an example of defining an initialize method here:


  1. That’s basically what the { id: "1003" } is ↩︎

Fantastic… thanks for the quick help! This was the detail I needed.

Where is the best place to look for info about callbacks (supplied from Python) and user defined models? I have the feeling that the way I’ve defined MyDataSource is perhaps some old way of doing it…

@jetson Normally property change callbacks (e.g. on_change or js_on_change) are used, but since you are wanting some callback that executes unconditionally without any corresponding property change, what you are doing with a dedicated callback property seems fine.

Cool. Thanks again for the help…

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.