dara-core 1.19.0__py3-none-any.whl → 1.19.1__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/interactivity/__init__.py +2 -0
- dara/core/interactivity/actions.py +6 -0
- dara/core/interactivity/derived_variable.py +46 -0
- dara/core/interactivity/state_variable.py +69 -0
- dara/core/internal/pool/task_pool.py +8 -5
- dara/core/internal/pool/utils.py +19 -14
- dara/core/umd/dara.core.umd.js +348 -91
- dara/core/visual/dynamic_component.py +7 -0
- {dara_core-1.19.0.dist-info → dara_core-1.19.1.dist-info}/METADATA +11 -11
- {dara_core-1.19.0.dist-info → dara_core-1.19.1.dist-info}/RECORD +13 -12
- {dara_core-1.19.0.dist-info → dara_core-1.19.1.dist-info}/LICENSE +0 -0
- {dara_core-1.19.0.dist-info → dara_core-1.19.1.dist-info}/WHEEL +0 -0
- {dara_core-1.19.0.dist-info → dara_core-1.19.1.dist-info}/entry_points.txt +0 -0
|
@@ -42,6 +42,7 @@ from dara.core.interactivity.derived_data_variable import DerivedDataVariable
|
|
|
42
42
|
from dara.core.interactivity.derived_variable import DerivedVariable
|
|
43
43
|
from dara.core.interactivity.non_data_variable import NonDataVariable
|
|
44
44
|
from dara.core.interactivity.plain_variable import Variable
|
|
45
|
+
from dara.core.interactivity.state_variable import StateVariable
|
|
45
46
|
from dara.core.interactivity.switch_variable import SwitchVariable
|
|
46
47
|
from dara.core.interactivity.url_variable import UrlVariable
|
|
47
48
|
|
|
@@ -53,6 +54,7 @@ __all__ = [
|
|
|
53
54
|
'DataVariable',
|
|
54
55
|
'NonDataVariable',
|
|
55
56
|
'Variable',
|
|
57
|
+
'StateVariable',
|
|
56
58
|
'SwitchVariable',
|
|
57
59
|
'DerivedVariable',
|
|
58
60
|
'DerivedDataVariable',
|
|
@@ -55,6 +55,7 @@ from dara.core.base_definitions import (
|
|
|
55
55
|
)
|
|
56
56
|
from dara.core.base_definitions import DaraBaseModel as BaseModel
|
|
57
57
|
from dara.core.interactivity.data_variable import DataVariable
|
|
58
|
+
from dara.core.interactivity.state_variable import StateVariable
|
|
58
59
|
from dara.core.internal.download import generate_download_code
|
|
59
60
|
from dara.core.internal.registry_lookup import RegistryLookup
|
|
60
61
|
from dara.core.internal.utils import run_user_handler
|
|
@@ -1456,6 +1457,11 @@ class action:
|
|
|
1456
1457
|
dynamic_kwargs: Dict[str, AnyVariable] = {}
|
|
1457
1458
|
static_kwargs: Dict[str, Any] = {}
|
|
1458
1459
|
for key, kwarg in all_kwargs.items():
|
|
1460
|
+
if isinstance(kwarg, StateVariable):
|
|
1461
|
+
raise ValueError(
|
|
1462
|
+
'StateVariable cannot be used as input to actions. '
|
|
1463
|
+
"StateVariables are internal variables for tracking DerivedVariable ephemeral client state shouldn't be used as action payloads."
|
|
1464
|
+
)
|
|
1459
1465
|
if isinstance(kwarg, AnyVariable):
|
|
1460
1466
|
dynamic_kwargs[key] = kwarg
|
|
1461
1467
|
else:
|
|
@@ -139,6 +139,19 @@ class DerivedVariable(NonDataVariable, Generic[VariableType]):
|
|
|
139
139
|
if nested is None:
|
|
140
140
|
nested = []
|
|
141
141
|
|
|
142
|
+
# Validate that StateVariables are not used as inputs
|
|
143
|
+
from dara.core.interactivity.state_variable import StateVariable
|
|
144
|
+
|
|
145
|
+
for var in variables:
|
|
146
|
+
if isinstance(var, StateVariable):
|
|
147
|
+
raise ValueError(
|
|
148
|
+
'StateVariable cannot be used as input to DerivedVariable. '
|
|
149
|
+
'StateVariables are internal variables for tracking DerivedVariable states '
|
|
150
|
+
'and using them as inputs would create complex dependencies that are '
|
|
151
|
+
'difficult to debug. Consider using the parent DerivedVariable directly instead,'
|
|
152
|
+
' or use the StateVariable with an If component or SwitchVariable.'
|
|
153
|
+
)
|
|
154
|
+
|
|
142
155
|
if cache is not None:
|
|
143
156
|
cache = Cache.Policy.from_arg(cache)
|
|
144
157
|
|
|
@@ -214,6 +227,39 @@ class DerivedVariable(NonDataVariable, Generic[VariableType]):
|
|
|
214
227
|
assert_no_context('ctx.trigger')
|
|
215
228
|
return TriggerVariable(variable=self, force=force)
|
|
216
229
|
|
|
230
|
+
@property
|
|
231
|
+
def is_loading(self):
|
|
232
|
+
"""
|
|
233
|
+
Get a StateVariable that tracks the loading state of this DerivedVariable.
|
|
234
|
+
|
|
235
|
+
:return: StateVariable that is True when this DerivedVariable is loading, False otherwise
|
|
236
|
+
"""
|
|
237
|
+
from dara.core.interactivity.state_variable import StateVariable
|
|
238
|
+
|
|
239
|
+
return StateVariable(parent_variable=self, property_name='loading')
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def has_error(self):
|
|
243
|
+
"""
|
|
244
|
+
Get a StateVariable that tracks the error state of this DerivedVariable.
|
|
245
|
+
|
|
246
|
+
:return: StateVariable that is True when this DerivedVariable has an error, False otherwise
|
|
247
|
+
"""
|
|
248
|
+
from dara.core.interactivity.state_variable import StateVariable
|
|
249
|
+
|
|
250
|
+
return StateVariable(parent_variable=self, property_name='error')
|
|
251
|
+
|
|
252
|
+
@property
|
|
253
|
+
def has_value(self):
|
|
254
|
+
"""
|
|
255
|
+
Get a StateVariable that tracks whether this DerivedVariable has a resolved value.
|
|
256
|
+
|
|
257
|
+
:return: StateVariable that is True when this DerivedVariable has a value, False otherwise
|
|
258
|
+
"""
|
|
259
|
+
from dara.core.interactivity.state_variable import StateVariable
|
|
260
|
+
|
|
261
|
+
return StateVariable(parent_variable=self, property_name='hasValue')
|
|
262
|
+
|
|
217
263
|
@staticmethod
|
|
218
264
|
def _get_cache_key(*args, uid: str, deps: Optional[List[int]] = None):
|
|
219
265
|
"""
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2023 Impulse Innovations Limited
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
See the License for the specific language governing permissions and
|
|
15
|
+
limitations under the License.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from typing import TYPE_CHECKING, Optional
|
|
21
|
+
|
|
22
|
+
from pydantic import SerializerFunctionWrapHandler, model_serializer
|
|
23
|
+
from typing_extensions import Literal
|
|
24
|
+
|
|
25
|
+
from dara.core.interactivity.non_data_variable import NonDataVariable
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from dara.core.interactivity.derived_variable import DerivedVariable
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class StateVariable(NonDataVariable):
|
|
32
|
+
"""
|
|
33
|
+
A StateVariable is an internal variable type used to track client-side state of other variables.
|
|
34
|
+
It is not meant to be created directly by users, but rather returned by properties like
|
|
35
|
+
DerivedVariable.is_loading, DerivedVariable.has_error, etc.
|
|
36
|
+
|
|
37
|
+
This variable tracks the state of a parent DerivedVariable and maps to specific properties
|
|
38
|
+
like loading state, error state, etc.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
parent_variable: 'DerivedVariable'
|
|
42
|
+
property_name: Literal['loading', 'error', 'hasValue']
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
parent_variable: 'DerivedVariable',
|
|
47
|
+
property_name: Literal['loading', 'error', 'hasValue'],
|
|
48
|
+
uid: Optional[str] = None,
|
|
49
|
+
**kwargs,
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Initialize a StateVariable.
|
|
53
|
+
|
|
54
|
+
:param parent_variable: The DerivedVariable this StateVariable tracks
|
|
55
|
+
:param property_name: The property name this StateVariable represents ('loading', 'error', etc.)
|
|
56
|
+
:param uid: Optional unique identifier; if not provided, one is generated
|
|
57
|
+
"""
|
|
58
|
+
super().__init__(uid=uid, parent_variable=parent_variable, property_name=property_name, **kwargs)
|
|
59
|
+
|
|
60
|
+
@model_serializer(mode='wrap')
|
|
61
|
+
def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
|
|
62
|
+
parent_dict = nxt(self)
|
|
63
|
+
return {
|
|
64
|
+
**parent_dict,
|
|
65
|
+
'__typename': 'StateVariable',
|
|
66
|
+
'uid': str(parent_dict['uid']),
|
|
67
|
+
'parent_variable': self.parent_variable,
|
|
68
|
+
'property_name': self.property_name,
|
|
69
|
+
}
|
|
@@ -499,11 +499,14 @@ class TaskPool:
|
|
|
499
499
|
while self.status not in (PoolStatus.ERROR, PoolStatus.STOPPED):
|
|
500
500
|
await anyio.sleep(0.1)
|
|
501
501
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
502
|
+
try:
|
|
503
|
+
self._handle_excess_workers()
|
|
504
|
+
self._handle_orphaned_workers()
|
|
505
|
+
self._handle_dead_workers()
|
|
506
|
+
self._create_workers()
|
|
507
|
+
await self._process_next_worker_message()
|
|
508
|
+
except Exception as e:
|
|
509
|
+
dev_logger.error('Error in task pool', e)
|
|
507
510
|
finally:
|
|
508
511
|
self.loop_stopped.set()
|
|
509
512
|
|
dara/core/internal/pool/utils.py
CHANGED
|
@@ -27,6 +27,8 @@ from typing import Any, Callable, Optional, Tuple
|
|
|
27
27
|
import anyio
|
|
28
28
|
from tblib import Traceback
|
|
29
29
|
|
|
30
|
+
from dara.core.logging import dev_logger
|
|
31
|
+
|
|
30
32
|
|
|
31
33
|
class SubprocessException:
|
|
32
34
|
"""
|
|
@@ -131,20 +133,23 @@ async def stop_process_async(process: BaseProcess, timeout: float = 3):
|
|
|
131
133
|
# Terminate and wait for it to shutdown
|
|
132
134
|
process.terminate()
|
|
133
135
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
136
|
+
try:
|
|
137
|
+
# mimic process.join() in an async way to not block
|
|
138
|
+
await wait_while(process.is_alive, timeout)
|
|
139
|
+
|
|
140
|
+
# If it's still alive
|
|
141
|
+
if process.is_alive():
|
|
142
|
+
try:
|
|
143
|
+
os.kill(process.pid, signal.SIGKILL)
|
|
144
|
+
await wait_while(process.is_alive, timeout)
|
|
145
|
+
except OSError as e:
|
|
146
|
+
raise RuntimeError(f'Unable to terminate subprocess with PID {process.pid}') from e
|
|
147
|
+
|
|
148
|
+
# If it's still alive raise an exception
|
|
149
|
+
if process.is_alive():
|
|
150
|
+
raise RuntimeError(f'Unable to terminate subprocess with PID {process.pid}')
|
|
151
|
+
except Exception as e:
|
|
152
|
+
dev_logger.error('Error stopping process', e)
|
|
148
153
|
|
|
149
154
|
|
|
150
155
|
def stop_process(process: BaseProcess, timeout: float = 3):
|