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.
- dara/core/__init__.py +3 -0
- dara/core/actions.py +1 -2
- dara/core/auth/basic.py +22 -16
- dara/core/auth/definitions.py +2 -2
- dara/core/auth/routes.py +5 -5
- dara/core/auth/utils.py +5 -5
- dara/core/base_definitions.py +22 -64
- dara/core/cli.py +8 -7
- dara/core/configuration.py +5 -2
- dara/core/css.py +1 -2
- dara/core/data_utils.py +18 -19
- dara/core/defaults.py +6 -7
- dara/core/definitions.py +50 -19
- dara/core/http.py +7 -3
- dara/core/interactivity/__init__.py +6 -0
- dara/core/interactivity/actions.py +52 -50
- dara/core/interactivity/any_data_variable.py +7 -134
- dara/core/interactivity/any_variable.py +5 -8
- dara/core/interactivity/client_variable.py +71 -0
- dara/core/interactivity/data_variable.py +8 -266
- dara/core/interactivity/derived_data_variable.py +7 -290
- dara/core/interactivity/derived_variable.py +416 -176
- dara/core/interactivity/filtering.py +46 -27
- dara/core/interactivity/loop_variable.py +2 -2
- dara/core/interactivity/non_data_variable.py +5 -68
- dara/core/interactivity/plain_variable.py +89 -15
- dara/core/interactivity/server_variable.py +325 -0
- dara/core/interactivity/state_variable.py +69 -0
- dara/core/interactivity/switch_variable.py +19 -19
- dara/core/interactivity/tabular_variable.py +94 -0
- dara/core/interactivity/url_variable.py +10 -90
- dara/core/internal/cache_store/base_impl.py +2 -1
- dara/core/internal/cache_store/cache_store.py +22 -25
- dara/core/internal/cache_store/keep_all.py +4 -1
- dara/core/internal/cache_store/lru.py +5 -1
- dara/core/internal/cache_store/ttl.py +4 -1
- dara/core/internal/cgroup.py +1 -1
- dara/core/internal/dependency_resolution.py +60 -66
- dara/core/internal/devtools.py +12 -5
- dara/core/internal/download.py +13 -4
- dara/core/internal/encoder_registry.py +7 -7
- dara/core/internal/execute_action.py +13 -13
- dara/core/internal/hashing.py +1 -3
- dara/core/internal/import_discovery.py +3 -4
- dara/core/internal/multi_resource_lock.py +70 -0
- dara/core/internal/normalization.py +9 -18
- dara/core/internal/pandas_utils.py +107 -5
- dara/core/internal/pool/definitions.py +1 -1
- dara/core/internal/pool/task_pool.py +25 -16
- dara/core/internal/pool/utils.py +21 -18
- dara/core/internal/pool/worker.py +3 -2
- dara/core/internal/port_utils.py +1 -1
- dara/core/internal/registries.py +12 -6
- dara/core/internal/registry.py +4 -2
- dara/core/internal/registry_lookup.py +11 -5
- dara/core/internal/routing.py +109 -145
- dara/core/internal/scheduler.py +13 -8
- dara/core/internal/settings.py +2 -2
- dara/core/internal/store.py +2 -29
- dara/core/internal/tasks.py +379 -195
- dara/core/internal/utils.py +36 -13
- dara/core/internal/websocket.py +21 -20
- dara/core/js_tooling/js_utils.py +28 -26
- dara/core/js_tooling/templates/vite.config.template.ts +12 -3
- dara/core/logging.py +13 -12
- dara/core/main.py +14 -11
- dara/core/metrics/cache.py +1 -1
- dara/core/metrics/utils.py +3 -3
- dara/core/persistence.py +27 -5
- dara/core/umd/dara.core.umd.js +68291 -64718
- dara/core/visual/components/__init__.py +2 -2
- dara/core/visual/components/fallback.py +30 -4
- dara/core/visual/components/for_cmp.py +4 -1
- dara/core/visual/css/__init__.py +30 -31
- dara/core/visual/dynamic_component.py +31 -28
- dara/core/visual/progress_updater.py +4 -3
- {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/METADATA +12 -11
- dara_core-1.20.1a3.dist-info/RECORD +119 -0
- dara_core-1.20.1a1.dist-info/RECORD +0 -114
- {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/LICENSE +0 -0
- {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/WHEEL +0 -0
- {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/entry_points.txt +0 -0
dara/core/definitions.py
CHANGED
|
@@ -19,15 +19,14 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import json
|
|
21
21
|
import uuid
|
|
22
|
+
from collections.abc import Awaitable, Mapping
|
|
22
23
|
from enum import Enum
|
|
23
24
|
from typing import (
|
|
24
25
|
Any,
|
|
25
|
-
Awaitable,
|
|
26
26
|
Callable,
|
|
27
27
|
ClassVar,
|
|
28
28
|
List,
|
|
29
29
|
Literal,
|
|
30
|
-
Mapping,
|
|
31
30
|
Optional,
|
|
32
31
|
Protocol,
|
|
33
32
|
Type,
|
|
@@ -85,22 +84,22 @@ class ErrorHandlingConfig(BaseModel):
|
|
|
85
84
|
raw_css: Optional[Any] = None
|
|
86
85
|
"""
|
|
87
86
|
Raw styling to apply to the displayed error boundary.
|
|
88
|
-
Accepts a CSSProperties, dict, str, or
|
|
87
|
+
Accepts a CSSProperties, dict, str, or ClientVariable.
|
|
89
88
|
"""
|
|
90
89
|
|
|
91
90
|
@field_validator('raw_css', mode='before')
|
|
92
91
|
@classmethod
|
|
93
92
|
def validate_raw_css(cls, value):
|
|
94
|
-
from dara.core.interactivity.
|
|
93
|
+
from dara.core.interactivity.client_variable import ClientVariable
|
|
95
94
|
|
|
96
95
|
if value is None:
|
|
97
96
|
return None
|
|
98
|
-
if isinstance(value, (str,
|
|
97
|
+
if isinstance(value, (str, ClientVariable, CSSProperties)):
|
|
99
98
|
return value
|
|
100
99
|
if isinstance(value, dict):
|
|
101
100
|
return {_kebab_to_camel(k): v for k, v in value.items()}
|
|
102
101
|
|
|
103
|
-
raise ValueError(f'raw_css must be a CSSProperties, dict, str, None or
|
|
102
|
+
raise ValueError(f'raw_css must be a CSSProperties, dict, str, None or ClientVariable, got {type(value)}')
|
|
104
103
|
|
|
105
104
|
def model_dump(self, *args, **kwargs):
|
|
106
105
|
result = super().model_dump(*args, **kwargs)
|
|
@@ -146,7 +145,7 @@ class ComponentInstance(BaseModel):
|
|
|
146
145
|
"""
|
|
147
146
|
Raw styling to apply to the component.
|
|
148
147
|
Can be an dict/CSSProperties instance representing the `styles` tag, a string injected directly into the CSS of the wrapping component,
|
|
149
|
-
or a
|
|
148
|
+
or a ClientVariable resoling to either of the above.
|
|
150
149
|
|
|
151
150
|
```python
|
|
152
151
|
|
|
@@ -175,7 +174,7 @@ class ComponentInstance(BaseModel):
|
|
|
175
174
|
error_handler: Optional[ErrorHandlingConfig] = None
|
|
176
175
|
"""Configure the error handling for the component"""
|
|
177
176
|
|
|
178
|
-
fallback: Optional[BaseFallback] = None
|
|
177
|
+
fallback: Optional[Union[BaseFallback, ComponentInstance]] = None
|
|
179
178
|
"""
|
|
180
179
|
Fallback component to render in place of the actual UI if it has not finished loading
|
|
181
180
|
"""
|
|
@@ -197,10 +196,27 @@ class ComponentInstance(BaseModel):
|
|
|
197
196
|
def __repr__(self):
|
|
198
197
|
return '__dara__' + json.dumps(jsonable_encoder(self))
|
|
199
198
|
|
|
199
|
+
@field_validator('fallback', mode='before')
|
|
200
|
+
@classmethod
|
|
201
|
+
def validate_fallback(cls, fallback):
|
|
202
|
+
if fallback is None:
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
if isinstance(fallback, BaseFallback):
|
|
206
|
+
return fallback
|
|
207
|
+
|
|
208
|
+
# wrap custom component in fallback.custom
|
|
209
|
+
if isinstance(fallback, ComponentInstance):
|
|
210
|
+
from dara.core.visual.components.fallback import Fallback
|
|
211
|
+
|
|
212
|
+
return Fallback.Custom(component=fallback)
|
|
213
|
+
|
|
214
|
+
raise ValueError(f'fallback must be a BaseFallback or ComponentInstance, got {type(fallback)}')
|
|
215
|
+
|
|
200
216
|
@field_validator('raw_css', mode='before')
|
|
201
217
|
@classmethod
|
|
202
218
|
def parse_css(cls, css: Optional[Any]):
|
|
203
|
-
from dara.core.interactivity.
|
|
219
|
+
from dara.core.interactivity.client_variable import ClientVariable
|
|
204
220
|
|
|
205
221
|
if css is None:
|
|
206
222
|
return None
|
|
@@ -209,10 +225,10 @@ class ComponentInstance(BaseModel):
|
|
|
209
225
|
if isinstance(css, dict):
|
|
210
226
|
return {_kebab_to_camel(k): v for k, v in css.items()}
|
|
211
227
|
|
|
212
|
-
if isinstance(css, (
|
|
228
|
+
if isinstance(css, (ClientVariable, CSSProperties, str)):
|
|
213
229
|
return css
|
|
214
230
|
|
|
215
|
-
raise ValueError(f'raw_css must be a CSSProperties, dict, str, None or
|
|
231
|
+
raise ValueError(f'raw_css must be a CSSProperties, dict, str, None or ClientVariable, got {type(css)}')
|
|
216
232
|
|
|
217
233
|
@classmethod
|
|
218
234
|
def isinstance(cls, obj: Any) -> bool:
|
|
@@ -264,8 +280,7 @@ class CallableClassComponent(Protocol):
|
|
|
264
280
|
Callable class component protocol. Describes any class with a __call__ instance method returning a component instance.
|
|
265
281
|
"""
|
|
266
282
|
|
|
267
|
-
def __call__(self) -> ComponentInstance:
|
|
268
|
-
...
|
|
283
|
+
def __call__(self) -> ComponentInstance: ...
|
|
269
284
|
|
|
270
285
|
|
|
271
286
|
DiscoverTarget = Union[Callable[..., ComponentInstance], ComponentInstance, Type[CallableClassComponent]]
|
|
@@ -279,7 +294,7 @@ def discover(outer_obj: DiscoverT) -> DiscoverT:
|
|
|
279
294
|
Will make sure to statically register all encountered dependencies of marked functional component or component class.
|
|
280
295
|
Should not be necessary in most cases, mainly useful when creating component libraries.
|
|
281
296
|
"""
|
|
282
|
-
outer_obj.__wrapped_by__ = discover
|
|
297
|
+
outer_obj.__wrapped_by__ = discover # type: ignore
|
|
283
298
|
return outer_obj
|
|
284
299
|
|
|
285
300
|
|
|
@@ -419,9 +434,8 @@ class BaseFallback(StyledComponentInstance):
|
|
|
419
434
|
@field_validator('suspend_render')
|
|
420
435
|
@classmethod
|
|
421
436
|
def validate_suspend_render(cls, value):
|
|
422
|
-
if isinstance(value, int):
|
|
423
|
-
|
|
424
|
-
raise ValueError('suspend_render must be a positive integer')
|
|
437
|
+
if isinstance(value, int) and value < 0:
|
|
438
|
+
raise ValueError('suspend_render must be a positive integer')
|
|
425
439
|
|
|
426
440
|
return value
|
|
427
441
|
|
|
@@ -461,13 +475,30 @@ class PyComponentDef(BaseModel):
|
|
|
461
475
|
func: Optional[Callable[..., Any]] = None
|
|
462
476
|
name: str
|
|
463
477
|
dynamic_kwargs: Optional[Mapping[str, AnyVariable]] = None
|
|
464
|
-
fallback: Optional[BaseFallback] = None
|
|
478
|
+
fallback: Optional[Union[BaseFallback, ComponentInstance]] = None
|
|
465
479
|
polling_interval: Optional[int] = None
|
|
466
480
|
render_component: Callable[..., Awaitable[Any]]
|
|
467
481
|
"""Handler to render the component. Defaults to dara.core.visual.dynamic_component.render_component"""
|
|
468
482
|
|
|
469
483
|
type: Literal[ComponentType.PY] = ComponentType.PY
|
|
470
484
|
|
|
485
|
+
@field_validator('fallback', mode='before')
|
|
486
|
+
@classmethod
|
|
487
|
+
def validate_fallback(cls, fallback):
|
|
488
|
+
if fallback is None:
|
|
489
|
+
return None
|
|
490
|
+
|
|
491
|
+
if isinstance(fallback, BaseFallback):
|
|
492
|
+
return fallback
|
|
493
|
+
|
|
494
|
+
# wrap custom component in fallback.custom
|
|
495
|
+
if isinstance(fallback, ComponentInstance):
|
|
496
|
+
from dara.core.visual.components.fallback import Fallback
|
|
497
|
+
|
|
498
|
+
return Fallback.Custom(component=fallback)
|
|
499
|
+
|
|
500
|
+
raise ValueError(f'fallback must be a BaseFallback or ComponentInstance, got {type(fallback)}')
|
|
501
|
+
|
|
471
502
|
|
|
472
503
|
# Helper type annotation for working with components
|
|
473
504
|
ComponentTypeAnnotation = Union[PyComponentDef, JsComponentDef]
|
|
@@ -505,7 +536,7 @@ class Page(BaseModel):
|
|
|
505
536
|
icon: Optional[str] = None
|
|
506
537
|
content: ComponentInstanceType
|
|
507
538
|
name: str
|
|
508
|
-
sub_pages: Optional[List[
|
|
539
|
+
sub_pages: Optional[List[Page]] = []
|
|
509
540
|
url_safe_name: str
|
|
510
541
|
include_in_menu: Optional[bool] = None
|
|
511
542
|
on_load: Optional[Action] = None
|
dara/core/http.py
CHANGED
|
@@ -18,7 +18,7 @@ limitations under the License.
|
|
|
18
18
|
import inspect
|
|
19
19
|
from collections import OrderedDict
|
|
20
20
|
from functools import wraps
|
|
21
|
-
from typing import Callable, Dict, List, Type
|
|
21
|
+
from typing import Callable, Dict, List, Type, Union
|
|
22
22
|
|
|
23
23
|
from fastapi import Depends
|
|
24
24
|
from fastapi.params import Depends as DependsType
|
|
@@ -54,7 +54,10 @@ def _get_config_instances(annotations: Dict[str, type]) -> Dict[str, EndpointCon
|
|
|
54
54
|
def _method_decorator(method: HttpMethod):
|
|
55
55
|
"""Create a decorator for a given HTTP method"""
|
|
56
56
|
|
|
57
|
-
def _decorator(url: str, dependencies: List[DependsType] =
|
|
57
|
+
def _decorator(url: str, dependencies: Union[List[DependsType], None] = None, authenticated: bool = True):
|
|
58
|
+
if dependencies is None:
|
|
59
|
+
dependencies = []
|
|
60
|
+
|
|
58
61
|
def _inner(func: Callable):
|
|
59
62
|
# Make sure we're using a copy of the dependencies list
|
|
60
63
|
final_dependencies = dependencies[:]
|
|
@@ -104,7 +107,8 @@ def _method_decorator(method: HttpMethod):
|
|
|
104
107
|
|
|
105
108
|
new_handler.__annotations__ = new_annotations
|
|
106
109
|
new_handler.__signature__ = inspect.Signature( # type: ignore
|
|
107
|
-
parameters=list(params.values()),
|
|
110
|
+
parameters=list(params.values()),
|
|
111
|
+
return_annotation=sig.return_annotation, # type: ignore
|
|
108
112
|
)
|
|
109
113
|
|
|
110
114
|
return ApiRoute(
|
|
@@ -36,12 +36,15 @@ from dara.core.interactivity.actions import (
|
|
|
36
36
|
)
|
|
37
37
|
from dara.core.interactivity.any_data_variable import AnyDataVariable
|
|
38
38
|
from dara.core.interactivity.any_variable import AnyVariable
|
|
39
|
+
from dara.core.interactivity.client_variable import ClientVariable
|
|
39
40
|
from dara.core.interactivity.condition import Condition, Operator
|
|
40
41
|
from dara.core.interactivity.data_variable import DataVariable
|
|
41
42
|
from dara.core.interactivity.derived_data_variable import DerivedDataVariable
|
|
42
43
|
from dara.core.interactivity.derived_variable import DerivedVariable
|
|
43
44
|
from dara.core.interactivity.non_data_variable import NonDataVariable
|
|
44
45
|
from dara.core.interactivity.plain_variable import Variable
|
|
46
|
+
from dara.core.interactivity.server_variable import ServerVariable
|
|
47
|
+
from dara.core.interactivity.state_variable import StateVariable
|
|
45
48
|
from dara.core.interactivity.switch_variable import SwitchVariable
|
|
46
49
|
from dara.core.interactivity.url_variable import UrlVariable
|
|
47
50
|
|
|
@@ -50,9 +53,11 @@ __all__ = [
|
|
|
50
53
|
'ActionCtx',
|
|
51
54
|
'AnyVariable',
|
|
52
55
|
'AnyDataVariable',
|
|
56
|
+
'ClientVariable',
|
|
53
57
|
'DataVariable',
|
|
54
58
|
'NonDataVariable',
|
|
55
59
|
'Variable',
|
|
60
|
+
'StateVariable',
|
|
56
61
|
'SwitchVariable',
|
|
57
62
|
'DerivedVariable',
|
|
58
63
|
'DerivedDataVariable',
|
|
@@ -67,6 +72,7 @@ __all__ = [
|
|
|
67
72
|
'TriggerVariable',
|
|
68
73
|
'UpdateVariable',
|
|
69
74
|
'UpdateVariableImpl',
|
|
75
|
+
'ServerVariable',
|
|
70
76
|
'SideEffect',
|
|
71
77
|
'Condition',
|
|
72
78
|
'Operator',
|
|
@@ -17,16 +17,17 @@ limitations under the License.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
+
import contextlib
|
|
20
21
|
import inspect
|
|
21
22
|
import math
|
|
22
23
|
import uuid
|
|
24
|
+
from collections.abc import Awaitable
|
|
23
25
|
from contextvars import ContextVar
|
|
24
26
|
from enum import Enum
|
|
25
27
|
from functools import partial, update_wrapper
|
|
26
28
|
from typing import (
|
|
27
29
|
TYPE_CHECKING,
|
|
28
30
|
Any,
|
|
29
|
-
Awaitable,
|
|
30
31
|
Callable,
|
|
31
32
|
ClassVar,
|
|
32
33
|
Dict,
|
|
@@ -43,17 +44,18 @@ import anyio
|
|
|
43
44
|
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
|
44
45
|
from pandas import DataFrame
|
|
45
46
|
from pydantic import ConfigDict
|
|
46
|
-
from typing_extensions import deprecated
|
|
47
|
+
from typing_extensions import TypeAlias, deprecated
|
|
47
48
|
|
|
48
49
|
from dara.core.base_definitions import (
|
|
49
50
|
ActionDef,
|
|
50
51
|
ActionImpl,
|
|
51
52
|
ActionResolverDef,
|
|
52
53
|
AnnotatedAction,
|
|
54
|
+
TaskProgressUpdate,
|
|
53
55
|
)
|
|
54
56
|
from dara.core.base_definitions import DaraBaseModel as BaseModel
|
|
55
|
-
from dara.core.
|
|
56
|
-
from dara.core.interactivity.
|
|
57
|
+
from dara.core.interactivity.server_variable import ServerVariable
|
|
58
|
+
from dara.core.interactivity.state_variable import StateVariable
|
|
57
59
|
from dara.core.internal.download import generate_download_code
|
|
58
60
|
from dara.core.internal.registry_lookup import RegistryLookup
|
|
59
61
|
from dara.core.internal.utils import run_user_handler
|
|
@@ -63,10 +65,8 @@ if TYPE_CHECKING:
|
|
|
63
65
|
from dara.core.interactivity import (
|
|
64
66
|
AnyVariable,
|
|
65
67
|
DerivedVariable,
|
|
66
|
-
UrlVariable,
|
|
67
68
|
Variable,
|
|
68
69
|
)
|
|
69
|
-
from dara.core.internal.cache_store import CacheStore
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
class ActionInputs(BaseModel):
|
|
@@ -97,7 +97,7 @@ class ComponentActionContext(ActionContext):
|
|
|
97
97
|
ActionContext for actions that only require component value
|
|
98
98
|
"""
|
|
99
99
|
|
|
100
|
-
inputs: ComponentActionInputs
|
|
100
|
+
inputs: ComponentActionInputs # type: ignore
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
class UpdateVariableImpl(ActionImpl):
|
|
@@ -124,7 +124,7 @@ class UpdateVariableImpl(ActionImpl):
|
|
|
124
124
|
|
|
125
125
|
py_name = 'UpdateVariable'
|
|
126
126
|
|
|
127
|
-
variable: Union[Variable,
|
|
127
|
+
variable: Union[Variable, ServerVariable]
|
|
128
128
|
value: Any
|
|
129
129
|
|
|
130
130
|
INPUT: ClassVar[str] = '__dara_input__'
|
|
@@ -134,19 +134,18 @@ class UpdateVariableImpl(ActionImpl):
|
|
|
134
134
|
"""Special value for `value` that will toggle the variable value"""
|
|
135
135
|
|
|
136
136
|
async def execute(self, ctx: ActionCtx) -> Any:
|
|
137
|
-
if isinstance(self.variable,
|
|
137
|
+
if isinstance(self.variable, ServerVariable):
|
|
138
138
|
# Update on the backend
|
|
139
139
|
from dara.core.internal.registries import (
|
|
140
|
-
|
|
140
|
+
server_variable_registry,
|
|
141
141
|
utils_registry,
|
|
142
142
|
)
|
|
143
143
|
|
|
144
|
-
store: CacheStore = utils_registry.get('Store')
|
|
145
144
|
registry_mgr: RegistryLookup = utils_registry.get('RegistryLookup')
|
|
146
145
|
|
|
147
|
-
var_entry = await registry_mgr.get(
|
|
148
|
-
|
|
149
|
-
# Don't notify frontend explicitly, all clients will be notified by
|
|
146
|
+
var_entry = await registry_mgr.get(server_variable_registry, self.variable.uid)
|
|
147
|
+
await ServerVariable.write_value(var_entry, self.value)
|
|
148
|
+
# Don't notify frontend explicitly, all clients will be notified by write_value above
|
|
150
149
|
return None
|
|
151
150
|
|
|
152
151
|
# for non-data variables just ping frontend with the new value
|
|
@@ -162,7 +161,7 @@ class UpdateVariableInputs(ActionInputs):
|
|
|
162
161
|
|
|
163
162
|
|
|
164
163
|
class UpdateVariableContext(ActionContext):
|
|
165
|
-
inputs: UpdateVariableInputs
|
|
164
|
+
inputs: UpdateVariableInputs # type: ignore
|
|
166
165
|
|
|
167
166
|
|
|
168
167
|
@deprecated('Use @action or `UpdateVariableImpl` for simple cases')
|
|
@@ -171,7 +170,7 @@ class UpdateVariable(AnnotatedAction):
|
|
|
171
170
|
@deprecated: Passing in resolvers is deprecated, use `ctx.update` in an `@action` or `UpdateVariableImpl` instead.
|
|
172
171
|
`UpdateVariableImpl` will be renamed to `UpdateVariable` in Dara 2.0.
|
|
173
172
|
|
|
174
|
-
The UpdateVariable action can be passed to any `ComponentInstance` prop accepting an action and trigger the update of a Variable
|
|
173
|
+
The UpdateVariable action can be passed to any `ComponentInstance` prop accepting an action and trigger the update of a Variable or ServerVariable.
|
|
175
174
|
The resolver function takes a Context param which will feed the `inputs`: `old` and `new` as well as any `extras` passed through.
|
|
176
175
|
|
|
177
176
|
Below an example of how a resolver might look:
|
|
@@ -239,13 +238,13 @@ class UpdateVariable(AnnotatedAction):
|
|
|
239
238
|
|
|
240
239
|
Ctx: ClassVar[type[UpdateVariableContext]] = UpdateVariableContext
|
|
241
240
|
|
|
242
|
-
variable: Union[Variable,
|
|
241
|
+
variable: Union[Variable, ServerVariable]
|
|
243
242
|
extras: Optional[List[AnyVariable]]
|
|
244
243
|
|
|
245
244
|
def __init__(
|
|
246
245
|
self,
|
|
247
246
|
resolver: Callable[[UpdateVariableContext], Any],
|
|
248
|
-
variable: Union[Variable,
|
|
247
|
+
variable: Union[Variable, ServerVariable],
|
|
249
248
|
extras: Optional[List[AnyVariable]] = None,
|
|
250
249
|
):
|
|
251
250
|
"""
|
|
@@ -254,8 +253,8 @@ class UpdateVariable(AnnotatedAction):
|
|
|
254
253
|
:param extras: any extra variables to resolve and pass to the resolution function context
|
|
255
254
|
"""
|
|
256
255
|
|
|
257
|
-
async def _update(ctx: action.Ctx, **kwargs):
|
|
258
|
-
ctx = cast(ActionCtx, ctx)
|
|
256
|
+
async def _update(ctx: action.Ctx, **kwargs): # type: ignore
|
|
257
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
259
258
|
old = kwargs.pop('old')
|
|
260
259
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
261
260
|
old_ctx = UpdateVariableContext(inputs=UpdateVariableInputs(old=old, new=ctx.input), extras=extras)
|
|
@@ -271,7 +270,7 @@ class UpdateVariable(AnnotatedAction):
|
|
|
271
270
|
for idx in range(len(extras or []))
|
|
272
271
|
],
|
|
273
272
|
]
|
|
274
|
-
_update.__signature__ = inspect.Signature(params)
|
|
273
|
+
_update.__signature__ = inspect.Signature(params) # type: ignore
|
|
275
274
|
|
|
276
275
|
# Pass in variable and extras as kwargs
|
|
277
276
|
kwargs = {f'kwarg_{idx}': value for idx, value in enumerate(extras or [])}
|
|
@@ -428,11 +427,11 @@ def NavigateTo(
|
|
|
428
427
|
return NavigateToImpl(url=url, new_tab=new_tab)
|
|
429
428
|
|
|
430
429
|
# Otherwise create a new @action with the provided resolver
|
|
431
|
-
async def _navigate(ctx: action.Ctx, **kwargs):
|
|
432
|
-
ctx = cast(ActionCtx, ctx)
|
|
430
|
+
async def _navigate(ctx: action.Ctx, **kwargs): # type: ignore
|
|
431
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
433
432
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
434
433
|
old_ctx = ComponentActionContext(inputs=ComponentActionInputs(value=ctx.input), extras=extras)
|
|
435
|
-
result = await run_user_handler(url, args=(old_ctx,))
|
|
434
|
+
result = await run_user_handler(url, args=(old_ctx,)) # type: ignore
|
|
436
435
|
# Navigate to resulting url
|
|
437
436
|
await ctx.navigate(result, new_tab)
|
|
438
437
|
|
|
@@ -444,14 +443,14 @@ def NavigateTo(
|
|
|
444
443
|
for idx in range(len(extras or []))
|
|
445
444
|
],
|
|
446
445
|
]
|
|
447
|
-
_navigate.__signature__ = inspect.Signature(params)
|
|
446
|
+
_navigate.__signature__ = inspect.Signature(params) # type: ignore
|
|
448
447
|
|
|
449
448
|
# Pass in variable and extras as kwargs
|
|
450
449
|
kwargs = {f'kwarg_{idx}': value for idx, value in enumerate(extras or [])}
|
|
451
450
|
return action(_navigate)(**kwargs)
|
|
452
451
|
|
|
453
452
|
|
|
454
|
-
NavigateTo.Ctx = ComponentActionContext
|
|
453
|
+
NavigateTo.Ctx = ComponentActionContext # type: ignore
|
|
455
454
|
|
|
456
455
|
|
|
457
456
|
def Logout():
|
|
@@ -627,13 +626,13 @@ def DownloadContent(
|
|
|
627
626
|
|
|
628
627
|
```python
|
|
629
628
|
|
|
630
|
-
from dara.core import action, ConfigurationBuilder,
|
|
629
|
+
from dara.core import action, ConfigurationBuilder, ServerVariable, DownloadContent
|
|
631
630
|
from dara.components.components import Button, Stack
|
|
632
631
|
|
|
633
632
|
|
|
634
633
|
# generate data, alternatively you could load it from a file
|
|
635
634
|
df = pandas.DataFrame(data={'x': [1, 2, 3], 'y':[4, 5, 6]})
|
|
636
|
-
my_var =
|
|
635
|
+
my_var = ServerVariable(df)
|
|
637
636
|
|
|
638
637
|
config = ConfigurationBuilder()
|
|
639
638
|
|
|
@@ -663,8 +662,8 @@ def DownloadContent(
|
|
|
663
662
|
```
|
|
664
663
|
"""
|
|
665
664
|
|
|
666
|
-
async def _download(ctx: action.Ctx, **kwargs):
|
|
667
|
-
ctx = cast(ActionCtx, ctx)
|
|
665
|
+
async def _download(ctx: action.Ctx, **kwargs): # type: ignore
|
|
666
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
668
667
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
669
668
|
old_ctx = ComponentActionContext(inputs=ComponentActionInputs(value=ctx.input), extras=extras)
|
|
670
669
|
result = await run_user_handler(resolver, args=(old_ctx,))
|
|
@@ -678,7 +677,7 @@ def DownloadContent(
|
|
|
678
677
|
for idx in range(len(extras or []))
|
|
679
678
|
],
|
|
680
679
|
]
|
|
681
|
-
_download.__signature__ = inspect.Signature(params)
|
|
680
|
+
_download.__signature__ = inspect.Signature(params) # type: ignore
|
|
682
681
|
|
|
683
682
|
# Pass in extras as kwargs
|
|
684
683
|
kwargs = {f'kwarg_{idx}': value for idx, value in enumerate(extras or [])}
|
|
@@ -686,7 +685,7 @@ def DownloadContent(
|
|
|
686
685
|
return action(_download)(**kwargs)
|
|
687
686
|
|
|
688
687
|
|
|
689
|
-
DownloadContent.Ctx = ComponentActionContext
|
|
688
|
+
DownloadContent.Ctx = ComponentActionContext # type: ignore
|
|
690
689
|
"""@deprecated retained for backwards compatibility, to be removed in 2.0"""
|
|
691
690
|
|
|
692
691
|
DownloadVariableDef = ActionDef(name='DownloadVariable', js_module='@darajs/core', py_module='dara.core')
|
|
@@ -769,8 +768,8 @@ def SideEffect(
|
|
|
769
768
|
```
|
|
770
769
|
"""
|
|
771
770
|
|
|
772
|
-
async def _effect(ctx: action.Ctx, **kwargs):
|
|
773
|
-
ctx = cast(ActionCtx, ctx)
|
|
771
|
+
async def _effect(ctx: action.Ctx, **kwargs): # type: ignore
|
|
772
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
774
773
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
775
774
|
old_ctx = ComponentActionContext(inputs=ComponentActionInputs(value=ctx.input), extras=extras)
|
|
776
775
|
# Simply run the user handler
|
|
@@ -784,7 +783,7 @@ def SideEffect(
|
|
|
784
783
|
for idx in range(len(extras or []))
|
|
785
784
|
],
|
|
786
785
|
]
|
|
787
|
-
_effect.__signature__ = inspect.Signature(params)
|
|
786
|
+
_effect.__signature__ = inspect.Signature(params) # type: ignore
|
|
788
787
|
|
|
789
788
|
# Pass in extras as kwargs
|
|
790
789
|
kwargs = {f'kwarg_{idx}': value for idx, value in enumerate(extras or [])}
|
|
@@ -792,7 +791,7 @@ def SideEffect(
|
|
|
792
791
|
return action(_effect)(**kwargs)
|
|
793
792
|
|
|
794
793
|
|
|
795
|
-
SideEffect.Ctx = ComponentActionContext
|
|
794
|
+
SideEffect.Ctx = ComponentActionContext # type: ignore
|
|
796
795
|
"""@deprecated retained for backwards compatibility, to be removed in 2.0"""
|
|
797
796
|
|
|
798
797
|
VariableT = TypeVar('VariableT')
|
|
@@ -825,14 +824,12 @@ class ActionCtx:
|
|
|
825
824
|
self._on_action = _on_action
|
|
826
825
|
|
|
827
826
|
@overload
|
|
828
|
-
async def update(self, variable:
|
|
829
|
-
...
|
|
827
|
+
async def update(self, variable: ServerVariable, value: Optional[DataFrame]): ...
|
|
830
828
|
|
|
831
829
|
@overload
|
|
832
|
-
async def update(self, variable:
|
|
833
|
-
...
|
|
830
|
+
async def update(self, variable: Variable[VariableT], value: VariableT): ...
|
|
834
831
|
|
|
835
|
-
async def update(self, variable: Union[Variable,
|
|
832
|
+
async def update(self, variable: Union[Variable, ServerVariable], value: Any):
|
|
836
833
|
"""
|
|
837
834
|
Update a given variable to provided value.
|
|
838
835
|
|
|
@@ -1115,12 +1112,12 @@ class ActionCtx:
|
|
|
1115
1112
|
|
|
1116
1113
|
```python
|
|
1117
1114
|
|
|
1118
|
-
from dara.core import action, ConfigurationBuilder,
|
|
1115
|
+
from dara.core import action, ConfigurationBuilder, ServerVariable
|
|
1119
1116
|
from dara.components.components import Button, Stack
|
|
1120
1117
|
|
|
1121
1118
|
# generate data, alternatively you could load it from a file
|
|
1122
1119
|
df = pandas.DataFrame(data={'x': [1, 2, 3], 'y':[4, 5, 6]})
|
|
1123
|
-
my_var =
|
|
1120
|
+
my_var = ServerVariable(df)
|
|
1124
1121
|
|
|
1125
1122
|
config = ConfigurationBuilder()
|
|
1126
1123
|
|
|
@@ -1246,6 +1243,7 @@ class ActionCtx:
|
|
|
1246
1243
|
task_mgr: TaskManager = utils_registry.get('TaskManager')
|
|
1247
1244
|
|
|
1248
1245
|
task = Task(func=func, args=args, kwargs=kwargs, on_progress=on_progress)
|
|
1246
|
+
task_mgr.register_task(task)
|
|
1249
1247
|
pending_task = await task_mgr.run_task(task)
|
|
1250
1248
|
return await pending_task.value()
|
|
1251
1249
|
|
|
@@ -1330,7 +1328,7 @@ class action:
|
|
|
1330
1328
|
```
|
|
1331
1329
|
"""
|
|
1332
1330
|
|
|
1333
|
-
Ctx:
|
|
1331
|
+
Ctx: TypeAlias = ActionCtx
|
|
1334
1332
|
|
|
1335
1333
|
def __init__(self, func: Callable[..., Any]):
|
|
1336
1334
|
from dara.core.internal.execute_action import execute_action
|
|
@@ -1389,11 +1387,10 @@ class action:
|
|
|
1389
1387
|
return bound_f
|
|
1390
1388
|
|
|
1391
1389
|
@overload
|
|
1392
|
-
def __call__(self, ctx: ActionCtx, *args: Any, **kwargs: Any) -> Any:
|
|
1393
|
-
...
|
|
1390
|
+
def __call__(self, ctx: ActionCtx, *args: Any, **kwargs: Any) -> Any: ...
|
|
1394
1391
|
|
|
1395
1392
|
@overload
|
|
1396
|
-
def __call__(self, *args: Any, **kwargs: Any) -> AnnotatedAction:
|
|
1393
|
+
def __call__(self, *args: Any, **kwargs: Any) -> AnnotatedAction: # type: ignore
|
|
1397
1394
|
...
|
|
1398
1395
|
|
|
1399
1396
|
def __call__(self, *args, **kwargs) -> Union[AnnotatedAction, Any]:
|
|
@@ -1444,10 +1441,10 @@ class action:
|
|
|
1444
1441
|
for key, value in all_kwargs.items():
|
|
1445
1442
|
if key in self.func.__annotations__:
|
|
1446
1443
|
valid_value = True
|
|
1447
|
-
|
|
1444
|
+
# The type is either not set or something tricky to verify, e.g. union
|
|
1445
|
+
with contextlib.suppress(Exception):
|
|
1448
1446
|
valid_value = isinstance(value, (self.func.__annotations__[key], AnyVariable))
|
|
1449
|
-
|
|
1450
|
-
pass # The type is either not set or something tricky to verify, e.g. union
|
|
1447
|
+
|
|
1451
1448
|
if not valid_value:
|
|
1452
1449
|
raise TypeError(
|
|
1453
1450
|
f'Argument: {key} was passed as a {type(value)}, but it should be '
|
|
@@ -1458,6 +1455,11 @@ class action:
|
|
|
1458
1455
|
dynamic_kwargs: Dict[str, AnyVariable] = {}
|
|
1459
1456
|
static_kwargs: Dict[str, Any] = {}
|
|
1460
1457
|
for key, kwarg in all_kwargs.items():
|
|
1458
|
+
if isinstance(kwarg, StateVariable):
|
|
1459
|
+
raise ValueError(
|
|
1460
|
+
'StateVariable cannot be used as input to actions. '
|
|
1461
|
+
"StateVariables are internal variables for tracking DerivedVariable ephemeral client state shouldn't be used as action payloads."
|
|
1462
|
+
)
|
|
1461
1463
|
if isinstance(kwarg, AnyVariable):
|
|
1462
1464
|
dynamic_kwargs[key] = kwarg
|
|
1463
1465
|
else:
|