Access Python REPL/console connected to Bokeh server

I haven’t actually tried to use Bokeh yet, but I’ve been searching through the documentation to make sure what I want is possible.

I would like to have a Bokeh server application with a browser visualization. But on top of that, I want users to be able to open a Python REPL with access to objects created in the server. I think this sounds a lot like using Jupyter, but I don’t want my visualizations to be embedded within Jupyter. It’s not clear to me what this documentation describes, but it sounds like the application is still embedded within a notebook rather than being the full page.

Are there any other alternatives? I thought of running pyqtconsole from my server application. I think that would give me a console. But it would be nice if it were integrated with the rest of Bokeh.

Actually, upon closer examination, maybe I’m wrong about that documentation. The example has

show(bkapp) # notebook_url="http://localhost:8888" 

which implies that the visualizations will be shown in a dedicated window, rather than within Jupyter. That’s what I want. But can you still access the Python REPL after calling show? I presume so. I’ll have to try that example myself.

Nope, it looks like the visualization is embedded within the notebook. Is there a way to spawn a separate server in its own thread or something?

Sure, you can do what this Server API example does, in a notebook:

bokeh/examples/server/api/standalone_embed.py at branch-3.5 · bokeh/bokeh · GitHub

And then navigate a browser to the normal app URL (e.g. localhost:5006 by default).

You’d probably want to just install and start the Bokeh app on the existing Jupyter IOLoop. But it’s probably possible to start things on a new IOLoop in a thread similar to this example if that’s preferable for some reason.

All that said, please note that the possibility for arbitrary, out of order cell (re-)execution in Jupyter [1], combined with two multi-runtime systems interacting at once, makes things very hard to reason about effectively, and easy to break. Case in point, the BokehJS frontend objects are “connected” to specific instances of Bokeh Python objects. If you, e.g. re-evaluate a cell that originally made some Bokeh objects (e.g. data sources or whatever), you will create new instances that will have nothing to do with whatever is currently displayed in the app page. Probably not what you want. It’s for this reason we don’t really demonstrate this kind of usage anywhere at all, and try to steer people away from it.

You might actually want to look at Holoviz Panel which is built on top of Bokeh, but created by folks much more invested in these sorts of notebook use-cases. cc @Philipp_Rudiger @James_A_Bednar1


  1. A terrible mis-feature, IMO ↩︎

Thanks for your reply. I’ll check out the examples you gave.

And your point is well taken about the complexity of the system. But I think what I have in mind would be easy to reason about.

In my vision, the Bokeh pieces of the application are fixed and unchanging. Users would not be creating or modifying Bokeh objects. Rather, they would launch a pre-defined Bokeh server. Users would only have access to tailored API.

I had also not really envisioned using Jupyter notebooks. I was really more focused on an actual console/REPL. Maybe I could just launch the Bokeh IO loop from a normal Python REPL. That cuts out one runtime. I’ll have to dig in to the various options.

Hmm, this is very strange. I tried to run that standalone_embedded example in Jupyter, but it crashed in some way, naming a .cpp file. It also prevented me from cleanly shutting down the Jupyter kernel.

Then separately, if I try to run it in a REPL, creating a Server object does strange and awful things to my REPL. It starts repeating the prompt multiple times per line, and I can’t properly enter text. I have no idea what’s going on here.

>>> server = Server({'/': blah.bkapp}, num_procs=4)
>>> >>> >>> >>> >>> 
>>> >>> >>> >>> e.... Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'vrat' is not defined

Could that be coming from the app code? There is nothing called vrat anywhere in the Bokeh codebase (nor is there any C++ code). Would need more information (really, an actual Minimal Reproducible Example) in order to be able to speculate in any detail.

Sorry, I wasn’t clear. vrat seems to be what the the REPL received, even though I was typing the next line of the script, server.start().

The standalone_embedded example is the minimal example. All I did was try to run it from within a normal Python REPL. The moment you press enter after the line server = Server({'/': bkapp}, num_procs=4), the REPL seems to go crazy, and it won’t let you type text correctly anymore. It also seems to prevent CTRL+D from closing the REPL. It’s all very strange.

In any event, that should probably be a separate bug report, since the example runs fine as a standalone script.

That example starts an IOLoop, which is a blocking operation. You would need to move that off to a thread if you want to be able to continue with any interactivity (in a notebook, you could piggy back of the existing already-running Jupyter event loop, but not here).

Sure, and that’s what I expected. Half the reason I tried the example was to see whether it was blocking or not (I should probably just go read the docs).

But I never got that far. What I’m saying is that merely creating a Server object seems to somehow interfere with the operation of the REPL. I know it sounds weird and doesn’t make any sense, but that’s exactly the problem. The behavior is so strange and unexpected that I can’t even describe it well. I would encourage you to try it on your end. I’m using Python 3.8 on Ubuntu 20.04 if that matters. I will try it on another machine and create a bug report.

are you setting num_procs=4 as in the example code? I see what you describe if I leave that in, but I see no issues if I remove it. I had not noticed the example set num_procs. I would not expect that to behave well in the REPL (and I would not expect we’d try to do anything at all to make it work, if that is even possible, which it probably isn’t).

Yes, I was trying to run the example unmodified. I’m surprised that merely creating the Server object does anything to effect the REPL before calling start(). But if that’s the expected behavior, then I won’t bother with a bug report.

I’m sure I can figure out some solution to my problem with direct access to the IO loop, so I think my problem is solved for now. Thanks