dara-core 1.16.11__py3-none-any.whl → 1.16.13__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/persistence.py +84 -15
- dara/core/umd/dara.core.umd.js +11 -1
- {dara_core-1.16.11.dist-info → dara_core-1.16.13.dist-info}/METADATA +10 -10
- {dara_core-1.16.11.dist-info → dara_core-1.16.13.dist-info}/RECORD +7 -7
- {dara_core-1.16.11.dist-info → dara_core-1.16.13.dist-info}/LICENSE +0 -0
- {dara_core-1.16.11.dist-info → dara_core-1.16.13.dist-info}/WHEEL +0 -0
- {dara_core-1.16.11.dist-info → dara_core-1.16.13.dist-info}/entry_points.txt +0 -0
dara/core/persistence.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Set
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Literal, Optional, Set
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
7
|
import aiorwlock
|
|
@@ -21,13 +21,12 @@ from dara.core.internal.websocket import WS_CHANNEL
|
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
23
|
from dara.core.interactivity.plain_variable import Variable
|
|
24
|
+
from dara.core.internal.websocket import WebsocketManager
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class PersistenceBackend(BaseModel, abc.ABC):
|
|
27
28
|
"""
|
|
28
|
-
Abstract base class for a BackendStore backend
|
|
29
|
-
|
|
30
|
-
Warning: the API is not stable yet and may change in the future
|
|
29
|
+
Abstract base class for a BackendStore backend.
|
|
31
30
|
"""
|
|
32
31
|
|
|
33
32
|
@abc.abstractmethod
|
|
@@ -60,6 +59,12 @@ class PersistenceBackend(BaseModel, abc.ABC):
|
|
|
60
59
|
Get all the values as a dictionary of key-value pairs
|
|
61
60
|
"""
|
|
62
61
|
|
|
62
|
+
async def subscribe(self, on_value: Callable[[str, Any], Awaitable[None]]):
|
|
63
|
+
"""
|
|
64
|
+
Subscribe to changes in the backend. Called with a callback that should be invoked whenever a value is updated.
|
|
65
|
+
"""
|
|
66
|
+
# Default implementation does nothing, not all backends need to support this
|
|
67
|
+
|
|
63
68
|
|
|
64
69
|
class InMemoryBackend(PersistenceBackend):
|
|
65
70
|
"""
|
|
@@ -173,17 +178,23 @@ class BackendStore(PersistenceStore):
|
|
|
173
178
|
:param backend: the backend to use for storing data; defaults to an in-memory backend
|
|
174
179
|
:param scope: the scope for the store; if 'global' a single value is stored for all users,
|
|
175
180
|
if 'user' a value is stored per user
|
|
181
|
+
:param readonly: whether to use the backend in read-only mode, i.e. skip syncing values from client to backend and raise if write()/delete() is called
|
|
176
182
|
"""
|
|
177
183
|
|
|
178
184
|
uid: str = Field(default_factory=lambda: str(uuid4()))
|
|
179
185
|
backend: PersistenceBackend = Field(default_factory=InMemoryBackend, exclude=True)
|
|
180
186
|
scope: Literal['global', 'user'] = 'global'
|
|
187
|
+
readonly: bool = False
|
|
181
188
|
|
|
182
189
|
default_value: Any = Field(default=None, exclude=True)
|
|
183
190
|
initialized_scopes: Set[str] = Field(default_factory=set, exclude=True)
|
|
184
191
|
|
|
185
192
|
def __init__(
|
|
186
|
-
self,
|
|
193
|
+
self,
|
|
194
|
+
backend: Optional[PersistenceBackend] = None,
|
|
195
|
+
uid: Optional[str] = None,
|
|
196
|
+
scope: Optional[str] = None,
|
|
197
|
+
readonly: bool = False,
|
|
187
198
|
):
|
|
188
199
|
"""
|
|
189
200
|
Persistence store implementation that uses a backend implementation to store data server-side
|
|
@@ -192,6 +203,7 @@ class BackendStore(PersistenceStore):
|
|
|
192
203
|
:param backend: the backend to use for storing data; defaults to an in-memory backend
|
|
193
204
|
:param scope: the scope for the store; if 'global' a single value is stored for all users,
|
|
194
205
|
if 'user' a value is stored per user
|
|
206
|
+
:param readonly: whether to use the backend in read-only mode, i.e. skip syncing values from client to backend and raise if write()/delete() is called
|
|
195
207
|
"""
|
|
196
208
|
kwargs: Dict[str, Any] = {}
|
|
197
209
|
if backend:
|
|
@@ -203,6 +215,9 @@ class BackendStore(PersistenceStore):
|
|
|
203
215
|
if scope:
|
|
204
216
|
kwargs['scope'] = scope
|
|
205
217
|
|
|
218
|
+
if readonly:
|
|
219
|
+
kwargs['readonly'] = readonly
|
|
220
|
+
|
|
206
221
|
super().__init__(**kwargs)
|
|
207
222
|
|
|
208
223
|
async def _get_key(self):
|
|
@@ -235,6 +250,17 @@ class BackendStore(PersistenceStore):
|
|
|
235
250
|
|
|
236
251
|
raise ValueError('User not found when trying to compute the key for a user-scoped store')
|
|
237
252
|
|
|
253
|
+
def _get_user(self, key: str) -> Optional[str]:
|
|
254
|
+
"""
|
|
255
|
+
Get the user for a given key. Returns None if the key is global.
|
|
256
|
+
Reverts the `_get_key` method to get the user for a given key.
|
|
257
|
+
"""
|
|
258
|
+
if key == 'global':
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
# otherwise key is a user identity_id or identity_name
|
|
262
|
+
return key
|
|
263
|
+
|
|
238
264
|
def _register(self):
|
|
239
265
|
"""
|
|
240
266
|
Register this store in the backend store registry.
|
|
@@ -253,20 +279,50 @@ class BackendStore(PersistenceStore):
|
|
|
253
279
|
except ValueError as e:
|
|
254
280
|
raise ValueError('BackendStore uid must be unique') from e
|
|
255
281
|
|
|
256
|
-
|
|
282
|
+
@property
|
|
283
|
+
def ws_mgr(self) -> 'WebsocketManager':
|
|
284
|
+
from dara.core.internal.registries import utils_registry
|
|
285
|
+
|
|
286
|
+
return utils_registry.get('WebsocketManager')
|
|
287
|
+
|
|
288
|
+
def _create_msg(self, value: Any) -> Dict[str, Any]:
|
|
289
|
+
"""
|
|
290
|
+
Create a message to send to the frontend.
|
|
291
|
+
:param value: value to send
|
|
257
292
|
"""
|
|
258
|
-
|
|
293
|
+
return {'store_uid': self.uid, 'value': value}
|
|
259
294
|
|
|
295
|
+
async def _notify_user(self, user_identifier: str, value: Any):
|
|
296
|
+
"""
|
|
297
|
+
Notify a given user about the new value for this store.
|
|
298
|
+
:param user_identifier: user to notify
|
|
260
299
|
:param value: value to notify about
|
|
261
300
|
"""
|
|
262
|
-
|
|
263
|
-
|
|
301
|
+
return await self.ws_mgr.send_message_to_user(
|
|
302
|
+
user_identifier,
|
|
303
|
+
self._create_msg(value),
|
|
304
|
+
ignore_channel=WS_CHANNEL.get(),
|
|
305
|
+
)
|
|
264
306
|
|
|
265
|
-
|
|
266
|
-
|
|
307
|
+
async def _notify_global(self, value: Any):
|
|
308
|
+
"""
|
|
309
|
+
Notify all users about the new value for this store.
|
|
310
|
+
:param value: value to notify about
|
|
311
|
+
"""
|
|
312
|
+
return await self.ws_mgr.broadcast(
|
|
313
|
+
self._create_msg(value),
|
|
314
|
+
ignore_channel=WS_CHANNEL.get(),
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
async def _notify_value(self, value: Any):
|
|
318
|
+
"""
|
|
319
|
+
Notify all clients about the new value for this store.
|
|
320
|
+
Broadcasts to all users if scope is global or sends to the current user if scope is user.
|
|
267
321
|
|
|
322
|
+
:param value: value to notify about
|
|
323
|
+
"""
|
|
268
324
|
if self.scope == 'global':
|
|
269
|
-
return await
|
|
325
|
+
return await self._notify_global(value)
|
|
270
326
|
|
|
271
327
|
# For user scope, we need to find channels for the user and notify them
|
|
272
328
|
user = USER.get()
|
|
@@ -275,7 +331,7 @@ class BackendStore(PersistenceStore):
|
|
|
275
331
|
return
|
|
276
332
|
|
|
277
333
|
user_identifier = user.identity_id or user.identity_name
|
|
278
|
-
return await
|
|
334
|
+
return await self._notify_user(user_identifier, value)
|
|
279
335
|
|
|
280
336
|
async def init(self, variable: 'Variable'):
|
|
281
337
|
"""
|
|
@@ -286,6 +342,13 @@ class BackendStore(PersistenceStore):
|
|
|
286
342
|
self._register()
|
|
287
343
|
self.default_value = variable.default
|
|
288
344
|
|
|
345
|
+
async def _on_value(key: str, value: Any):
|
|
346
|
+
if user := self._get_user(key):
|
|
347
|
+
return await self._notify_user(user, value)
|
|
348
|
+
return await self._notify_global(value)
|
|
349
|
+
|
|
350
|
+
await self.backend.subscribe(_on_value)
|
|
351
|
+
|
|
289
352
|
async def write(self, value: Any, notify=True):
|
|
290
353
|
"""
|
|
291
354
|
Persist a value to the store.
|
|
@@ -296,10 +359,13 @@ class BackendStore(PersistenceStore):
|
|
|
296
359
|
:param value: value to write
|
|
297
360
|
:param notify: whether to broadcast the new value to clients
|
|
298
361
|
"""
|
|
362
|
+
if self.readonly:
|
|
363
|
+
raise ValueError('Cannot write to a read-only store')
|
|
364
|
+
|
|
299
365
|
key = await self._get_key()
|
|
300
366
|
|
|
301
367
|
if notify:
|
|
302
|
-
await self.
|
|
368
|
+
await self._notify_value(value)
|
|
303
369
|
|
|
304
370
|
return await run_user_handler(self.backend.write, (key, value))
|
|
305
371
|
|
|
@@ -325,10 +391,13 @@ class BackendStore(PersistenceStore):
|
|
|
325
391
|
|
|
326
392
|
:param notify: whether to broadcast that the value was deleted to clients
|
|
327
393
|
"""
|
|
394
|
+
if self.readonly:
|
|
395
|
+
raise ValueError('Cannot delete from a read-only store')
|
|
396
|
+
|
|
328
397
|
key = await self._get_key()
|
|
329
398
|
if notify:
|
|
330
399
|
# Schedule notification on delete
|
|
331
|
-
await self.
|
|
400
|
+
await self._notify_value(None)
|
|
332
401
|
return await run_user_handler(self.backend.delete, (key,))
|
|
333
402
|
|
|
334
403
|
async def get_all(self) -> Dict[str, Any]:
|
dara/core/umd/dara.core.umd.js
CHANGED
|
@@ -7527,6 +7527,7 @@ var __privateWrapper = (obj, member, setter, getter) => ({
|
|
|
7527
7527
|
useRetain: Recoil_useRetain,
|
|
7528
7528
|
retentionZone: retentionZone$1
|
|
7529
7529
|
};
|
|
7530
|
+
var Recoil_index_1 = Recoil_index.DefaultValue;
|
|
7530
7531
|
var Recoil_index_5 = Recoil_index.RecoilRoot;
|
|
7531
7532
|
var Recoil_index_8 = Recoil_index.atom;
|
|
7532
7533
|
var Recoil_index_10 = Recoil_index.atomFamily;
|
|
@@ -57943,7 +57944,16 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
|
|
|
57943
57944
|
return RecoilSync_index_2({
|
|
57944
57945
|
itemKey: variable.store.uid,
|
|
57945
57946
|
refine: Refine_index_6(),
|
|
57946
|
-
storeKey: "BackendStore"
|
|
57947
|
+
storeKey: "BackendStore",
|
|
57948
|
+
write({ write: write2 }, newValue) {
|
|
57949
|
+
if (variable.store.readonly) {
|
|
57950
|
+
return;
|
|
57951
|
+
}
|
|
57952
|
+
if (newValue instanceof Recoil_index_1) {
|
|
57953
|
+
return;
|
|
57954
|
+
}
|
|
57955
|
+
write2(variable.store.uid, newValue);
|
|
57956
|
+
}
|
|
57947
57957
|
});
|
|
57948
57958
|
}
|
|
57949
57959
|
function getSessionKey(uid2) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dara-core
|
|
3
|
-
Version: 1.16.
|
|
3
|
+
Version: 1.16.13
|
|
4
4
|
Summary: Dara Framework Core
|
|
5
5
|
Home-page: https://dara.causalens.com/
|
|
6
6
|
License: Apache-2.0
|
|
@@ -20,10 +20,10 @@ Requires-Dist: async-asgi-testclient (>=1.4.11,<2.0.0)
|
|
|
20
20
|
Requires-Dist: certifi (>=2024.7.4)
|
|
21
21
|
Requires-Dist: click (==8.1.3)
|
|
22
22
|
Requires-Dist: colorama (>=0.4.6,<0.5.0)
|
|
23
|
-
Requires-Dist: create-dara-app (==1.16.
|
|
23
|
+
Requires-Dist: create-dara-app (==1.16.13)
|
|
24
24
|
Requires-Dist: croniter (>=1.0.15,<3.0.0)
|
|
25
25
|
Requires-Dist: cryptography (>=42.0.4)
|
|
26
|
-
Requires-Dist: dara-components (==1.16.
|
|
26
|
+
Requires-Dist: dara-components (==1.16.13) ; extra == "all"
|
|
27
27
|
Requires-Dist: exceptiongroup (>=1.1.3,<2.0.0)
|
|
28
28
|
Requires-Dist: fastapi (>=0.115.0,<0.116.0)
|
|
29
29
|
Requires-Dist: fastapi_vite_dara (==0.4.0)
|
|
@@ -53,7 +53,7 @@ Description-Content-Type: text/markdown
|
|
|
53
53
|
|
|
54
54
|
# Dara Application Framework
|
|
55
55
|
|
|
56
|
-
<img src="https://github.com/causalens/dara/blob/v1.16.
|
|
56
|
+
<img src="https://github.com/causalens/dara/blob/v1.16.13/img/dara_light.svg?raw=true">
|
|
57
57
|
|
|
58
58
|

|
|
59
59
|
[](https://www.apache.org/licenses/LICENSE-2.0)
|
|
@@ -98,7 +98,7 @@ source .venv/bin/activate
|
|
|
98
98
|
dara start
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-

|
|
102
102
|
|
|
103
103
|
Note: `pip` installation uses [PEP 660](https://peps.python.org/pep-0660/) `pyproject.toml`-based editable installs which require `pip >= 21.3` and `setuptools >= 64.0.0`. You can upgrade both with:
|
|
104
104
|
|
|
@@ -115,9 +115,9 @@ Explore some of our favorite apps - a great way of getting started and getting t
|
|
|
115
115
|
|
|
116
116
|
| Dara App | Description |
|
|
117
117
|
| -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
118
|
-
|  | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
|
|
119
|
+
|  | Demonstrates how to enable the user to interact with plots, trigger actions based on clicks, mouse movements and other interactions with `Bokeh` or `Plotly` plots |
|
|
120
|
+
|  | Demonstrates how to use the `CausalGraphViewer` component to display your graphs or networks, customising the displayed information through colors and tooltips, and updating the page based on user interaction. |
|
|
121
121
|
|
|
122
122
|
Check out our [App Gallery](https://dara.causalens.com/gallery) for more inspiration!
|
|
123
123
|
|
|
@@ -144,9 +144,9 @@ And the supporting UI packages and tools.
|
|
|
144
144
|
- `ui-utils` - miscellaneous utility functions
|
|
145
145
|
- `ui-widgets` - widget components
|
|
146
146
|
|
|
147
|
-
More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.16.
|
|
147
|
+
More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.16.13/CONTRIBUTING.md) file.
|
|
148
148
|
|
|
149
149
|
## License
|
|
150
150
|
|
|
151
|
-
Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.16.
|
|
151
|
+
Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.16.13/LICENSE).
|
|
152
152
|
|
|
@@ -80,8 +80,8 @@ dara/core/metrics/__init__.py,sha256=2UqpWHv-Ie58QLJIHJ9Szfjq8xifAuwy5FYGUIFwWtI
|
|
|
80
80
|
dara/core/metrics/cache.py,sha256=bGXwjO_rSc-FkS3PnPi1mvIZf1x-hvmG113dAUk1g-Y,2616
|
|
81
81
|
dara/core/metrics/runtime.py,sha256=YP-6Dz0GeI9_Yr7bUk_-OqShyFySGH_AKpDO126l6es,1833
|
|
82
82
|
dara/core/metrics/utils.py,sha256=aKaa_hskV3M3h4xOGZYvegDLq_OWOEUlslkQKrrPQiI,2281
|
|
83
|
-
dara/core/persistence.py,sha256=
|
|
84
|
-
dara/core/umd/dara.core.umd.js,sha256=
|
|
83
|
+
dara/core/persistence.py,sha256=SsWGyBO1Wn9rlkaUqf0pqNVatRN8Aw-pK_CVV7v5fJc,13150
|
|
84
|
+
dara/core/umd/dara.core.umd.js,sha256=zv9FZRX2NtIjgKhiqjzyeFmwuGnkRtxgxFNVfzePAOI,4870020
|
|
85
85
|
dara/core/umd/style.css,sha256=YQtQ4veiSktnyONl0CU1iU1kKfcQhreH4iASi1MP7Ak,4095007
|
|
86
86
|
dara/core/visual/__init__.py,sha256=QN0wbG9HPQ_vXh8BO8DnBXeYLIENVTNtRmYzZf1lx7c,577
|
|
87
87
|
dara/core/visual/components/__init__.py,sha256=4km21GLMLM2cr42SwehLUD7DJOlzvhwHl5hdG1OqmIM,2274
|
|
@@ -104,8 +104,8 @@ dara/core/visual/themes/__init__.py,sha256=aM4mgoIYo2neBSw5FRzswsht7PUKjLthiHLmF
|
|
|
104
104
|
dara/core/visual/themes/dark.py,sha256=UQGDooOc8ric73eHs9E0ltYP4UCrwqQ3QxqN_fb4PwY,1942
|
|
105
105
|
dara/core/visual/themes/definitions.py,sha256=nS_gQvOzCt5hTmj74d0_siq_9QWuj6wNuir4VCHy0Dk,2779
|
|
106
106
|
dara/core/visual/themes/light.py,sha256=-Tviq8oEwGbdFULoDOqPuHO0UpAZGsBy8qFi0kAGolQ,1944
|
|
107
|
-
dara_core-1.16.
|
|
108
|
-
dara_core-1.16.
|
|
109
|
-
dara_core-1.16.
|
|
110
|
-
dara_core-1.16.
|
|
111
|
-
dara_core-1.16.
|
|
107
|
+
dara_core-1.16.13.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
|
|
108
|
+
dara_core-1.16.13.dist-info/METADATA,sha256=uUJ6n6ebwTD7eQUw8Gn_NwAMdrKGmb8E1IJZmZQT8Do,7473
|
|
109
|
+
dara_core-1.16.13.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
110
|
+
dara_core-1.16.13.dist-info/entry_points.txt,sha256=H__D5sNIGuPIhVam0DChNL-To5k8Y7nY7TAFz9Mz6cc,139
|
|
111
|
+
dara_core-1.16.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|