1
1
import collections
2
2
import hashlib
3
3
from functools import wraps
4
+ from typing import Callable , Optional , Any
4
5
5
6
import flask
6
7
@@ -67,6 +68,7 @@ def callback(
67
68
cancel = None ,
68
69
manager = None ,
69
70
cache_args_to_ignore = None ,
71
+ on_error : Optional [Callable [[Exception ], Any ]] = None ,
70
72
** _kwargs ,
71
73
):
72
74
"""
@@ -137,6 +139,10 @@ def callback(
137
139
this should be a list of argument indices as integers.
138
140
:param interval:
139
141
Time to wait between the long callback update requests.
142
+ :param on_error:
143
+ Function to call when the callback raises an exception. Receives the
144
+ exception object as first argument. The callback_context can be used
145
+ to access the original callback inputs, states and output.
140
146
"""
141
147
142
148
long_spec = None
@@ -186,6 +192,7 @@ def callback(
186
192
long = long_spec ,
187
193
manager = manager ,
188
194
running = running ,
195
+ on_error = on_error ,
189
196
)
190
197
191
198
@@ -272,8 +279,8 @@ def insert_callback(
272
279
return callback_id
273
280
274
281
275
- # pylint: disable=R0912, R0915
276
- def register_callback ( # pylint: disable=R0914
282
+ # pylint: disable=too-many-branches,too-many-statements
283
+ def register_callback (
277
284
callback_list , callback_map , config_prevent_initial_callbacks , * _args , ** _kwargs
278
285
):
279
286
(
@@ -297,6 +304,7 @@ def register_callback( # pylint: disable=R0914
297
304
long = _kwargs .get ("long" )
298
305
manager = _kwargs .get ("manager" )
299
306
running = _kwargs .get ("running" )
307
+ on_error = _kwargs .get ("on_error" )
300
308
if running is not None :
301
309
if not isinstance (running [0 ], (list , tuple )):
302
310
running = [running ]
@@ -342,6 +350,8 @@ def add_context(*args, **kwargs):
342
350
"callback_context" , AttributeDict ({"updated_props" : {}})
343
351
)
344
352
callback_manager = long and long .get ("manager" , app_callback_manager )
353
+ app_on_error = kwargs .pop ("app_on_error" , None )
354
+
345
355
if has_output :
346
356
_validate .validate_output_spec (insert_output , output_spec , Output )
347
357
@@ -403,6 +413,7 @@ def add_context(*args, **kwargs):
403
413
triggered_inputs = callback_ctx .triggered_inputs ,
404
414
ignore_register_page = True ,
405
415
),
416
+ on_error = on_error or app_on_error ,
406
417
)
407
418
408
419
data = {
@@ -462,7 +473,15 @@ def add_context(*args, **kwargs):
462
473
if output_value is callback_manager .UNDEFINED :
463
474
return to_json (response )
464
475
else :
465
- output_value = _invoke_callback (func , * func_args , ** func_kwargs )
476
+ try :
477
+ output_value = _invoke_callback (func , * func_args , ** func_kwargs )
478
+ except Exception as err : # pylint: disable=broad-exception-caught
479
+ if on_error :
480
+ output_value = on_error (err )
481
+ elif app_on_error :
482
+ output_value = app_on_error (err )
483
+ else :
484
+ raise err
466
485
467
486
if NoUpdate .is_no_update (output_value ):
468
487
raise PreventUpdate
0 commit comments