Custom model not found when used with bokeh client pull-session

BACKGROUND: I have an application that embeds a bokeh server (actually a panel server in case this detail becomes important) in a Flask app run via gunicorn.

Everything works as expected when serving the application on a self-managed Linux or OSX system. Recent steps have demonstrated the feasibility of deploying this to Heroku by reorganizing the implementation to run on two dynos that respectively run the Flask app and the bokeh/panel server that Flask embeds. See Bokeh Discourse Topic 6199.

ISSUE: My actual application implements a custom Div model using TypeScript, the purpose of which is to isolate the HTML div from triggering a re-layout when a user interacts with the app via sliders and other widgets. See Bokeh Discourse Topic 5119.

This custom model generates a model-not-found error in the Flask app when the code is reorganized into two separate applications. The Flask app is now getting the server document via Bokeh client pull_session().

A stack trace of the error follows. NB: I tried two additional things to remedy the problem without success (1) including the custom div model code in the Flask app directory and importing it; and (2) attempting to create a dummy IsoDiv() model in the Flask app in the hopes this would make the model able to be found/recognized.

The second modification resulted in a different error related to panel.markup.HTML, which I assume is part of the scaffolding for div.

Versions: Bokeh 2.2.0, Panel 0.9.7, Python 3.8.3

ERROR:main:Exception on /demo [GET]
Traceback (most recent call last):
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/flask_login/utils.py", line 272, in decorated_view
    return func(*args, **kwargs)
  File "/Users/x/Desktop/my_flask/main.py", line 246, in demo
    with pull_session(url=MY_PANEL_SERVER_URL, arguments=arguments) as session:
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/bokeh/client/session.py", line 120, in pull_session
    session.pull()
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/bokeh/client/session.py", line 387, in pull
    self._connection.pull_doc(doc)
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/bokeh/client/connection.py", line 199, in pull_doc
    reply.push_to_document(document)
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/bokeh/protocol/messages/pull_doc_reply.py", line 83, in push_to_document
    doc.replace_with_json(self.content['doc'])
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/bokeh/document/document.py", line 832, in replace_with_json
    replacement = self.from_json(json)
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/bokeh/document/document.py", line 555, in from_json
    references = instantiate_references_json(references_json, {})
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/bokeh/document/util.py", line 112, in instantiate_references_json
    cls = get_class(obj_type)
  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/bokeh/model.py", line 147, in get_class
    raise KeyError("View model name '%s' not found" % view_model_name)
KeyError: "View model name 'IsoDiv' not found"

@_jm Your Flask app will need to import the custom extension somewhere. Bokeh models get registered when their class definition is executed, so if the process that is calling pull_session has never imported the extension, it won’t know anything about it or that it exists.

1 Like

Thanks for the quick reply. Unfortunately, I had tried that; this was one of the steps I tried upon receiving the error and it generated a separate error. (I prefaced the discussion with the point that I am using a panel server in case it is specific to panel and not bokeh because of this error.)

  File "/Users/x/opt/anaconda3/envs/heroku/lib/python3.8/site-packages/bokeh/model.py", line 147, in get_class
    raise KeyError("View model name '%s' not found" % view_model_name)
KeyError: "View model name 'panel.models.markup.HTML' not found"

It works if I additionally import panel in the Flask app.

Thanks.

Right, if an external session form a separate process uses anything at all that is not part of “built-in” Bokeh, then those same things will need to be explicitly imported in order for another new process to be able to de-serialize the session.