Hi,
I have a question about handling exceptions in a Bokeh application. We have a “bokeh serve” application which, if it has an unhandled exception, prevents all later sessions from working. This is different from other frameworks, in which a request exception affects only that request, but the application can continue to serve traffic after an exception.
This is the same issue raised in https://groups.google.com/a/continuum.io/d/topic/bokeh/nqiMn756zq0/discussion, in which Bryan said the described behaviour was unexpected. But no example code was produced. So I will produce some, copying from Bokeh server — Bokeh 3.3.2 Documentation
myapp.py
from random import random
from bokeh.layouts import column
from bokeh.models import Button
from bokeh.palettes import RdYlBu3
from bokeh.plotting import figure, curdoc
create a plot and style its properties
p = figure(x_range=(0, 100), y_range=(0, 100), toolbar_location=None)
p.border_fill_color = ‘black’
p.background_fill_color = ‘black’
p.outline_line_color = None
p.grid.grid_line_color = None
add a text renderer to our plot (no data yet)
r = p.text(x=, y=, text=, text_color=, text_font_size=“20pt”,
text_baseline="middle", text_align="center")
i = 0
ds = r.data_source
create a callback that will add a number in a random location
def callback():
global i
# BEST PRACTICE --- update .data in one step with a new dict
new_data = dict()
new_data['x'] = ds.data['x'] + [random()*70 + 15]
new_data['y'] = ds.data['y'] + [random()*70 + 15]
new_data['text_color'] = ds.data['text_color'] + [RdYlBu3[i%3]]
new_data['text'] = ds.data['text'] + [str(i)]
ds.data = new_data
i = i + 1
add a button widget and configure with the call back
button = Button(label=“Press Me”)
button.on_click(callback)
boom = random()
print(boom)
if boom < 0.3333:
raise Exception(‘boom!’)
put the button and plot in a layout and add to the document
curdoc().add_root(column(button, p))
This is exactly the same code as from the user guide, except that 33% of the time it will raise an exception.
To duplicate the issue, do:
-
Run “bokeh serve myapp.py”
-
Open a browser to http://localhost:5006/boom. If you don’t see “Exception: boom!” in the console, you can click the “Press Me” button to see numbers appear.
-
Refresh the page until you see “Exception: boom!” in the console. The Bokeh app won’t appear in the browser.
-
Refresh the page again. The app still doesn’t appear.
-
Restart the server and refresh the page. If the exception isn’t raised, the app will load. It will work until a new session triggers the exception.
This makes it difficult to run a reliable Bokeh service, because crashes aren’t isolated to the request that caused the crash.
I’m not familiar with Bokeh internals, but if I had to guess, could this be due to how Applications seem to use a single ScriptHandler/DirectoryHandler (not one per session), which has a failed attribute that prevents the Bokeh user code from running when the attribute is set? See https://github.com/bokeh/bokeh/blob/master/bokeh/application/handlers/code.py#L138
If this is unexpected behaviour, I’ll make a GitHub issue, but some direction on a workaround would be appreciated.
I’d also like to capture exceptions and report to Sentry.io, but that seems to be closely related, so I’ll wait for the resolution of this.
Thanks!
Matt Fox