@@ -1309,259 +1309,130 @@ def long_callback(
1309
1309
)
1310
1310
1311
1311
# pylint: disable=R0915
1312
- async def async_dispatch (self ):
1313
- body = flask .request .get_json ()
1314
-
1312
+ def _initialize_context (self , body ):
1313
+ """Initialize the global context for the request."""
1315
1314
g = AttributeDict ({})
1316
-
1317
- g .inputs_list = inputs = body .get ( # pylint: disable=assigning-non-slot
1318
- "inputs" , []
1319
- )
1320
- g .states_list = state = body .get ( # pylint: disable=assigning-non-slot
1321
- "state" , []
1322
- )
1323
- output = body ["output" ]
1324
- outputs_list = body .get ("outputs" )
1325
- g .outputs_list = outputs_list # pylint: disable=assigning-non-slot
1326
-
1327
- g .input_values = ( # pylint: disable=assigning-non-slot
1328
- input_values
1329
- ) = inputs_to_dict (inputs )
1330
- g .state_values = inputs_to_dict (state ) # pylint: disable=assigning-non-slot
1331
- changed_props = body .get ("changedPropIds" , [])
1332
- g .triggered_inputs = [ # pylint: disable=assigning-non-slot
1333
- {"prop_id" : x , "value" : input_values .get (x )} for x in changed_props
1315
+ g .inputs_list = body .get ("inputs" , [])
1316
+ g .states_list = body .get ("state" , [])
1317
+ g .outputs_list = body .get ("outputs" , [])
1318
+ g .input_values = inputs_to_dict (g .inputs_list )
1319
+ g .state_values = inputs_to_dict (g .states_list )
1320
+ g .triggered_inputs = [
1321
+ {"prop_id" : x , "value" : g .input_values .get (x )}
1322
+ for x in body .get ("changedPropIds" , [])
1334
1323
]
1335
-
1336
- response = (
1337
- g .dash_response # pylint: disable=assigning-non-slot
1338
- ) = flask .Response (mimetype = "application/json" )
1339
-
1340
- args = inputs_to_vals (inputs + state )
1341
-
1324
+ g .dash_response = flask .Response (mimetype = "application/json" )
1325
+ g .cookies = dict (** flask .request .cookies )
1326
+ g .headers = dict (** flask .request .headers )
1327
+ g .path = flask .request .full_path
1328
+ g .remote = flask .request .remote_addr
1329
+ g .origin = flask .request .origin
1330
+ g .updated_props = {}
1331
+ return g
1332
+
1333
+ def _prepare_callback (self , g , body ):
1334
+ """Prepare callback-related data."""
1335
+ output = body ["output" ]
1342
1336
try :
1343
1337
cb = self .callback_map [output ]
1344
1338
func = cb ["callback" ]
1345
- g .background_callback_manager = (
1346
- cb .get ("manager" ) or self ._background_manager
1347
- )
1339
+ g .background_callback_manager = cb .get ("manager" ) or self ._background_manager
1348
1340
g .ignore_register_page = cb .get ("long" , False )
1349
1341
1350
1342
# Add args_grouping
1351
1343
inputs_state_indices = cb ["inputs_state_indices" ]
1352
- inputs_state = inputs + state
1353
- inputs_state = convert_to_AttributeDict (inputs_state )
1344
+ inputs_state = convert_to_AttributeDict (g .inputs_list + g .states_list )
1354
1345
1355
- if cb .get ("no_output" ):
1356
- outputs_list = []
1357
- elif not outputs_list :
1358
- # FIXME Old renderer support?
1346
+ # Legacy support for older renderers
1347
+ if not g .outputs_list :
1359
1348
split_callback_id (output )
1360
1349
1361
- # update args_grouping attributes
1350
+ # Update args_grouping attributes
1362
1351
for s in inputs_state :
1363
1352
# check for pattern matching: list of inputs or state
1364
1353
if isinstance (s , list ):
1365
1354
for pattern_match_g in s :
1366
- update_args_group (pattern_match_g , changed_props )
1367
- update_args_group (s , changed_props )
1355
+ update_args_group (pattern_match_g , body . get ( "changedPropIds" , []) )
1356
+ update_args_group (s , body . get ( "changedPropIds" , []) )
1368
1357
1369
- args_grouping = map_grouping (
1370
- lambda ind : inputs_state [ ind ] , inputs_state_indices
1358
+ g . args_grouping , g . using_args_grouping = self . _prepare_grouping (
1359
+ inputs_state , inputs_state_indices
1371
1360
)
1372
-
1373
- g .args_grouping = args_grouping # pylint: disable=assigning-non-slot
1374
- g .using_args_grouping = ( # pylint: disable=assigning-non-slot
1375
- not isinstance (inputs_state_indices , int )
1376
- and (
1377
- inputs_state_indices
1378
- != list (range (grouping_len (inputs_state_indices )))
1379
- )
1361
+ g .outputs_grouping , g .using_outputs_grouping = self ._prepare_grouping (
1362
+ g .outputs_list , cb .get ("outputs_indices" , [])
1380
1363
)
1364
+ except KeyError as e :
1365
+ raise KeyError (f"Callback function not found for output '{ output } '." ) from e
1366
+ return func
1367
+
1368
+ def _prepare_grouping (self , data_list , indices ):
1369
+ """Prepare grouping logic for inputs or outputs."""
1370
+ if not isinstance (data_list , list ):
1371
+ flat_data = [data_list ]
1372
+ else :
1373
+ flat_data = data_list
1381
1374
1382
- # Add outputs_grouping
1383
- outputs_indices = cb ["outputs_indices" ]
1384
- if not isinstance (outputs_list , list ):
1385
- flat_outputs = [outputs_list ]
1386
- else :
1387
- flat_outputs = outputs_list
1388
-
1389
- if len (flat_outputs ) > 0 :
1390
- outputs_grouping = map_grouping (
1391
- lambda ind : flat_outputs [ind ], outputs_indices
1392
- )
1393
- g .outputs_grouping = (
1394
- outputs_grouping # pylint: disable=assigning-non-slot
1395
- )
1396
- g .using_outputs_grouping = ( # pylint: disable=assigning-non-slot
1397
- not isinstance (outputs_indices , int )
1398
- and outputs_indices != list (range (grouping_len (outputs_indices )))
1399
- )
1400
- else :
1401
- g .outputs_grouping = []
1402
- g .using_outputs_grouping = []
1403
- g .updated_props = {}
1404
-
1405
- g .cookies = dict (** flask .request .cookies )
1406
- g .headers = dict (** flask .request .headers )
1407
- g .path = flask .request .full_path
1408
- g .remote = flask .request .remote_addr
1409
- g .origin = flask .request .origin
1375
+ if len (flat_data ) > 0 :
1376
+ grouping = map_grouping (lambda ind : flat_data [ind ], indices )
1377
+ using_grouping = not isinstance (indices , int ) and indices != list (range (grouping_len (indices )))
1378
+ else :
1379
+ grouping , using_grouping = [], False
1410
1380
1411
- except KeyError as missing_callback_function :
1412
- msg = f"Callback function not found for output '{ output } ', perhaps you forgot to prepend the '@'?"
1413
- raise KeyError (msg ) from missing_callback_function
1381
+ return grouping , using_grouping
1414
1382
1415
- ctx = copy_context ()
1416
- # Create a partial function with the necessary arguments
1383
+ def _execute_callback ( self , func , args , outputs_list , g ):
1384
+ """Execute the callback with the prepared arguments."""
1417
1385
# noinspection PyArgumentList
1418
1386
partial_func = functools .partial (
1419
- execute_async_function ,
1420
1387
func ,
1421
1388
* args ,
1422
1389
outputs_list = outputs_list ,
1423
- long_callback_manager = self . _background_manager ,
1390
+ long_callback_manager = g . background_callback_manager ,
1424
1391
callback_context = g ,
1425
1392
app = self ,
1426
1393
app_on_error = self ._on_error ,
1427
1394
app_use_async = self ._use_async ,
1428
1395
)
1396
+ return partial_func
1397
+
1398
+ async def async_dispatch (self ):
1399
+ body = flask .request .get_json ()
1400
+ g = self ._initialize_context (body )
1401
+ func = self ._prepare_callback (g , body )
1402
+ args = inputs_to_vals (g .inputs_list + g .states_list )
1429
1403
1430
- response_data = await ctx .run (partial_func )
1404
+ ctx = copy_context ()
1405
+ partial_func = self ._execute_callback (func , args , g .outputs_list , g )
1406
+ if asyncio .iscoroutine (func ):
1407
+ response_data = await ctx .run (partial_func )
1408
+ else :
1409
+ response_data = ctx .run (partial_func )
1431
1410
1432
- # Check if the response is a coroutine
1433
1411
if asyncio .iscoroutine (response_data ):
1434
1412
response_data = await response_data
1435
1413
1436
- response .set_data (response_data )
1437
- return response
1414
+ g . dash_response .set_data (response_data )
1415
+ return g . dash_response
1438
1416
1439
1417
def dispatch (self ):
1440
1418
body = flask .request .get_json ()
1441
-
1442
- g = AttributeDict ({})
1443
-
1444
- g .inputs_list = inputs = body .get ( # pylint: disable=assigning-non-slot
1445
- "inputs" , []
1446
- )
1447
- g .states_list = state = body .get ( # pylint: disable=assigning-non-slot
1448
- "state" , []
1449
- )
1450
- output = body ["output" ]
1451
- outputs_list = body .get ("outputs" )
1452
- g .outputs_list = outputs_list # pylint: disable=assigning-non-slot
1453
-
1454
- g .input_values = ( # pylint: disable=assigning-non-slot
1455
- input_values
1456
- ) = inputs_to_dict (inputs )
1457
- g .state_values = inputs_to_dict (state ) # pylint: disable=assigning-non-slot
1458
- changed_props = body .get ("changedPropIds" , [])
1459
- g .triggered_inputs = [ # pylint: disable=assigning-non-slot
1460
- {"prop_id" : x , "value" : input_values .get (x )} for x in changed_props
1461
- ]
1462
-
1463
- response = (
1464
- g .dash_response # pylint: disable=assigning-non-slot
1465
- ) = flask .Response (mimetype = "application/json" )
1466
-
1467
- args = inputs_to_vals (inputs + state )
1468
-
1469
- try :
1470
- cb = self .callback_map [output ]
1471
- func = cb ["callback" ]
1472
- g .background_callback_manager = (
1473
- cb .get ("manager" ) or self ._background_manager
1474
- )
1475
- g .ignore_register_page = cb .get ("long" , False )
1476
-
1477
- # Add args_grouping
1478
- inputs_state_indices = cb ["inputs_state_indices" ]
1479
- inputs_state = inputs + state
1480
- inputs_state = convert_to_AttributeDict (inputs_state )
1481
-
1482
- if cb .get ("no_output" ):
1483
- outputs_list = []
1484
- elif not outputs_list :
1485
- # FIXME Old renderer support?
1486
- split_callback_id (output )
1487
-
1488
- # update args_grouping attributes
1489
- for s in inputs_state :
1490
- # check for pattern matching: list of inputs or state
1491
- if isinstance (s , list ):
1492
- for pattern_match_g in s :
1493
- update_args_group (pattern_match_g , changed_props )
1494
- update_args_group (s , changed_props )
1495
-
1496
- args_grouping = map_grouping (
1497
- lambda ind : inputs_state [ind ], inputs_state_indices
1498
- )
1499
-
1500
- g .args_grouping = args_grouping # pylint: disable=assigning-non-slot
1501
- g .using_args_grouping = ( # pylint: disable=assigning-non-slot
1502
- not isinstance (inputs_state_indices , int )
1503
- and (
1504
- inputs_state_indices
1505
- != list (range (grouping_len (inputs_state_indices )))
1506
- )
1507
- )
1508
-
1509
- # Add outputs_grouping
1510
- outputs_indices = cb ["outputs_indices" ]
1511
- if not isinstance (outputs_list , list ):
1512
- flat_outputs = [outputs_list ]
1513
- else :
1514
- flat_outputs = outputs_list
1515
-
1516
- if len (flat_outputs ) > 0 :
1517
- outputs_grouping = map_grouping (
1518
- lambda ind : flat_outputs [ind ], outputs_indices
1519
- )
1520
- g .outputs_grouping = (
1521
- outputs_grouping # pylint: disable=assigning-non-slot
1522
- )
1523
- g .using_outputs_grouping = ( # pylint: disable=assigning-non-slot
1524
- not isinstance (outputs_indices , int )
1525
- and outputs_indices != list (range (grouping_len (outputs_indices )))
1526
- )
1527
- else :
1528
- g .outputs_grouping = []
1529
- g .using_outputs_grouping = []
1530
- g .updated_props = {}
1531
-
1532
- g .cookies = dict (** flask .request .cookies )
1533
- g .headers = dict (** flask .request .headers )
1534
- g .path = flask .request .full_path
1535
- g .remote = flask .request .remote_addr
1536
- g .origin = flask .request .origin
1537
-
1538
- except KeyError as missing_callback_function :
1539
- msg = f"Callback function not found for output '{ output } ', perhaps you forgot to prepend the '@'?"
1540
- raise KeyError (msg ) from missing_callback_function
1419
+ g = self ._initialize_context (body )
1420
+ func = self ._prepare_callback (g , body )
1421
+ args = inputs_to_vals (g .inputs_list + g .states_list )
1541
1422
1542
1423
ctx = copy_context ()
1543
- # Create a partial function with the necessary arguments
1544
- # noinspection PyArgumentList
1545
- partial_func = functools .partial (
1546
- func ,
1547
- * args ,
1548
- outputs_list = outputs_list ,
1549
- long_callback_manager = self ._background_manager ,
1550
- callback_context = g ,
1551
- app = self ,
1552
- app_on_error = self ._on_error ,
1553
- app_use_async = self ._use_async ,
1554
- )
1555
-
1424
+ partial_func = self ._execute_callback (func , args , g .outputs_list , g )
1556
1425
response_data = ctx .run (partial_func )
1557
1426
1558
1427
if asyncio .iscoroutine (response_data ):
1559
1428
raise Exception (
1560
- "You are trying to use a coroutine without dash[async], please install the dependencies via `pip install dash[async]` and make sure you arent passing `use_async=False` to the app."
1429
+ "You are trying to use a coroutine without dash[async]. "
1430
+ "Please install the dependencies via `pip install dash[async]` and ensure "
1431
+ "that `use_async=False` is not being passed to the app."
1561
1432
)
1562
1433
1563
- response .set_data (response_data )
1564
- return response
1434
+ g . dash_response .set_data (response_data )
1435
+ return g . dash_response
1565
1436
1566
1437
def _setup_server (self ):
1567
1438
if self ._got_first_request ["setup_server" ]:
0 commit comments