dara-core 1.20.1a1__py3-none-any.whl → 1.20.1a3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. dara/core/__init__.py +3 -0
  2. dara/core/actions.py +1 -2
  3. dara/core/auth/basic.py +22 -16
  4. dara/core/auth/definitions.py +2 -2
  5. dara/core/auth/routes.py +5 -5
  6. dara/core/auth/utils.py +5 -5
  7. dara/core/base_definitions.py +22 -64
  8. dara/core/cli.py +8 -7
  9. dara/core/configuration.py +5 -2
  10. dara/core/css.py +1 -2
  11. dara/core/data_utils.py +18 -19
  12. dara/core/defaults.py +6 -7
  13. dara/core/definitions.py +50 -19
  14. dara/core/http.py +7 -3
  15. dara/core/interactivity/__init__.py +6 -0
  16. dara/core/interactivity/actions.py +52 -50
  17. dara/core/interactivity/any_data_variable.py +7 -134
  18. dara/core/interactivity/any_variable.py +5 -8
  19. dara/core/interactivity/client_variable.py +71 -0
  20. dara/core/interactivity/data_variable.py +8 -266
  21. dara/core/interactivity/derived_data_variable.py +7 -290
  22. dara/core/interactivity/derived_variable.py +416 -176
  23. dara/core/interactivity/filtering.py +46 -27
  24. dara/core/interactivity/loop_variable.py +2 -2
  25. dara/core/interactivity/non_data_variable.py +5 -68
  26. dara/core/interactivity/plain_variable.py +89 -15
  27. dara/core/interactivity/server_variable.py +325 -0
  28. dara/core/interactivity/state_variable.py +69 -0
  29. dara/core/interactivity/switch_variable.py +19 -19
  30. dara/core/interactivity/tabular_variable.py +94 -0
  31. dara/core/interactivity/url_variable.py +10 -90
  32. dara/core/internal/cache_store/base_impl.py +2 -1
  33. dara/core/internal/cache_store/cache_store.py +22 -25
  34. dara/core/internal/cache_store/keep_all.py +4 -1
  35. dara/core/internal/cache_store/lru.py +5 -1
  36. dara/core/internal/cache_store/ttl.py +4 -1
  37. dara/core/internal/cgroup.py +1 -1
  38. dara/core/internal/dependency_resolution.py +60 -66
  39. dara/core/internal/devtools.py +12 -5
  40. dara/core/internal/download.py +13 -4
  41. dara/core/internal/encoder_registry.py +7 -7
  42. dara/core/internal/execute_action.py +13 -13
  43. dara/core/internal/hashing.py +1 -3
  44. dara/core/internal/import_discovery.py +3 -4
  45. dara/core/internal/multi_resource_lock.py +70 -0
  46. dara/core/internal/normalization.py +9 -18
  47. dara/core/internal/pandas_utils.py +107 -5
  48. dara/core/internal/pool/definitions.py +1 -1
  49. dara/core/internal/pool/task_pool.py +25 -16
  50. dara/core/internal/pool/utils.py +21 -18
  51. dara/core/internal/pool/worker.py +3 -2
  52. dara/core/internal/port_utils.py +1 -1
  53. dara/core/internal/registries.py +12 -6
  54. dara/core/internal/registry.py +4 -2
  55. dara/core/internal/registry_lookup.py +11 -5
  56. dara/core/internal/routing.py +109 -145
  57. dara/core/internal/scheduler.py +13 -8
  58. dara/core/internal/settings.py +2 -2
  59. dara/core/internal/store.py +2 -29
  60. dara/core/internal/tasks.py +379 -195
  61. dara/core/internal/utils.py +36 -13
  62. dara/core/internal/websocket.py +21 -20
  63. dara/core/js_tooling/js_utils.py +28 -26
  64. dara/core/js_tooling/templates/vite.config.template.ts +12 -3
  65. dara/core/logging.py +13 -12
  66. dara/core/main.py +14 -11
  67. dara/core/metrics/cache.py +1 -1
  68. dara/core/metrics/utils.py +3 -3
  69. dara/core/persistence.py +27 -5
  70. dara/core/umd/dara.core.umd.js +68291 -64718
  71. dara/core/visual/components/__init__.py +2 -2
  72. dara/core/visual/components/fallback.py +30 -4
  73. dara/core/visual/components/for_cmp.py +4 -1
  74. dara/core/visual/css/__init__.py +30 -31
  75. dara/core/visual/dynamic_component.py +31 -28
  76. dara/core/visual/progress_updater.py +4 -3
  77. {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/METADATA +12 -11
  78. dara_core-1.20.1a3.dist-info/RECORD +119 -0
  79. dara_core-1.20.1a1.dist-info/RECORD +0 -114
  80. {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/LICENSE +0 -0
  81. {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/WHEEL +0 -0
  82. {dara_core-1.20.1a1.dist-info → dara_core-1.20.1a3.dist-info}/entry_points.txt +0 -0
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 NonDataVariable.
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.non_data_variable import NonDataVariable
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, NonDataVariable, CSSProperties)):
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 NonDataVariable, got {type(value)}')
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 NonDataVariable resoling to either of the above.
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.non_data_variable import NonDataVariable
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, (NonDataVariable, CSSProperties, str)):
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 NonDataVariable, got {type(css)}')
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 # type: ignore
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
- if value < 0:
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['Page']] = []
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] = [], authenticated: bool = True):
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()), return_annotation=sig.return_annotation # type: ignore
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.base_definitions import TaskProgressUpdate
56
- from dara.core.interactivity.data_variable import DataVariable
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 # type: ignore
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, UrlVariable, DataVariable]
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, DataVariable):
137
+ if isinstance(self.variable, ServerVariable):
138
138
  # Update on the backend
139
139
  from dara.core.internal.registries import (
140
- data_variable_registry,
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(data_variable_registry, self.variable.uid)
148
- DataVariable.update_value(var_entry, store, self.value)
149
- # Don't notify frontend explicitly, all clients will be notified by update_value above
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 # type: ignore
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, UrlVariable or DataVariable.
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, DataVariable, UrlVariable]
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, DataVariable, UrlVariable],
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): # type: ignore
258
- ctx = cast(ActionCtx, ctx) # type: ignore
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) # type: ignore
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): # type: ignore
432
- ctx = cast(ActionCtx, ctx) # type: ignore
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,)) # type: ignore
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) # type: ignore
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 # type: ignore
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, DataVariable, DownloadContent
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 = DataVariable(df)
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): # type: ignore
667
- ctx = cast(ActionCtx, ctx) # type: ignore
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) # type: ignore
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 # type: ignore
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): # type: ignore
773
- ctx = cast(ActionCtx, ctx) # type: ignore
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) # type: ignore
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 # type: ignore
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: DataVariable, value: Optional[DataFrame]):
829
- ...
827
+ async def update(self, variable: ServerVariable, value: Optional[DataFrame]): ...
830
828
 
831
829
  @overload
832
- async def update(self, variable: Union[Variable[VariableT], UrlVariable[VariableT]], value: VariableT):
833
- ...
830
+ async def update(self, variable: Variable[VariableT], value: VariableT): ...
834
831
 
835
- async def update(self, variable: Union[Variable, UrlVariable, DataVariable], value: Any):
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, DataVariable
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 = DataVariable(df)
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: ClassVar[type[ActionCtx]] = ActionCtx
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: # type: ignore
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
- try:
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
- except Exception:
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: