dara-core 1.19.1__py3-none-any.whl → 1.20.0a1__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.
Files changed (51) hide show
  1. dara/core/__init__.py +1 -0
  2. dara/core/auth/basic.py +13 -7
  3. dara/core/auth/definitions.py +2 -2
  4. dara/core/auth/utils.py +1 -1
  5. dara/core/base_definitions.py +7 -42
  6. dara/core/data_utils.py +16 -17
  7. dara/core/definitions.py +8 -8
  8. dara/core/interactivity/__init__.py +4 -0
  9. dara/core/interactivity/actions.py +20 -22
  10. dara/core/interactivity/any_data_variable.py +7 -135
  11. dara/core/interactivity/any_variable.py +1 -1
  12. dara/core/interactivity/client_variable.py +71 -0
  13. dara/core/interactivity/data_variable.py +8 -266
  14. dara/core/interactivity/derived_data_variable.py +6 -290
  15. dara/core/interactivity/derived_variable.py +333 -199
  16. dara/core/interactivity/filtering.py +29 -2
  17. dara/core/interactivity/loop_variable.py +2 -2
  18. dara/core/interactivity/non_data_variable.py +5 -68
  19. dara/core/interactivity/plain_variable.py +87 -14
  20. dara/core/interactivity/server_variable.py +325 -0
  21. dara/core/interactivity/state_variable.py +2 -2
  22. dara/core/interactivity/switch_variable.py +15 -15
  23. dara/core/interactivity/tabular_variable.py +94 -0
  24. dara/core/interactivity/url_variable.py +10 -90
  25. dara/core/internal/cache_store/cache_store.py +5 -20
  26. dara/core/internal/dependency_resolution.py +27 -69
  27. dara/core/internal/devtools.py +10 -3
  28. dara/core/internal/execute_action.py +9 -3
  29. dara/core/internal/multi_resource_lock.py +70 -0
  30. dara/core/internal/normalization.py +0 -5
  31. dara/core/internal/pandas_utils.py +105 -3
  32. dara/core/internal/pool/definitions.py +1 -1
  33. dara/core/internal/pool/task_pool.py +1 -1
  34. dara/core/internal/registries.py +3 -2
  35. dara/core/internal/registry.py +1 -1
  36. dara/core/internal/registry_lookup.py +5 -3
  37. dara/core/internal/routing.py +52 -121
  38. dara/core/internal/store.py +2 -29
  39. dara/core/internal/tasks.py +372 -182
  40. dara/core/internal/utils.py +25 -3
  41. dara/core/internal/websocket.py +1 -1
  42. dara/core/js_tooling/js_utils.py +2 -0
  43. dara/core/logging.py +10 -6
  44. dara/core/persistence.py +26 -4
  45. dara/core/umd/dara.core.umd.js +742 -1381
  46. dara/core/visual/dynamic_component.py +10 -13
  47. {dara_core-1.19.1.dist-info → dara_core-1.20.0a1.dist-info}/METADATA +10 -10
  48. {dara_core-1.19.1.dist-info → dara_core-1.20.0a1.dist-info}/RECORD +51 -47
  49. {dara_core-1.19.1.dist-info → dara_core-1.20.0a1.dist-info}/LICENSE +0 -0
  50. {dara_core-1.19.1.dist-info → dara_core-1.20.0a1.dist-info}/WHEEL +0 -0
  51. {dara_core-1.19.1.dist-info → dara_core-1.20.0a1.dist-info}/entry_points.txt +0 -0
@@ -15,13 +15,24 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from typing import Optional, TypeVar, cast
18
+ import json
19
+ import uuid
20
+ from typing import Any, Literal, Optional, TypeVar, Union, cast, overload
19
21
 
20
- from pandas import DataFrame, MultiIndex
22
+ from pandas import DataFrame, MultiIndex, Series
23
+ from typing_extensions import TypedDict, TypeGuard
21
24
 
22
25
  INDEX = '__index__'
23
26
 
24
27
 
28
+ @overload
29
+ def append_index(df: DataFrame) -> DataFrame: ...
30
+
31
+
32
+ @overload
33
+ def append_index(df: None) -> None: ...
34
+
35
+
25
36
  def append_index(df: Optional[DataFrame]) -> Optional[DataFrame]:
26
37
  """
27
38
  Add a numerical index column to the dataframe
@@ -65,6 +76,12 @@ def df_convert_to_internal(original_df: DataFrame) -> DataFrame:
65
76
  if any(isinstance(c, str) and c.startswith('__col__') for c in df.columns):
66
77
  return df
67
78
 
79
+ # Apply display transformations to the DataFrame
80
+ format_for_display(df)
81
+
82
+ # Append index to match the way we process the original DataFrame
83
+ df = cast(DataFrame, append_index(df))
84
+
68
85
  # Handle hierarchical columns: [(A, B), (A, C)] -> ['A_B', 'A_C']
69
86
  if isinstance(df.columns, MultiIndex):
70
87
  df.columns = ['_'.join(col).strip() if col[0] != INDEX else INDEX for col in df.columns.values]
@@ -90,4 +107,89 @@ def df_convert_to_internal(original_df: DataFrame) -> DataFrame:
90
107
 
91
108
 
92
109
  def df_to_json(df: DataFrame) -> str:
93
- return df_convert_to_internal(df).to_json(orient='records') or ''
110
+ return df_convert_to_internal(df).to_json(orient='records', date_unit='ns') or ''
111
+
112
+
113
+ def format_for_display(df: DataFrame) -> None:
114
+ """
115
+ Apply transformations to a DataFrame to make it suitable for display.
116
+ Not: this does NOT make a copy of the DataFrame
117
+ """
118
+ for col in df.columns:
119
+ column_data = df[col]
120
+ if isinstance(column_data, DataFrame):
121
+ # Handle duplicate column names - format each column in the sub-DataFrame
122
+ for sub_col in column_data.columns:
123
+ if isinstance(column_data[sub_col], Series) and column_data[sub_col].dtype == 'object':
124
+ column_data.loc[:, sub_col] = column_data[sub_col].apply(str)
125
+ elif column_data.dtype == 'object':
126
+ # We need to convert all values to string to avoid issues with
127
+ # displaying data in the Table component, for example when
128
+ # displaying datetime and number objects in the same column
129
+ df.loc[:, col] = column_data.apply(str)
130
+
131
+
132
+ class FieldType(TypedDict):
133
+ name: Union[str, tuple[str, ...]]
134
+ type: Literal['integer', 'number', 'boolean', 'datetime', 'duration', 'any', 'str']
135
+
136
+
137
+ class DataFrameSchema(TypedDict):
138
+ fields: list[FieldType]
139
+ primaryKey: list[str]
140
+
141
+
142
+ class DataResponse(TypedDict):
143
+ data: Optional[DataFrame]
144
+ count: int
145
+ schema: Optional[DataFrameSchema]
146
+
147
+
148
+ def is_data_response(response: Any) -> TypeGuard[DataResponse]:
149
+ has_shape = isinstance(response, dict) and 'data' in response and 'count' in response
150
+ if not has_shape:
151
+ return False
152
+ return response['data'] is None or isinstance(response['data'], DataFrame)
153
+
154
+
155
+ def data_response_to_json(response: DataResponse) -> str:
156
+ """
157
+ Serialize a DataResponse to JSON.
158
+
159
+ json.dumps() custom serializers only accept value->value mappings, whereas `to_json` on pandas returns a string directly.
160
+ To avoid double serialization, we first insert a placeholder string and then replace it with the actual serialized JSON.
161
+ """
162
+ placeholder = str(uuid.uuid4())
163
+
164
+ def _custom_serializer(obj: Any) -> Any:
165
+ if isinstance(obj, DataFrame):
166
+ return placeholder
167
+ raise TypeError(f'Object of type {type(obj)} is not JSON serializable')
168
+
169
+ result = json.dumps(response, default=_custom_serializer)
170
+ result = result.replace(
171
+ f'"{placeholder}"', df_to_json(response['data']) if response['data'] is not None else 'null'
172
+ )
173
+ return result
174
+
175
+
176
+ def build_data_response(data: DataFrame, count: int) -> DataResponse:
177
+ data_internal = df_convert_to_internal(data)
178
+ schema = get_schema(data_internal)
179
+
180
+ return DataResponse(data=data, count=count, schema=schema)
181
+
182
+
183
+ def get_schema(df: DataFrame):
184
+ from pandas.io.json._table_schema import build_table_schema
185
+
186
+ raw_schema = build_table_schema(df)
187
+
188
+ for field_data in cast(list, raw_schema['fields']):
189
+ if field_data.get('type') == 'datetime':
190
+ # for datetime fields we need to know the resolution, so we get the actual e.g. `datetime64[ns]` string
191
+ column_name = field_data.get('name')
192
+ dtype_str = str(df[column_name].dtype)
193
+ field_data['type'] = dtype_str
194
+
195
+ return cast(DataFrameSchema, raw_schema)
@@ -95,7 +95,7 @@ class TaskDefinition:
95
95
  def __await__(self):
96
96
  """Await the underlying event, then return or raise the result"""
97
97
  yield from self.event.wait().__await__()
98
- if isinstance(self.result, Exception):
98
+ if isinstance(self.result, BaseException):
99
99
  raise self.result
100
100
  return self.result
101
101
 
@@ -158,7 +158,7 @@ class TaskPool:
158
158
 
159
159
  task = self.tasks.pop(task_uid)
160
160
  if not task.event.is_set():
161
- task.result = Exception('Task cancelled')
161
+ task.result = anyio.get_cancelled_exc_class()()
162
162
  task.event.set()
163
163
 
164
164
  # Task in progress, stop the worker
@@ -27,11 +27,11 @@ from dara.core.definitions import (
27
27
  EndpointConfiguration,
28
28
  Template,
29
29
  )
30
- from dara.core.interactivity.data_variable import DataVariableRegistryEntry
31
30
  from dara.core.interactivity.derived_variable import (
32
31
  DerivedVariableRegistryEntry,
33
32
  LatestValueRegistryEntry,
34
33
  )
34
+ from dara.core.interactivity.server_variable import ServerVariableRegistryEntry
35
35
  from dara.core.internal.download import DownloadDataEntry
36
36
  from dara.core.internal.registry import Registry, RegistryType
37
37
  from dara.core.internal.websocket import CustomClientMessagePayload
@@ -44,7 +44,8 @@ upload_resolver_registry = Registry[UploadResolverDef](
44
44
  ) # functions for upload resolvers requiring backend calls
45
45
  component_registry = Registry[ComponentTypeAnnotation](RegistryType.COMPONENTS, CORE_COMPONENTS)
46
46
  config_registry = Registry[EndpointConfiguration](RegistryType.ENDPOINT_CONFIG)
47
- data_variable_registry = Registry[DataVariableRegistryEntry](RegistryType.DATA_VARIABLE, allow_duplicates=False)
47
+ server_variable_registry = Registry[ServerVariableRegistryEntry](RegistryType.SERVER_VARIABLE, allow_duplicates=False)
48
+ """map of server variable uid -> server variable entry"""
48
49
  derived_variable_registry = Registry[DerivedVariableRegistryEntry](
49
50
  RegistryType.DERIVED_VARIABLE, allow_duplicates=False
50
51
  )
@@ -32,7 +32,7 @@ class RegistryType(str, Enum):
32
32
  DOWNLOAD = 'Download'
33
33
  COMPONENTS = 'Components'
34
34
  ENDPOINT_CONFIG = 'Endpoint Configuration'
35
- DATA_VARIABLE = 'DataVariable'
35
+ SERVER_VARIABLE = 'ServerVariable'
36
36
  DERIVED_VARIABLE = 'DerivedVariable'
37
37
  LAST_VALUE = 'LatestValue'
38
38
  TEMPLATE = 'Template'
@@ -16,7 +16,7 @@ limitations under the License.
16
16
  """
17
17
 
18
18
  from collections.abc import Coroutine
19
- from typing import Callable, Dict, Literal, Union
19
+ from typing import Callable, Dict, Literal, TypeVar, Union
20
20
 
21
21
  from dara.core.internal.registry import Registry, RegistryType
22
22
  from dara.core.internal.utils import async_dedupe
@@ -24,8 +24,8 @@ from dara.core.internal.utils import async_dedupe
24
24
  RegistryLookupKey = Literal[
25
25
  RegistryType.ACTION,
26
26
  RegistryType.COMPONENTS,
27
- RegistryType.DATA_VARIABLE,
28
27
  RegistryType.DERIVED_VARIABLE,
28
+ RegistryType.SERVER_VARIABLE,
29
29
  RegistryType.STATIC_KWARGS,
30
30
  RegistryType.UPLOAD_RESOLVER,
31
31
  RegistryType.BACKEND_STORE,
@@ -33,6 +33,8 @@ RegistryLookupKey = Literal[
33
33
  ]
34
34
  CustomRegistryLookup = Dict[RegistryLookupKey, Callable[[str], Coroutine]]
35
35
 
36
+ RegistryType = TypeVar('RegistryType')
37
+
36
38
 
37
39
  class RegistryLookup:
38
40
  """
@@ -45,7 +47,7 @@ class RegistryLookup:
45
47
  self.handlers = handlers
46
48
 
47
49
  @async_dedupe
48
- async def get(self, registry: Registry, uid: str):
50
+ async def get(self, registry: Registry[RegistryType], uid: str) -> RegistryType:
49
51
  """
50
52
  Get the entry from registry by uid.
51
53
  If uid is not in registry and it has a external handler that defined, will execute the handler
@@ -16,7 +16,6 @@ limitations under the License.
16
16
  """
17
17
 
18
18
  import inspect
19
- import json
20
19
  import os
21
20
  from collections.abc import Mapping
22
21
  from functools import wraps
@@ -24,7 +23,6 @@ from importlib.metadata import version
24
23
  from typing import Any, Callable, Dict, List, Optional
25
24
 
26
25
  import anyio
27
- import pandas
28
26
  from fastapi import (
29
27
  APIRouter,
30
28
  Body,
@@ -40,26 +38,28 @@ from fastapi.responses import StreamingResponse
40
38
  from pandas import DataFrame
41
39
  from pydantic import BaseModel
42
40
  from starlette.background import BackgroundTask
41
+ from starlette.status import HTTP_415_UNSUPPORTED_MEDIA_TYPE
43
42
 
44
43
  from dara.core.auth.routes import verify_session
45
- from dara.core.base_definitions import ActionResolverDef, BaseTask, UploadResolverDef
44
+ from dara.core.base_definitions import ActionResolverDef, BaseTask, NonTabularDataError, UploadResolverDef
46
45
  from dara.core.configuration import Configuration
47
- from dara.core.interactivity.any_data_variable import DataVariableRegistryEntry, upload
46
+ from dara.core.interactivity.any_data_variable import upload
48
47
  from dara.core.interactivity.filtering import FilterQuery, Pagination
48
+ from dara.core.interactivity.server_variable import ServerVariable
49
49
  from dara.core.internal.cache_store import CacheStore
50
50
  from dara.core.internal.download import DownloadRegistryEntry
51
51
  from dara.core.internal.execute_action import CURRENT_ACTION_ID
52
52
  from dara.core.internal.normalization import NormalizedPayload, denormalize, normalize
53
- from dara.core.internal.pandas_utils import df_to_json
53
+ from dara.core.internal.pandas_utils import data_response_to_json, df_to_json, is_data_response
54
54
  from dara.core.internal.registries import (
55
55
  action_def_registry,
56
56
  action_registry,
57
57
  backend_store_registry,
58
58
  component_registry,
59
- data_variable_registry,
60
59
  derived_variable_registry,
61
60
  download_code_registry,
62
61
  latest_value_registry,
62
+ server_variable_registry,
63
63
  static_kwargs_registry,
64
64
  template_registry,
65
65
  upload_resolver_registry,
@@ -307,135 +307,65 @@ def create_router(config: Configuration):
307
307
  except KeyError as err:
308
308
  raise ValueError(f'Could not find latest value for derived variable with uid: {uid}') from err
309
309
 
310
- class DataVariableRequestBody(BaseModel):
310
+ class TabularRequestBody(BaseModel):
311
311
  filters: Optional[FilterQuery] = None
312
- cache_key: Optional[str] = None
313
- ws_channel: Optional[str] = None
312
+ ws_channel: str
313
+ dv_values: Optional[NormalizedPayload[List[Any]]] = None
314
+ """DerivedVariable values if variable is a DerivedVariable"""
315
+ force_key: Optional[str] = None
316
+ """Optional force key if variable is a DerivedVariable and a recalculation is forced"""
314
317
 
315
- @core_api_router.post('/data-variable/{uid}', dependencies=[Depends(verify_session)])
316
- async def get_data_variable(
318
+ @core_api_router.post('/tabular-variable/{uid}', dependencies=[Depends(verify_session)])
319
+ async def get_tabular_variable(
317
320
  uid: str,
318
- body: DataVariableRequestBody,
321
+ body: TabularRequestBody,
319
322
  offset: Optional[int] = None,
320
323
  limit: Optional[int] = None,
321
324
  order_by: Optional[str] = None,
322
325
  index: Optional[str] = None,
323
326
  ):
327
+ """
328
+ Generic endpoint for getting tabular data from a variable.
329
+ Supports ServerVariables and DerivedVariables.
330
+ """
331
+ WS_CHANNEL.set(body.ws_channel)
332
+
324
333
  try:
325
- store: CacheStore = utils_registry.get('Store')
326
- task_mgr: TaskManager = utils_registry.get('TaskManager')
334
+ pagination = Pagination(offset=offset, limit=limit, orderBy=order_by, index=index)
327
335
  registry_mgr: RegistryLookup = utils_registry.get('RegistryLookup')
328
- data_variable_entry: DataVariableRegistryEntry = await registry_mgr.get(data_variable_registry, uid)
329
-
330
- data = None
331
- WS_CHANNEL.set(body.ws_channel)
332
-
333
- if data_variable_entry.type == 'derived':
334
- if body.cache_key is None:
335
- raise HTTPException(
336
- status_code=400,
337
- detail='Cache key is required for derived data variables',
338
- )
339
-
340
- if body.ws_channel is None:
341
- raise HTTPException(
342
- status_code=400,
343
- detail='Websocket channel is required for derived data variables',
344
- )
345
-
346
- derived_variable_entry = await registry_mgr.get(derived_variable_registry, uid)
347
-
348
- data = await data_variable_entry.get_data(
349
- derived_variable_entry,
350
- data_variable_entry,
351
- body.cache_key,
352
- store,
353
- body.filters,
354
- Pagination(offset=offset, limit=limit, orderBy=order_by, index=index),
355
- format_for_display=True,
356
- )
357
- if isinstance(data, BaseTask):
358
- await task_mgr.run_task(data, body.ws_channel)
359
- return {'task_id': data.task_id}
360
- elif data_variable_entry.type == 'plain':
361
- data = await data_variable_entry.get_data(
362
- data_variable_entry,
363
- store,
364
- body.filters,
365
- Pagination(offset=offset, limit=limit, orderBy=order_by, index=index),
366
- format_for_display=True,
367
- )
368
336
 
369
- dev_logger.debug(
370
- f'DataVariable {data_variable_entry.uid[:3]}..{data_variable_entry.uid[-3:]}',
371
- 'return value',
372
- {
373
- 'value': data.describe() if isinstance(data, pandas.DataFrame) else None,
374
- 'uid': uid,
375
- }, # type: ignore
376
- )
377
-
378
- if data is None:
379
- return None
380
-
381
- # Explicitly convert to JSON to avoid implicit serialization;
382
- # return as records as that makes more sense in a JSON structure
383
- return Response(
384
- content=df_to_json(data) if isinstance(data, pandas.DataFrame) else data,
385
- media_type='application/json',
386
- ) # type: ignore
387
- except ValueError as e:
388
- raise HTTPException(status_code=400, detail=str(e)) from e
337
+ # ServerVariable
338
+ if body.dv_values is None:
339
+ server_variable_entry = await registry_mgr.get(server_variable_registry, uid)
340
+ data_response = await ServerVariable.get_tabular_data(server_variable_entry, body.filters, pagination)
341
+ return Response(data_response_to_json(data_response), media_type='application/json')
389
342
 
390
- class DataVariableCountRequestBody(BaseModel):
391
- cache_key: Optional[str] = None
392
- filters: Optional[FilterQuery] = None
393
-
394
- @core_api_router.post('/data-variable/{uid}/count', dependencies=[Depends(verify_session)])
395
- async def get_data_variable_count(uid: str, body: Optional[DataVariableCountRequestBody] = None):
396
- try:
343
+ # DerivedVariable
397
344
  store: CacheStore = utils_registry.get('Store')
398
- registry_mgr: RegistryLookup = utils_registry.get('RegistryLookup')
399
- variable_def = await registry_mgr.get(data_variable_registry, uid)
345
+ task_mgr: TaskManager = utils_registry.get('TaskManager')
346
+ variable_def = await registry_mgr.get(derived_variable_registry, uid)
347
+ values = denormalize(body.dv_values.data, body.dv_values.lookup)
400
348
 
401
- if variable_def.type == 'plain':
402
- return await variable_def.get_total_count(
403
- variable_def, store, body.filters if body is not None else None
404
- )
349
+ result = await variable_def.get_tabular_data(
350
+ variable_def, store, task_mgr, values, body.force_key, pagination, body.filters
351
+ )
405
352
 
406
- if body is None or body.cache_key is None:
407
- raise HTTPException(
408
- status_code=400,
409
- detail="Cache key is required when requesting DerivedDataVariable's count",
410
- )
353
+ if isinstance(result, BaseTask):
354
+ await task_mgr.run_task(result, body.ws_channel)
355
+ return {'task_id': result.task_id}
411
356
 
412
- return await variable_def.get_total_count(variable_def, store, body.cache_key, body.filters)
413
- except ValueError as e:
414
- raise HTTPException(status_code=400, detail=str(e)) from e
357
+ return Response(data_response_to_json(result), media_type='application/json')
358
+ except NonTabularDataError as e:
359
+ raise HTTPException(status_code=HTTP_415_UNSUPPORTED_MEDIA_TYPE, detail=str(e)) from e
415
360
 
416
- @core_api_router.get('/data-variable/{uid}/schema', dependencies=[Depends(verify_session)])
417
- async def get_data_variable_schema(uid: str, cache_key: Optional[str] = None):
418
- try:
419
- store: CacheStore = utils_registry.get('Store')
420
- registry_mgr: RegistryLookup = utils_registry.get('RegistryLookup')
421
- data_def = await registry_mgr.get(data_variable_registry, uid)
422
-
423
- if data_def.type == 'plain':
424
- return await data_def.get_schema(data_def, store)
425
-
426
- if cache_key is None:
427
- raise HTTPException(
428
- status_code=400,
429
- detail='Cache key is required when requesting DerivedDataVariable schema',
430
- )
431
-
432
- # Use the other registry for derived variables
433
- derived_ref = await registry_mgr.get(derived_variable_registry, uid)
434
- data = await data_def.get_schema(derived_ref, store, cache_key)
435
- content = json.dumps(jsonable_encoder(data)) if isinstance(data, dict) else data
436
- return Response(content=content, media_type='application/json')
437
- except ValueError as e:
438
- raise HTTPException(status_code=400, detail=str(e)) from e
361
+ @core_api_router.get('/server-variable/{uid}/sequence', dependencies=[Depends(verify_session)])
362
+ async def get_server_variable_sequence(
363
+ uid: str,
364
+ ):
365
+ registry_mgr: RegistryLookup = utils_registry.get('RegistryLookup')
366
+ server_variable_entry = await registry_mgr.get(server_variable_registry, uid)
367
+ seq_num = await ServerVariable.get_sequence_number(server_variable_entry)
368
+ return {'sequence_number': seq_num}
439
369
 
440
370
  @core_api_router.post('/data/upload', dependencies=[Depends(verify_session)])
441
371
  async def upload_data(
@@ -473,7 +403,6 @@ def create_router(config: Configuration):
473
403
  values: NormalizedPayload[List[Any]]
474
404
  force_key: Optional[str] = None
475
405
  ws_channel: str
476
- is_data_variable: Optional[bool] = False
477
406
 
478
407
  @core_api_router.post('/derived-variable/{uid}', dependencies=[Depends(verify_session)])
479
408
  async def get_derived_variable(uid: str, body: DerivedStateRequestBody):
@@ -553,9 +482,11 @@ def create_router(config: Configuration):
553
482
  {'value': res},
554
483
  )
555
484
 
556
- # Serialize dataframes correctly
485
+ # Serialize dataframes correctly, either direct or as a DataResponse
557
486
  if isinstance(res, DataFrame):
558
- return Response(df_to_json(res))
487
+ return Response(df_to_json(res), media_type='application/json')
488
+ elif is_data_response(res):
489
+ return Response(data_response_to_json(res), media_type='application/json')
559
490
 
560
491
  return res
561
492
  except Exception as err:
@@ -17,7 +17,7 @@ limitations under the License.
17
17
 
18
18
  from typing import Any, Dict, List, Optional
19
19
 
20
- from dara.core.base_definitions import CacheType, PendingTask, PendingValue
20
+ from dara.core.base_definitions import CacheType, PendingTask
21
21
  from dara.core.internal.utils import get_cache_scope
22
22
 
23
23
 
@@ -59,8 +59,6 @@ class Store:
59
59
  cache_key = get_cache_scope(cache_type)
60
60
  value = self._store.get(cache_key, {}).get(key)
61
61
 
62
- if isinstance(value, PendingValue):
63
- return await value.wait()
64
62
  if isinstance(value, PendingTask):
65
63
  return await value.run()
66
64
  return value
@@ -86,33 +84,8 @@ class Store:
86
84
  if self._store.get(cache_key) is None:
87
85
  self._store[cache_key] = {}
88
86
 
89
- # If there is a PendingValue set for this key then trigger its resolution
90
- if isinstance(self._store[cache_key].get(key), PendingValue):
91
- if error is not None:
92
- self._store[cache_key][key].error(error)
93
- else:
94
- self._store[cache_key][key].resolve(value)
95
-
96
87
  self._store[cache_key][key] = value
97
88
 
98
- def set_pending_value(self, key: str, cache_type: Optional[CacheType] = CacheType.GLOBAL):
99
- """
100
- Set a pending state for a value in the store. This will trigger the async behavior of the get call if subsequent
101
- requests ask for the same key. A future is created in the store, which all requests then listen for the
102
- resolution of before returning.
103
-
104
- :param key: the key to set as pending
105
- :param cache_type: whether to pull the value from the specified cache specific store or the global one, defaults to
106
- the global one
107
- """
108
- cache_key = get_cache_scope(cache_type)
109
- if self._store.get(cache_key) is None:
110
- self._store[cache_key] = {}
111
-
112
- pending_val = PendingValue()
113
-
114
- self._store[cache_key][key] = pending_val
115
-
116
89
  def set_pending_task(self, key: str, pending_task: PendingTask, cache_type: Optional[CacheType] = CacheType.GLOBAL):
117
90
  """
118
91
  Store a pending task state for a given key in the store. This will trigger the async behavior of the get call if subsequent
@@ -158,7 +131,7 @@ class Store:
158
131
  # Otherwise go through and remove any non-pending values
159
132
  keys = list(cache_type_store.keys())
160
133
  for key in keys:
161
- if not isinstance(cache_type_store[key], (PendingValue, PendingTask)):
134
+ if not isinstance(cache_type_store[key], PendingTask):
162
135
  cache_type_store.pop(key)
163
136
 
164
137
  def list(self, cache_type: Optional[CacheType] = CacheType.GLOBAL) -> List[str]: