dara-core 1.15.6a1__py3-none-any.whl → 1.16.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.
- dara/core/__init__.py +16 -27
- dara/core/auth/base.py +3 -2
- dara/core/auth/definitions.py +0 -3
- dara/core/auth/utils.py +1 -1
- dara/core/base_definitions.py +122 -65
- dara/core/cli.py +12 -0
- dara/core/configuration.py +5 -8
- dara/core/defaults.py +0 -3
- dara/core/definitions.py +95 -231
- dara/core/interactivity/__init__.py +12 -18
- dara/core/interactivity/actions.py +22 -19
- dara/core/interactivity/any_data_variable.py +2 -4
- dara/core/interactivity/any_variable.py +10 -2
- dara/core/interactivity/condition.py +7 -10
- dara/core/interactivity/data_variable.py +11 -12
- dara/core/interactivity/derived_data_variable.py +7 -7
- dara/core/interactivity/derived_variable.py +20 -17
- dara/core/interactivity/filtering.py +1 -1
- dara/core/interactivity/plain_variable.py +10 -6
- dara/core/interactivity/url_variable.py +7 -6
- dara/core/internal/download.py +1 -1
- dara/core/internal/hashing.py +1 -1
- dara/core/internal/normalization.py +0 -24
- dara/core/internal/routing.py +10 -10
- dara/core/internal/scheduler.py +3 -2
- dara/core/internal/settings.py +2 -4
- dara/core/internal/store.py +0 -3
- dara/core/internal/tasks.py +2 -2
- dara/core/internal/websocket.py +29 -20
- dara/core/js_tooling/js_utils.py +1 -1
- dara/core/main.py +2 -2
- dara/core/persistence.py +12 -4
- dara/core/umd/dara.core.umd.js +13 -277
- dara/core/visual/components/__init__.py +0 -3
- dara/core/visual/components/fallback.py +3 -3
- dara/core/visual/components/invalid_component.py +3 -3
- dara/core/visual/components/menu.py +3 -3
- dara/core/visual/components/progress_tracker.py +3 -2
- dara/core/visual/components/raw_string.py +3 -3
- dara/core/visual/components/router_content.py +3 -3
- dara/core/visual/components/sidebar_frame.py +3 -3
- dara/core/visual/components/topbar_frame.py +3 -3
- dara/core/visual/css/__init__.py +2 -6
- dara/core/visual/dynamic_component.py +3 -6
- {dara_core-1.15.6a1.dist-info → dara_core-1.16.0a1.dist-info}/METADATA +13 -12
- {dara_core-1.15.6a1.dist-info → dara_core-1.16.0a1.dist-info}/RECORD +49 -50
- dara/core/visual/components/for_cmp.py +0 -150
- {dara_core-1.15.6a1.dist-info → dara_core-1.16.0a1.dist-info}/LICENSE +0 -0
- {dara_core-1.15.6a1.dist-info → dara_core-1.16.0a1.dist-info}/WHEEL +0 -0
- {dara_core-1.15.6a1.dist-info → dara_core-1.16.0a1.dist-info}/entry_points.txt +0 -0
|
@@ -35,13 +35,14 @@ from typing import (
|
|
|
35
35
|
Optional,
|
|
36
36
|
TypeVar,
|
|
37
37
|
Union,
|
|
38
|
+
cast,
|
|
38
39
|
overload,
|
|
39
40
|
)
|
|
40
41
|
|
|
41
42
|
import anyio
|
|
42
43
|
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
|
43
44
|
from pandas import DataFrame
|
|
44
|
-
from pydantic import BaseModel
|
|
45
|
+
from pydantic import BaseModel, ConfigDict
|
|
45
46
|
from typing_extensions import deprecated
|
|
46
47
|
|
|
47
48
|
from dara.core.base_definitions import (
|
|
@@ -49,7 +50,6 @@ from dara.core.base_definitions import (
|
|
|
49
50
|
ActionImpl,
|
|
50
51
|
ActionResolverDef,
|
|
51
52
|
AnnotatedAction,
|
|
52
|
-
TemplateMarker,
|
|
53
53
|
)
|
|
54
54
|
from dara.core.interactivity.data_variable import DataVariable
|
|
55
55
|
from dara.core.internal.download import generate_download_code
|
|
@@ -72,8 +72,7 @@ class ActionInputs(BaseModel):
|
|
|
72
72
|
Base class for all action inputs
|
|
73
73
|
"""
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
extra = 'allow'
|
|
75
|
+
model_config = ConfigDict(extra='allow')
|
|
77
76
|
|
|
78
77
|
|
|
79
78
|
class ActionContext(BaseModel):
|
|
@@ -96,7 +95,7 @@ class ComponentActionContext(ActionContext):
|
|
|
96
95
|
ActionContext for actions that only require component value
|
|
97
96
|
"""
|
|
98
97
|
|
|
99
|
-
inputs: ComponentActionInputs
|
|
98
|
+
inputs: ComponentActionInputs # type: ignore
|
|
100
99
|
|
|
101
100
|
|
|
102
101
|
class UpdateVariableImpl(ActionImpl):
|
|
@@ -161,7 +160,7 @@ class UpdateVariableInputs(ActionInputs):
|
|
|
161
160
|
|
|
162
161
|
|
|
163
162
|
class UpdateVariableContext(ActionContext):
|
|
164
|
-
inputs: UpdateVariableInputs
|
|
163
|
+
inputs: UpdateVariableInputs # type: ignore
|
|
165
164
|
|
|
166
165
|
|
|
167
166
|
@deprecated('Use @action or `UpdateVariableImpl` for simple cases')
|
|
@@ -236,16 +235,16 @@ class UpdateVariable(AnnotatedAction):
|
|
|
236
235
|
```
|
|
237
236
|
"""
|
|
238
237
|
|
|
239
|
-
Ctx = UpdateVariableContext
|
|
238
|
+
Ctx: ClassVar[type[UpdateVariableContext]] = UpdateVariableContext
|
|
240
239
|
|
|
241
240
|
variable: Union[Variable, DataVariable, UrlVariable]
|
|
242
|
-
extras: Optional[List[
|
|
241
|
+
extras: Optional[List[AnyVariable]]
|
|
243
242
|
|
|
244
243
|
def __init__(
|
|
245
244
|
self,
|
|
246
245
|
resolver: Callable[[UpdateVariableContext], Any],
|
|
247
246
|
variable: Union[Variable, DataVariable, UrlVariable],
|
|
248
|
-
extras: Optional[List[
|
|
247
|
+
extras: Optional[List[AnyVariable]] = None,
|
|
249
248
|
):
|
|
250
249
|
"""
|
|
251
250
|
:param resolver: a function to resolve the new value for the variable. Takes one arguments: containing a context of type `Updatevariable.Ctx`
|
|
@@ -253,7 +252,8 @@ class UpdateVariable(AnnotatedAction):
|
|
|
253
252
|
:param extras: any extra variables to resolve and pass to the resolution function context
|
|
254
253
|
"""
|
|
255
254
|
|
|
256
|
-
async def _update(ctx: action.Ctx, **kwargs):
|
|
255
|
+
async def _update(ctx: action.Ctx, **kwargs): # type: ignore
|
|
256
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
257
257
|
old = kwargs.pop('old')
|
|
258
258
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
259
259
|
old_ctx = UpdateVariableContext(inputs=UpdateVariableInputs(old=old, new=ctx.input), extras=extras)
|
|
@@ -371,7 +371,7 @@ class NavigateToImpl(ActionImpl):
|
|
|
371
371
|
|
|
372
372
|
py_name = 'NavigateTo'
|
|
373
373
|
|
|
374
|
-
url: Optional[str]
|
|
374
|
+
url: Optional[str] = None
|
|
375
375
|
new_tab: bool
|
|
376
376
|
|
|
377
377
|
|
|
@@ -426,7 +426,8 @@ def NavigateTo(
|
|
|
426
426
|
return NavigateToImpl(url=url, new_tab=new_tab)
|
|
427
427
|
|
|
428
428
|
# Otherwise create a new @action with the provided resolver
|
|
429
|
-
async def _navigate(ctx: action.Ctx, **kwargs):
|
|
429
|
+
async def _navigate(ctx: action.Ctx, **kwargs): # type: ignore
|
|
430
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
430
431
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
431
432
|
old_ctx = ComponentActionContext(inputs=ComponentActionInputs(value=ctx.input), extras=extras)
|
|
432
433
|
result = await run_user_handler(url, args=(old_ctx,)) # type: ignore
|
|
@@ -573,8 +574,8 @@ class Notify(ActionImpl):
|
|
|
573
574
|
status: NotificationStatus
|
|
574
575
|
title: str
|
|
575
576
|
|
|
576
|
-
Status = NotificationStatus
|
|
577
|
-
Ctx = ComponentActionContext
|
|
577
|
+
Status: ClassVar[type[NotificationStatus]] = NotificationStatus
|
|
578
|
+
Ctx: ClassVar[type[ComponentActionContext]] = ComponentActionContext
|
|
578
579
|
"""@deprecated retained for backwards compatibility, to be removed in 2.0"""
|
|
579
580
|
|
|
580
581
|
|
|
@@ -660,7 +661,8 @@ def DownloadContent(
|
|
|
660
661
|
```
|
|
661
662
|
"""
|
|
662
663
|
|
|
663
|
-
async def _download(ctx: action.Ctx, **kwargs):
|
|
664
|
+
async def _download(ctx: action.Ctx, **kwargs): # type: ignore
|
|
665
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
664
666
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
665
667
|
old_ctx = ComponentActionContext(inputs=ComponentActionInputs(value=ctx.input), extras=extras)
|
|
666
668
|
result = await run_user_handler(resolver, args=(old_ctx,))
|
|
@@ -725,7 +727,7 @@ class DownloadVariable(ActionImpl):
|
|
|
725
727
|
@deprecated('Use @action instead')
|
|
726
728
|
def SideEffect(
|
|
727
729
|
function: Callable[[ComponentActionContext], Any],
|
|
728
|
-
extras: Optional[List[
|
|
730
|
+
extras: Optional[List[AnyVariable]] = None,
|
|
729
731
|
block: bool = False,
|
|
730
732
|
):
|
|
731
733
|
"""
|
|
@@ -765,7 +767,8 @@ def SideEffect(
|
|
|
765
767
|
```
|
|
766
768
|
"""
|
|
767
769
|
|
|
768
|
-
async def _effect(ctx: action.Ctx, **kwargs):
|
|
770
|
+
async def _effect(ctx: action.Ctx, **kwargs): # type: ignore
|
|
771
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
769
772
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
770
773
|
old_ctx = ComponentActionContext(inputs=ComponentActionInputs(value=ctx.input), extras=extras)
|
|
771
774
|
# Simply run the user handler
|
|
@@ -1274,7 +1277,7 @@ class action:
|
|
|
1274
1277
|
```
|
|
1275
1278
|
"""
|
|
1276
1279
|
|
|
1277
|
-
Ctx: ClassVar =
|
|
1280
|
+
Ctx: ClassVar[type[ActionContext]] = ActionContext
|
|
1278
1281
|
|
|
1279
1282
|
def __init__(self, func: Callable[..., Any]):
|
|
1280
1283
|
from dara.core.internal.execute_action import execute_action
|
|
@@ -1337,7 +1340,7 @@ class action:
|
|
|
1337
1340
|
...
|
|
1338
1341
|
|
|
1339
1342
|
@overload
|
|
1340
|
-
def __call__(self, *args: Any, **kwargs: Any) -> AnnotatedAction:
|
|
1343
|
+
def __call__(self, *args: Any, **kwargs: Any) -> AnnotatedAction: # type: ignore
|
|
1341
1344
|
...
|
|
1342
1345
|
|
|
1343
1346
|
def __call__(self, *args, **kwargs) -> Union[AnnotatedAction, Any]:
|
|
@@ -22,6 +22,7 @@ from typing import Any, Awaitable, Callable, Literal, Optional, TypedDict, Union
|
|
|
22
22
|
|
|
23
23
|
import pandas
|
|
24
24
|
from fastapi import UploadFile
|
|
25
|
+
from pydantic import ConfigDict
|
|
25
26
|
|
|
26
27
|
from dara.core.base_definitions import CachedRegistryEntry, UploadResolverDef
|
|
27
28
|
from dara.core.interactivity.any_variable import AnyVariable
|
|
@@ -73,10 +74,7 @@ class DataVariableRegistryEntry(CachedRegistryEntry):
|
|
|
73
74
|
|
|
74
75
|
get_schema: Callable[..., Awaitable[DataFrameSchema]]
|
|
75
76
|
"""Handler to get the schema for data variable. Defaults to DataVariable.get_schema for type=plain, and DerivedDataVariable.get_schema for type=derived"""
|
|
76
|
-
|
|
77
|
-
class Config:
|
|
78
|
-
extra = 'forbid'
|
|
79
|
-
arbitrary_types_allowed = True
|
|
77
|
+
model_config = ConfigDict(extra='forbid', arbitrary_types_allowed=True)
|
|
80
78
|
|
|
81
79
|
|
|
82
80
|
async def upload(data: UploadFile, data_uid: Optional[str] = None, resolver_id: Optional[str] = None):
|
|
@@ -27,10 +27,12 @@ from typing import Any, Callable, Dict, Optional, Set
|
|
|
27
27
|
|
|
28
28
|
import anyio
|
|
29
29
|
from fastapi.encoders import jsonable_encoder
|
|
30
|
-
from pydantic import
|
|
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
|
|
34
|
+
from dara.core.base_definitions import DaraBaseModel as BaseModel
|
|
35
|
+
from dara.core.base_definitions import PendingTask
|
|
34
36
|
from dara.core.interactivity.condition import Condition, Operator
|
|
35
37
|
from dara.core.internal.cache_store import CacheStore
|
|
36
38
|
from dara.core.internal.tasks import TaskManager
|
|
@@ -275,6 +277,8 @@ class AnyVariable(BaseModel, abc.ABC):
|
|
|
275
277
|
Base class for all variables. Used for typing to specify that any variable can be provided.
|
|
276
278
|
"""
|
|
277
279
|
|
|
280
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
281
|
+
|
|
278
282
|
uid: str
|
|
279
283
|
|
|
280
284
|
def __init__(self, uid: Optional[str] = None, **kwargs) -> None:
|
|
@@ -328,6 +332,10 @@ class AnyVariable(BaseModel, abc.ABC):
|
|
|
328
332
|
assert_no_context('ctx.reset')
|
|
329
333
|
return ResetVariables(variables=[self])
|
|
330
334
|
|
|
335
|
+
@classmethod
|
|
336
|
+
def isinstance(cls, obj: Any) -> bool:
|
|
337
|
+
return isinstance(obj, cls)
|
|
338
|
+
|
|
331
339
|
async def get_current_value(self, timeout: float = 3) -> Any:
|
|
332
340
|
"""
|
|
333
341
|
Retrieve the current value of the variable for the current user
|
|
@@ -18,11 +18,9 @@ limitations under the License.
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
from enum import Enum
|
|
21
|
-
from typing import TYPE_CHECKING, Union
|
|
21
|
+
from typing import TYPE_CHECKING, ClassVar, Union
|
|
22
22
|
|
|
23
|
-
from
|
|
24
|
-
|
|
25
|
-
from dara.core.base_definitions import TemplateMarker
|
|
23
|
+
from dara.core.base_definitions import DaraBaseModel as BaseModel
|
|
26
24
|
|
|
27
25
|
# Type-only imports
|
|
28
26
|
if TYPE_CHECKING:
|
|
@@ -39,13 +37,12 @@ class Operator(Enum):
|
|
|
39
37
|
TRUTHY = 'truthy'
|
|
40
38
|
|
|
41
39
|
|
|
40
|
+
OperatorType = type[Operator]
|
|
41
|
+
|
|
42
|
+
|
|
42
43
|
class Condition(BaseModel):
|
|
43
44
|
operator: Operator
|
|
44
45
|
other: Union[BaseModel, int, float, str, bool, None, AnyVariable]
|
|
45
|
-
variable:
|
|
46
|
-
|
|
47
|
-
Operator = Operator
|
|
46
|
+
variable: AnyVariable
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
# This makes it properly check the union type rather than coercing to variable type
|
|
51
|
-
smart_union = True
|
|
48
|
+
Operator: ClassVar[OperatorType] = Operator
|
|
@@ -23,7 +23,12 @@ from typing import Optional, Union, cast
|
|
|
23
23
|
from anyio.abc import TaskGroup
|
|
24
24
|
from pandas import DataFrame
|
|
25
25
|
from pandas.io.json._table_schema import build_table_schema
|
|
26
|
-
from pydantic import
|
|
26
|
+
from pydantic import (
|
|
27
|
+
BaseModel,
|
|
28
|
+
ConfigDict,
|
|
29
|
+
SerializerFunctionWrapHandler,
|
|
30
|
+
model_serializer,
|
|
31
|
+
)
|
|
27
32
|
|
|
28
33
|
from dara.core.base_definitions import BaseCachePolicy, Cache, CacheArgType
|
|
29
34
|
from dara.core.interactivity.any_data_variable import (
|
|
@@ -67,11 +72,7 @@ class DataVariable(AnyDataVariable):
|
|
|
67
72
|
uid: str
|
|
68
73
|
filters: Optional[FilterQuery] = None
|
|
69
74
|
cache: Optional[BaseCachePolicy] = None
|
|
70
|
-
|
|
71
|
-
class Config:
|
|
72
|
-
extra = 'forbid'
|
|
73
|
-
arbitrary_types_allowed = True
|
|
74
|
-
use_enum_values = True
|
|
75
|
+
model_config = ConfigDict(extra='forbid', arbitrary_types_allowed=True, use_enum_values=True)
|
|
75
76
|
|
|
76
77
|
def __init__(
|
|
77
78
|
self,
|
|
@@ -304,8 +305,9 @@ class DataVariable(AnyDataVariable):
|
|
|
304
305
|
|
|
305
306
|
return UpdateVariableImpl(variable=self, value=value)
|
|
306
307
|
|
|
307
|
-
|
|
308
|
-
|
|
308
|
+
@model_serializer(mode='wrap')
|
|
309
|
+
def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
|
|
310
|
+
parent_dict = nxt(self)
|
|
309
311
|
if 'data' in parent_dict:
|
|
310
312
|
parent_dict.pop('data') # make sure data is not included in the serialised dict
|
|
311
313
|
return {**parent_dict, '__typename': 'DataVariable', 'uid': str(parent_dict['uid'])}
|
|
@@ -319,7 +321,4 @@ class DataStoreEntry(BaseModel):
|
|
|
319
321
|
|
|
320
322
|
data: Optional[DataFrame] = None
|
|
321
323
|
path: Optional[str] = None
|
|
322
|
-
|
|
323
|
-
class Config:
|
|
324
|
-
extra = 'forbid'
|
|
325
|
-
arbitrary_types_allowed = True
|
|
324
|
+
model_config = ConfigDict(extra='forbid', arbitrary_types_allowed=True)
|
|
@@ -23,6 +23,7 @@ from uuid import uuid4
|
|
|
23
23
|
|
|
24
24
|
from pandas import DataFrame
|
|
25
25
|
from pandas.io.json._table_schema import build_table_schema
|
|
26
|
+
from pydantic import ConfigDict, SerializerFunctionWrapHandler, model_serializer
|
|
26
27
|
|
|
27
28
|
from dara.core.base_definitions import (
|
|
28
29
|
BaseTask,
|
|
@@ -66,11 +67,9 @@ class DerivedDataVariable(AnyDataVariable, DerivedVariable):
|
|
|
66
67
|
uid: str
|
|
67
68
|
filters: Optional[FilterQuery] = None
|
|
68
69
|
variables: List[AnyVariable]
|
|
69
|
-
polling_interval: Optional[int]
|
|
70
|
-
deps: Optional[List[AnyVariable]]
|
|
71
|
-
|
|
72
|
-
class Config:
|
|
73
|
-
extra = 'forbid'
|
|
70
|
+
polling_interval: Optional[int] = None
|
|
71
|
+
deps: Optional[List[AnyVariable]] = None
|
|
72
|
+
model_config = ConfigDict(extra='forbid')
|
|
74
73
|
|
|
75
74
|
def __init__(
|
|
76
75
|
self,
|
|
@@ -365,8 +364,9 @@ class DerivedDataVariable(AnyDataVariable, DerivedVariable):
|
|
|
365
364
|
|
|
366
365
|
return await cls.get_data(dv_entry, data_entry, dv_result['cache_key'], store, filters)
|
|
367
366
|
|
|
368
|
-
|
|
369
|
-
|
|
367
|
+
@model_serializer(mode='wrap')
|
|
368
|
+
def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
|
|
369
|
+
parent_dict = nxt(self)
|
|
370
370
|
# nested is not supported for DerivedDataVariable so remove from serialised form
|
|
371
371
|
# it's included because we inherit from DV which has the field
|
|
372
372
|
parent_dict.pop('nested')
|
|
@@ -24,15 +24,22 @@ from typing import (
|
|
|
24
24
|
Any,
|
|
25
25
|
Awaitable,
|
|
26
26
|
Callable,
|
|
27
|
-
Dict,
|
|
28
27
|
Generic,
|
|
29
28
|
List,
|
|
30
29
|
Optional,
|
|
31
30
|
TypeVar,
|
|
32
31
|
Union,
|
|
32
|
+
cast,
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
-
from pydantic import
|
|
35
|
+
from pydantic import (
|
|
36
|
+
ConfigDict,
|
|
37
|
+
Field,
|
|
38
|
+
SerializerFunctionWrapHandler,
|
|
39
|
+
ValidationInfo,
|
|
40
|
+
field_validator,
|
|
41
|
+
model_serializer,
|
|
42
|
+
)
|
|
36
43
|
from typing_extensions import TypedDict
|
|
37
44
|
|
|
38
45
|
from dara.core.base_definitions import (
|
|
@@ -76,13 +83,10 @@ class DerivedVariable(NonDataVariable, Generic[VariableType]):
|
|
|
76
83
|
cache: Optional[BaseCachePolicy]
|
|
77
84
|
variables: List[AnyVariable]
|
|
78
85
|
polling_interval: Optional[int]
|
|
79
|
-
deps: Optional[List[AnyVariable]]
|
|
86
|
+
deps: Optional[List[AnyVariable]] = Field(validate_default=True)
|
|
80
87
|
nested: List[str] = []
|
|
81
88
|
uid: str
|
|
82
|
-
|
|
83
|
-
class Config:
|
|
84
|
-
extra = 'forbid'
|
|
85
|
-
use_enum_values = True
|
|
89
|
+
model_config = ConfigDict(extra='forbid', use_enum_values=True)
|
|
86
90
|
|
|
87
91
|
def __init__(
|
|
88
92
|
self,
|
|
@@ -130,7 +134,7 @@ class DerivedVariable(NonDataVariable, Generic[VariableType]):
|
|
|
130
134
|
# Explicitly disallow run_as_task within a Jupyter environment
|
|
131
135
|
if run_as_task:
|
|
132
136
|
try:
|
|
133
|
-
from IPython import get_ipython
|
|
137
|
+
from IPython import get_ipython # type: ignore
|
|
134
138
|
except ImportError:
|
|
135
139
|
pass
|
|
136
140
|
else:
|
|
@@ -164,20 +168,20 @@ class DerivedVariable(NonDataVariable, Generic[VariableType]):
|
|
|
164
168
|
),
|
|
165
169
|
)
|
|
166
170
|
|
|
167
|
-
@
|
|
171
|
+
@field_validator('deps', mode='before')
|
|
168
172
|
@classmethod
|
|
169
|
-
def validate_deps(cls, deps: Any,
|
|
173
|
+
def validate_deps(cls, deps: Any, info: ValidationInfo) -> List[AnyVariable]:
|
|
170
174
|
"""
|
|
171
175
|
If deps is not specified, set deps to include all variables used
|
|
172
176
|
"""
|
|
173
177
|
if deps is None:
|
|
174
178
|
# This will always be set on the variable with the type verified by pydantic
|
|
175
|
-
return
|
|
179
|
+
return cast(List[AnyVariable], info.data.get('variables'))
|
|
176
180
|
|
|
177
181
|
return deps
|
|
178
182
|
|
|
179
183
|
def get(self, key: str):
|
|
180
|
-
return self.
|
|
184
|
+
return self.model_copy(update={'nested': [*self.nested, key]}, deep=True)
|
|
181
185
|
|
|
182
186
|
def trigger(self, force: bool = True):
|
|
183
187
|
"""
|
|
@@ -479,8 +483,9 @@ class DerivedVariable(NonDataVariable, Generic[VariableType]):
|
|
|
479
483
|
return True
|
|
480
484
|
return False
|
|
481
485
|
|
|
482
|
-
|
|
483
|
-
|
|
486
|
+
@model_serializer(mode='wrap')
|
|
487
|
+
def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
|
|
488
|
+
parent_dict = nxt(self)
|
|
484
489
|
return {**parent_dict, '__typename': 'DerivedVariable', 'uid': str(parent_dict['uid'])}
|
|
485
490
|
|
|
486
491
|
|
|
@@ -492,9 +497,7 @@ class DerivedVariableRegistryEntry(CachedRegistryEntry):
|
|
|
492
497
|
polling_interval: Optional[int]
|
|
493
498
|
get_value: Callable[..., Awaitable[Any]]
|
|
494
499
|
"""Handler to get the value of the derived variable. Defaults to DerivedVariable.get_value, should match the signature"""
|
|
495
|
-
|
|
496
|
-
class Config:
|
|
497
|
-
extra = 'forbid'
|
|
500
|
+
model_config = ConfigDict(extra='forbid')
|
|
498
501
|
|
|
499
502
|
|
|
500
503
|
class LatestValueRegistryEntry(CachedRegistryEntry):
|
|
@@ -86,7 +86,7 @@ FilterQuery = Union[ClauseQuery, ValueQuery]
|
|
|
86
86
|
Filter query to be applied
|
|
87
87
|
"""
|
|
88
88
|
|
|
89
|
-
ClauseQuery.
|
|
89
|
+
ClauseQuery.model_rebuild()
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
def coerce_to_filter_query(filters: Union[ClauseQuery, ValueQuery, dict, None]) -> Optional[FilterQuery]:
|
|
@@ -21,6 +21,8 @@ from contextlib import contextmanager
|
|
|
21
21
|
from contextvars import ContextVar
|
|
22
22
|
from typing import Any, Callable, Generic, List, Optional, TypeVar
|
|
23
23
|
|
|
24
|
+
from pydantic import ConfigDict, SerializerFunctionWrapHandler, model_serializer
|
|
25
|
+
|
|
24
26
|
from dara.core.interactivity.derived_data_variable import DerivedDataVariable
|
|
25
27
|
from dara.core.interactivity.derived_variable import DerivedVariable
|
|
26
28
|
from dara.core.interactivity.non_data_variable import NonDataVariable
|
|
@@ -38,14 +40,12 @@ class Variable(NonDataVariable, Generic[VariableType]):
|
|
|
38
40
|
A Variable represents a dynamic value in the system that can be read and written to by components and actions
|
|
39
41
|
"""
|
|
40
42
|
|
|
41
|
-
default: Optional[VariableType]
|
|
43
|
+
default: Optional[VariableType] = None
|
|
42
44
|
persist_value: bool = False
|
|
43
45
|
store: Optional[PersistenceStore] = None
|
|
44
46
|
uid: str
|
|
45
47
|
nested: List[str] = []
|
|
46
|
-
|
|
47
|
-
class Config:
|
|
48
|
-
extra = 'forbid'
|
|
48
|
+
model_config = ConfigDict(extra='forbid')
|
|
49
49
|
|
|
50
50
|
def __init__(
|
|
51
51
|
self,
|
|
@@ -53,6 +53,7 @@ class Variable(NonDataVariable, Generic[VariableType]):
|
|
|
53
53
|
persist_value: Optional[bool] = False,
|
|
54
54
|
uid: Optional[str] = None,
|
|
55
55
|
store: Optional[PersistenceStoreType_co] = None,
|
|
56
|
+
nested: Optional[List[str]] = None,
|
|
56
57
|
):
|
|
57
58
|
"""
|
|
58
59
|
A Variable represents a dynamic value in the system that can be read and written to by components and actions
|
|
@@ -62,6 +63,8 @@ class Variable(NonDataVariable, Generic[VariableType]):
|
|
|
62
63
|
:param uid: the unique identifier for this variable; if not provided a random one is generated
|
|
63
64
|
:param store: a persistence store to attach to the variable; modifies where the source of truth for the variable is
|
|
64
65
|
"""
|
|
66
|
+
if nested is None:
|
|
67
|
+
nested = []
|
|
65
68
|
kwargs = {'default': default, 'persist_value': persist_value, 'uid': uid, 'store': store}
|
|
66
69
|
|
|
67
70
|
# If an override is active, run the kwargs through it
|
|
@@ -229,7 +232,8 @@ class Variable(NonDataVariable, Generic[VariableType]):
|
|
|
229
232
|
|
|
230
233
|
return cls(default=other) # type: ignore
|
|
231
234
|
|
|
232
|
-
|
|
233
|
-
|
|
235
|
+
@model_serializer(mode='wrap')
|
|
236
|
+
def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
|
|
237
|
+
parent_dict = nxt(self)
|
|
234
238
|
|
|
235
239
|
return {**parent_dict, '__typename': 'Variable', 'uid': str(parent_dict['uid'])}
|
|
@@ -19,6 +19,8 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
from typing import Any, Generic, Optional, TypeVar
|
|
21
21
|
|
|
22
|
+
from pydantic import ConfigDict, SerializerFunctionWrapHandler, model_serializer
|
|
23
|
+
|
|
22
24
|
from dara.core.interactivity.non_data_variable import NonDataVariable
|
|
23
25
|
|
|
24
26
|
VariableType = TypeVar('VariableType')
|
|
@@ -31,12 +33,10 @@ class UrlVariable(NonDataVariable, Generic[VariableType]):
|
|
|
31
33
|
pages as you switch from one to the other.
|
|
32
34
|
"""
|
|
33
35
|
|
|
34
|
-
default: Optional[VariableType]
|
|
36
|
+
default: Optional[VariableType] = None
|
|
35
37
|
query: str
|
|
36
38
|
uid: str
|
|
37
|
-
|
|
38
|
-
class Config:
|
|
39
|
-
extra = 'forbid'
|
|
39
|
+
model_config = ConfigDict(extra='forbid')
|
|
40
40
|
|
|
41
41
|
def __init__(self, query: str, default: Optional[VariableType] = None, uid: Optional[str] = None):
|
|
42
42
|
"""
|
|
@@ -130,6 +130,7 @@ class UrlVariable(NonDataVariable, Generic[VariableType]):
|
|
|
130
130
|
assert_no_context('ctx.update')
|
|
131
131
|
return UpdateVariableImpl(variable=self, value=value)
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
@model_serializer(mode='wrap')
|
|
134
|
+
def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
|
|
135
|
+
parent_dict = nxt(self)
|
|
135
136
|
return {**parent_dict, '__typename': 'UrlVariable', 'uid': str(parent_dict['uid'])}
|
dara/core/internal/download.py
CHANGED
|
@@ -36,7 +36,7 @@ class DownloadDataEntry(BaseModel):
|
|
|
36
36
|
uid: str
|
|
37
37
|
file_path: str
|
|
38
38
|
cleanup_file: bool
|
|
39
|
-
identity_name: Optional[str]
|
|
39
|
+
identity_name: Optional[str] = None
|
|
40
40
|
download: Callable[['DownloadDataEntry'], Awaitable[Tuple[anyio.AsyncFile, Callable[..., Awaitable]]]]
|
|
41
41
|
"""Handler for getting the file from the entry"""
|
|
42
42
|
|
dara/core/internal/hashing.py
CHANGED
|
@@ -68,27 +68,6 @@ class ReferrableWithFilters(Referrable):
|
|
|
68
68
|
filters: dict
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
def _has_template_marker(obj: Any) -> bool:
|
|
72
|
-
"""
|
|
73
|
-
Check if an object has a TemplateMarker
|
|
74
|
-
anywhere in its data
|
|
75
|
-
"""
|
|
76
|
-
if isinstance(obj, dict):
|
|
77
|
-
if obj.get('__typename') == 'TemplateMarker':
|
|
78
|
-
return True
|
|
79
|
-
|
|
80
|
-
for val in obj.values():
|
|
81
|
-
if _has_template_marker(val):
|
|
82
|
-
return True
|
|
83
|
-
|
|
84
|
-
if isinstance(obj, list):
|
|
85
|
-
for item in obj:
|
|
86
|
-
if _has_template_marker(item):
|
|
87
|
-
return True
|
|
88
|
-
|
|
89
|
-
return False
|
|
90
|
-
|
|
91
|
-
|
|
92
71
|
def _get_identifier(obj: Referrable) -> str:
|
|
93
72
|
"""
|
|
94
73
|
Get a unique identifier from a 'referrable' object
|
|
@@ -112,15 +91,12 @@ def _get_identifier(obj: Referrable) -> str:
|
|
|
112
91
|
def _is_referrable(obj: Any) -> TypeGuard[Referrable]:
|
|
113
92
|
"""
|
|
114
93
|
Check if a dict is a Referrable type with a '__typename' field and 'uid'
|
|
115
|
-
|
|
116
|
-
Bails out if the object has a TemplateMarker
|
|
117
94
|
"""
|
|
118
95
|
return (
|
|
119
96
|
isinstance(obj, dict)
|
|
120
97
|
and '__typename' in obj
|
|
121
98
|
and 'Variable' in obj['__typename'] # Right now this is meant for Variable objects only
|
|
122
99
|
and 'uid' in obj
|
|
123
|
-
and not _has_template_marker(obj)
|
|
124
100
|
)
|
|
125
101
|
|
|
126
102
|
|
dara/core/internal/routing.py
CHANGED
|
@@ -120,7 +120,7 @@ def create_router(config: Configuration):
|
|
|
120
120
|
values: NormalizedPayload[Mapping[str, Any]]
|
|
121
121
|
"""Dynamic kwarg values"""
|
|
122
122
|
|
|
123
|
-
input: Any
|
|
123
|
+
input: Any = None
|
|
124
124
|
"""Input from the component"""
|
|
125
125
|
|
|
126
126
|
ws_channel: str
|
|
@@ -194,7 +194,7 @@ def create_router(config: Configuration):
|
|
|
194
194
|
@core_api_router.get('/config', dependencies=[Depends(verify_session)])
|
|
195
195
|
async def get_config(): # pylint: disable=unused-variable
|
|
196
196
|
return {
|
|
197
|
-
**config.
|
|
197
|
+
**config.model_dump(
|
|
198
198
|
include={
|
|
199
199
|
'enable_devtools',
|
|
200
200
|
'live_reload',
|
|
@@ -211,7 +211,7 @@ def create_router(config: Configuration):
|
|
|
211
211
|
@core_api_router.get('/auth-config')
|
|
212
212
|
async def get_auth_config(): # pylint: disable=unused-variable
|
|
213
213
|
return {
|
|
214
|
-
'auth_components': config.auth_config.component_config.
|
|
214
|
+
'auth_components': config.auth_config.component_config.model_dump(),
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
@core_api_router.get('/components', dependencies=[Depends(verify_session)])
|
|
@@ -225,7 +225,7 @@ def create_router(config: Configuration):
|
|
|
225
225
|
registry_mgr: RegistryLookup = utils_registry.get('RegistryLookup')
|
|
226
226
|
await registry_mgr.get(component_registry, name)
|
|
227
227
|
|
|
228
|
-
return {k: comp.
|
|
228
|
+
return {k: comp.model_dump(exclude={'func'}) for k, comp in component_registry.get_all().items()}
|
|
229
229
|
|
|
230
230
|
class ComponentRequestBody(BaseModel):
|
|
231
231
|
# Dynamic kwarg values
|
|
@@ -294,9 +294,9 @@ def create_router(config: Configuration):
|
|
|
294
294
|
)
|
|
295
295
|
|
|
296
296
|
class DataVariableRequestBody(BaseModel):
|
|
297
|
-
filters: Optional[FilterQuery]
|
|
298
|
-
cache_key: Optional[str]
|
|
299
|
-
ws_channel: Optional[str]
|
|
297
|
+
filters: Optional[FilterQuery] = None
|
|
298
|
+
cache_key: Optional[str] = None
|
|
299
|
+
ws_channel: Optional[str] = None
|
|
300
300
|
|
|
301
301
|
@core_api_router.post('/data-variable/{uid}', dependencies=[Depends(verify_session)])
|
|
302
302
|
async def get_data_variable(
|
|
@@ -366,8 +366,8 @@ def create_router(config: Configuration):
|
|
|
366
366
|
raise HTTPException(status_code=400, detail=str(e))
|
|
367
367
|
|
|
368
368
|
class DataVariableCountRequestBody(BaseModel):
|
|
369
|
-
cache_key: Optional[str]
|
|
370
|
-
filters: Optional[FilterQuery]
|
|
369
|
+
cache_key: Optional[str] = None
|
|
370
|
+
filters: Optional[FilterQuery] = None
|
|
371
371
|
|
|
372
372
|
@core_api_router.post('/data-variable/{uid}/count', dependencies=[Depends(verify_session)])
|
|
373
373
|
async def get_data_variable_count(uid: str, body: Optional[DataVariableCountRequestBody] = None):
|
|
@@ -538,7 +538,7 @@ def create_router(config: Configuration):
|
|
|
538
538
|
async def get_template(template: str): # pylint: disable=unused-variable
|
|
539
539
|
try:
|
|
540
540
|
selected_template = template_registry.get(template)
|
|
541
|
-
normalized_template, lookup = normalize(selected_template
|
|
541
|
+
normalized_template, lookup = normalize(jsonable_encoder(selected_template))
|
|
542
542
|
return {'data': normalized_template, 'lookup': lookup}
|
|
543
543
|
except KeyError:
|
|
544
544
|
raise HTTPException(status_code=404, detail=f'Template: {template}, not found in registry')
|
dara/core/internal/scheduler.py
CHANGED
|
@@ -23,7 +23,7 @@ from pickle import PicklingError
|
|
|
23
23
|
from typing import Any, List, Optional, Union
|
|
24
24
|
|
|
25
25
|
from croniter import croniter
|
|
26
|
-
from pydantic import BaseModel,
|
|
26
|
+
from pydantic import BaseModel, field_validator
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class ScheduledJob(BaseModel):
|
|
@@ -174,7 +174,8 @@ class ScheduledJobFactory(BaseModel):
|
|
|
174
174
|
weekday: Optional[datetime] = None
|
|
175
175
|
run_once: bool
|
|
176
176
|
|
|
177
|
-
@
|
|
177
|
+
@field_validator('weekday', mode='before')
|
|
178
|
+
@classmethod
|
|
178
179
|
def validate_weekday(cls, weekday: Any) -> datetime: # pylint: disable=E0213
|
|
179
180
|
if isinstance(weekday, datetime):
|
|
180
181
|
return weekday
|