-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Add support for custom error handling in Dash #2829
Conversation
…r universal error handling of callbacks.
@BSd3v could you add an example of using this new |
Here is an example app: from dash import *
from dash._utils import to_json
import traceback
from dash import ctx
from dash.exceptions import PreventUpdate
import json
def callback_fallback(output=None):
error_message = "error in callback"
def decorator(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
if str(e) == "":
raise PreventUpdate
alertError(f"{error_message}", f"{output}\n {traceback.format_exc()}")
resp = {
"app_notification": {
"children": json.loads(
to_json(
notification(
"error", "there was an issue, IT has been notified", ctx
)
)
)
}
}
return json.dumps({"multi": True, "response": resp})
return wrapper
return decorator
def callback_fallback2(output=None):
error_message = "error in callback"
def decorator(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
if str(e) == "":
raise PreventUpdate
alertError(f"{error_message}", f"{output}\n {traceback.format_exc()}")
resp = {
"app_notification": {
"children": json.loads(
to_json(
notification(
"error", "I'm sorry Dave, I'm afraid I can't do that", ctx
)
)
)
}
}
return json.dumps({"multi": True, "response": resp})
return wrapper
return decorator
app = Dash(
__name__,
callback_fallback=callback_fallback,
)
def notification(type, msg, ctx=None):
if type == 'error':
return html.Div(
children=msg + (f' - {ctx.triggered[0]["value"]}' if ctx else ''),
style={'color': 'red'}
)
return ''
def alertError(subject, message):
print(subject)
print(message)
pass
@callback(
Output("children", "children"),
Input("click", "n_clicks"),
State("testChecked", "value"),
prevent_initial_call=True,
)
def partialFailingCall(n, c):
if c:
return rawr
return f"I ran properly - {n}"
@callback(
Output("children2", "children"),
Input("click2", "n_clicks"),
State("testChecked2", "value"),
callback_fallback=callback_fallback2,
prevent_initial_call=True,
)
def partialFailingCall(n, c):
if not c:
return rawr
return f"I ran properly - {n}"
app.layout = html.Div(
children=[html.Div(id="app_notification"),
html.Div([
html.Div("When checked, the callback will fail"),
html.Button("test callback", id="click"),
dcc.Checklist([True], id="testChecked"),
html.Div(id="children")
]
),
html.Div([
html.Div("When not checked, the callback will fail, callback handler is different"),
html.Button("test callback", id="click2"),
dcc.Checklist([True], id="testChecked2"),
html.Div(id="children2")
]
)
]
)
app.run(debug=True) Notice that in the second callback I am overriding the default callback_fallback in the callback definition. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this pattern is a bit confusing. We're passing a decorator as an argument: With this API I would expect to pass a function as an argument. I guess in general I feel like there's still some API design to be done here.
What is the ideal way to pass in an error fallback for a Dash callback? It's not immediately obvious to me, but I think we can definitely simplify the pattern from having to pass a decorator. @T4rk1n might have some other comments... but we might want to spend some time in an issue ticket discussing this feature more broadly before starting implementation?
So just pass something like this instead? |
Maybe, but I'd want to get to something even simpler to use. @T4rk1n to open a ticket describing the feature and we can hone in on a spec from there I think makes the most sense. |
callback_fallback
Here's another use-case for this feature from the forum: |
Closed with this PR: #2903 |
implementing
callback_fallback
onDash
andcallback
to allow for universal error handling of callbacks.This manipulates a new variable in
_callback
GLOBAL_CALLBACK_FALLBACK
in order to pass the configuration to all other callbacks from theDash()
registrationsee here for a forum post demonstrating this functionality:
https://community.plotly.com/t/error-handling-for-callbacks-and-layouts/83586/2