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
@@ -1,138 +1,11 @@
1
- """
2
- Copyright 2023 Impulse Innovations Limited
3
-
4
-
5
- Licensed under the Apache License, Version 2.0 (the "License");
6
- you may not use this file except in compliance with the License.
7
- You may obtain a copy of the License at
8
-
9
- http://www.apache.org/licenses/LICENSE-2.0
10
-
11
- Unless required by applicable law or agreed to in writing, software
12
- distributed under the License is distributed on an "AS IS" BASIS,
13
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- See the License for the specific language governing permissions and
15
- limitations under the License.
16
- """
17
-
18
- import abc
19
- import io
20
- import os
21
- from typing import Any, Awaitable, Callable, Literal, Optional, TypedDict, Union, cast
22
-
23
- import pandas
24
- from fastapi import UploadFile
25
- from pydantic import ConfigDict
1
+ from typing_extensions import TypeAlias
26
2
 
27
- from dara.core.base_definitions import CachedRegistryEntry, UploadResolverDef
28
3
  from dara.core.interactivity.any_variable import AnyVariable
29
- from dara.core.interactivity.filtering import FilterQuery
30
- from dara.core.internal.cache_store.cache_store import CacheStore
31
- from dara.core.internal.registry_lookup import RegistryLookup
32
- from dara.core.internal.utils import run_user_handler
33
-
34
-
35
- class AnyDataVariable(AnyVariable, abc.ABC):
36
- """
37
- AnyDataVariable represents any variable that is specifically designed to hold datasets (i.e. DataVariable, DerivedDataVariable)
38
-
39
- :param uid: the unique identifier for this variable; if not provided a random one is generated
40
- :param filters: a dictionary of filters to apply to the data
41
- """
42
-
43
- uid: str
44
- filters: Optional[FilterQuery] = None
45
-
46
- def __init__(self, uid: Optional[str] = None, **kwargs) -> None:
47
- super().__init__(uid=uid, **kwargs)
48
-
49
- def filter(self, filters: FilterQuery):
50
- return self.copy(update={'filters': filters}, deep=True)
51
-
52
-
53
- class FieldType(TypedDict):
54
- name: Union[str, tuple[str, ...]]
55
- type: Literal['integer', 'number', 'boolean', 'datetime', 'duration', 'any', 'str']
56
-
57
-
58
- class DataFrameSchema(TypedDict):
59
- fields: list[FieldType]
60
- primaryKey: list[str]
61
-
62
4
 
63
- class DataVariableRegistryEntry(CachedRegistryEntry):
64
- """
65
- Registry entry for DataVariable.
66
- """
5
+ # re-export for backwards compatibility
6
+ from .tabular_variable import * # noqa: F403
67
7
 
68
- type: Literal['plain', 'derived']
69
- get_data: Callable[..., Awaitable[Any]]
70
- """Handler to get the data from the data variable. Defaults to DataVariable.get_value for type=plain, and DerivedDataVariable.get_data for type=derived"""
71
-
72
- get_total_count: Callable[..., Awaitable[int]]
73
- """Handler to get the total number of rows in the data variable. Defaults to DataVariable.get_total_count for type=plain, and DerivedDataVariable.get_total_count for type=derived"""
74
-
75
- get_schema: Callable[..., Awaitable[DataFrameSchema]]
76
- """Handler to get the schema for data variable. Defaults to DataVariable.get_schema for type=plain, and DerivedDataVariable.get_schema for type=derived"""
77
- model_config = ConfigDict(extra='forbid', arbitrary_types_allowed=True)
78
-
79
-
80
- async def upload(data: UploadFile, data_uid: Optional[str] = None, resolver_id: Optional[str] = None):
81
- """
82
- Handler for uploading data.
83
-
84
- :param data: the file to upload
85
- :param data_uid: optional uid of the data variable to upload to
86
- :param resolver_id: optional id of the upload resolver to use, falls back to default handlers for csv/xlsx
87
- """
88
- from dara.core.interactivity.data_variable import DataVariable
89
- from dara.core.internal.registries import (
90
- data_variable_registry,
91
- upload_resolver_registry,
92
- utils_registry,
93
- )
94
-
95
- store: CacheStore = utils_registry.get('Store')
96
- registry_mgr: RegistryLookup = utils_registry.get('RegistryLookup')
97
-
98
- if data.filename is None:
99
- raise ValueError('Filename not provided')
100
-
101
- variable = None
102
-
103
- _name, file_type = os.path.splitext(data.filename)
104
-
105
- if data_uid is not None:
106
- try:
107
- variable = await registry_mgr.get(data_variable_registry, data_uid)
108
- except KeyError:
109
- raise ValueError(f'Data Variable {data_uid} does not exist')
110
-
111
- if variable.type == 'derived':
112
- raise ValueError('Cannot upload data to DerivedDataVariable')
113
-
114
- content = cast(bytes, await data.read())
115
-
116
- resolver = None
117
-
118
- # If Id is provided, lookup the definition from registry
119
- if resolver_id is not None:
120
- resolver_def: UploadResolverDef = await registry_mgr.get(upload_resolver_registry, resolver_id)
121
- resolver = resolver_def.resolver
122
-
123
- if resolver:
124
- content = await run_user_handler(handler=resolver, args=(content, data.filename))
125
- # If resolver is not provided, follow roughly the cl_dataset_parser logic
126
- elif file_type == '.xlsx':
127
- file_object_xlsx = io.BytesIO(content)
128
- content = pandas.read_excel(file_object_xlsx, index_col=None)
129
- content.columns = content.columns.str.replace('Unnamed: *', 'column_', regex=True) # type: ignore
130
- else:
131
- # default to csv
132
- file_object_csv = io.StringIO(content.decode('utf-8'))
133
- content = pandas.read_csv(file_object_csv, index_col=0)
134
- content.columns = content.columns.str.replace('Unnamed: *', 'column_', regex=True) # type: ignore
135
-
136
- # If a data variable is provided, update it with the new content
137
- if variable:
138
- DataVariable.update_value(variable, store, content)
8
+ AnyDataVariable: TypeAlias = AnyVariable
9
+ """
10
+ Deprecated alias. Tabular variables are now DerivedVariable or ServerVariable
11
+ """
@@ -30,9 +30,8 @@ from fastapi.encoders import jsonable_encoder
30
30
  from pydantic import ConfigDict
31
31
 
32
32
  from dara.core.auth.definitions import SESSION_ID, USER, UserData
33
- from dara.core.base_definitions import BaseTask
33
+ from dara.core.base_definitions import BaseTask, PendingTask
34
34
  from dara.core.base_definitions import DaraBaseModel as BaseModel
35
- from dara.core.base_definitions import PendingTask
36
35
  from dara.core.interactivity.condition import Condition, Operator
37
36
  from dara.core.internal.cache_store import CacheStore
38
37
  from dara.core.internal.tasks import TaskManager
@@ -112,7 +111,7 @@ async def get_current_value(variable: dict, timeout: float = 3, raw: bool = Fals
112
111
  user_identity = None
113
112
 
114
113
  if current_user is not None:
115
- user_identity = current_user.identity_id or current_user.identity_name
114
+ user_identity = current_user.identity_id
116
115
  elif isinstance(auth_config, BasicAuthConfig):
117
116
  # basic auth - assume it's the single existing user
118
117
  user_identity = list(auth_config.users.keys())[0]
@@ -204,7 +203,6 @@ async def get_current_value(variable: dict, timeout: float = 3, raw: bool = Fals
204
203
 
205
204
  for session, channels in session_channels.items():
206
205
  for ws in channels:
207
-
208
206
  raw_result = raw_results[ws]
209
207
  # Skip values from clients where the variable is not registered
210
208
  if raw_result == NOT_REGISTERED:
@@ -238,12 +236,12 @@ async def get_current_value(variable: dict, timeout: float = 3, raw: bool = Fals
238
236
 
239
237
  # If we're returning multiple values, in Jupyter environments print an explainer
240
238
  try:
241
- from IPython import get_ipython
239
+ from IPython import get_ipython # pyright: ignore[reportMissingImports]
242
240
  except ImportError:
243
241
  pass
244
242
  else:
245
243
  if get_ipython() is not None:
246
- from IPython.display import HTML, display
244
+ from IPython.display import HTML, display # pyright: ignore[reportMissingImports]
247
245
 
248
246
  display(
249
247
  HTML(
@@ -272,7 +270,7 @@ async def get_current_value(variable: dict, timeout: float = 3, raw: bool = Fals
272
270
  return results
273
271
 
274
272
 
275
- class AnyVariable(BaseModel, abc.ABC):
273
+ class AnyVariable(BaseModel, abc.ABC): # noqa: PLW1641 # we override equals to create conditions, otherwise we should define hash
276
274
  """
277
275
  Base class for all variables. Used for typing to specify that any variable can be provided.
278
276
  """
@@ -282,7 +280,6 @@ class AnyVariable(BaseModel, abc.ABC):
282
280
  uid: str
283
281
 
284
282
  def __init__(self, uid: Optional[str] = None, **kwargs) -> None:
285
-
286
283
  new_uid = uid
287
284
  if new_uid is None:
288
285
  new_uid = str(uuid.uuid4())
@@ -0,0 +1,71 @@
1
+ """
2
+ Copyright 2023 Impulse Innovations Limited
3
+
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import abc
21
+ from typing import Optional
22
+
23
+ from dara.core.interactivity.any_variable import AnyVariable
24
+
25
+
26
+ class ClientVariable(AnyVariable, abc.ABC):
27
+ """
28
+ Client represents any variable which can be ordinarily serialized to the client
29
+
30
+ :param uid: the unique identifier for this variable; if not provided a random one is generated
31
+ """
32
+
33
+ uid: str
34
+
35
+ def __init__(self, uid: Optional[str] = None, **kwargs) -> None:
36
+ super().__init__(uid=uid, **kwargs)
37
+
38
+ @property
39
+ def list_item(self):
40
+ """
41
+ Get a LoopVariable that represents the current item in the list.
42
+ Should only be used in conjunction with the `For` component.
43
+
44
+ Note that it is a type of a Variable so it can be used in places where a regular Variable is expected.
45
+
46
+ By default, the entire list item is used as the item.
47
+
48
+ `LoopVariable` supports nested property access using `get` or index access i.e. `[]`.
49
+ You can mix and match those two methods to access nested properties as they are equivalent.
50
+
51
+ ```python
52
+ my_list = Variable(['foo', 'bar', 'baz'])
53
+
54
+ # Represents the entire item in the list
55
+ my_list.list_item
56
+
57
+ my_list_of_objects = Variable([
58
+ {'id': 1, 'name': 'John', 'data': {'city': 'London', 'country': 'UK'}},
59
+ {'id': 2, 'name': 'Jane', 'data': {'city': 'Paris', 'country': 'France'}},
60
+ ])
61
+
62
+ # Represents the item 'name' property
63
+ my_list_of_objects.list_item['name']
64
+
65
+ # Represents the item 'data.country' property
66
+ my_list_of_objects.list_item.get('data')['country']
67
+ """
68
+
69
+ from .loop_variable import LoopVariable
70
+
71
+ return LoopVariable()
@@ -17,40 +17,15 @@ limitations under the License.
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- import asyncio
21
- from typing import Optional, Union, cast
20
+ from typing import Optional
22
21
 
23
- from anyio.abc import TaskGroup
24
22
  from pandas import DataFrame
25
- from pandas.io.json._table_schema import build_table_schema
26
- from pydantic import (
27
- BaseModel,
28
- ConfigDict,
29
- SerializerFunctionWrapHandler,
30
- model_serializer,
31
- )
32
23
 
33
- from dara.core.base_definitions import BaseCachePolicy, Cache, CacheArgType
34
- from dara.core.interactivity.any_data_variable import (
35
- AnyDataVariable,
36
- DataFrameSchema,
37
- DataVariableRegistryEntry,
38
- )
39
- from dara.core.interactivity.filtering import (
40
- FilterQuery,
41
- Pagination,
42
- apply_filters,
43
- coerce_to_filter_query,
44
- )
45
- from dara.core.internal.cache_store import CacheStore
46
- from dara.core.internal.hashing import hash_object
47
- from dara.core.internal.pandas_utils import append_index, df_convert_to_internal
48
- from dara.core.internal.utils import call_async
49
- from dara.core.internal.websocket import WebsocketManager
50
- from dara.core.logging import eng_logger
24
+ from dara.core.base_definitions import Cache, CacheArgType
25
+ from dara.core.interactivity.server_variable import ServerVariable
51
26
 
52
27
 
53
- class DataVariable(AnyDataVariable):
28
+ class DataVariable(ServerVariable):
54
29
  """
55
30
  DataVariable represents a variable that is specifically designed to hold datasets.
56
31
 
@@ -69,11 +44,6 @@ class DataVariable(AnyDataVariable):
69
44
  >>> UpdateVariable(func=some_data_transformation, variable=session_data)
70
45
  """
71
46
 
72
- uid: str
73
- filters: Optional[FilterQuery] = None
74
- cache: Optional[BaseCachePolicy] = None
75
- model_config = ConfigDict(extra='forbid', arbitrary_types_allowed=True, use_enum_values=True)
76
-
77
47
  def __init__(
78
48
  self,
79
49
  data: Optional[DataFrame] = None,
@@ -89,239 +59,11 @@ class DataVariable(AnyDataVariable):
89
59
  :param filters: a dictionary of filters to apply to the data
90
60
  :param cache: how to cache the result; 'user' per user, 'session' per session, 'global' for all users
91
61
  """
92
- cache = Cache.Policy.from_arg(cache)
62
+ cache_policy = Cache.Policy.from_arg(cache)
93
63
 
94
- if data is not None and cache.cache_type is not Cache.Type.GLOBAL:
64
+ if data is not None and cache_policy.cache_type is not Cache.Type.GLOBAL:
95
65
  raise ValueError('Data cannot be cached per session or per user if provided upfront')
96
66
 
97
- super().__init__(cache=cache, uid=uid, **kwargs)
98
-
99
- # Register the variable with the dataset
100
- from dara.core.internal.registries import data_variable_registry
101
-
102
- var_entry = DataVariableRegistryEntry(
103
- cache=cache,
104
- uid=str(self.uid),
105
- type='plain',
106
- get_data=DataVariable.get_value,
107
- get_total_count=DataVariable.get_total_count,
108
- get_schema=DataVariable.get_schema,
109
- )
110
- data_variable_registry.register(
111
- str(self.uid),
112
- var_entry,
113
- )
114
-
115
- # Put the data entry into the store if not empty (so cache='global')
116
- # We don't create an entry in a different case since session key will be global anyway at this point
117
- if data is not None:
118
- from dara.core.internal.registries import utils_registry
119
-
120
- store: CacheStore = utils_registry.get('Store')
121
- call_async(self._update, var_entry, store, data)
122
-
123
- @staticmethod
124
- def _get_cache_key(uid: str) -> str:
125
- """
126
- Get a unique cache key for the data variable.
127
-
128
- :param uid: uid of the DataVariable
129
- """
130
- return f'data-{uid}'
131
-
132
- @staticmethod
133
- def _get_schema_cache_key(uid: str) -> str:
134
- """
135
- Get a unique cache key for the data variable's schema.
136
-
137
- :param uid: uid of the DataVariable
138
- """
139
- return f'schema-{uid}'
140
-
141
- @classmethod
142
- def _get_count_cache_key(cls, uid: str, filters: Optional[Union[FilterQuery, dict]]) -> str:
143
- return f'{cls._get_cache_key(uid)}_{hash_object(filters)}'
144
-
145
- @classmethod
146
- async def _update(cls, var_entry: DataVariableRegistryEntry, store: CacheStore, data: Optional[DataFrame]):
147
- """
148
- Internal helper which updates the data variable entry in store.
149
-
150
- TODO: for now data is always kept in store, in the future depending on the size data might be cached on disk
151
- """
152
- await store.set(var_entry, key=cls._get_cache_key(var_entry.uid), value=DataStoreEntry(data=append_index(data)))
153
-
154
- @classmethod
155
- def update_value(cls, var_entry: DataVariableRegistryEntry, store: CacheStore, data: Optional[DataFrame]):
156
- """
157
- Update the data entry and notify all clients about the update.
158
-
159
- :param var_entry: the entry in the registry
160
- :param data: the data to update
161
- :param store: store instance
162
- """
163
- from dara.core.internal.registries import utils_registry
164
-
165
- ws_mgr: WebsocketManager = utils_registry.get('WebsocketManager')
166
- task_group: TaskGroup = utils_registry.get('TaskGroup')
167
-
168
- # Update store
169
- task_group.start_soon(cls._update, var_entry, store, data)
170
-
171
- # Broadcast the update to all clients
172
- task_group.start_soon(
173
- ws_mgr.broadcast,
174
- {
175
- 'data_id': str(var_entry.uid),
176
- },
177
- )
178
-
179
- @classmethod
180
- async def get_value(
181
- cls,
182
- var_entry: DataVariableRegistryEntry,
183
- store: CacheStore,
184
- filters: Optional[Union[FilterQuery, dict]] = None,
185
- pagination: Optional[Pagination] = None,
186
- format_for_display: bool = False,
187
- ) -> Optional[DataFrame]:
188
- """
189
- Get the value of this DataVariable.
190
- """
191
- _uid_short = f'{var_entry.uid[:3]}..{var_entry.uid[-3:]}'
192
- eng_logger.info(
193
- f'Data Variable {_uid_short} get_value',
194
- {'uid': var_entry.uid, 'filters': filters, 'pagination': pagination},
195
- )
196
-
197
- cache_key = cls._get_cache_key(var_entry.uid)
198
- entry = await store.get(var_entry, key=cache_key)
199
-
200
- eng_logger.debug(
201
- f'Data Variable {_uid_short}',
202
- 'retrieved from cache',
203
- {
204
- 'uid': var_entry.uid,
205
- 'size': len(entry.data.index) if entry is not None and entry.data is not None else 0,
206
- },
207
- )
208
-
209
- if entry is None:
210
- await asyncio.gather(
211
- store.set(var_entry, key=cls._get_count_cache_key(var_entry.uid, filters), value=0, pin=True),
212
- store.set(var_entry, key=cls._get_schema_cache_key(var_entry.uid), value=None, pin=True),
213
- )
214
- return None
215
-
216
- data = None
217
-
218
- if entry.data is not None:
219
- filtered_data, count = apply_filters(entry.data, coerce_to_filter_query(filters), pagination)
220
- if format_for_display and filtered_data is not None:
221
- filtered_data = filtered_data.copy()
222
- for col in filtered_data.columns:
223
- if filtered_data[col].dtype == 'object':
224
- # 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
225
- filtered_data.loc[:, col] = filtered_data[col].apply(str)
226
- data = filtered_data
227
- # Store count for given filters and schema
228
- await asyncio.gather(
229
- store.set(var_entry, key=cls._get_count_cache_key(var_entry.uid, filters), value=count, pin=True),
230
- store.set(
231
- var_entry,
232
- key=cls._get_schema_cache_key(var_entry.uid),
233
- value=build_table_schema(df_convert_to_internal(entry.data)),
234
- pin=True,
235
- ),
236
- )
237
- else:
238
- await asyncio.gather(
239
- store.set(var_entry, key=cls._get_count_cache_key(var_entry.uid, filters), value=0, pin=True),
240
- store.set(var_entry, key=cls._get_schema_cache_key(var_entry.uid), value=None, pin=True),
241
- )
242
-
243
- # TODO: once path is supported, stream&filter from disk
244
- if entry.path:
245
- raise NotImplementedError('DataVariable.get_value() does not support disk caching yet')
246
-
247
- eng_logger.info(
248
- f'Data Variable {_uid_short} returning filtered data',
249
- {'uid': var_entry.uid, 'size': len(data.index) if data is not None else 0},
250
- )
251
-
252
- return data
253
-
254
- @classmethod
255
- async def get_total_count(
256
- cls, var_entry: DataVariableRegistryEntry, store: CacheStore, filters: Optional[FilterQuery]
257
- ):
258
- """
259
- Get total count of the data variable.
260
-
261
- :param var_entry: variable entry
262
- :param store: store
263
- :param filters: filters to get count for
264
- """
265
- cache_key = cls._get_count_cache_key(var_entry.uid, filters)
266
- entry = await store.get(var_entry, key=cache_key, unpin=True)
267
-
268
- if entry is None:
269
- raise ValueError('Requested count for filter setup which has not been performed yet')
270
-
271
- return entry
272
-
273
- @classmethod
274
- async def get_schema(cls, var_entry: DataVariableRegistryEntry, store: CacheStore):
275
- """
276
- Get the schema of the data variable.
277
-
278
- :param var_entry: variable entry
279
- :param store: store
280
- """
281
- cache_key = cls._get_schema_cache_key(var_entry.uid)
282
- entry = await store.get(var_entry, key=cache_key, unpin=True)
283
-
284
- return cast(DataFrameSchema, entry)
285
-
286
- def reset(self):
287
- raise NotImplementedError('DataVariable cannot be reset')
288
-
289
- def update(self, value: Optional[DataFrame]):
290
- """
291
- Create an action to update the value of this Variable to a provided value.
292
-
293
- ```python
294
- import pandas as pd
295
- from dara.core import DataVariable
296
- from dara.components import Button
297
-
298
- data = DataVariable(pd.DataFrame({'a': [1, 2, 3]}))
299
-
300
- Button(
301
- 'Empty Data',
302
- onclick=data.update(None),
303
- )
304
-
305
- ```
306
- """
307
- from dara.core.interactivity.actions import UpdateVariableImpl
308
-
309
- return UpdateVariableImpl(variable=self, value=value)
310
-
311
- @model_serializer(mode='wrap')
312
- def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
313
- parent_dict = nxt(self)
314
- if 'data' in parent_dict:
315
- parent_dict.pop('data') # make sure data is not included in the serialised dict
316
- return {**parent_dict, '__typename': 'DataVariable', 'uid': str(parent_dict['uid'])}
317
-
318
-
319
- class DataStoreEntry(BaseModel):
320
- """
321
- Entry in the cache store for a DataVariable.
322
- Can either be a DataFrame or a path to a file on disk.
323
- """
67
+ scope = 'global' if cache_policy.cache_type == Cache.Type.GLOBAL else 'user'
324
68
 
325
- data: Optional[DataFrame] = None
326
- path: Optional[str] = None
327
- model_config = ConfigDict(extra='forbid', arbitrary_types_allowed=True)
69
+ super().__init__(scope=scope, uid=uid, default=data, **kwargs)