dara-core 1.20.1a1__py3-none-any.whl → 1.20.1a3__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 (82) hide show
  1. dara/core/__init__.py +3 -0
  2. dara/core/actions.py +1 -2
  3. dara/core/auth/basic.py +22 -16
  4. dara/core/auth/definitions.py +2 -2
  5. dara/core/auth/routes.py +5 -5
  6. dara/core/auth/utils.py +5 -5
  7. dara/core/base_definitions.py +22 -64
  8. dara/core/cli.py +8 -7
  9. dara/core/configuration.py +5 -2
  10. dara/core/css.py +1 -2
  11. dara/core/data_utils.py +18 -19
  12. dara/core/defaults.py +6 -7
  13. dara/core/definitions.py +50 -19
  14. dara/core/http.py +7 -3
  15. dara/core/interactivity/__init__.py +6 -0
  16. dara/core/interactivity/actions.py +52 -50
  17. dara/core/interactivity/any_data_variable.py +7 -134
  18. dara/core/interactivity/any_variable.py +5 -8
  19. dara/core/interactivity/client_variable.py +71 -0
  20. dara/core/interactivity/data_variable.py +8 -266
  21. dara/core/interactivity/derived_data_variable.py +7 -290
  22. dara/core/interactivity/derived_variable.py +416 -176
  23. dara/core/interactivity/filtering.py +46 -27
  24. dara/core/interactivity/loop_variable.py +2 -2
  25. dara/core/interactivity/non_data_variable.py +5 -68
  26. dara/core/interactivity/plain_variable.py +89 -15
  27. dara/core/interactivity/server_variable.py +325 -0
  28. dara/core/interactivity/state_variable.py +69 -0
  29. dara/core/interactivity/switch_variable.py +19 -19
  30. dara/core/interactivity/tabular_variable.py +94 -0
  31. dara/core/interactivity/url_variable.py +10 -90
  32. dara/core/internal/cache_store/base_impl.py +2 -1
  33. dara/core/internal/cache_store/cache_store.py +22 -25
  34. dara/core/internal/cache_store/keep_all.py +4 -1
  35. dara/core/internal/cache_store/lru.py +5 -1
  36. dara/core/internal/cache_store/ttl.py +4 -1
  37. dara/core/internal/cgroup.py +1 -1
  38. dara/core/internal/dependency_resolution.py +60 -66
  39. dara/core/internal/devtools.py +12 -5
  40. dara/core/internal/download.py +13 -4
  41. dara/core/internal/encoder_registry.py +7 -7
  42. dara/core/internal/execute_action.py +13 -13
  43. dara/core/internal/hashing.py +1 -3
  44. dara/core/internal/import_discovery.py +3 -4
  45. dara/core/internal/multi_resource_lock.py +70 -0
  46. dara/core/internal/normalization.py +9 -18
  47. dara/core/internal/pandas_utils.py +107 -5
  48. dara/core/internal/pool/definitions.py +1 -1
  49. dara/core/internal/pool/task_pool.py +25 -16
  50. dara/core/internal/pool/utils.py +21 -18
  51. dara/core/internal/pool/worker.py +3 -2
  52. dara/core/internal/port_utils.py +1 -1
  53. dara/core/internal/registries.py +12 -6
  54. dara/core/internal/registry.py +4 -2
  55. dara/core/internal/registry_lookup.py +11 -5
  56. dara/core/internal/routing.py +109 -145
  57. dara/core/internal/scheduler.py +13 -8
  58. dara/core/internal/settings.py +2 -2
  59. dara/core/internal/store.py +2 -29
  60. dara/core/internal/tasks.py +379 -195
  61. dara/core/internal/utils.py +36 -13
  62. dara/core/internal/websocket.py +21 -20
  63. dara/core/js_tooling/js_utils.py +28 -26
  64. dara/core/js_tooling/templates/vite.config.template.ts +12 -3
  65. dara/core/logging.py +13 -12
  66. dara/core/main.py +14 -11
  67. dara/core/metrics/cache.py +1 -1
  68. dara/core/metrics/utils.py +3 -3
  69. dara/core/persistence.py +27 -5
  70. dara/core/umd/dara.core.umd.js +68291 -64718
  71. dara/core/visual/components/__init__.py +2 -2
  72. dara/core/visual/components/fallback.py +30 -4
  73. dara/core/visual/components/for_cmp.py +4 -1
  74. dara/core/visual/css/__init__.py +30 -31
  75. dara/core/visual/dynamic_component.py +31 -28
  76. dara/core/visual/progress_updater.py +4 -3
  77. {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/METADATA +12 -11
  78. dara_core-1.20.1a3.dist-info/RECORD +119 -0
  79. dara_core-1.20.1a1.dist-info/RECORD +0 -114
  80. {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/LICENSE +0 -0
  81. {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/WHEEL +0 -0
  82. {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/entry_points.txt +0 -0
@@ -17,46 +17,26 @@ limitations under the License.
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- import asyncio
21
- from typing import Any, Callable, Coroutine, List, Optional, Union, cast
22
- from uuid import uuid4
20
+ from collections.abc import Coroutine
21
+ from typing import Any, Callable, List, Optional, Union
23
22
 
24
23
  from pandas import DataFrame
25
- from pandas.io.json._table_schema import build_table_schema
26
- from pydantic import ConfigDict, SerializerFunctionWrapHandler, model_serializer
24
+ from typing_extensions import deprecated
27
25
 
28
26
  from dara.core.base_definitions import (
29
- BaseTask,
30
27
  Cache,
31
28
  CacheArgType,
32
- PendingTask,
33
- PendingValue,
34
- )
35
- from dara.core.interactivity.any_data_variable import (
36
- AnyDataVariable,
37
- DataFrameSchema,
38
- DataVariableRegistryEntry,
39
29
  )
40
30
  from dara.core.interactivity.any_variable import AnyVariable
41
31
  from dara.core.interactivity.derived_variable import (
42
32
  DerivedVariable,
43
- DerivedVariableRegistryEntry,
44
- DerivedVariableResult,
45
- )
46
- from dara.core.interactivity.filtering import (
47
- FilterQuery,
48
- Pagination,
49
- apply_filters,
50
- coerce_to_filter_query,
51
33
  )
52
- from dara.core.internal.cache_store import CacheStore
53
- from dara.core.internal.hashing import hash_object
54
- from dara.core.internal.pandas_utils import append_index, df_convert_to_internal
55
- from dara.core.internal.tasks import MetaTask, Task, TaskManager
56
- from dara.core.logging import eng_logger
57
34
 
58
35
 
59
- class DerivedDataVariable(AnyDataVariable, DerivedVariable):
36
+ @deprecated(
37
+ 'DerivedDataVariable is deprecated and will be removed in a future version. Use dara.core.interactivity.derived_variable.DerivedVariable instead, it can now return DataFrames'
38
+ )
39
+ class DerivedDataVariable(DerivedVariable):
60
40
  """
61
41
  DerivedDataVariable represents a variable designed to hold datasets computed
62
42
  by a resolver function like a normal DerivedVariable.
@@ -64,13 +44,6 @@ class DerivedDataVariable(AnyDataVariable, DerivedVariable):
64
44
  Note: the resolver function must return a DataFrame.
65
45
  """
66
46
 
67
- uid: str
68
- filters: Optional[FilterQuery] = None
69
- variables: List[AnyVariable]
70
- polling_interval: Optional[int] = None
71
- deps: Optional[List[AnyVariable]] = None
72
- model_config = ConfigDict(extra='forbid')
73
-
74
47
  def __init__(
75
48
  self,
76
49
  func: Union[
@@ -106,9 +79,6 @@ class DerivedDataVariable(AnyDataVariable, DerivedVariable):
106
79
  - `deps = [var1, var2]` - `func` is ran whenever one of these vars changes
107
80
  :param uid: the unique identifier for this variable; if not provided a random one is generated
108
81
  """
109
- cache = Cache.Policy.from_arg(cache)
110
-
111
- # Initialize the DV underneath, which puts an entry in the derived variable registry
112
82
  super().__init__(
113
83
  func=func,
114
84
  cache=cache,
@@ -117,257 +87,4 @@ class DerivedDataVariable(AnyDataVariable, DerivedVariable):
117
87
  polling_interval=polling_interval,
118
88
  deps=deps,
119
89
  run_as_task=run_as_task,
120
- _get_value=DerivedDataVariable.get_value,
121
- )
122
-
123
- # Also put an entry in the data variable registry under the same uid; this way we can send a request
124
- # for either the DV (to update the cached value) or the DataVariable (to get the cached value)
125
- from dara.core.internal.registries import data_variable_registry
126
-
127
- data_variable_registry.register(
128
- str(self.uid),
129
- DataVariableRegistryEntry(
130
- type='derived',
131
- cache=cache,
132
- uid=str(self.uid),
133
- get_data=DerivedDataVariable.get_data,
134
- get_total_count=DerivedDataVariable.get_total_count,
135
- get_schema=DerivedDataVariable.get_schema,
136
- ),
137
- )
138
-
139
- @staticmethod
140
- def _get_schema_cache_key(cache_key: str) -> str:
141
- """
142
- Get a unique cache key for the data variable's schema.
143
-
144
- :param cache_key: cache_key of the DerivedDataVariable
145
- """
146
- return f'schema-{cache_key}'
147
-
148
- @staticmethod
149
- async def _filter_data(
150
- data: Union[DataFrame, Any, None],
151
- count_cache_key: str,
152
- var_entry: DataVariableRegistryEntry,
153
- store: CacheStore,
154
- filters: Optional[Union[FilterQuery, dict]] = None,
155
- pagination: Optional[Pagination] = None,
156
- ) -> Optional[DataFrame]:
157
- """
158
- Helper function to apply filters and pagination to a dataframe.
159
- Also verifies if the data is a DataFrame.
160
-
161
- :param data: data to filter
162
- :param count_cache_key: cache key to store the count under
163
- :param var_entry: data variable entry
164
- :param store: store instance
165
- :param filters: filters to use
166
- :param pagination: pagination to use
167
- """
168
- if data is not None and not isinstance(data, DataFrame):
169
- raise ValueError(f'Data returned by DerivedDataVariable resolver must be a DataFrame, found {type(data)}')
170
-
171
- # Right before we filter, append index column to the dataset
172
- data = append_index(data)
173
-
174
- filtered_data, count = apply_filters(data, coerce_to_filter_query(filters), pagination)
175
-
176
- # Cache the count
177
- await store.set(var_entry, key=count_cache_key, value=count, pin=True)
178
-
179
- return filtered_data
180
-
181
- @classmethod
182
- async def get_value(
183
- cls,
184
- var_entry: DerivedVariableRegistryEntry,
185
- store: CacheStore,
186
- task_mgr: TaskManager,
187
- args: List[Any],
188
- force: bool = False,
189
- ) -> DerivedVariableResult:
190
- """
191
- Update the underlying derived variable.
192
- Wrapper around DerivedVariable.get_value which does not return the value (returns `True` instead).
193
-
194
- :param var: the registry entry for the underlying derived variable
195
- :param store: the store instance to check for cached values
196
- :param task_mgr: task manager instance
197
- :param args: the arguments to call the underlying function with
198
- :param force: whether to ignore cache
199
- """
200
- _uid_short = f'{var_entry.uid[:3]}..{var_entry.uid[-3:]}'
201
- eng_logger.info(
202
- f'Derived Data Variable {_uid_short} calling superclass get_value', {'uid': var_entry.uid, 'args': args}
203
- )
204
- value = await super().get_value(var_entry, store, task_mgr, args, force)
205
-
206
- # Pin the value in the store until it's read by get data
207
- await asyncio.gather(
208
- store.set(registry_entry=var_entry, key=value['cache_key'], value=value['value'], pin=True),
209
- store.set(
210
- registry_entry=var_entry,
211
- key=cls._get_schema_cache_key(value['cache_key']),
212
- value=build_table_schema(
213
- df_convert_to_internal(cast(DataFrame, value['value'])),
214
- )
215
- if isinstance(value['value'], DataFrame)
216
- else None,
217
- pin=True,
218
- ),
219
- )
220
-
221
- eng_logger.info(
222
- f'Derived Data Variable {_uid_short} received result from superclass',
223
- {'uid': var_entry.uid, 'result': value},
224
90
  )
225
-
226
- # If the value is a task, then we need to return it
227
- if isinstance(value['value'], BaseTask):
228
- return value
229
-
230
- return {'cache_key': value['cache_key'], 'value': True}
231
-
232
- @classmethod
233
- async def get_data(
234
- cls,
235
- dv_entry: DerivedVariableRegistryEntry,
236
- data_entry: DataVariableRegistryEntry,
237
- cache_key: str,
238
- store: CacheStore,
239
- filters: Optional[Union[FilterQuery, dict]] = None,
240
- pagination: Optional[Pagination] = None,
241
- format_for_display: bool = False,
242
- ) -> Union[BaseTask, DataFrame, None]:
243
- """
244
- Get the filtered data from the underlying derived variable stored under the specified cache_key.
245
-
246
- :param var_entry: the registry entry for the data variable
247
- :param cache_key: cache_key of the underlying DerivedVariable
248
- :param store: the store instance to check for cached values
249
- :param filters: the filters to apply to the data
250
- :param pagination: the pagination to apply to the data
251
- """
252
- _uid_short = f'{data_entry.uid[:3]}..{data_entry.uid[-3:]}'
253
- data_cache_key = f'{cache_key}_{hash_object(filters or {})}_{hash_object(pagination or {})}'
254
- count_cache_key = f'{cache_key}_{hash_object(filters or {})}'
255
-
256
- # Check for cached result of the entire data variable
257
- data_store_entry = await store.get(data_entry, key=data_cache_key)
258
-
259
- # if there's a pending task for this exact request, subscribe to the pending task and return it
260
- if isinstance(data_store_entry, PendingTask):
261
- data_store_entry.add_subscriber()
262
- return data_store_entry
263
-
264
- # Found cached result
265
- if isinstance(data_store_entry, DataFrame):
266
- return data_store_entry
267
-
268
- # First retrieve the cached data for underlying DV
269
- data = await store.get(dv_entry, key=cache_key, unpin=True)
270
-
271
- # Value could have been made pending in the meantime
272
- if isinstance(data, PendingValue):
273
- data = await data.wait()
274
-
275
- eng_logger.info(
276
- f'Derived Data Variable {_uid_short} retrieved underlying DV value', {'uid': dv_entry.uid, 'value': data}
277
- )
278
-
279
- # if the DV returned a task (Task/PendingTask), return a MetaTask which will do the filtering on the task result
280
- if isinstance(data, BaseTask):
281
- task_id = f'{dv_entry.uid}_Filter_MetaTask_{str(uuid4())}'
282
-
283
- eng_logger.info(
284
- f'Derived Data Variable {_uid_short} creating filtering metatask',
285
- {'uid': dv_entry.uid, 'task_id': task_id, 'cache_key': data_cache_key},
286
- )
287
-
288
- return MetaTask(
289
- cls._filter_data,
290
- [data, count_cache_key, data_entry, store, filters, pagination],
291
- notify_channels=data.notify_channels,
292
- process_as_task=False,
293
- cache_key=data_cache_key,
294
- reg_entry=data_entry, # task results are set as the variable result
295
- task_id=task_id,
296
- )
297
-
298
- # Run the filtering
299
- data = await cls._filter_data(data, count_cache_key, data_entry, store, filters, pagination)
300
- if format_for_display and data is not None:
301
- data = data.copy()
302
- for col in data.columns:
303
- if data[col].dtype == 'object':
304
- # We need to convert all values to string to avoid issues with displaying data in the Table component, for example when displaying datetime and number objects in the same column
305
- data.loc[:, col] = data[col].apply(str)
306
-
307
- return data
308
-
309
- @classmethod
310
- async def get_total_count(
311
- cls, data_entry: DataVariableRegistryEntry, store: CacheStore, cache_key: str, filters: Optional[FilterQuery]
312
- ):
313
- """
314
- Get total count of the derived data variable.
315
- """
316
- count_cache_key = f'{cache_key}_{hash_object(filters or {})}'
317
- entry = await store.get(data_entry, key=count_cache_key, unpin=True)
318
-
319
- # No entry means this filter setup has not been done yet, this shouldn't happen
320
- if entry is None:
321
- raise ValueError('Requested count for filter setup which has not been performed yet')
322
-
323
- return entry
324
-
325
- @classmethod
326
- async def get_schema(cls, derived_entry: DerivedVariableRegistryEntry, store: CacheStore, cache_key: str):
327
- """
328
- Get the schema of the derived data variable.
329
- """
330
- return cast(
331
- DataFrameSchema, await store.get(derived_entry, key=cls._get_schema_cache_key(cache_key), unpin=True)
332
- )
333
-
334
- @classmethod
335
- async def resolve_value(
336
- cls,
337
- data_entry: DataVariableRegistryEntry,
338
- dv_entry: DerivedVariableRegistryEntry,
339
- store: CacheStore,
340
- task_mgr: TaskManager,
341
- args: List[Any],
342
- force: bool,
343
- filters: Optional[Union[FilterQuery, dict]] = None,
344
- ):
345
- """
346
- Helper method to resolve the filtered value of a derived data variable.
347
- Under the hood runs the underlying DerivedVariable, starts its task if required, and then filters the result.
348
-
349
- :param data_entry: the registry entry for the data variable
350
- :param dv_entry: the registry entry for the underlying derived variable
351
- :param store: the store instance to check for cached values
352
- :param task_mgr: task manager instance
353
- :param args: the arguments to call the underlying function with
354
- :param force: whether to ignore cache
355
- :param filters: the filters to apply to the data
356
- :param pagination: the pagination to apply to the data
357
- """
358
- dv_result = await cls.get_value(dv_entry, store, task_mgr, args, force)
359
-
360
- # If the intermediate result was a task/metatask, we need to run it
361
- # get_data will then pick up the result from the pending task for it
362
- if isinstance(dv_result['value'], (Task, MetaTask)):
363
- await task_mgr.run_task(dv_result['value'], None)
364
-
365
- return await cls.get_data(dv_entry, data_entry, dv_result['cache_key'], store, filters)
366
-
367
- @model_serializer(mode='wrap')
368
- def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
369
- parent_dict = nxt(self)
370
- # nested is not supported for DerivedDataVariable so remove from serialised form
371
- # it's included because we inherit from DV which has the field
372
- parent_dict.pop('nested')
373
- return {**parent_dict, '__typename': 'DerivedDataVariable', 'uid': str(parent_dict['uid'])}