Custom pre-built extension in a package

I have the following skeletal structure to my project, where a custom pre-built Bokeh extension exists in a sub-package of a Python package.

package
├── __init__.py
├── notebooks
│   └── nb.ipynb
└── sub_package
    ├── bokeh_extensions
    │   ├── extension.py
    │   ├── extension.ts
    │   ├── index.ts
    │   └── __init__.py
    ├── bokeh.ext.json
    ├── index.ts
    ├── __init__.py
    ├── package.json
    └── tsconfig.json

I am getting the following error when I try to use the extension in a notebook, e.g. the nb.ipynb in the notebooks folder. I should note that the package has been locally installed via pip, and bokeh build was run manually in the sub_package folder before trying to have the extension display in the Jupyter notebook.

Javascript Error: Model 'package.subpackage.bokeh_extensions.extension.Extension' does not exist. This could be due to a widget or a custom model not being registered before first usage.

I’ve come across this error before, when experimenting with the custom Bokeh extension workflow, and the fix was to add the line;

__module__ = 'package.sub_package.bokeh_extensions.extension'

to the TypeScript class named Extension that contains the boilerplate properties, __view_type__, constructor, this.prototype.default_view property definitions. I always seem to get the same error when I update the __module__ property definition to anything, and was wondering if there is a path issue I am not properly fixing by modifying the __module__ property since this is a sub-package in a Python package.

I should also ask if creating a pre-built custom Bokeh extension in a package, and it not being its own installable package, is a viable solution? An outline of the relevant files for the workflow are below.

// package/sub_package/bokeh_extensions/extensions.ts
import {HTMLBox, HTMLBoxView} from '@bokehjs/models/layouts/html_box';
export class ExtensionView extends HTMLBoxView {
  model: Extension
  ...
};
export class Extension extends HTMLBox {
  __module__: 'package.sub_package.bokeh_extensions.extension';
  ...
};
# package/sub_package/bokeh_extensions/extension.py
class Extension:
    ...
// package/sub_package/bokeh_extensions/index.ts
export {Extension} from './extension'
// package/sub_package/index.ts
import {register_models} from '@bokehjs/base';
import * as Models from './bokeh_extensions';
register_models(Models as any);
export {Models};
# package/sub_package/__init__.py
from .extension import Extension

I know this is an odd use case, but I was not able to make Python Bokeh apps for use in a notebook as the tools being created in the custom extension had to be written in TypeScript. I’ve combed over the work done by @MarcSkovMadsen for inspiration, but I cannot seem to find anyone making custom Bokeh extensions specifically for an existing Python package.

Here’s a little more info on what I’ve tried, and the results of those attempts.

I tried to work around the ...does not exist. error and supplied the TypeScript directly to the Python object. I did this by refactoring the following;

# package/sub_package/bokeh_extensions/extension.py
from pathlib import Path
from bokeh.util.compiler import TypeScript
def get_ts():
    cwd = Path(__file__).parent.resolve()
    ts_path = cwd.joinpath("extension.ts")
    with open(ts_path, "r") as f:
        ts = f.read()
    return ts
class Extension:
    __implementation__ = TypeScript(get_ts())

This required an update to the extension’s TypeScript property as follows.

// package/sub_package/bokeh_extensions/extension.ts
...
export class Extension extends HTMLBox {
  ...
  static __module__ = 'extension';
}

After these updates, I no longer get the ...does not exist. error, but I get a new one having to deal with the TypeScript compiler. The new error is now;

error TS2550: Property 'flat' does not exist on type 'number[][]'.
Do you need to change your target library? Try changing the 'lib'
compiler option to 'es2019' or later.

This only sort of makes sense to me, since I’m supplying the TypeScript directly to the Python object. I’m guessing there is no build step in which the compiler would have been informed that my target in tsconfig.json is es2020. If someone knows for certain, it would be great to have a definitive correct/incorrect response to the following statement:

One should not supply any TypeScript directly to the Python object if creating a pre-compiled Bokeh extension.

If it is a definitive correct, then the statement in the documentation about not having to supply a __name__ property as it gets “filled in” by TypeScript make more sense now.

If the above statement is not definitively correct, then I’m even more lost than before the path error in the original post, and any insights would be appreciated.

@Bryan I’m assuming you want me to put this thread on GitHub Discussions, and if so just tell me to do so.

@ndmlny-qs definitely a GH discussion seems advised. I am just not an expert on this side of BokehJS and so GH will be better for reaching the relevant people.