Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

ianthomas23
Copy link
Member

This is a candidate fix for #13732.

Initialisation of HasProps-derived classes in BokehJS currently occurs in the HasProps constructor, provided it is not deferred because we are deserialising from JSON. This sets attributes of derived classes which can be dangerous as those attributes are not guaranteed to exist before we have reached the bodies of the derived-class constructors. This limits the use of BokehJS in frontend-only applications (i.e. without Python).

Here the problem is fixed by explicitly initialising in the final constructor rather than in HasProps. This is achieved by the constructor of each class derived from HasProps calling a new function maybe_initialize. If it is called from a final constructor and initialisation has not been deferred then initialisation occurs. The code path for deferred initialisation is not altered except for multiple calls to maybe_initialize which immediately return. The runtime identification of if an object is of a specific class has to be done carefully as it has to work when BokehJS is minified and actual class names are replaced by single letters. Fortunately SomeClass.__name__ and object.constructor.__name__ already exist in BokehJS for this.

The initialisation is explicit, occurring precisely where it should occur. This PR is necessarily large as it touches many files, but the changes are simple to understand.

Testing this locally there are just 2 BokehJS unit test failures, and it works with my various experiments of using BokehJS in TypeScript projects. The size of the built bokeh.min.js increases from 1106 kB to 1119 kB, so a 1.2% increase.

There are two separate Figure classes in the same class hierarchy (models/plots/figure.ts and api/figure.ts) which potentially could break the class name identification used due to non-uniqueness. However the latter is derived from the former and does not add any new attributes or properties, so it works provided there is no maybe_initialize call in the latter.

Strictly speaking only concrete (non-abstract) classes need the maybe_initialize call. However, node make test:defaults instantiates abstract classes too so it has been necessary to add constructors containing maybe_initialize to all classes derived from HasProps. This includes classes used for testing.

Within the Bokeh repo this is fully backward-compatible (assuming I can fix the few failing tests), but it is an API breaking change for anyone who has written a BokehJS custom extension as each class derived from HasProps will now have to have a constructor that calls maybe_initialize in the correct way. There could potentially be some specialised used of derived classes here that is broken by this, so this is targetting Bokeh 4.0.

@bryevdv
Copy link
Member

bryevdv commented Mar 20, 2025

Should we go ahead and start a branch-4.0?

@ianthomas23
Copy link
Member Author

Should we go ahead and start a branch-4.0?

Yes, good idea.

@bryevdv
Copy link
Member

bryevdv commented Mar 20, 2025

@bokeh/dev I've gone ahead and created branch-4.0 we'll probably want to discuss in a weekly meeting the best strategy for when to actually merge new PRs against it and how best to keep it up to date with branch-3.8

@mattpap mattpap added this to the 4.0 milestone Mar 21, 2025
@mattpap mattpap changed the base branch from branch-3.8 to branch-4.0 March 21, 2025 07:39
@ianthomas23 ianthomas23 force-pushed the ianthomas23/13732_maybe_initialize branch 4 times, most recently from cec8878 to cdc072f Compare April 9, 2025 16:16
@bryevdv
Copy link
Member

bryevdv commented Apr 20, 2025

@ianthomas23 do you have ready any concrete examples of the sort of things this would enable that are not currently possible, e.g. with JS framework integrations? I would be interested to run/study them.

@ianthomas23
Copy link
Member Author

@ianthomas23 do you have ready any concrete examples of the sort of things this would enable that are not currently possible, e.g. with JS framework integrations? I would be interested to run/study them.

@bryevdv Yes, see the bokehjs-examples repo. There are plain vanilla TypeScript examples and a React and a Vue example. It is not well documented yet, I'll be spending my last planned 1.5 days to fix this in the next 2 weeks, and will also change the example included to not include random numbers so that they are deterministic and I can use playwright screenshot tests in CI.

If you want to try them out before I have updated the documentation, the best approach is to download the 2 artifacts from the latest CI run (https://github.com/bokeh/bokehjs-examples/actions/runs/14555646830). The bokehjs-artifact contains the npm package of this branch, and the test-artifact contains the various auto-generated recipes. If you unzip the latter, cd typescript/vanilla_rspack for example and you shouid be able to change the path to @bokeh/bokehjs in the package.json, then perhaps

rm -rf node_modules
npm install
npm run build
npm run serve

to see it working.

@ianthomas23 ianthomas23 force-pushed the ianthomas23/13732_maybe_initialize branch 2 times, most recently from c40c1b0 to 98f426f Compare April 25, 2025 14:06
@ianthomas23
Copy link
Member Author

There were two bokehjs integration tests that were failing, but now pass. The tests were regressions for issues #13104 and #13771 that defined new figure classes derived from the Figure class in api/figure.ts. This PR does not support subclassing that Figure class as it modifies the attrs passed to it above here

super({...attrs, x_range, y_range, x_scale, y_scale})
which means that initialising the properties in the final concrete subclass does not use the modified attrs.

However, the new Figure classes were not key to their tests, they were just ways to obtain different behaviour to test the correct bugs, and I have rewritten those tests to remove the new classes and instead hot-swap the relevant attributes or functions.

@ianthomas23 ianthomas23 force-pushed the ianthomas23/13732_maybe_initialize branch from c2e996f to df95ad5 Compare May 16, 2025 07:09
@bryevdv
Copy link
Member

bryevdv commented Sep 16, 2025

@mattpap what shall we do here for 4.0? I believe you had mentioned an alternate approach but unless it's something that can be put forward sooner rather than later, or can avoid the API change entirely, then I think we have to consider that something is better than nothing in terms of making BokehJS more widely adoptable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Problems initializing Models using BokehJS without Python
3 participants