-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Problems initializing Models using BokehJS without Python #13732
Comments
This is a known problem, I think, from the very beginning, though we don't have prior issues open for this. This hasn't been fixed for so long, because of the strong bias towards initialization from JSON, which implies deferred initialization and is handled at the
Both have to be done after properties are initialized, because properties are initialized all at once in Having This would be very easy to resolve if JavaScript allowed static callable signatures for classes. It kind of supports this, but only for the old-style "classes", not the new-style that use A different and more practical approach I've been considering, is to |
Thanks for the clarification Mateusz. I like the solution in your last paragraph. I would happily work on this, but I understand from the notes of this week's meeting (which unfortunately I couldn't attend as I was on a delayed flight) that you've already started working on it? |
I've looked at the idea of moving the bokeh/bokehjs/src/lib/models/ranges/range1d.ts Lines 22 to 34 in 04970ea
But this is called during |
I have been experimenting using BokehJS without Python in TypeScript projects using webpack, React, etc, and as Jupyter extensions. Here is a demo of a WIP frontend-only Jupyter extension using BokehJS:
jupyter_bokehjs.webm
Note that I am talking about explicitly creating BokehJS objects in the browser rather than serving Bokeh documents via say Flask and the BokehJS code decoding the JSON received from the server and using
embed
. And I am talking about using BokehJS's TypeScript code rather than just JavaScript which can be achieved more simply using the minified bundles from CDN. I am using the BokehJS 3.4.0 dev/RC NPM packages as they have the dependencies specified which is really helpful.There is a problem with the design of the class hierarchy of BokehJS for this use case which I would like to fix. The
HasProps
constructor, under certain circumstances, callsfinalize()
herebokeh/bokehjs/src/lib/core/has_props.ts
Line 340 in b0a6b98
which calls
initialize()
bokeh/bokehjs/src/lib/core/has_props.ts
Line 366 in b0a6b98
which is overridden in various derived classes such as
ColumnarDataSource
bokeh/bokehjs/src/lib/models/sources/columnar_data_source.ts
Lines 72 to 76 in b0a6b98
and here initialises two member variables.
So we have a derived class constructor that calls a base class constructor that calls a derived class function to initialise derived class member variables. Calling an overridden function from a base class constructor is considered bad practice as the member variables we are trying to initialise have not necessarily been allocated yet. Here is some simple TypeScript code to illustrate the scenario:
In some situations I have tried, this code works as expected. But in others
variable
is set to 23 but thevariable
that clients can access remainsundefined
. This is pretty much a deal-breaker for my various experiments.In most Bokeh usage this is not a problem as the
HasProps
initialization is deferred if we are decoding a JSON document, by which time we are outside of the object constructors and all member variables have been allocated. When running the BokehJS test suite locally and in CI is does appear to be a problem either. I do not understand this, but it gives me hope that there might be some TS compiler options that give a reliable solution.I have created a little repo https://github.com/ianthomas23/bokehjs-expts to demonstrate. The
webpack-simple
directory uses theDerived
andBase
code from above. It is possible to avoid the problem here by targetting a different ES version as mentioned in the various READMEs. Thewebpack-bokehjs
directory is a BokehJS example which you can get part-working by forcing theCDS
to be initialized again but eventually you hit the problem asDataRange1d
has similar problematic initialization of member variables. Here it cannot be worked around using a different ES version.If we had to redesign the code to avoid calling overridden functions in base class constructors, how would we do it?
Option 1
If a
Derived.initialize
needs to initialize member variables, call it fromDerived.constructor
notBase.constructor
. To do this we would have to preventBase.constructor
from calling it first. The only communication betweenDerived
andBase
here is via constructor arguments, so we'd have to have adefer_initialization
boolean passed down the constructor hierarchy. This is probably not acceptable as the argument would become part of the public API when it should be an internal implementation detail.Option 2
Add a global (?) flag for deferring all initialization which is explicitly set in client code. We would have to temporarily store a sequence of all objects created this way so that when
show
or whatever is called, objects are popped off it and initialized then. This would be fine for single-shot plots but if we are doing clever stuff with callbacks to create new objects we might have to perform the "check and initialize sequence" in a number of places.Option 3
Move the initialization of member variables outside of the
initialize()
functions. This effectively means we no longer overrideHasProps.initialize()
. Those member variables would have to be initialized some other way, such as when they are first needed.DataRange1d
has 8 member variables so maybe we'd have to have a single sentinel boolean for all of them and initialise them together.Option 4
Add an extra level of indirection in constructors so that when we construct an object is has the chance to construct its underlying implementation object and initialise it outside of its own constructor. This would mean lots of boilerplate code because the class the client uses, which is really a proxy for the underlying implementation class, would need to expose the same interface. Probably only really possible using some automatic code generation tools and would be pretty invasive.
It would be great if someone else could take a look at this and see if my analysis make sense or if I have made some fundamental error in understanding the situation.
The text was updated successfully, but these errors were encountered: