dara-core 1.21.15__py3-none-any.whl → 1.21.17__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.
- dara/core/auth/base.py +5 -5
- dara/core/auth/basic.py +3 -3
- dara/core/auth/definitions.py +13 -14
- dara/core/auth/routes.py +7 -5
- dara/core/auth/utils.py +11 -10
- dara/core/base_definitions.py +30 -36
- dara/core/cli.py +7 -8
- dara/core/configuration.py +51 -58
- dara/core/css.py +2 -2
- dara/core/data_utils.py +12 -17
- dara/core/defaults.py +3 -3
- dara/core/definitions.py +58 -63
- dara/core/http.py +4 -4
- dara/core/interactivity/actions.py +34 -42
- dara/core/interactivity/any_data_variable.py +1 -1
- dara/core/interactivity/any_variable.py +6 -5
- dara/core/interactivity/client_variable.py +1 -2
- dara/core/interactivity/condition.py +2 -2
- dara/core/interactivity/data_variable.py +2 -4
- dara/core/interactivity/derived_data_variable.py +7 -10
- dara/core/interactivity/derived_variable.py +45 -51
- dara/core/interactivity/filtering.py +19 -19
- dara/core/interactivity/loop_variable.py +2 -4
- dara/core/interactivity/non_data_variable.py +1 -1
- dara/core/interactivity/plain_variable.py +21 -18
- dara/core/interactivity/server_variable.py +13 -15
- dara/core/interactivity/state_variable.py +4 -5
- dara/core/interactivity/switch_variable.py +16 -16
- dara/core/interactivity/tabular_variable.py +3 -3
- dara/core/interactivity/url_variable.py +3 -3
- dara/core/internal/cache_store/cache_store.py +6 -6
- dara/core/internal/cache_store/keep_all.py +3 -3
- dara/core/internal/cache_store/lru.py +8 -8
- dara/core/internal/cache_store/ttl.py +4 -4
- dara/core/internal/custom_response.py +3 -3
- dara/core/internal/dependency_resolution.py +6 -10
- dara/core/internal/devtools.py +2 -3
- dara/core/internal/download.py +5 -6
- dara/core/internal/encoder_registry.py +7 -11
- dara/core/internal/execute_action.py +5 -5
- dara/core/internal/hashing.py +1 -2
- dara/core/internal/import_discovery.py +7 -9
- dara/core/internal/normalization.py +12 -15
- dara/core/internal/pandas_utils.py +6 -6
- dara/core/internal/pool/channel.py +3 -4
- dara/core/internal/pool/definitions.py +9 -9
- dara/core/internal/pool/task_pool.py +8 -8
- dara/core/internal/pool/utils.py +4 -3
- dara/core/internal/pool/worker.py +3 -3
- dara/core/internal/registries.py +4 -4
- dara/core/internal/registry.py +3 -3
- dara/core/internal/registry_lookup.py +4 -4
- dara/core/internal/routing.py +23 -22
- dara/core/internal/scheduler.py +8 -8
- dara/core/internal/settings.py +1 -2
- dara/core/internal/store.py +9 -9
- dara/core/internal/tasks.py +30 -30
- dara/core/internal/utils.py +9 -15
- dara/core/internal/websocket.py +18 -18
- dara/core/js_tooling/js_utils.py +19 -19
- dara/core/logging.py +13 -13
- dara/core/main.py +4 -5
- dara/core/metrics/cache.py +2 -4
- dara/core/persistence.py +19 -25
- dara/core/router/compat.py +1 -3
- dara/core/router/components.py +10 -10
- dara/core/router/dependency_graph.py +2 -4
- dara/core/router/router.py +43 -42
- dara/core/visual/components/dynamic_component.py +1 -3
- dara/core/visual/components/fallback.py +3 -3
- dara/core/visual/components/for_cmp.py +5 -5
- dara/core/visual/components/menu.py +1 -3
- dara/core/visual/components/router_content.py +1 -3
- dara/core/visual/components/sidebar_frame.py +8 -10
- dara/core/visual/components/theme_provider.py +3 -3
- dara/core/visual/components/topbar_frame.py +8 -10
- dara/core/visual/css/__init__.py +277 -277
- dara/core/visual/dynamic_component.py +18 -22
- dara/core/visual/progress_updater.py +1 -1
- dara/core/visual/template.py +10 -12
- dara/core/visual/themes/definitions.py +46 -46
- {dara_core-1.21.15.dist-info → dara_core-1.21.17.dist-info}/METADATA +13 -14
- dara_core-1.21.17.dist-info/RECORD +127 -0
- dara_core-1.21.15.dist-info/RECORD +0 -127
- {dara_core-1.21.15.dist-info → dara_core-1.21.17.dist-info}/LICENSE +0 -0
- {dara_core-1.21.15.dist-info → dara_core-1.21.17.dist-info}/WHEEL +0 -0
- {dara_core-1.21.15.dist-info → dara_core-1.21.17.dist-info}/entry_points.txt +0 -0
|
@@ -21,21 +21,17 @@ import contextlib
|
|
|
21
21
|
import inspect
|
|
22
22
|
import math
|
|
23
23
|
import uuid
|
|
24
|
-
from collections.abc import Awaitable
|
|
24
|
+
from collections.abc import Awaitable, Callable
|
|
25
25
|
from contextvars import ContextVar
|
|
26
26
|
from enum import Enum
|
|
27
27
|
from functools import partial, update_wrapper
|
|
28
28
|
from typing import (
|
|
29
29
|
TYPE_CHECKING,
|
|
30
30
|
Any,
|
|
31
|
-
Callable,
|
|
32
31
|
ClassVar,
|
|
33
|
-
Dict,
|
|
34
|
-
List,
|
|
35
32
|
Literal,
|
|
36
|
-
|
|
33
|
+
TypeAlias,
|
|
37
34
|
TypeVar,
|
|
38
|
-
Union,
|
|
39
35
|
cast,
|
|
40
36
|
overload,
|
|
41
37
|
)
|
|
@@ -44,7 +40,7 @@ import anyio
|
|
|
44
40
|
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
|
45
41
|
from pandas import DataFrame
|
|
46
42
|
from pydantic import ConfigDict
|
|
47
|
-
from typing_extensions import
|
|
43
|
+
from typing_extensions import deprecated
|
|
48
44
|
|
|
49
45
|
from dara.core.base_definitions import (
|
|
50
46
|
ActionDef,
|
|
@@ -86,7 +82,7 @@ class ActionContext(BaseModel):
|
|
|
86
82
|
@deprecated: used in deprecated action wrappers
|
|
87
83
|
"""
|
|
88
84
|
|
|
89
|
-
extras:
|
|
85
|
+
extras: list[Any] = []
|
|
90
86
|
inputs: ActionInputs
|
|
91
87
|
|
|
92
88
|
|
|
@@ -126,7 +122,7 @@ class UpdateVariableImpl(ActionImpl):
|
|
|
126
122
|
|
|
127
123
|
py_name = 'UpdateVariable'
|
|
128
124
|
|
|
129
|
-
variable:
|
|
125
|
+
variable: Variable | ServerVariable
|
|
130
126
|
value: Any
|
|
131
127
|
|
|
132
128
|
INPUT: ClassVar[str] = '__dara_input__'
|
|
@@ -240,14 +236,14 @@ class UpdateVariable(AnnotatedAction):
|
|
|
240
236
|
|
|
241
237
|
Ctx: ClassVar[type[UpdateVariableContext]] = UpdateVariableContext
|
|
242
238
|
|
|
243
|
-
variable:
|
|
244
|
-
extras:
|
|
239
|
+
variable: Variable | ServerVariable
|
|
240
|
+
extras: list[AnyVariable] | None
|
|
245
241
|
|
|
246
242
|
def __init__(
|
|
247
243
|
self,
|
|
248
244
|
resolver: Callable[[UpdateVariableContext], Any],
|
|
249
|
-
variable:
|
|
250
|
-
extras:
|
|
245
|
+
variable: Variable | ServerVariable,
|
|
246
|
+
extras: list[AnyVariable] | None = None,
|
|
251
247
|
):
|
|
252
248
|
"""
|
|
253
249
|
:param resolver: a function to resolve the new value for the variable. Takes one arguments: containing a context of type `Updatevariable.Ctx`
|
|
@@ -374,20 +370,18 @@ class NavigateToImpl(ActionImpl):
|
|
|
374
370
|
|
|
375
371
|
py_name = 'NavigateTo'
|
|
376
372
|
|
|
377
|
-
url:
|
|
373
|
+
url: str | RouterPath
|
|
378
374
|
|
|
379
375
|
new_tab: bool = False
|
|
380
376
|
|
|
381
|
-
options:
|
|
377
|
+
options: NavigateOptions | None = None
|
|
382
378
|
"""
|
|
383
379
|
Options for relative navigations
|
|
384
380
|
"""
|
|
385
381
|
|
|
386
382
|
|
|
387
383
|
@deprecated('Use @action or `NavigateToImpl` for simple cases')
|
|
388
|
-
def NavigateTo(
|
|
389
|
-
url: Union[str, Callable[[Any], str]], new_tab: bool = False, extras: Optional[List[AnyVariable]] = None
|
|
390
|
-
):
|
|
384
|
+
def NavigateTo(url: str | Callable[[Any], str], new_tab: bool = False, extras: list[AnyVariable] | None = None):
|
|
391
385
|
"""
|
|
392
386
|
@deprecated: Passing in resolvers is deprecated, use `ctx.navigate` in an `@action` or `NavigateToImpl` instead.
|
|
393
387
|
|
|
@@ -526,7 +520,7 @@ class ResetVariables(ActionImpl):
|
|
|
526
520
|
:param variables: list of variables to reset
|
|
527
521
|
"""
|
|
528
522
|
|
|
529
|
-
variables:
|
|
523
|
+
variables: list[AnyVariable]
|
|
530
524
|
|
|
531
525
|
|
|
532
526
|
class NotificationStatus(str, Enum):
|
|
@@ -578,7 +572,7 @@ class Notify(ActionImpl):
|
|
|
578
572
|
```
|
|
579
573
|
"""
|
|
580
574
|
|
|
581
|
-
key:
|
|
575
|
+
key: str | None = None
|
|
582
576
|
message: str
|
|
583
577
|
status: NotificationStatus
|
|
584
578
|
title: str
|
|
@@ -624,7 +618,7 @@ DownloadContentDef = ActionDef(name='DownloadContent', js_module='@darajs/core',
|
|
|
624
618
|
@deprecated('Use @action instead')
|
|
625
619
|
def DownloadContent(
|
|
626
620
|
resolver: Callable[[ComponentActionContext], str],
|
|
627
|
-
extras:
|
|
621
|
+
extras: list[AnyVariable] | None = None,
|
|
628
622
|
cleanup_file: bool = False,
|
|
629
623
|
):
|
|
630
624
|
"""
|
|
@@ -729,14 +723,14 @@ class DownloadVariable(ActionImpl):
|
|
|
729
723
|
"""
|
|
730
724
|
|
|
731
725
|
variable: AnyVariable
|
|
732
|
-
file_name:
|
|
726
|
+
file_name: str | None = None
|
|
733
727
|
type: Literal['csv', 'xlsx', 'json'] = 'csv'
|
|
734
728
|
|
|
735
729
|
|
|
736
730
|
@deprecated('Use @action instead')
|
|
737
731
|
def SideEffect(
|
|
738
732
|
function: Callable[[ComponentActionContext], Any],
|
|
739
|
-
extras:
|
|
733
|
+
extras: list[AnyVariable] | None = None,
|
|
740
734
|
block: bool = False,
|
|
741
735
|
):
|
|
742
736
|
"""
|
|
@@ -820,12 +814,12 @@ class ActionCtx:
|
|
|
820
814
|
_action_receive_stream: MemoryObjectReceiveStream[ActionImpl]
|
|
821
815
|
"""Memory object receive stream for receiving actions to send to the frontend."""
|
|
822
816
|
|
|
823
|
-
_on_action: Callable[[
|
|
817
|
+
_on_action: Callable[[ActionImpl | None], Awaitable]
|
|
824
818
|
"""Callback for when an action is pushed to the stream."""
|
|
825
819
|
|
|
826
820
|
input: Any
|
|
827
821
|
|
|
828
|
-
def __init__(self, _input: Any, _on_action: Callable[[
|
|
822
|
+
def __init__(self, _input: Any, _on_action: Callable[[ActionImpl | None], Awaitable]):
|
|
829
823
|
self.input = _input
|
|
830
824
|
self._action_send_stream, self._action_receive_stream = anyio.create_memory_object_stream[ActionImpl](
|
|
831
825
|
max_buffer_size=math.inf
|
|
@@ -833,12 +827,12 @@ class ActionCtx:
|
|
|
833
827
|
self._on_action = _on_action
|
|
834
828
|
|
|
835
829
|
@overload
|
|
836
|
-
async def update(self, variable: ServerVariable, value:
|
|
830
|
+
async def update(self, variable: ServerVariable, value: DataFrame | None): ...
|
|
837
831
|
|
|
838
832
|
@overload
|
|
839
833
|
async def update(self, variable: Variable[VariableT], value: VariableT): ...
|
|
840
834
|
|
|
841
|
-
async def update(self, variable:
|
|
835
|
+
async def update(self, variable: Variable | ServerVariable, value: Any):
|
|
842
836
|
"""
|
|
843
837
|
Update a given variable to provided value.
|
|
844
838
|
|
|
@@ -955,9 +949,7 @@ class ActionCtx:
|
|
|
955
949
|
"""
|
|
956
950
|
return await TriggerVariable(variable=variable, force=force).execute(self)
|
|
957
951
|
|
|
958
|
-
async def navigate(
|
|
959
|
-
self, url: Union[str, RouterPath], new_tab: bool = False, options: Optional[NavigateOptions] = None
|
|
960
|
-
):
|
|
952
|
+
async def navigate(self, url: str | RouterPath, new_tab: bool = False, options: NavigateOptions | None = None):
|
|
961
953
|
"""
|
|
962
954
|
Navigate to a given url
|
|
963
955
|
|
|
@@ -1036,8 +1028,8 @@ class ActionCtx:
|
|
|
1036
1028
|
self,
|
|
1037
1029
|
message: str,
|
|
1038
1030
|
title: str,
|
|
1039
|
-
status:
|
|
1040
|
-
key:
|
|
1031
|
+
status: NotificationStatus | NotificationStatusString,
|
|
1032
|
+
key: str | None = None,
|
|
1041
1033
|
):
|
|
1042
1034
|
"""
|
|
1043
1035
|
Display a notification toast on the frontend
|
|
@@ -1078,7 +1070,7 @@ class ActionCtx:
|
|
|
1078
1070
|
|
|
1079
1071
|
return await Notify(key=key, message=message, status=status, title=title).execute(self)
|
|
1080
1072
|
|
|
1081
|
-
async def reset_variables(self, variables:
|
|
1073
|
+
async def reset_variables(self, variables: list[AnyVariable] | AnyVariable):
|
|
1082
1074
|
"""
|
|
1083
1075
|
Reset a list of variables to their default values.
|
|
1084
1076
|
|
|
@@ -1164,7 +1156,7 @@ class ActionCtx:
|
|
|
1164
1156
|
return await NavigateToImpl(url=f'/api/core/download?code={code}', new_tab=True).execute(self)
|
|
1165
1157
|
|
|
1166
1158
|
async def download_variable(
|
|
1167
|
-
self, variable: AnyVariable, file_name:
|
|
1159
|
+
self, variable: AnyVariable, file_name: str | None = None, type: Literal['csv', 'xlsx', 'json'] = 'csv'
|
|
1168
1160
|
):
|
|
1169
1161
|
"""
|
|
1170
1162
|
Download the content of a given variable as a file.
|
|
@@ -1205,9 +1197,9 @@ class ActionCtx:
|
|
|
1205
1197
|
async def run_task(
|
|
1206
1198
|
self,
|
|
1207
1199
|
func: Callable,
|
|
1208
|
-
args:
|
|
1209
|
-
kwargs:
|
|
1210
|
-
on_progress:
|
|
1200
|
+
args: list[Any] | None = None,
|
|
1201
|
+
kwargs: dict[str, Any] | None = None,
|
|
1202
|
+
on_progress: Callable[[TaskProgressUpdate], None | Awaitable[None]] | None = None,
|
|
1211
1203
|
):
|
|
1212
1204
|
"""
|
|
1213
1205
|
Run a calculation as a task in a separate process. Recommended for CPU intensive tasks.
|
|
@@ -1287,7 +1279,7 @@ class ActionCtx:
|
|
|
1287
1279
|
self._action_send_stream.close()
|
|
1288
1280
|
|
|
1289
1281
|
|
|
1290
|
-
ACTION_CONTEXT = ContextVar[
|
|
1282
|
+
ACTION_CONTEXT = ContextVar[ActionCtx | None]('action_context', default=None)
|
|
1291
1283
|
"""Current execution context"""
|
|
1292
1284
|
|
|
1293
1285
|
|
|
@@ -1363,7 +1355,7 @@ class action:
|
|
|
1363
1355
|
action_registry.register(self.definition_uid, act_def)
|
|
1364
1356
|
|
|
1365
1357
|
# Modify the function signature
|
|
1366
|
-
bound_name:
|
|
1358
|
+
bound_name: str | None = None
|
|
1367
1359
|
|
|
1368
1360
|
# Check if first parameter is 'self' or 'cls' - we have to use the name as otherwise it's
|
|
1369
1361
|
# not possible to distinguish between a bound method or non-bound method
|
|
@@ -1405,7 +1397,7 @@ class action:
|
|
|
1405
1397
|
def __call__(self, *args: Any, **kwargs: Any) -> AnnotatedAction: # type: ignore
|
|
1406
1398
|
...
|
|
1407
1399
|
|
|
1408
|
-
def __call__(self, *args, **kwargs) ->
|
|
1400
|
+
def __call__(self, *args, **kwargs) -> AnnotatedAction | Any:
|
|
1409
1401
|
from dara.core.interactivity.any_variable import AnyVariable
|
|
1410
1402
|
from dara.core.internal.registries import static_kwargs_registry
|
|
1411
1403
|
|
|
@@ -1464,8 +1456,8 @@ class action:
|
|
|
1464
1456
|
)
|
|
1465
1457
|
|
|
1466
1458
|
# Split args based on whether they are static or dynamic
|
|
1467
|
-
dynamic_kwargs:
|
|
1468
|
-
static_kwargs:
|
|
1459
|
+
dynamic_kwargs: dict[str, AnyVariable] = {}
|
|
1460
|
+
static_kwargs: dict[str, Any] = {}
|
|
1469
1461
|
for key, kwarg in all_kwargs.items():
|
|
1470
1462
|
if isinstance(kwarg, StateVariable):
|
|
1471
1463
|
raise ValueError(
|
|
@@ -20,10 +20,11 @@ from __future__ import annotations
|
|
|
20
20
|
import abc
|
|
21
21
|
import inspect
|
|
22
22
|
import uuid
|
|
23
|
+
from collections.abc import Callable
|
|
23
24
|
from contextlib import contextmanager
|
|
24
25
|
from contextvars import ContextVar
|
|
25
26
|
from datetime import datetime
|
|
26
|
-
from typing import Any
|
|
27
|
+
from typing import Any
|
|
27
28
|
|
|
28
29
|
import anyio
|
|
29
30
|
from fastapi.encoders import jsonable_encoder
|
|
@@ -40,7 +41,7 @@ from dara.core.logging import dev_logger
|
|
|
40
41
|
|
|
41
42
|
NOT_REGISTERED = '__NOT_REGISTERED__'
|
|
42
43
|
|
|
43
|
-
GET_VALUE_OVERRIDE = ContextVar[
|
|
44
|
+
GET_VALUE_OVERRIDE = ContextVar[Callable[[dict], Any] | None]('GET_VALUE_OVERRIDE', default=None)
|
|
44
45
|
"""
|
|
45
46
|
Optional context variable which can be used to override the default behaviour of `get_current_value()`.
|
|
46
47
|
"""
|
|
@@ -135,7 +136,7 @@ async def get_current_value(variable: dict, timeout: float = 3, raw: bool = Fals
|
|
|
135
136
|
)
|
|
136
137
|
return None
|
|
137
138
|
|
|
138
|
-
session_channels:
|
|
139
|
+
session_channels: dict[str, set[str]] = {}
|
|
139
140
|
saved_ws_channel = WS_CHANNEL.get()
|
|
140
141
|
|
|
141
142
|
# Collect sessions which are active
|
|
@@ -154,7 +155,7 @@ async def get_current_value(variable: dict, timeout: float = 3, raw: bool = Fals
|
|
|
154
155
|
)
|
|
155
156
|
return None
|
|
156
157
|
|
|
157
|
-
raw_results:
|
|
158
|
+
raw_results: dict[str, Any] = {}
|
|
158
159
|
registered_value_found = False
|
|
159
160
|
|
|
160
161
|
async def retrieve_value(channel: str):
|
|
@@ -279,7 +280,7 @@ class AnyVariable(BaseModel, abc.ABC): # noqa: PLW1641 # we override equals to
|
|
|
279
280
|
|
|
280
281
|
uid: str
|
|
281
282
|
|
|
282
|
-
def __init__(self, uid:
|
|
283
|
+
def __init__(self, uid: str | None = None, **kwargs) -> None:
|
|
283
284
|
new_uid = uid
|
|
284
285
|
if new_uid is None:
|
|
285
286
|
new_uid = str(uuid.uuid4())
|
|
@@ -18,7 +18,6 @@ limitations under the License.
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import abc
|
|
21
|
-
from typing import Optional
|
|
22
21
|
|
|
23
22
|
from dara.core.interactivity.any_variable import AnyVariable
|
|
24
23
|
|
|
@@ -32,7 +31,7 @@ class ClientVariable(AnyVariable, abc.ABC):
|
|
|
32
31
|
|
|
33
32
|
uid: str
|
|
34
33
|
|
|
35
|
-
def __init__(self, uid:
|
|
34
|
+
def __init__(self, uid: str | None = None, **kwargs) -> None:
|
|
36
35
|
super().__init__(uid=uid, **kwargs)
|
|
37
36
|
|
|
38
37
|
@property
|
|
@@ -18,7 +18,7 @@ limitations under the License.
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
from enum import Enum
|
|
21
|
-
from typing import TYPE_CHECKING, ClassVar
|
|
21
|
+
from typing import TYPE_CHECKING, ClassVar
|
|
22
22
|
|
|
23
23
|
from pydantic import SerializerFunctionWrapHandler, model_serializer
|
|
24
24
|
|
|
@@ -44,7 +44,7 @@ OperatorType = type[Operator]
|
|
|
44
44
|
|
|
45
45
|
class Condition(BaseModel):
|
|
46
46
|
operator: Operator
|
|
47
|
-
other:
|
|
47
|
+
other: BaseModel | int | float | str | bool | None | AnyVariable
|
|
48
48
|
variable: AnyVariable
|
|
49
49
|
|
|
50
50
|
Operator: ClassVar[OperatorType] = Operator
|
|
@@ -17,8 +17,6 @@ limitations under the License.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
from typing import Optional
|
|
21
|
-
|
|
22
20
|
from pandas import DataFrame
|
|
23
21
|
|
|
24
22
|
from dara.core.base_definitions import Cache, CacheArgType
|
|
@@ -46,9 +44,9 @@ class DataVariable(ServerVariable):
|
|
|
46
44
|
|
|
47
45
|
def __init__(
|
|
48
46
|
self,
|
|
49
|
-
data:
|
|
47
|
+
data: DataFrame | None = None,
|
|
50
48
|
cache: CacheArgType = Cache.Type.GLOBAL,
|
|
51
|
-
uid:
|
|
49
|
+
uid: str | None = None,
|
|
52
50
|
**kwargs,
|
|
53
51
|
) -> None:
|
|
54
52
|
"""
|
|
@@ -17,8 +17,8 @@ limitations under the License.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
from collections.abc import Coroutine
|
|
21
|
-
from typing import Any
|
|
20
|
+
from collections.abc import Callable, Coroutine
|
|
21
|
+
from typing import Any
|
|
22
22
|
|
|
23
23
|
from pandas import DataFrame
|
|
24
24
|
from typing_extensions import deprecated
|
|
@@ -46,16 +46,13 @@ class DerivedDataVariable(DerivedVariable):
|
|
|
46
46
|
|
|
47
47
|
def __init__(
|
|
48
48
|
self,
|
|
49
|
-
func:
|
|
50
|
-
|
|
51
|
-
Callable[..., Coroutine[Any, Any, Union[DataFrame, None]]],
|
|
52
|
-
],
|
|
53
|
-
variables: List[AnyVariable],
|
|
49
|
+
func: Callable[..., DataFrame | None] | Callable[..., Coroutine[Any, Any, DataFrame | None]],
|
|
50
|
+
variables: list[AnyVariable],
|
|
54
51
|
cache: CacheArgType = Cache.Type.GLOBAL,
|
|
55
52
|
run_as_task: bool = False,
|
|
56
|
-
polling_interval:
|
|
57
|
-
deps:
|
|
58
|
-
uid:
|
|
53
|
+
polling_interval: int | None = None,
|
|
54
|
+
deps: list[AnyVariable] | None = None,
|
|
55
|
+
uid: str | None = None,
|
|
59
56
|
) -> None:
|
|
60
57
|
"""
|
|
61
58
|
DerivedDataVariable represents a variable designed to hold datasets computed
|
|
@@ -19,18 +19,12 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import json
|
|
21
21
|
import uuid
|
|
22
|
-
from collections.abc import Awaitable
|
|
22
|
+
from collections.abc import Awaitable, Callable
|
|
23
23
|
from inspect import Parameter, signature
|
|
24
24
|
from typing import (
|
|
25
25
|
Any,
|
|
26
|
-
Callable,
|
|
27
26
|
Generic,
|
|
28
|
-
List,
|
|
29
|
-
Optional,
|
|
30
27
|
Protocol,
|
|
31
|
-
Tuple,
|
|
32
|
-
TypeVar,
|
|
33
|
-
Union,
|
|
34
28
|
cast,
|
|
35
29
|
)
|
|
36
30
|
|
|
@@ -45,7 +39,7 @@ from pydantic import (
|
|
|
45
39
|
field_validator,
|
|
46
40
|
model_serializer,
|
|
47
41
|
)
|
|
48
|
-
from typing_extensions import TypedDict, runtime_checkable
|
|
42
|
+
from typing_extensions import TypedDict, TypeVar, runtime_checkable
|
|
49
43
|
|
|
50
44
|
from dara.core.base_definitions import (
|
|
51
45
|
BaseCachePolicy,
|
|
@@ -69,7 +63,7 @@ from dara.core.internal.utils import get_cache_scope, run_user_handler
|
|
|
69
63
|
from dara.core.logging import dev_logger, eng_logger
|
|
70
64
|
from dara.core.metrics import RUNTIME_METRICS_TRACKER
|
|
71
65
|
|
|
72
|
-
VariableType = TypeVar('VariableType')
|
|
66
|
+
VariableType = TypeVar('VariableType', default=Any)
|
|
73
67
|
|
|
74
68
|
# Static lock for all DV computations, keyed by cache_key
|
|
75
69
|
# Explicitly not re-entrant, this prevents variable loops
|
|
@@ -88,19 +82,19 @@ Sentinel value to indicate that a value is missing from the cache
|
|
|
88
82
|
|
|
89
83
|
class DerivedVariableResult(TypedDict):
|
|
90
84
|
cache_key: str
|
|
91
|
-
value:
|
|
85
|
+
value: Any | BaseTask
|
|
92
86
|
|
|
93
87
|
|
|
94
88
|
@runtime_checkable
|
|
95
89
|
class FilterResolver(Protocol):
|
|
96
90
|
async def __call__(
|
|
97
|
-
self, data: Any, filters:
|
|
98
|
-
) ->
|
|
91
|
+
self, data: Any, filters: FilterQuery | None = None, pagination: Pagination | None = None
|
|
92
|
+
) -> tuple[DataFrame, int]: ...
|
|
99
93
|
|
|
100
94
|
|
|
101
95
|
async def default_filter_resolver(
|
|
102
|
-
data: Any, filters:
|
|
103
|
-
) ->
|
|
96
|
+
data: Any, filters: FilterQuery | None = None, pagination: Pagination | None = None
|
|
97
|
+
) -> tuple[DataFrame, int]:
|
|
104
98
|
if not isinstance(data, DataFrame):
|
|
105
99
|
raise NonTabularDataError(
|
|
106
100
|
f'Default filter resolver expects a DataFrame to be returned from the DerivedVariable function, got {type(data)}'
|
|
@@ -174,25 +168,25 @@ class DerivedVariable(ClientVariable, Generic[VariableType]):
|
|
|
174
168
|
:param uid: the unique identifier for this variable; if not provided a random one is generated
|
|
175
169
|
"""
|
|
176
170
|
|
|
177
|
-
cache:
|
|
178
|
-
variables:
|
|
179
|
-
polling_interval:
|
|
180
|
-
deps:
|
|
181
|
-
nested:
|
|
171
|
+
cache: BaseCachePolicy | None
|
|
172
|
+
variables: list[AnyVariable]
|
|
173
|
+
polling_interval: int | None
|
|
174
|
+
deps: list[AnyVariable] | None = Field(validate_default=True)
|
|
175
|
+
nested: list[str] = Field(default_factory=list)
|
|
182
176
|
uid: str
|
|
183
177
|
model_config = ConfigDict(extra='forbid', use_enum_values=True, arbitrary_types_allowed=True)
|
|
184
178
|
|
|
185
179
|
def __init__(
|
|
186
180
|
self,
|
|
187
|
-
func:
|
|
188
|
-
variables:
|
|
189
|
-
cache:
|
|
181
|
+
func: Callable[..., VariableType] | Callable[..., Awaitable[VariableType]],
|
|
182
|
+
variables: list[AnyVariable],
|
|
183
|
+
cache: CacheArgType | None = Cache.Type.GLOBAL,
|
|
190
184
|
run_as_task: bool = False,
|
|
191
|
-
polling_interval:
|
|
192
|
-
deps:
|
|
193
|
-
uid:
|
|
194
|
-
nested:
|
|
195
|
-
filter_resolver:
|
|
185
|
+
polling_interval: int | None = None,
|
|
186
|
+
deps: list[AnyVariable] | None = None,
|
|
187
|
+
uid: str | None = None,
|
|
188
|
+
nested: list[str] | None = None,
|
|
189
|
+
filter_resolver: FilterResolver | None = None,
|
|
196
190
|
**kwargs,
|
|
197
191
|
):
|
|
198
192
|
if nested is None:
|
|
@@ -241,7 +235,7 @@ class DerivedVariable(ClientVariable, Generic[VariableType]):
|
|
|
241
235
|
# Import the registry of variables and register the function at import
|
|
242
236
|
from dara.core.internal.registries import derived_variable_registry
|
|
243
237
|
|
|
244
|
-
deps_indexes:
|
|
238
|
+
deps_indexes: list[int] | None = None
|
|
245
239
|
|
|
246
240
|
# If deps is provided, compute list of indexes of values which are present in deps
|
|
247
241
|
if deps is not None:
|
|
@@ -267,13 +261,13 @@ class DerivedVariable(ClientVariable, Generic[VariableType]):
|
|
|
267
261
|
|
|
268
262
|
@field_validator('deps', mode='before')
|
|
269
263
|
@classmethod
|
|
270
|
-
def validate_deps(cls, deps: Any, info: ValidationInfo) ->
|
|
264
|
+
def validate_deps(cls, deps: Any, info: ValidationInfo) -> list[AnyVariable]:
|
|
271
265
|
"""
|
|
272
266
|
If deps is not specified, set deps to include all variables used
|
|
273
267
|
"""
|
|
274
268
|
if deps is None:
|
|
275
269
|
# This will always be set on the variable with the type verified by pydantic
|
|
276
|
-
return cast(
|
|
270
|
+
return cast(list[AnyVariable], info.data.get('variables'))
|
|
277
271
|
|
|
278
272
|
return deps
|
|
279
273
|
|
|
@@ -323,7 +317,7 @@ class DerivedVariable(ClientVariable, Generic[VariableType]):
|
|
|
323
317
|
return StateVariable(parent_variable=self, property_name='hasValue')
|
|
324
318
|
|
|
325
319
|
@staticmethod
|
|
326
|
-
def _get_cache_key(*args, uid: str, deps:
|
|
320
|
+
def _get_cache_key(*args, uid: str, deps: list[int] | None = None):
|
|
327
321
|
"""
|
|
328
322
|
Convert the set of args that will be passed into the function to a string for use as the cache key. For now this
|
|
329
323
|
assumes that no classes will be passed in as the underlying values will come from the UI.
|
|
@@ -364,14 +358,14 @@ class DerivedVariable(ClientVariable, Generic[VariableType]):
|
|
|
364
358
|
|
|
365
359
|
# If there is no *args argument then zip the signature with the args
|
|
366
360
|
if len(var_arg_idx) == 0:
|
|
367
|
-
for param, arg in zip(parameters, args):
|
|
361
|
+
for param, arg in zip(parameters, args, strict=False):
|
|
368
362
|
typ = param.annotation
|
|
369
363
|
parsed_args.append(deserialize(arg, typ))
|
|
370
364
|
|
|
371
365
|
return parsed_args
|
|
372
366
|
|
|
373
367
|
# If there is a *args argument then zip the signature and args up to that point, then spread the rest
|
|
374
|
-
for param, arg in zip(parameters[: var_arg_idx[0]], args[: var_arg_idx[0]]):
|
|
368
|
+
for param, arg in zip(parameters[: var_arg_idx[0]], args[: var_arg_idx[0]], strict=False):
|
|
375
369
|
typ = param.annotation
|
|
376
370
|
parsed_args.append(deserialize(arg, typ))
|
|
377
371
|
|
|
@@ -411,8 +405,8 @@ class DerivedVariable(ClientVariable, Generic[VariableType]):
|
|
|
411
405
|
var_entry: DerivedVariableRegistryEntry,
|
|
412
406
|
store: CacheStore,
|
|
413
407
|
task_mgr: TaskManager,
|
|
414
|
-
args:
|
|
415
|
-
force_key:
|
|
408
|
+
args: list[Any],
|
|
409
|
+
force_key: str | None = None,
|
|
416
410
|
_pin_result: bool = False,
|
|
417
411
|
) -> DerivedVariableResult:
|
|
418
412
|
"""
|
|
@@ -455,7 +449,7 @@ class DerivedVariable(ClientVariable, Generic[VariableType]):
|
|
|
455
449
|
|
|
456
450
|
with histogram.time():
|
|
457
451
|
# Extract and process nested derived variables
|
|
458
|
-
values:
|
|
452
|
+
values: list[Any] = [None] * len(args)
|
|
459
453
|
|
|
460
454
|
eng_logger.info(
|
|
461
455
|
f'Derived Variable {_uid_short} get_value',
|
|
@@ -659,10 +653,10 @@ class DerivedVariable(ClientVariable, Generic[VariableType]):
|
|
|
659
653
|
@classmethod
|
|
660
654
|
async def _filter_data(
|
|
661
655
|
cls,
|
|
662
|
-
data:
|
|
656
|
+
data: DataFrame | Any | None,
|
|
663
657
|
filter_resolver: FilterResolver,
|
|
664
|
-
filters:
|
|
665
|
-
pagination:
|
|
658
|
+
filters: FilterQuery | None = None,
|
|
659
|
+
pagination: Pagination | None = None,
|
|
666
660
|
) -> DataResponse:
|
|
667
661
|
if data is None:
|
|
668
662
|
return DataResponse(data=None, count=0, schema=None)
|
|
@@ -682,11 +676,11 @@ class DerivedVariable(ClientVariable, Generic[VariableType]):
|
|
|
682
676
|
var_entry: DerivedVariableRegistryEntry,
|
|
683
677
|
store: CacheStore,
|
|
684
678
|
task_mgr: TaskManager,
|
|
685
|
-
args:
|
|
686
|
-
force_key:
|
|
687
|
-
pagination:
|
|
688
|
-
filters:
|
|
689
|
-
) ->
|
|
679
|
+
args: list[Any],
|
|
680
|
+
force_key: str | None = None,
|
|
681
|
+
pagination: Pagination | None = None,
|
|
682
|
+
filters: FilterQuery | None = None,
|
|
683
|
+
) -> MetaTask | DataResponse:
|
|
690
684
|
"""
|
|
691
685
|
Get filtered tabular data from the underlying derived variable.
|
|
692
686
|
|
|
@@ -714,7 +708,7 @@ class DerivedVariable(ClientVariable, Generic[VariableType]):
|
|
|
714
708
|
return await cls._filter_data(result['value'], filter_resolver, filters, pagination)
|
|
715
709
|
|
|
716
710
|
@classmethod
|
|
717
|
-
def check_polling(cls, variables:
|
|
711
|
+
def check_polling(cls, variables: list[AnyVariable]):
|
|
718
712
|
for variable in variables:
|
|
719
713
|
if isinstance(variable, DerivedVariable) and (
|
|
720
714
|
variable.polling_interval or cls.check_polling(variables=variable.variables)
|
|
@@ -733,15 +727,15 @@ class DerivedVariable(ClientVariable, Generic[VariableType]):
|
|
|
733
727
|
|
|
734
728
|
|
|
735
729
|
class DerivedVariableRegistryEntry(CachedRegistryEntry):
|
|
736
|
-
deps:
|
|
737
|
-
func:
|
|
738
|
-
filter_resolver:
|
|
730
|
+
deps: list[int] | None
|
|
731
|
+
func: Callable[..., Any] | None
|
|
732
|
+
filter_resolver: FilterResolver | None
|
|
739
733
|
run_as_task: bool
|
|
740
|
-
variables:
|
|
741
|
-
polling_interval:
|
|
734
|
+
variables: list[AnyVariable]
|
|
735
|
+
polling_interval: int | None
|
|
742
736
|
get_value: Callable[..., Awaitable[Any]]
|
|
743
737
|
"""Handler to get the value of the derived variable. Defaults to DerivedVariable.get_value, should match the signature"""
|
|
744
|
-
get_tabular_data: Callable[..., Awaitable[
|
|
738
|
+
get_tabular_data: Callable[..., Awaitable[DataResponse | MetaTask]]
|
|
745
739
|
"""Handler to get the tabular data of the derived variable. Defaults to DerivedVariable.get_tabular_data, should match the signature"""
|
|
746
740
|
model_config = ConfigDict(extra='forbid', arbitrary_types_allowed=True)
|
|
747
741
|
|