dara-core 1.23.2a1__py3-none-any.whl → 1.24.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39079,8 +39079,6 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39079
39079
  style: Object.assign(Object.assign(Object.assign({}, floatingStyles), { maxHeight: 800, minWidth: 150, zIndex: 9999 }), style)
39080
39080
  }), { isOpen, children: jsxRuntimeExports.jsx(SectionedList, { items: allowColumnHiding ? [resetFunctions, columnToggles] : [resetFunctions], onSelect: onOptionSelect }, isOpen ? "open" : "closed") })), document.body)] });
39081
39081
  };
39082
- const { fontSize } = window.getComputedStyle(document.documentElement);
39083
- const DEFAULT_ROW_HEIGHT = parseFloat(fontSize) * 2.5;
39084
39082
  var __rest$1 = function(s, e2) {
39085
39083
  var t2 = {};
39086
39084
  for (var p2 in s) if (Object.prototype.hasOwnProperty.call(s, p2) && e2.indexOf(p2) < 0)
@@ -39092,6 +39090,8 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39092
39090
  }
39093
39091
  return t2;
39094
39092
  };
39093
+ const { fontSize } = window.getComputedStyle(document.documentElement);
39094
+ const ROW_HEIGHT = parseFloat(fontSize) * 2.5;
39095
39095
  const shouldForwardProp$2 = (prop) => !["isSorted", "onClickRow"].includes(prop);
39096
39096
  const Row = styled.div.withConfig({ shouldForwardProp: shouldForwardProp$2 })`
39097
39097
  cursor: ${(props) => props.onClickRow ? "pointer" : "default"};
@@ -39147,7 +39147,7 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39147
39147
  align-items: center;
39148
39148
 
39149
39149
  min-width: 80px;
39150
- height: ${({ rowHeight }) => `${rowHeight}px`};
39150
+ height: ${() => `${ROW_HEIGHT}px`};
39151
39151
 
39152
39152
  color: ${(props) => props.theme.colors.grey6};
39153
39153
 
@@ -39171,7 +39171,7 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39171
39171
  var _a;
39172
39172
  return areEqual(prevProps, nextProps) && !(((_a = nextProps.data) === null || _a === void 0 ? void 0 : _a.headerGroups) || []).some((headerGroup) => ((headerGroup === null || headerGroup === void 0 ? void 0 : headerGroup.headers) || []).some((header) => header.isResizing));
39173
39173
  };
39174
- const RenderRow = React__namespace.memo(({ data: { width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns, rowHeight }, index: index2, style: renderRowStyle }) => {
39174
+ const RenderRow = React__namespace.memo(({ data: { width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns }, index: index2, style: renderRowStyle }) => {
39175
39175
  let row = rows[index2];
39176
39176
  if (getItem) {
39177
39177
  const value = getItem(index2);
@@ -39184,8 +39184,8 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39184
39184
  }
39185
39185
  if (!row) {
39186
39186
  return jsxRuntimeExports.jsx("div", { children: headerGroups.map((headerGroup, gidx) => jsxRuntimeExports.jsx(RowPlaceholder, { style: {
39187
- height: rowHeight,
39188
- top: (index2 + 1) * rowHeight,
39187
+ height: ROW_HEIGHT,
39188
+ top: (index2 + 1) * ROW_HEIGHT,
39189
39189
  width: totalColumnsWidth > width ? totalColumnsWidth : "100%"
39190
39190
  }, children: headerGroup === null || headerGroup === void 0 ? void 0 : headerGroup.headers.map((col, cidx) => {
39191
39191
  const headerProps = col.getHeaderProps();
@@ -39203,23 +39203,15 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39203
39203
  }
39204
39204
  };
39205
39205
  const _a = row.getRowProps({ style: renderRowStyle }), { style: rowStyle } = _a, restRow = __rest$1(_a, ["style"]);
39206
- return React$1.createElement(Row, Object.assign({}, restRow, { key: `row-${index2}`, onClick, onClickRow, style: Object.assign(Object.assign({}, rowStyle), {
39207
- // The first row is the header row which is not controlled by this rowHeight prop so it needs to be part of the calculation.
39208
- top: index2 === 0 ? DEFAULT_ROW_HEIGHT : index2 * rowHeight + DEFAULT_ROW_HEIGHT,
39209
- width: totalColumnsWidth > width ? totalColumnsWidth : "100%"
39210
- }) }), row.cells.map((cell, colIdx) => {
39211
- var _a2, _b, _c, _d, _e2;
39206
+ return React$1.createElement(Row, Object.assign({}, restRow, { key: `row-${index2}`, onClick, onClickRow, style: Object.assign(Object.assign({}, rowStyle), { top: (index2 + 1) * ROW_HEIGHT, width: totalColumnsWidth > width ? totalColumnsWidth : "100%" }) }), row.cells.map((cell, colIdx) => {
39207
+ var _a2;
39212
39208
  const cellProps = cell.getCellProps();
39213
39209
  return React$1.createElement(
39214
39210
  Cell,
39215
- Object.assign({}, cellProps, { rowHeight, key: `cell-${index2}-${colIdx}`, style: Object.assign(Object.assign(Object.assign(Object.assign({}, cellProps.style), { backgroundColor, justifyContent: mappedColumns[colIdx].align, maxWidth: (_a2 = cell.column) === null || _a2 === void 0 ? void 0 : _a2.maxWidth, width: (
39211
+ Object.assign({}, cellProps, { key: `cell-${index2}-${colIdx}`, style: Object.assign(Object.assign({}, cellProps.style), { backgroundColor, justifyContent: mappedColumns[colIdx].align, maxWidth: (_a2 = cell.column) === null || _a2 === void 0 ? void 0 : _a2.maxWidth, width: (
39216
39212
  // If width calc has messed up then use the raw width from the column
39217
39213
  cellProps.style.width === "NaNpx" ? mappedColumns[colIdx].width : cellProps.style.width
39218
- ) }), ((_b = mappedColumns[colIdx]) === null || _b === void 0 ? void 0 : _b.sticky) === "left" && typeof ((_c = mappedColumns[colIdx]) === null || _c === void 0 ? void 0 : _c.stickyOffset) === "number" ? {
39219
- left: `${mappedColumns[colIdx].stickyOffset}px`
39220
- } : {}), ((_d = mappedColumns[colIdx]) === null || _d === void 0 ? void 0 : _d.sticky) === "right" && typeof ((_e2 = mappedColumns[colIdx]) === null || _e2 === void 0 ? void 0 : _e2.stickyOffset) === "number" ? {
39221
- right: `${mappedColumns[colIdx].stickyOffset}px`
39222
- } : {}) }),
39214
+ ) }) }),
39223
39215
  jsxRuntimeExports.jsx(CellContent, { children: cell.render("Cell", {
39224
39216
  colIdx,
39225
39217
  currentEditCell,
@@ -39300,7 +39292,7 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39300
39292
  justify-content: space-between;
39301
39293
 
39302
39294
  min-width: 80px;
39303
- height: ${DEFAULT_ROW_HEIGHT}px;
39295
+ height: ${ROW_HEIGHT}px;
39304
39296
 
39305
39297
  color: ${(props) => props.theme.colors.text};
39306
39298
 
@@ -39385,26 +39377,6 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39385
39377
  });
39386
39378
  return [...leftStickyCols, ...nonStickyCols, ...rightStickyCols];
39387
39379
  };
39388
- const appendStickyOffsets = (columns) => {
39389
- let leftOffset = 0;
39390
- let rightOffset = 0;
39391
- const rightStickyColumnWidths = columns.filter((col) => col.sticky === "right").slice(1).map((col) => parseInt(col.width) || 150);
39392
- return columns.map((col) => {
39393
- if (col.sticky === "left") {
39394
- const nextCol = Object.assign(Object.assign({}, col), { stickyOffset: leftOffset });
39395
- const width = parseInt(col.width) || 150;
39396
- leftOffset += width;
39397
- return nextCol;
39398
- }
39399
- if (col.sticky === "right") {
39400
- rightOffset = rightStickyColumnWidths.reduce((acc, width) => acc + width, 0);
39401
- const nextCol = Object.assign(Object.assign({}, col), { stickyOffset: rightOffset });
39402
- rightStickyColumnWidths.shift();
39403
- return nextCol;
39404
- }
39405
- return col;
39406
- });
39407
- };
39408
39380
  const filterComponentMap = {
39409
39381
  categorical: CategoricalFilter,
39410
39382
  datetime: DatetimeFilter,
@@ -39422,26 +39394,12 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39422
39394
  return Object.assign(Object.assign({}, col), { Filter: filterComponentMap[col.filter] });
39423
39395
  });
39424
39396
  };
39425
- const createActionColumn = (actions, accessor, sticky, disableSelectAll = false) => {
39426
- const width = actions.includes(Actions.SELECT) ? 52 : actions.length * 24 + 24;
39427
- return {
39428
- Cell: ActionCell,
39429
- Header: actions.includes(Actions.SELECT) && !disableSelectAll ? SelectHeader : "",
39430
- accessor: accessor || "actions",
39431
- actions,
39432
- disableSortBy: true,
39433
- maxWidth: width,
39434
- minWidth: actions.includes(Actions.SELECT) ? 52 : 48,
39435
- sticky: sticky || null,
39436
- width
39437
- };
39438
- };
39439
39397
  const cells = {
39440
39398
  DATETIME: DatetimeCell,
39441
39399
  EDIT_INPUT: EditInputCell,
39442
39400
  EDIT_SELECT: EditSelectCell
39443
39401
  };
39444
- const createItemData$1 = memoizeOne$1((width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns, rowHeight) => ({
39402
+ const createItemData$1 = memoizeOne$1((width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns) => ({
39445
39403
  backgroundColor,
39446
39404
  currentEditCell,
39447
39405
  getItem,
@@ -39450,14 +39408,12 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39450
39408
  onClickRow,
39451
39409
  prepareRow,
39452
39410
  rows,
39453
- rowHeight,
39454
39411
  throttledClickRow,
39455
39412
  totalColumnsWidth,
39456
39413
  width
39457
39414
  }));
39458
- const Table = React$1.forwardRef(({ allowHiding, actions, backgroundColor, className, columns, data: data2, getItem, initialSort = [], itemCount, maxRows, onAction, onChange: onChange2, onClickRow, onItemsRendered, onFilter, onSort, rowHeight, showTableOptions, style, tableOptionsStyle }, ref) => {
39415
+ const Table = React$1.forwardRef(({ allowHiding, backgroundColor, className, columns, data: data2, getItem, initialSort = [], itemCount, maxRows, onAction, onChange: onChange2, onClickRow, onItemsRendered, onFilter, onSort, showTableOptions, style, tableOptionsStyle }, ref) => {
39459
39416
  const [currentSortBy, setCurrentSortBy] = React$1.useState(initialSort);
39460
- const tableRowHeight = rowHeight || DEFAULT_ROW_HEIGHT;
39461
39417
  React$1.useEffect(
39462
39418
  () => {
39463
39419
  setCurrentSortBy(initialSort);
@@ -39487,14 +39443,7 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39487
39443
  throttledSetEditCell([Number(cell[0]), cell[1]]);
39488
39444
  };
39489
39445
  const infiniteData = React$1.useMemo(() => Array(itemCount).fill(0), [itemCount]);
39490
- const mappedColumns = React$1.useMemo(() => {
39491
- let processedColumns = columns;
39492
- if (actions && actions.length > 0) {
39493
- const actionColumn = createActionColumn(actions);
39494
- processedColumns = [...columns, actionColumn];
39495
- }
39496
- return appendStickyOffsets(appendFilterComponents(orderStickyCols(processedColumns)));
39497
- }, [columns, actions]);
39446
+ const mappedColumns = React$1.useMemo(() => appendFilterComponents(orderStickyCols(columns)), [columns]);
39498
39447
  const hasFixedColumns = React$1.useMemo(() => mappedColumns.some((column) => "sticky" in column), [mappedColumns]);
39499
39448
  const totalColumnsWidth = React$1.useMemo(() => mappedColumns.reduce((acc, column) => acc + (parseInt(column.width) || 150), 0), [mappedColumns]);
39500
39449
  const filterTypes = React$1.useMemo(() => ({
@@ -39550,15 +39499,11 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39550
39499
  const showHeaderCellButtonContainer = showSort || showFilter || showOptions;
39551
39500
  return React$1.createElement(
39552
39501
  HeaderCell,
39553
- Object.assign({}, headerProps, { key: `col-${gidx}-${cidx}`, style: Object.assign(Object.assign(Object.assign(Object.assign({}, headerProps.style), {
39502
+ Object.assign({}, headerProps, { key: `col-${gidx}-${cidx}`, style: Object.assign(Object.assign({}, headerProps.style), {
39554
39503
  maxWidth: col.maxWidth,
39555
39504
  // If width calc has messed up then use the raw width from the column
39556
39505
  width: headerProps.style.width === "NaNpx" ? mappedColumns[cidx].width : headerProps.style.width
39557
- }), col.sticky === "left" && typeof col.stickyOffset === "number" ? {
39558
- left: `${col.stickyOffset}px`
39559
- } : {}), col.sticky === "right" && typeof col.stickyOffset === "number" ? {
39560
- right: `${col.stickyOffset}px`
39561
- } : {}) }),
39506
+ }) }),
39562
39507
  jsxRuntimeExports.jsxs(HeaderTooltipContainer, { isPrimitiveHeader: typeof headerContent === "string", children: [jsxRuntimeExports.jsx(HeaderContentWrapper, Object.assign({}, sortProps, { isPrimitiveHeader: typeof headerContent === "string", title: typeof headerContent === "string" ? headerContent : "", children: headerContent })), col.tooltip && jsxRuntimeExports.jsx(Tooltip, { content: col.tooltip, children: jsxRuntimeExports.jsx(TooltipIcon, { icon: faCircleQuestion }) })] }),
39563
39508
  showHeaderCellButtonContainer && jsxRuntimeExports.jsxs(HeaderCellButtonContainer, { children: [jsxRuntimeExports.jsxs(HeaderIconsWrapper, { children: [showSort && jsxRuntimeExports.jsx(HeaderIconWrapper, { children: jsxRuntimeExports.jsx(SortIcon, Object.assign({}, sortProps, { className: "tableSortArrow", icon: getSortIcon(col.isSorted, col.isSortedDesc), isSorted: col.isSorted })) }), showFilter ? jsxRuntimeExports.jsx(FilterContainer, { col }) : null, showOptions && jsxRuntimeExports.jsx(OptionsMenu, { allColumns, allowColumnHiding: allowHiding, numVisibleColumns, resetResizing, setAllFilters, style: tableOptionsStyle })] }), jsxRuntimeExports.jsx(ResizeBorder, Object.assign({}, resizerProps))] })
39564
39509
  );
@@ -39567,15 +39512,28 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
39567
39512
  // eslint-disable-next-line react-hooks/exhaustive-deps
39568
39513
  useDeepCompare([tableProps, totalColumnsWidth, headerGroups])
39569
39514
  );
39570
- return jsxRuntimeExports.jsx(Wrapper$2, Object.assign({}, getTableProps(), { "$hasMaxRows": !!maxRows, className: `${className} ${hasFixedColumns ? "sticky" : ""}`, style: Object.assign({ height: maxRows ? (Math.min(rows.length, maxRows) + 1) * tableRowHeight : "100%" }, style), children: jsxRuntimeExports.jsx(AutoSizer, { children: ({ height, width }) => {
39571
- return jsxRuntimeExports.jsx(StyledFixedSizeList, { height, innerElementType: renderTable, itemCount: itemCount || rows.length, itemData: createItemData$1(width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns, tableRowHeight), itemSize: tableRowHeight, onItemsRendered, style: {
39515
+ return jsxRuntimeExports.jsx(Wrapper$2, Object.assign({}, getTableProps(), { "$hasMaxRows": !!maxRows, className: `${className} ${hasFixedColumns ? "sticky" : ""}`, style: Object.assign({ height: maxRows ? (Math.min(rows.length, maxRows) + 1) * ROW_HEIGHT : "100%" }, style), children: jsxRuntimeExports.jsx(AutoSizer, { children: ({ height, width }) => {
39516
+ return jsxRuntimeExports.jsx(StyledFixedSizeList, { height, innerElementType: renderTable, itemCount: itemCount || rows.length, itemData: createItemData$1(width, currentEditCell, headerGroups, rows, prepareRow, getItem, totalColumnsWidth, onClickRow, throttledClickRow, backgroundColor, mappedColumns), itemSize: ROW_HEIGHT, onItemsRendered, style: {
39572
39517
  overflowX: width < totalColumnsWidth ? "auto" : "hidden",
39573
- overflowY: height < (rows.length + 1) * tableRowHeight ? "auto" : "hidden"
39518
+ overflowY: height < (rows.length + 1) * ROW_HEIGHT ? "auto" : "hidden"
39574
39519
  }, width, children: RenderRow }, "table-list");
39575
39520
  } }) }));
39576
39521
  });
39577
39522
  Table.displayName = "Table";
39578
- Table.ActionColumn = createActionColumn;
39523
+ Table.ActionColumn = (actions, accessor, sticky, disableSelectAll = false) => {
39524
+ const width = actions.includes(Actions.SELECT) ? 52 : actions.length * 24 + 24;
39525
+ return {
39526
+ Cell: ActionCell,
39527
+ Header: actions.includes(Actions.SELECT) && !disableSelectAll ? SelectHeader : "",
39528
+ accessor: accessor || "actions",
39529
+ actions,
39530
+ disableSortBy: true,
39531
+ maxWidth: width,
39532
+ minWidth: actions.includes(Actions.SELECT) ? 52 : 48,
39533
+ sticky: sticky || null,
39534
+ width
39535
+ };
39536
+ };
39579
39537
  Table.Actions = Actions;
39580
39538
  Table.cells = cells;
39581
39539
  styled.div`
@@ -61,6 +61,33 @@ def is_resolved_switch_variable(obj: Any) -> TypeGuard[ResolvedSwitchVariable]:
61
61
  return isinstance(obj, dict) and 'uid' in obj and obj.get('type') == 'switch'
62
62
 
63
63
 
64
+ def _resolve_nested(value: Any, nested: list[str]) -> Any:
65
+ """
66
+ Resolve a nested value from an object using a list of keys.
67
+ This matches the frontend resolveNested function in nested.tsx.
68
+
69
+ :param value: The value to extract nested data from
70
+ :param nested: A list of keys to traverse
71
+ :return: The nested value, or None if the path doesn't exist
72
+ """
73
+ # Nested not provided
74
+ if not nested or len(nested) == 0:
75
+ return value
76
+
77
+ # Not a dict (equivalent to frontend's object check)
78
+ if value is None or not isinstance(value, dict):
79
+ return value
80
+
81
+ result = value
82
+ for key in nested:
83
+ # If the key doesn't exist, return None as we're referring to a path which doesn't exist yet
84
+ if not isinstance(result, dict) or key not in result:
85
+ return None
86
+ result = result[key]
87
+
88
+ return result
89
+
90
+
64
91
  def is_forced(value: Any) -> bool:
65
92
  """
66
93
  Whether a value is a Derived(Data)Variable with a force_key or any of its values are forced
@@ -93,25 +120,33 @@ async def resolve_dependency(
93
120
  entry: ResolvedDerivedVariable | ResolvedSwitchVariable | Any,
94
121
  store: CacheStore,
95
122
  task_mgr: TaskManager,
96
- ):
123
+ ) -> Any:
97
124
  """
98
125
  Resolve an incoming dependency to its value.
99
126
  Handles 'Resolved(Derived)(Data)Variable' structures, and returns the same value for any other input.
127
+ Also handles the 'nested' property to extract nested values from resolved variables.
100
128
 
101
129
  :param entry: dependency entry to resolve
102
130
  :param store: store instance
103
131
  :param task_mgr: task manager instance
104
132
  """
105
- if is_resolved_derived_variable(entry):
106
- return await _resolve_derived_var(entry, store, task_mgr)
133
+ # Extract nested property if present
134
+ nested: list[str] = entry.get('nested', []) if isinstance(entry, dict) else []
107
135
 
108
- if is_resolved_server_variable(entry):
109
- return await _resolve_server_var(entry)
136
+ if is_resolved_derived_variable(entry):
137
+ result = await _resolve_derived_var(entry, store, task_mgr)
138
+ elif is_resolved_server_variable(entry):
139
+ result = await _resolve_server_var(entry)
140
+ elif is_resolved_switch_variable(entry):
141
+ result = await _resolve_switch_var(entry, store, task_mgr)
142
+ else:
143
+ return entry
110
144
 
111
- if is_resolved_switch_variable(entry):
112
- return await _resolve_switch_var(entry, store, task_mgr)
145
+ # Apply nested extraction if present
146
+ if nested:
147
+ result = _resolve_nested(result, nested)
113
148
 
114
- return entry
149
+ return result
115
150
 
116
151
 
117
152
  async def _resolve_derived_var(
@@ -218,7 +253,7 @@ async def _resolve_switch_var(
218
253
  switch_variable_entry: ResolvedSwitchVariable,
219
254
  store: CacheStore,
220
255
  task_mgr: TaskManager,
221
- ):
256
+ ) -> Any:
222
257
  """
223
258
  Resolve a switch variable by evaluating its constituent parts and returning the appropriate value.
224
259
 
@@ -561,6 +561,14 @@ class PyComponentChunk(BaseModel):
561
561
  Chunk = DerivedVariableChunk | PyComponentChunk
562
562
 
563
563
 
564
+ class UnserializablePayloadError(Exception):
565
+ payload_type: type
566
+
567
+ def __init__(self, message: str, payload_type: type):
568
+ super().__init__(message)
569
+ self.payload_type = payload_type
570
+
571
+
564
572
  def create_loader_route(config: Configuration, app: FastAPI):
565
573
  route_map = config.router.to_route_map()
566
574
 
@@ -617,6 +625,14 @@ def create_loader_route(config: Configuration, app: FastAPI):
617
625
  CURRENT_ACTION_ID.set('')
618
626
  ACTION_CONTEXT.set(None)
619
627
 
628
+ def create_payload(x):
629
+ try:
630
+ return jsonable_encoder(x)
631
+ except Exception as e:
632
+ raise UnserializablePayloadError(
633
+ f'Unserializable payload found - {str(e)}', payload_type=type(x)
634
+ ) from e
635
+
620
636
  async def process_variables(send_stream: MemoryObjectSendStream[Chunk]):
621
637
  for payload in body.derived_variable_payloads:
622
638
  try:
@@ -629,7 +645,17 @@ def create_loader_route(config: Configuration, app: FastAPI):
629
645
  force_key=None,
630
646
  ),
631
647
  )
632
- await send_stream.send(DerivedVariableChunk(uid=payload.uid, result=Result.success(result)))
648
+
649
+ await send_stream.send(
650
+ DerivedVariableChunk(uid=payload.uid, result=Result.success(create_payload(result)))
651
+ )
652
+ except UnserializablePayloadError as e:
653
+ dev_logger.warning(
654
+ f'Unserializable payload found for derived variable, skipping preload - {str(e)}',
655
+ )
656
+ await send_stream.send(
657
+ DerivedVariableChunk(uid=payload.uid, result=Result.error(str(e))),
658
+ )
633
659
  except BaseException as e:
634
660
  dev_logger.error(f'Error streaming derived_variable {payload.uid}', error=e)
635
661
  await send_stream.send(
@@ -647,7 +673,16 @@ def create_loader_route(config: Configuration, app: FastAPI):
647
673
  ws_channel=body.ws_channel,
648
674
  ),
649
675
  )
650
- await send_stream.send(PyComponentChunk(uid=payload.uid, result=Result.success(result)))
676
+ await send_stream.send(
677
+ PyComponentChunk(uid=payload.uid, result=Result.success(create_payload(result)))
678
+ )
679
+ except UnserializablePayloadError as e:
680
+ dev_logger.warning(
681
+ f'Unserializable payload found for py_component, skipping preload - {str(e)}',
682
+ )
683
+ await send_stream.send(
684
+ PyComponentChunk(uid=payload.uid, result=Result.error(str(e))),
685
+ )
651
686
  except BaseException as e:
652
687
  dev_logger.error(f'Error streaming py_component {payload.name}', error=e)
653
688
  await send_stream.send(
@@ -369,7 +369,7 @@ class BuildCache(BaseModel):
369
369
  pkg_json['dependencies'] = {
370
370
  **deps,
371
371
  '@vitejs/plugin-react': '4.6.0',
372
- 'vite': '7.0.4',
372
+ 'vite': '7.0.8',
373
373
  'vite-plugin-node-polyfills': '0.24.0',
374
374
  }
375
375
 
@@ -34,6 +34,11 @@ def _analyze_component_dependencies(component: ComponentInstance, graph: Depende
34
34
  """
35
35
  Recursively analyze a component tree to build a dependency graph of DerivedVariables and PyComponentInstances.
36
36
  """
37
+ try:
38
+ from dara.components import Table
39
+ except ImportError:
40
+ Table = None
41
+
37
42
  # The component itself is a PyComponentInstance
38
43
  if isinstance(component, PyComponentInstance):
39
44
  if component.uid not in graph.py_components:
@@ -46,6 +51,9 @@ def _analyze_component_dependencies(component: ComponentInstance, graph: Depende
46
51
 
47
52
  # Handle encountered variables and py_components
48
53
  if isinstance(value, DerivedVariable) and value.uid not in graph.derived_variables:
54
+ # SPECIAL CASE: exclude Table.data since it's tabular and preloading it would be a waste
55
+ if Table and isinstance(component, Table) and attr == 'data':
56
+ continue
49
57
  graph.derived_variables[value.uid] = value
50
58
  elif isinstance(value, PyComponentInstance) and value.uid not in graph.py_components:
51
59
  graph.py_components[value.uid] = value
@@ -1,11 +1,14 @@
1
- from typing import Literal
1
+ from typing import TYPE_CHECKING, Literal
2
2
 
3
- from pydantic import Field
3
+ from pydantic import Field, model_validator
4
4
 
5
5
  from dara.core.base_definitions import DaraBaseModel
6
6
  from dara.core.definitions import ComponentInstance, JsComponentDef
7
7
  from dara.core.interactivity.any_variable import AnyVariable
8
8
 
9
+ if TYPE_CHECKING:
10
+ from dara.core.visual.dynamic_component import PyComponentInstance
11
+
9
12
  ForDef = JsComponentDef(name='For', js_module='@darajs/core', py_module='dara.core')
10
13
 
11
14
 
@@ -61,12 +64,16 @@ class For(ComponentInstance):
61
64
  )
62
65
  ```
63
66
 
64
- For interactivity can use list items in Dara actions, DerivedVariables, py_components, and `If` component for conditional rendering.
67
+ For interactivity can use list items in Dara actions, DerivedVariables, and `If` component for conditional rendering.
68
+
69
+ Note: Using `@py_component` inside a For renderer is not allowed as it would require a backend call for each
70
+ item rendered, defeating the purpose of the optimized loop primitive. Instead, precompute any derived fields
71
+ in the `items` variable itself using a DerivedVariable - this runs the logic once for the whole list rather
72
+ than per item.
65
73
 
66
74
  ```python
67
- import asyncio
68
- from dara.core import Variable, action, DerivedVariable, py_component
69
- from dara.components import Text, Card, Button, Stack, Input
75
+ from dara.core import Variable, action, DerivedVariable
76
+ from dara.components import Text, Card, Button, Stack
70
77
 
71
78
  # Define a variable that holds a list of items
72
79
  arr_var = Variable(
@@ -82,10 +89,6 @@ class For(ComponentInstance):
82
89
  async def say_hello(ctx: ActionCtx, item):
83
90
  await ctx.notify(f'Hello {item}!', title='Hello', status='SUCCESS')
84
91
 
85
- @py_component
86
- async def display_city(city: str):
87
- return Text(text=f'Server-rendered city: {city}')
88
-
89
92
  Stack(
90
93
  Text(text='Items:'),
91
94
  For(
@@ -107,8 +110,6 @@ class For(ComponentInstance):
107
110
  Text(
108
111
  text=DerivedVariable(lambda x: f'age doubled is {x * 2}', variables=[arr_var.list_item['age']])
109
112
  ),
110
- # Pass item data into a py_component
111
- display_city(arr_var.list_item['data']['city']),
112
113
  ),
113
114
  title=arr_var.list_item.get('name'),
114
115
  subtitle=arr_var.list_item.get('age'),
@@ -151,3 +152,55 @@ class For(ComponentInstance):
151
152
  placeholder: ComponentInstance | None = None
152
153
  key_accessor: str | None = None
153
154
  virtualization: VirtualizationConfig | None = None
155
+
156
+ @model_validator(mode='after')
157
+ def validate_no_py_components(self):
158
+ """
159
+ Validate that the renderer does not contain any PyComponentInstance.
160
+ Using @py_component inside a For loop is inefficient as it requires a backend call for each render,
161
+ defeating the purpose of the optimized loop primitive.
162
+ """
163
+ py_components = _find_py_components(self.renderer)
164
+ if py_components:
165
+ func_names = ', '.join(f"'{pc.func_name}'" for pc in py_components)
166
+ raise ValueError(
167
+ f'For component renderer contains @py_component(s): {func_names}. '
168
+ 'Using @py_component inside a For loop is inefficient as it requires a backend call '
169
+ 'for each item rendered. Instead, precompute any derived fields in the `items` variable '
170
+ 'using a DerivedVariable - this runs the logic once for the whole list rather than per item.'
171
+ )
172
+
173
+ return self
174
+
175
+
176
+ def _find_py_components(component: ComponentInstance) -> list['PyComponentInstance']:
177
+ """
178
+ Recursively search a component tree for any PyComponentInstance nodes.
179
+
180
+ :param component: The root component to search
181
+ :return: List of PyComponentInstance found in the tree
182
+ """
183
+ from dara.core.visual.dynamic_component import PyComponentInstance
184
+
185
+ found: list[PyComponentInstance] = []
186
+
187
+ if isinstance(component, PyComponentInstance):
188
+ found.append(component)
189
+ return found
190
+
191
+ # Check each field set on the component
192
+ for attr in component.model_fields_set:
193
+ value = getattr(component, attr, None)
194
+
195
+ if isinstance(value, PyComponentInstance):
196
+ found.append(value)
197
+ elif isinstance(value, ComponentInstance):
198
+ found.extend(_find_py_components(value))
199
+ elif isinstance(value, list):
200
+ for item in value:
201
+ if isinstance(item, PyComponentInstance):
202
+ found.append(item)
203
+ elif isinstance(item, ComponentInstance):
204
+ found.extend(_find_py_components(item))
205
+
206
+ return found
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dara-core
3
- Version: 1.23.2a1
3
+ Version: 1.24.0
4
4
  Summary: Dara Framework Core
5
5
  Home-page: https://dara.causalens.com/
6
6
  License: Apache-2.0
@@ -21,10 +21,10 @@ Requires-Dist: cachetools (>=5.0.0)
21
21
  Requires-Dist: certifi (>=2024.7.4)
22
22
  Requires-Dist: click (>=8.1.3,<9.0.0)
23
23
  Requires-Dist: colorama (>=0.4.6,<0.5.0)
24
- Requires-Dist: create-dara-app (==1.23.2-alpha.1)
24
+ Requires-Dist: create-dara-app (==1.24.0)
25
25
  Requires-Dist: croniter (>=6.0.0,<7.0.0)
26
26
  Requires-Dist: cryptography (>=42.0.4)
27
- Requires-Dist: dara-components (==1.23.2-alpha.1) ; extra == "all"
27
+ Requires-Dist: dara-components (==1.24.0) ; extra == "all"
28
28
  Requires-Dist: exceptiongroup (>=1.1.3,<2.0.0)
29
29
  Requires-Dist: fastapi (>=0.115.0,<0.121.0)
30
30
  Requires-Dist: fastapi_vite_dara (==0.4.0)
@@ -36,7 +36,7 @@ Requires-Dist: odfpy
36
36
  Requires-Dist: openpyxl
37
37
  Requires-Dist: packaging (>=23.1)
38
38
  Requires-Dist: pandas (>=1.1.0,<3.0.0)
39
- Requires-Dist: prometheus-client (>=0.14.1,<0.15.0)
39
+ Requires-Dist: prometheus-client (>=0.14.1,<1.0.0)
40
40
  Requires-Dist: pyarrow
41
41
  Requires-Dist: pydantic (>=2.11.7,<3.0.0)
42
42
  Requires-Dist: pydantic-settings (>=2.8.1,<3.0.0)
@@ -55,7 +55,7 @@ Description-Content-Type: text/markdown
55
55
 
56
56
  # Dara Application Framework
57
57
 
58
- <img src="https://github.com/causalens/dara/blob/v1.23.2-alpha.1/img/dara_light.svg?raw=true">
58
+ <img src="https://github.com/causalens/dara/blob/v1.24.0/img/dara_light.svg?raw=true">
59
59
 
60
60
  ![Master tests](https://github.com/causalens/dara/actions/workflows/tests.yml/badge.svg?branch=master)
61
61
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
@@ -100,7 +100,7 @@ source .venv/bin/activate
100
100
  dara start
101
101
  ```
102
102
 
103
- ![Dara App](https://github.com/causalens/dara/blob/v1.23.2-alpha.1/img/components_gallery.png?raw=true)
103
+ ![Dara App](https://github.com/causalens/dara/blob/v1.24.0/img/components_gallery.png?raw=true)
104
104
 
105
105
  Note: `pip` installation uses [PEP 660](https://peps.python.org/pep-0660/) `pyproject.toml`-based editable installs which require `pip >= 21.3` and `setuptools >= 64.0.0`. You can upgrade both with:
106
106
 
@@ -117,9 +117,9 @@ Explore some of our favorite apps - a great way of getting started and getting t
117
117
 
118
118
  | Dara App | Description |
119
119
  | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
120
- | ![Large Language Model](https://github.com/causalens/dara/blob/v1.23.2-alpha.1/img/llm.png?raw=true) | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
121
- | ![Plot Interactivity](https://github.com/causalens/dara/blob/v1.23.2-alpha.1/img/plot_interactivity.png?raw=true) | Demonstrates how to enable the user to interact with plots, trigger actions based on clicks, mouse movements and other interactions with `Bokeh` or `Plotly` plots |
122
- | ![Graph Editor](https://github.com/causalens/dara/blob/v1.23.2-alpha.1/img/graph_viewer.png?raw=true) | Demonstrates how to use the `CausalGraphViewer` component to display your graphs or networks, customising the displayed information through colors and tooltips, and updating the page based on user interaction. |
120
+ | ![Large Language Model](https://github.com/causalens/dara/blob/v1.24.0/img/llm.png?raw=true) | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
121
+ | ![Plot Interactivity](https://github.com/causalens/dara/blob/v1.24.0/img/plot_interactivity.png?raw=true) | Demonstrates how to enable the user to interact with plots, trigger actions based on clicks, mouse movements and other interactions with `Bokeh` or `Plotly` plots |
122
+ | ![Graph Editor](https://github.com/causalens/dara/blob/v1.24.0/img/graph_viewer.png?raw=true) | Demonstrates how to use the `CausalGraphViewer` component to display your graphs or networks, customising the displayed information through colors and tooltips, and updating the page based on user interaction. |
123
123
 
124
124
  Check out our [App Gallery](https://dara.causalens.com/gallery) for more inspiration!
125
125
 
@@ -146,9 +146,9 @@ And the supporting UI packages and tools.
146
146
  - `ui-utils` - miscellaneous utility functions
147
147
  - `ui-widgets` - widget components
148
148
 
149
- More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.23.2-alpha.1/CONTRIBUTING.md) file.
149
+ More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.24.0/CONTRIBUTING.md) file.
150
150
 
151
151
  ## License
152
152
 
153
- Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.23.2-alpha.1/LICENSE).
153
+ Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.24.0/LICENSE).
154
154
 
@@ -1,7 +1,7 @@
1
1
  dara/core/__init__.py,sha256=yTp-lXT0yy9XqLGYWlmjPgFG5g2eEg2KhKo8KheTHoo,1408
2
2
  dara/core/_assets/__init__.py,sha256=13vMoWHvl1zcFcjNHh8lbTwWOvu4f7krYSco978qxwM,723
3
3
  dara/core/_assets/auto_js/dara.core.css,sha256=yT3PKpi2sKI2-kQIF8xtVbTPQqgpK7-Ua7tfzDPuSsI,4095881
4
- dara/core/_assets/auto_js/dara.core.umd.cjs,sha256=JBxFuCmrugsMdISIVkEEWbdCSht8gydVvwt2Vksnmyw,5166175
4
+ dara/core/_assets/auto_js/dara.core.umd.cjs,sha256=hvmFXD-p5KiQFZdoyp6z-Js_DG6r2bFIzXKoGtj5QFQ,5163770
5
5
  dara/core/_assets/auto_js/react-dom.development.js,sha256=vR2Fq5LXMKS5JsTo2CMl6oGCJT8scJV2wXSteTtx8aE,1077040
6
6
  dara/core/_assets/auto_js/react-is.development.js,sha256=2IRgmaphdMq6wx2MbsmVUQ0UwapGETNjgano3XEkGcc,7932
7
7
  dara/core/_assets/auto_js/react-query.development.js,sha256=lI2fTKMvWmjbagGQaVtZYr51_-C_U_so064JwetuDS0,130366
@@ -56,7 +56,7 @@ dara/core/internal/cache_store/lru.py,sha256=6Jd4TmUX1LaEPnOraf8cSnL9Rx-A_LDhjqO
56
56
  dara/core/internal/cache_store/ttl.py,sha256=WzSZfdhK-nTf0fwCwtaRbJ-9I-FeTNBXgnZgSvZMbKQ,4966
57
57
  dara/core/internal/cgroup.py,sha256=yKCTY8RxyxnNy-Mw4tfzeZew1dL8qw381gHBVE96-OA,3217
58
58
  dara/core/internal/custom_response.py,sha256=MM0xEX7kVZWyX943Obl7sic3bxXr6kHGLWe6pc5pcYg,1304
59
- dara/core/internal/dependency_resolution.py,sha256=6dHMEFfT3Fg9bstZgxSFFJpF13HMNN_rlCdg0ozJF3A,10264
59
+ dara/core/internal/dependency_resolution.py,sha256=SmG-MyfOVrUDRikkLLKWZQ9Zsp70j3qb-tanhm1n3b8,11511
60
60
  dara/core/internal/devtools.py,sha256=4LsK7sqES33fAqb53dyH3n0OSWang3W-MiPsmo57aXU,2586
61
61
  dara/core/internal/download.py,sha256=wBSUapMY6evZ_sA5ZgvnWhaRtzVMIDIC51mPzFCMWuM,3548
62
62
  dara/core/internal/encoder_registry.py,sha256=Q8F5Y2uZVkx7EUtk4MZ99wvPBWaMnPKcFZzK92FYNPw,11159
@@ -76,7 +76,7 @@ dara/core/internal/port_utils.py,sha256=3NN94CubNrIYQKmPM4SEwstY-UMqsbbe1M_JhyPc
76
76
  dara/core/internal/registries.py,sha256=j_Z3KqmiNHK3HsYmQI2O23B12MsT31SPvo3ezemASR8,3654
77
77
  dara/core/internal/registry.py,sha256=Wqt7tEOmf5bR8D6lFIcLd3W1EtlENT364vRhxSqeOIQ,4356
78
78
  dara/core/internal/registry_lookup.py,sha256=XgZ4PL4RLyKWNTXkh08lL6mmU492xgdPUsvFKA6pKDY,2411
79
- dara/core/internal/routing.py,sha256=C4_GLsXHueQ2Q4VVDgrY9bezr3YGZFdL6Yfs6C81Op8,26458
79
+ dara/core/internal/routing.py,sha256=uqbFtp9Eyc_G3dXVH7NrqT-35Ec3ep-vNw9xlYSnqkE,27832
80
80
  dara/core/internal/scheduler.py,sha256=3LYdWAyFp8N1s9Xfud9XYwUbjcHpI-hCay3jZ5zRAiQ,12991
81
81
  dara/core/internal/settings.py,sha256=2g-PIoYVLlGoBNKoXTIbkH1FGV8TXUXRdwuhQ6cTrkE,2614
82
82
  dara/core/internal/store.py,sha256=aq37Tk4J3DoqzLVHuZRbZFkpghKP-oiCbpQfIE95gCo,6399
@@ -87,7 +87,7 @@ dara/core/jinja/index.html,sha256=O3uyA9Hozwbqh--wQq5IHuLNEFUNf2BykWLGuIZqUjo,30
87
87
  dara/core/jinja/index_autojs.html,sha256=ov9qg_ymClTL9BdhZw86jS4UKC5jy5PufIrb9F7sy9w,3009
88
88
  dara/core/js_tooling/custom_js_scaffold/index.tsx,sha256=FEzSV5o5Nyzxw6eXvGLi7BkEBkXf3brV34_7ATLnY7o,68
89
89
  dara/core/js_tooling/custom_js_scaffold/local-js-component.tsx,sha256=Ojsyeh9MYJnH_XJXGkSc8PAUZIqJcqcGTu_uRITo_88,709
90
- dara/core/js_tooling/js_utils.py,sha256=eZgt-gidj0yaxbu87cVzslTOCstBOfBouSlH3ouT8XI,29960
90
+ dara/core/js_tooling/js_utils.py,sha256=tQzmnhhrJ57uwCsLgFIftIpidi-8yxZ8xNyiBBAbO5g,29960
91
91
  dara/core/js_tooling/statics/favicon.ico,sha256=CJP26erMMIwQB8FFmyhoUJ-Cx1cXEopiVGxFwyxcpo0,47068
92
92
  dara/core/js_tooling/statics/tsconfig.json,sha256=ubnFjwvPHbx2F7wVWsU2VHLDeEnnOwXkTIk7hLNY8ZM,446
93
93
  dara/core/js_tooling/templates/.npmrc,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -106,13 +106,13 @@ dara/core/persistence.py,sha256=EAEER1T0MHBrtT-iLZ-eXzAsqwu4Klqw5mDlTPSdPkI,1932
106
106
  dara/core/router/__init__.py,sha256=yGI_MgLQU37ircCtYVNjnhqCjWQxKd5amoNqvMyrhOs,121
107
107
  dara/core/router/compat.py,sha256=B83wq46AwQARTDfZwGrpr0slrnQmi_T7shOUNpqltSM,3112
108
108
  dara/core/router/components.py,sha256=Wsf_bBnnN-1EE5jhbjPSOyDv_TVKEsY4royyOQOazM4,6132
109
- dara/core/router/dependency_graph.py,sha256=A0xGuZWSOCGs7rwHbeZC5vLvZouUt-2fnEX9VM2Ukp0,2280
109
+ dara/core/router/dependency_graph.py,sha256=4N8RVTLi1JD-zOMzgEFJgGemQw08i0ItBSWnRK73HP4,2577
110
110
  dara/core/router/router.py,sha256=ys3k48pIjTdbmch5516O4TAeo8IIUPThdqgeoXC-kMQ,32593
111
111
  dara/core/visual/__init__.py,sha256=QN0wbG9HPQ_vXh8BO8DnBXeYLIENVTNtRmYzZf1lx7c,577
112
112
  dara/core/visual/components/__init__.py,sha256=Jrh38Q4Lc8iFFVKPuEaDeloYHVLohTTHgK92u1e_ZtY,2515
113
113
  dara/core/visual/components/dynamic_component.py,sha256=F_UxogMQ9I5GB6BLqDmGaojRQyOOmwJBB5kaIk1iPpI,867
114
114
  dara/core/visual/components/fallback.py,sha256=-7lJwqPrm47Xbqs3rYEUw2JXuJRJpd_wYMt7no8uWkE,4746
115
- dara/core/visual/components/for_cmp.py,sha256=SxUR0KYYHbLYZqPGesfSM-GSzCBL2eXyqYDlYYLkYdQ,6316
115
+ dara/core/visual/components/for_cmp.py,sha256=5w7qRF9C-U3-PbZFfoxpoQJcjoSExoa1E34B43s83_Q,8615
116
116
  dara/core/visual/components/invalid_component.py,sha256=Q6O9rAwVUNZXjEmliAKLfWf2lsyYK5JhgQvMM6jVco8,901
117
117
  dara/core/visual/components/match_cmp.py,sha256=hwH38azC3DKgg36OKnM5_LFCrOEXtHN3PV9BkCsqwPA,1623
118
118
  dara/core/visual/components/menu.py,sha256=_Q_aGrCssOe29WFmRfRFOIOpFDn2HEn2TKF2ll_Ddss,1088
@@ -134,8 +134,8 @@ dara/core/visual/themes/__init__.py,sha256=aM4mgoIYo2neBSw5FRzswsht7PUKjLthiHLmF
134
134
  dara/core/visual/themes/dark.py,sha256=QazCRDqh_SCOyQhdwMkH1wbHf301oL7gCFj91plbLww,2020
135
135
  dara/core/visual/themes/definitions.py,sha256=dtET2YUlwXkO6gJ23MqSb8gIq-LxJ343CWsgueWSifM,2787
136
136
  dara/core/visual/themes/light.py,sha256=dtHb6Q1HOb5r_AvJfe0vZajikVc-GnBEUrGsTcI5MHA,2022
137
- dara_core-1.23.2a1.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
138
- dara_core-1.23.2a1.dist-info/METADATA,sha256=X5eVDXyUa0Ikj_AGLfwFOIrMgxyI2NeLL1rvPG-YZB8,7598
139
- dara_core-1.23.2a1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
140
- dara_core-1.23.2a1.dist-info/entry_points.txt,sha256=nAT9o1kJCmTK1saDh29PFGFD6cbxDDDjTj31HDEDwfU,197
141
- dara_core-1.23.2a1.dist-info/RECORD,,
137
+ dara_core-1.24.0.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
138
+ dara_core-1.24.0.dist-info/METADATA,sha256=nHHuPBW-sLrRPLWA_OW5N8F2oJOvoYNFJvNgAeeR22o,7523
139
+ dara_core-1.24.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
140
+ dara_core-1.24.0.dist-info/entry_points.txt,sha256=nAT9o1kJCmTK1saDh29PFGFD6cbxDDDjTj31HDEDwfU,197
141
+ dara_core-1.24.0.dist-info/RECORD,,