Skip to content

Commit 337ec26

Browse files
committed
attempt no 1 for refactoring dash for dispatch
-- failing lint on purpose for test
1 parent 76a9a56 commit 337ec26

File tree

1 file changed

+76
-205
lines changed

1 file changed

+76
-205
lines changed

dash/dash.py

Lines changed: 76 additions & 205 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,259 +1309,130 @@ def long_callback(
13091309
)
13101310

13111311
# 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."""
13151314
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", [])
13341323
]
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"]
13421336
try:
13431337
cb = self.callback_map[output]
13441338
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
13481340
g.ignore_register_page = cb.get("long", False)
13491341

13501342
# Add args_grouping
13511343
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)
13541345

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:
13591348
split_callback_id(output)
13601349

1361-
# update args_grouping attributes
1350+
# Update args_grouping attributes
13621351
for s in inputs_state:
13631352
# check for pattern matching: list of inputs or state
13641353
if isinstance(s, list):
13651354
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", []))
13681357

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
13711360
)
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", [])
13801363
)
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
13811374

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
14101380

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
14141382

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."""
14171385
# noinspection PyArgumentList
14181386
partial_func = functools.partial(
1419-
execute_async_function,
14201387
func,
14211388
*args,
14221389
outputs_list=outputs_list,
1423-
long_callback_manager=self._background_manager,
1390+
long_callback_manager=g.background_callback_manager,
14241391
callback_context=g,
14251392
app=self,
14261393
app_on_error=self._on_error,
14271394
app_use_async=self._use_async,
14281395
)
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)
14291403

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)
14311410

1432-
# Check if the response is a coroutine
14331411
if asyncio.iscoroutine(response_data):
14341412
response_data = await response_data
14351413

1436-
response.set_data(response_data)
1437-
return response
1414+
g.dash_response.set_data(response_data)
1415+
return g.dash_response
14381416

14391417
def dispatch(self):
14401418
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)
15411422

15421423
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)
15561425
response_data = ctx.run(partial_func)
15571426

15581427
if asyncio.iscoroutine(response_data):
15591428
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."
15611432
)
15621433

1563-
response.set_data(response_data)
1564-
return response
1434+
g.dash_response.set_data(response_data)
1435+
return g.dash_response
15651436

15661437
def _setup_server(self):
15671438
if self._got_first_request["setup_server"]:

0 commit comments

Comments
 (0)