dara-core 1.24.3__py3-none-any.whl → 1.25.0__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.
@@ -0,0 +1,334 @@
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
+ import asyncio
21
+ import contextlib
22
+ from collections.abc import AsyncGenerator, Callable
23
+ from dataclasses import dataclass
24
+ from typing import Any, Generic, Literal
25
+
26
+ from fastapi import Request
27
+ from pydantic import ConfigDict, Field, SerializerFunctionWrapHandler, field_validator, model_serializer
28
+ from typing_extensions import TypeVar
29
+
30
+ from dara.core.base_definitions import BaseTask
31
+ from dara.core.interactivity.any_variable import AnyVariable
32
+ from dara.core.interactivity.client_variable import ClientVariable
33
+ from dara.core.interactivity.stream_event import ReconnectException, StreamEvent
34
+ from dara.core.internal.cache_store import CacheStore
35
+ from dara.core.internal.tasks import TaskManager
36
+ from dara.core.logging import dev_logger
37
+
38
+ VariableType = TypeVar('VariableType', default=Any)
39
+
40
+
41
+ class StreamVariable(ClientVariable, Generic[VariableType]):
42
+ """
43
+ A StreamVariable represents a stream of events that are accumulated on the client.
44
+
45
+ It takes an async generator function that yields StreamEvents, and dependencies
46
+ (other variables). When dependencies change, a new stream is opened. Old streams
47
+ might be cleaned up when unused to save resources.
48
+
49
+ The stream is managed via Server-Sent Events (SSE) and the client handles
50
+ reconnection automatically with exponential backoff.
51
+
52
+ IMPORTANT: Handling Reconnection
53
+ --------------------------------
54
+ Stream functions MUST be idempotent - they should produce the correct state even
55
+ when called multiple times due to reconnection. Streams can disconnect and reconnect
56
+ at any time (network issues, browser tab suspension, server restarts). Your stream
57
+ function runs from the beginning on each reconnection.
58
+
59
+ Always start with ``StreamEvent.replace()`` (keyed mode) or ``StreamEvent.json_snapshot()``
60
+ (custom mode) to set the full initial state atomically. This ensures clients always
61
+ converge to the correct state regardless of when they connect, and avoids a flash of
62
+ empty content on reconnection.
63
+
64
+ Keyed mode (when ``key_accessor`` is set):
65
+
66
+ - Items are stored in a dict keyed by the accessor, exposed as a list
67
+ - Use ``replace()``, ``add()``, ``remove()``, ``clear()`` events
68
+
69
+ Custom state mode (no ``key_accessor``):
70
+
71
+ - State can be any JSON structure
72
+ - Use ``json_snapshot()``, ``json_patch()`` events
73
+
74
+ Examples
75
+ --------
76
+ Keyed event stream (e.g., events with unique IDs):
77
+
78
+ ```python
79
+ from dara.core import StreamVariable, StreamEvent
80
+
81
+ async def events_stream(invocation_id: str):
82
+ # Set initial state atomically (handles reconnection, no flash of empty)
83
+ initial_events = await api.get_events(invocation_id)
84
+ yield StreamEvent.replace(*initial_events)
85
+
86
+ # Stream new events
87
+ async for event in api.stream_events(invocation_id):
88
+ yield StreamEvent.add(event)
89
+
90
+ events = StreamVariable(
91
+ events_stream,
92
+ variables=[invocation_id_var],
93
+ key_accessor='id',
94
+ )
95
+
96
+ # Use in template
97
+ For(items=events, renderer=EventCard(events.list_item))
98
+ ```
99
+
100
+ Custom state with JSON patches:
101
+
102
+ ```python
103
+ async def dashboard_stream(dashboard_id: str):
104
+ # Set initial state atomically (handles reconnection)
105
+ current_state = await api.get_dashboard_state(dashboard_id)
106
+ yield StreamEvent.json_snapshot(current_state)
107
+
108
+ async for update in api.stream_updates(dashboard_id):
109
+ yield StreamEvent.json_patch([
110
+ {"op": "add", "path": f"/items/{update.id}", "value": update},
111
+ {"op": "replace", "path": "/count", "value": update.count}
112
+ ])
113
+
114
+ dashboard = StreamVariable(dashboard_stream, variables=[dashboard_id_var])
115
+ ```
116
+ """
117
+
118
+ __typename: Literal['StreamVariable'] = 'StreamVariable'
119
+ uid: str
120
+ variables: list[AnyVariable] = Field(default_factory=list)
121
+ key_accessor: str | None = None
122
+ nested: list[str] = Field(default_factory=list)
123
+
124
+ model_config = ConfigDict(extra='forbid')
125
+
126
+ @field_validator('variables')
127
+ @classmethod
128
+ def validate_variables(cls, variables: list[AnyVariable]) -> list[AnyVariable]:
129
+ """Validate that variables don't contain unsupported types."""
130
+ from dara.core.interactivity.derived_variable import DerivedVariable
131
+ from dara.core.internal.registries import derived_variable_registry
132
+
133
+ for v in variables:
134
+ # Disallow StreamVariable -> StreamVariable dependency
135
+ if isinstance(v, StreamVariable):
136
+ raise ValueError(
137
+ 'StreamVariable cannot depend on another StreamVariable. '
138
+ 'Compose generators in Python instead. Example:\n\n'
139
+ 'async def enriched_events(id):\n'
140
+ ' async for event in source_stream(id):\n'
141
+ ' yield StreamEvent.add(enrich(event))\n\n'
142
+ 'events = StreamVariable(enriched_events, variables=[id_var])'
143
+ )
144
+
145
+ # Disallow DerivedVariable with run_as_task=True
146
+ if isinstance(v, DerivedVariable):
147
+ entry = derived_variable_registry.get(str(v.uid))
148
+ if entry is not None and entry.run_as_task:
149
+ raise ValueError(
150
+ 'StreamVariable cannot depend on a DerivedVariable with run_as_task=True. '
151
+ 'Task-based variables resolve asynchronously which is incompatible with '
152
+ 'streaming. Remove run_as_task=True or compute the value in the stream function.'
153
+ )
154
+
155
+ return variables
156
+
157
+ def __init__(
158
+ self,
159
+ func: Callable[..., AsyncGenerator[StreamEvent, None]],
160
+ variables: list[AnyVariable] | None = None,
161
+ key_accessor: str | None = None,
162
+ uid: str | None = None,
163
+ nested: list[str] | None = None,
164
+ **kwargs,
165
+ ):
166
+ """
167
+ Create a new StreamVariable.
168
+
169
+ :param func: Async generator function that yields StreamEvents.
170
+ The function receives resolved values of `variables` as arguments.
171
+ :param variables: List of variables whose resolved values are passed to `func`.
172
+ When any of these change, a new stream is opened.
173
+ :param key_accessor: Property path to extract unique ID from items for deduplication.
174
+ Required when using StreamEvent.add(). E.g., 'id' or 'data.id'.
175
+ :param uid: Unique identifier for this variable. Auto-generated if not provided.
176
+ :param nested: Internal use - tracks nested path for .get() chains.
177
+ """
178
+ if variables is None:
179
+ variables = []
180
+ if nested is None:
181
+ nested = []
182
+
183
+ super().__init__(
184
+ uid=uid,
185
+ variables=variables,
186
+ key_accessor=key_accessor,
187
+ nested=nested,
188
+ **kwargs,
189
+ )
190
+
191
+ # Register with the stream variable registry
192
+ from dara.core.internal.registries import stream_variable_registry
193
+
194
+ stream_variable_registry.register(
195
+ str(self.uid),
196
+ StreamVariableRegistryEntry(
197
+ uid=str(self.uid),
198
+ func=func,
199
+ variables=variables,
200
+ key_accessor=key_accessor,
201
+ ),
202
+ )
203
+
204
+ def get(self, *keys: str) -> StreamVariable[Any]:
205
+ """
206
+ Access a nested value within the stream's accumulated state.
207
+
208
+ This is useful when the stream accumulates complex state (via snapshot/patch)
209
+ and you want to access a specific part of it.
210
+
211
+ Example::
212
+
213
+ dashboard = StreamVariable(dashboard_stream, variables=[id_var])
214
+
215
+ # Access nested value
216
+ Text(dashboard.get('meta', 'count'))
217
+
218
+ :param keys: One or more keys to traverse into the nested structure.
219
+ :return: A new StreamVariable pointing to the nested path.
220
+ """
221
+ return self.model_copy(
222
+ update={'nested': [*self.nested, *[str(k) for k in keys]]},
223
+ deep=True,
224
+ )
225
+
226
+ @property
227
+ def list_item(self):
228
+ """
229
+ Get a LoopVariable that represents the current item when iterating.
230
+
231
+ This is used with the For component to access the current item in the loop.
232
+
233
+ Example::
234
+
235
+ For(items=events, renderer=EventCard(events.list_item))
236
+ """
237
+ from dara.core.interactivity.loop_variable import LoopVariable
238
+
239
+ return LoopVariable()
240
+
241
+ @model_serializer(mode='wrap')
242
+ def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
243
+ parent_dict = nxt(self)
244
+ return {
245
+ **parent_dict,
246
+ '__typename': 'StreamVariable',
247
+ 'uid': str(parent_dict['uid']),
248
+ }
249
+
250
+
251
+ @dataclass
252
+ class StreamVariableRegistryEntry:
253
+ """Registry entry for a StreamVariable."""
254
+
255
+ uid: str
256
+ func: Callable[..., AsyncGenerator[StreamEvent, None]]
257
+ variables: list[AnyVariable]
258
+ key_accessor: str | None
259
+
260
+
261
+ class StreamVariableModeError(Exception):
262
+ """Raised when a StreamEvent is used with the wrong mode (keyed vs custom)."""
263
+
264
+ pass
265
+
266
+
267
+ # Event types that require key_accessor (keyed mode)
268
+ _KEYED_MODE_EVENT_TYPES = {'add', 'remove', 'replace', 'clear'}
269
+ # Event types for custom mode (no key_accessor)
270
+ _CUSTOM_MODE_EVENT_TYPES = {'json_snapshot', 'json_patch'}
271
+
272
+
273
+ def _validate_event_mode(event: StreamEvent, key_accessor: str | None) -> None:
274
+ """
275
+ Validate that the event type matches the StreamVariable mode.
276
+
277
+ Raises StreamVariableModeError if:
278
+ - Keyed mode event (add/remove/replace/clear) is used without key_accessor
279
+ - Custom mode event (json_snapshot/json_patch) is used with key_accessor
280
+ """
281
+ event_type = event.type.value if hasattr(event.type, 'value') else str(event.type)
282
+
283
+ if event_type in _KEYED_MODE_EVENT_TYPES and key_accessor is None:
284
+ raise StreamVariableModeError(
285
+ f'StreamEvent.{event_type}() requires key_accessor to be set on StreamVariable. '
286
+ f"Either set key_accessor='your_id_field' on the StreamVariable, "
287
+ f'or use StreamEvent.json_snapshot()/json_patch() for custom state mode.'
288
+ )
289
+
290
+ if event_type in _CUSTOM_MODE_EVENT_TYPES and key_accessor is not None:
291
+ raise StreamVariableModeError(
292
+ f'StreamEvent.{event_type}() is for custom state mode but key_accessor is set. '
293
+ f'Either remove key_accessor from StreamVariable, '
294
+ f'or use StreamEvent.add()/remove()/replace()/clear() for keyed mode.'
295
+ )
296
+
297
+
298
+ async def run_stream(
299
+ entry: StreamVariableRegistryEntry, request: Request, values: list[Any], store: CacheStore, task_mgr: TaskManager
300
+ ):
301
+ """Run a StreamVariable."""
302
+ # dynamic import due to circular import
303
+ from dara.core.internal.dependency_resolution import (
304
+ resolve_dependency,
305
+ )
306
+
307
+ resolved_values = await asyncio.gather(*[resolve_dependency(v, store, task_mgr) for v in values])
308
+
309
+ has_tasks = any(isinstance(v, BaseTask) for v in resolved_values)
310
+ if has_tasks:
311
+ raise NotImplementedError('StreamVariable does not support tasks')
312
+
313
+ generator = None
314
+ try:
315
+ generator = entry.func(*resolved_values)
316
+ async for event in generator:
317
+ if await request.is_disconnected():
318
+ break
319
+ # Validate event type matches the StreamVariable mode
320
+ _validate_event_mode(event, entry.key_accessor)
321
+ yield f'data: {event.model_dump_json()}\n\n'
322
+ except ReconnectException:
323
+ yield f'data: {StreamEvent.reconnect().model_dump_json()}\n\n'
324
+ except StreamVariableModeError as e:
325
+ dev_logger.error('Stream mode error', error=e)
326
+ yield f'data: {StreamEvent.error(str(e)).model_dump_json()}\n\n'
327
+ except Exception as e:
328
+ dev_logger.error('Stream error', error=e)
329
+ yield f'data: {StreamEvent.error(str(e)).model_dump_json()}\n\n'
330
+ finally:
331
+ # Cleanup: close generator if it's still open
332
+ if generator is not None:
333
+ with contextlib.suppress(Exception):
334
+ await generator.aclose()
@@ -32,6 +32,7 @@ from dara.core.interactivity.derived_variable import (
32
32
  LatestValueRegistryEntry,
33
33
  )
34
34
  from dara.core.interactivity.server_variable import ServerVariableRegistryEntry
35
+ from dara.core.interactivity.stream_variable import StreamVariableRegistryEntry
35
36
  from dara.core.internal.download import DownloadDataEntry
36
37
  from dara.core.internal.registry import Registry, RegistryType
37
38
  from dara.core.internal.websocket import CustomClientMessagePayload
@@ -75,3 +76,6 @@ backend_store_registry = Registry[BackendStoreEntry](RegistryType.BACKEND_STORE,
75
76
 
76
77
  download_code_registry = Registry[DownloadDataEntry](RegistryType.DOWNLOAD_CODE, allow_duplicates=False)
77
78
  """map of download codes -> download data entry, used only to allow overriding download code behaviour via RegistryLookup"""
79
+
80
+ stream_variable_registry = Registry[StreamVariableRegistryEntry](RegistryType.STREAM_VARIABLE, allow_duplicates=False)
81
+ """map of stream variable uid -> stream variable registry entry"""
@@ -45,6 +45,7 @@ class RegistryType(str, Enum):
45
45
  CUSTOM_WS_HANDLERS = 'Custom WS handlers'
46
46
  BACKEND_STORE = 'Backend Store'
47
47
  DOWNLOAD_CODE = 'Download Code'
48
+ STREAM_VARIABLE = 'StreamVariable'
48
49
 
49
50
 
50
51
  class Registry(Generic[T]):
@@ -39,6 +39,7 @@ from fastapi import (
39
39
  HTTPException,
40
40
  Path,
41
41
  Query,
42
+ Request,
42
43
  Response,
43
44
  UploadFile,
44
45
  )
@@ -56,6 +57,8 @@ from dara.core.interactivity.actions import ACTION_CONTEXT
56
57
  from dara.core.interactivity.any_data_variable import upload
57
58
  from dara.core.interactivity.filtering import FilterQuery, Pagination
58
59
  from dara.core.interactivity.server_variable import ServerVariable
60
+ from dara.core.interactivity.stream_utils import track_stream
61
+ from dara.core.interactivity.stream_variable import run_stream
59
62
  from dara.core.internal.cache_store import CacheStore
60
63
  from dara.core.internal.devtools import print_stacktrace
61
64
  from dara.core.internal.download import DownloadRegistryEntry
@@ -72,6 +75,7 @@ from dara.core.internal.registries import (
72
75
  latest_value_registry,
73
76
  server_variable_registry,
74
77
  static_kwargs_registry,
78
+ stream_variable_registry,
75
79
  upload_resolver_registry,
76
80
  utils_registry,
77
81
  )
@@ -508,6 +512,57 @@ async def get_version():
508
512
  core_api_router.add_api_websocket_route('/ws', ws_handler)
509
513
 
510
514
 
515
+ class StreamRequestBody(BaseModel):
516
+ """Request body for StreamVariable SSE endpoint."""
517
+
518
+ values: NormalizedPayload[list[Any]]
519
+ """Normalized payload of resolved variable values to pass to the stream generator."""
520
+
521
+
522
+ @core_api_router.post('/stream/{stream_uid}', dependencies=[Depends(verify_session)])
523
+ async def stream_endpoint(
524
+ request: Request,
525
+ stream_uid: str,
526
+ body: StreamRequestBody,
527
+ ):
528
+ """
529
+ SSE endpoint for StreamVariable.
530
+
531
+ Opens a Server-Sent Events connection and streams events from the
532
+ StreamVariable's async generator.
533
+
534
+ :param stream_uid: The UID of the StreamVariable
535
+ :param body: Request body containing resolved variable values
536
+ """
537
+ registry_mgr: RegistryLookup = utils_registry.get('RegistryLookup')
538
+ store: CacheStore = utils_registry.get('Store')
539
+ task_mgr: TaskManager = utils_registry.get('TaskManager')
540
+
541
+ try:
542
+ entry = await registry_mgr.get(stream_variable_registry, stream_uid)
543
+ except (KeyError, ValueError) as err:
544
+ raise HTTPException(status_code=404, detail=f'StreamVariable {stream_uid} not found') from err
545
+
546
+ # Denormalize the values (resolve any nested structures)
547
+ values = denormalize(body.values.data, body.values.lookup)
548
+
549
+ @track_stream
550
+ async def stream():
551
+ async for event in run_stream(entry, request, values, store, task_mgr):
552
+ yield event
553
+
554
+ return StreamingResponse(
555
+ stream(),
556
+ media_type='text/event-stream',
557
+ headers={
558
+ 'Cache-Control': 'no-cache',
559
+ 'X-Accel-Buffering': 'no',
560
+ 'Connection': 'keep-alive',
561
+ 'Content-Type': 'text/event-stream',
562
+ },
563
+ )
564
+
565
+
511
566
  class ActionPayload(BaseModel):
512
567
  uid: str
513
568
  definition_uid: str
dara/core/main.py CHANGED
@@ -44,6 +44,7 @@ from dara.core.defaults import (
44
44
  top_template,
45
45
  )
46
46
  from dara.core.definitions import JsComponentDef
47
+ from dara.core.interactivity.stream_utils import setup_signal_handlers
47
48
  from dara.core.internal.cache_store import CacheStore
48
49
  from dara.core.internal.cgroup import get_cpu_count, set_memory_limit
49
50
  from dara.core.internal.custom_response import CustomResponse
@@ -136,6 +137,7 @@ def _start_application(config: Configuration):
136
137
  @asynccontextmanager
137
138
  async def lifespan(app: FastAPI):
138
139
  # STARTUP
140
+ setup_signal_handlers()
139
141
 
140
142
  # Retrieve the existing Store instance for the application
141
143
  # Store must exist before the app starts as instantiating e.g. Variables
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dara-core
3
- Version: 1.24.3
3
+ Version: 1.25.0
4
4
  Summary: Dara Framework Core
5
5
  Home-page: https://dara.causalens.com/
6
6
  License: Apache-2.0
@@ -21,10 +21,10 @@ Requires-Dist: cachetools (>=5.0.0)
21
21
  Requires-Dist: certifi (>=2024.7.4)
22
22
  Requires-Dist: click (>=8.1.3,<9.0.0)
23
23
  Requires-Dist: colorama (>=0.4.6,<0.5.0)
24
- Requires-Dist: create-dara-app (==1.24.3)
24
+ Requires-Dist: create-dara-app (==1.25.0)
25
25
  Requires-Dist: croniter (>=6.0.0,<7.0.0)
26
26
  Requires-Dist: cryptography (>=42.0.4)
27
- Requires-Dist: dara-components (==1.24.3) ; extra == "all"
27
+ Requires-Dist: dara-components (==1.25.0) ; extra == "all"
28
28
  Requires-Dist: exceptiongroup (>=1.1.3,<2.0.0)
29
29
  Requires-Dist: fastapi (>=0.115.0,<0.121.0)
30
30
  Requires-Dist: fastapi_vite_dara (==0.4.0)
@@ -55,7 +55,7 @@ Description-Content-Type: text/markdown
55
55
 
56
56
  # Dara Application Framework
57
57
 
58
- <img src="https://github.com/causalens/dara/blob/v1.24.3/img/dara_light.svg?raw=true">
58
+ <img src="https://github.com/causalens/dara/blob/v1.25.0/img/dara_light.svg?raw=true">
59
59
 
60
60
  ![Master tests](https://github.com/causalens/dara/actions/workflows/tests.yml/badge.svg?branch=master)
61
61
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
@@ -100,7 +100,7 @@ source .venv/bin/activate
100
100
  dara start
101
101
  ```
102
102
 
103
- ![Dara App](https://github.com/causalens/dara/blob/v1.24.3/img/components_gallery.png?raw=true)
103
+ ![Dara App](https://github.com/causalens/dara/blob/v1.25.0/img/components_gallery.png?raw=true)
104
104
 
105
105
  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:
106
106
 
@@ -117,9 +117,9 @@ Explore some of our favorite apps - a great way of getting started and getting t
117
117
 
118
118
  | Dara App | Description |
119
119
  | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
120
- | ![Large Language Model](https://github.com/causalens/dara/blob/v1.24.3/img/llm.png?raw=true) | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
121
- | ![Plot Interactivity](https://github.com/causalens/dara/blob/v1.24.3/img/plot_interactivity.png?raw=true) | 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 |
122
- | ![Graph Editor](https://github.com/causalens/dara/blob/v1.24.3/img/graph_viewer.png?raw=true) | 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. |
120
+ | ![Large Language Model](https://github.com/causalens/dara/blob/v1.25.0/img/llm.png?raw=true) | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
121
+ | ![Plot Interactivity](https://github.com/causalens/dara/blob/v1.25.0/img/plot_interactivity.png?raw=true) | 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 |
122
+ | ![Graph Editor](https://github.com/causalens/dara/blob/v1.25.0/img/graph_viewer.png?raw=true) | 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. |
123
123
 
124
124
  Check out our [App Gallery](https://dara.causalens.com/gallery) for more inspiration!
125
125
 
@@ -146,9 +146,9 @@ And the supporting UI packages and tools.
146
146
  - `ui-utils` - miscellaneous utility functions
147
147
  - `ui-widgets` - widget components
148
148
 
149
- More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.24.3/CONTRIBUTING.md) file.
149
+ More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.25.0/CONTRIBUTING.md) file.
150
150
 
151
151
  ## License
152
152
 
153
- Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.24.3/LICENSE).
153
+ Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.25.0/LICENSE).
154
154
 
@@ -1,7 +1,7 @@
1
1
  dara/core/__init__.py,sha256=yTp-lXT0yy9XqLGYWlmjPgFG5g2eEg2KhKo8KheTHoo,1408
2
2
  dara/core/_assets/__init__.py,sha256=13vMoWHvl1zcFcjNHh8lbTwWOvu4f7krYSco978qxwM,723
3
3
  dara/core/_assets/auto_js/dara.core.css,sha256=yT3PKpi2sKI2-kQIF8xtVbTPQqgpK7-Ua7tfzDPuSsI,4095881
4
- dara/core/_assets/auto_js/dara.core.umd.cjs,sha256=GdefQG4ttuW0Zo5_EO5WRuprJWlfzW4AeeVaGaHLR7k,5163836
4
+ dara/core/_assets/auto_js/dara.core.umd.cjs,sha256=oEcImrkdIvWJ_zE4my8n9EGECjIQ5JZTVm8q_ChmbVM,5186433
5
5
  dara/core/_assets/auto_js/react-dom.development.js,sha256=vR2Fq5LXMKS5JsTo2CMl6oGCJT8scJV2wXSteTtx8aE,1077040
6
6
  dara/core/_assets/auto_js/react-is.development.js,sha256=2IRgmaphdMq6wx2MbsmVUQ0UwapGETNjgano3XEkGcc,7932
7
7
  dara/core/_assets/auto_js/react-query.development.js,sha256=lI2fTKMvWmjbagGQaVtZYr51_-C_U_so064JwetuDS0,130366
@@ -29,7 +29,7 @@ dara/core/data_utils.py,sha256=haR-SPCFm90RfOIndPBGOpFtOEmhiJgizmzcqzZOZBg,12455
29
29
  dara/core/defaults.py,sha256=ejSXYKm78vd0_3b-3x6K8lffU-7BWyEhqQuwdBkkWwU,4884
30
30
  dara/core/definitions.py,sha256=uf8dVhEff9LMBLEK4SaZjod_WP5PH2jnuasni__ZeL8,18010
31
31
  dara/core/http.py,sha256=gFkUV3jBY6lx0ZIIzeFw8zEH6WAb8uIKfo_t8heZDEw,4739
32
- dara/core/interactivity/__init__.py,sha256=tgPyP6invwQJuSmKvrP5WQI3xtebGLvlm3nkiQ7Fx4g,2855
32
+ dara/core/interactivity/__init__.py,sha256=m29ylqTDHSftQA4oYD0O7VeEKOw8KeB9d5pbKRchdUM,3070
33
33
  dara/core/interactivity/actions.py,sha256=AOvOGnl0rgf5QYpwccM9kFMiehMSRw84HUSAVHmdZRA,50741
34
34
  dara/core/interactivity/any_data_variable.py,sha256=qnJAgTV_AleBwobz2GovezvX2pXrcSwD18yKQPjt32I,306
35
35
  dara/core/interactivity/any_variable.py,sha256=mOCBW5m1CCM-gzOMlN-rnlAIktb2rZJAPWE-WKiR3-A,13648
@@ -44,6 +44,9 @@ dara/core/interactivity/non_data_variable.py,sha256=k2yXTowWmH5UhG9iJfYo_bUG7AT5
44
44
  dara/core/interactivity/plain_variable.py,sha256=tg4D8UMno5cwGtM4U6FJKPbjhRRk6rZ6hJeiGgbGdrw,13123
45
45
  dara/core/interactivity/server_variable.py,sha256=s1OoLIyWIVF0TAId5Pv3b6ZZpBEHjzTotYQufAYHjZI,11109
46
46
  dara/core/interactivity/state_variable.py,sha256=Xpiazq25XS12dgvr3m1ojoqSy7svdmMaRMBxABusOzQ,2441
47
+ dara/core/interactivity/stream_event.py,sha256=83zAwf_MqjoWONNvutGW4uoIlTXXBJvLUGg7JuPNIWs,10059
48
+ dara/core/interactivity/stream_utils.py,sha256=xcqr4mQj4IH0bI_hfZ2jb2FImYftJea1qGmC_-Wh1oY,2404
49
+ dara/core/interactivity/stream_variable.py,sha256=M13XrqRC9Gj4ZNAHXsTnR-Lw4SKYzvbTK8qOvCAzMoc,12750
47
50
  dara/core/interactivity/switch_variable.py,sha256=dhmAnF2KmL4944TJVipV-30BPYSLoD-uxyBP49W3V9Q,14099
48
51
  dara/core/interactivity/tabular_variable.py,sha256=rUz9jnU6ya_0J7dhIN8d0VCDfzO7N5AsvksrHNtCO7k,3337
49
52
  dara/core/interactivity/url_variable.py,sha256=lAtHgYvLA8RE-eFfv4Ix27T5YLGT5e0c0q9O5eQPOos,2282
@@ -73,10 +76,10 @@ dara/core/internal/pool/task_pool.py,sha256=zwIq2GssU6PRb1fmvtgpxwN0u_JmxU5fDi39
73
76
  dara/core/internal/pool/utils.py,sha256=DpWGfD-7fnQzjdgMk0qki2ZvRo-cjUHYkQxoFjJxrZw,5477
74
77
  dara/core/internal/pool/worker.py,sha256=Ih0z1mqCNrX8Mo9I-LybfTDp46O9bvkl6TW7MZ-_oAU,6801
75
78
  dara/core/internal/port_utils.py,sha256=3NN94CubNrIYQKmPM4SEwstY-UMqsbbe1M_JhyPcncA,1654
76
- dara/core/internal/registries.py,sha256=j_Z3KqmiNHK3HsYmQI2O23B12MsT31SPvo3ezemASR8,3654
77
- dara/core/internal/registry.py,sha256=Wqt7tEOmf5bR8D6lFIcLd3W1EtlENT364vRhxSqeOIQ,4356
79
+ dara/core/internal/registries.py,sha256=Sl9ajDCkUQCEGYMOyo08F2dru51tZbew4Mxlphn01F0,3921
80
+ dara/core/internal/registry.py,sha256=WPQpW4MksVU5kzOJEiH5aBPVVf7AMntNLFyt-DZVMow,4395
78
81
  dara/core/internal/registry_lookup.py,sha256=XgZ4PL4RLyKWNTXkh08lL6mmU492xgdPUsvFKA6pKDY,2411
79
- dara/core/internal/routing.py,sha256=uqbFtp9Eyc_G3dXVH7NrqT-35Ec3ep-vNw9xlYSnqkE,27832
82
+ dara/core/internal/routing.py,sha256=jt85ymFfk-mf5lLEbDE_3Dg_2IcrfWJjluT-cUjOJkA,29681
80
83
  dara/core/internal/scheduler.py,sha256=3LYdWAyFp8N1s9Xfud9XYwUbjcHpI-hCay3jZ5zRAiQ,12991
81
84
  dara/core/internal/settings.py,sha256=2g-PIoYVLlGoBNKoXTIbkH1FGV8TXUXRdwuhQ6cTrkE,2614
82
85
  dara/core/internal/store.py,sha256=aq37Tk4J3DoqzLVHuZRbZFkpghKP-oiCbpQfIE95gCo,6399
@@ -97,7 +100,7 @@ dara/core/js_tooling/templates/dara.config.json,sha256=RZG_R_xJv_5rYIoz2QZulcG49
97
100
  dara/core/js_tooling/templates/vite.config.template.ts,sha256=W-R9LtXPMv7vQkqMawMmeFrjaQ22xrGU0iYyS_nHkh4,1199
98
101
  dara/core/log_configs/logging.yaml,sha256=YJyD18psAmSVz6587dcEOyoulLuRFFu1g8yMXl1ylM0,706
99
102
  dara/core/logging.py,sha256=Exe8dz8sbE1NDwRnqSoPCTnWtYe8BPLrO1n9gJQ1neQ,13214
100
- dara/core/main.py,sha256=QQZ0KflOHX8OLHpOyvwesmczDt03F6FRH5FVNDwlIdY,21253
103
+ dara/core/main.py,sha256=qXt2jjJ6pAo4HolqNThBAR24c8TuHQdSv5_J2QnNMyU,21356
101
104
  dara/core/metrics/__init__.py,sha256=2UqpWHv-Ie58QLJIHJ9Szfjq8xifAuwy5FYGUIFwWtI,823
102
105
  dara/core/metrics/cache.py,sha256=3Sw2JWofa1vxc39NwQ8LOdTd9sX3gkrPSojjjnr-u1I,2594
103
106
  dara/core/metrics/runtime.py,sha256=YP-6Dz0GeI9_Yr7bUk_-OqShyFySGH_AKpDO126l6es,1833
@@ -134,8 +137,8 @@ dara/core/visual/themes/__init__.py,sha256=aM4mgoIYo2neBSw5FRzswsht7PUKjLthiHLmF
134
137
  dara/core/visual/themes/dark.py,sha256=QazCRDqh_SCOyQhdwMkH1wbHf301oL7gCFj91plbLww,2020
135
138
  dara/core/visual/themes/definitions.py,sha256=dtET2YUlwXkO6gJ23MqSb8gIq-LxJ343CWsgueWSifM,2787
136
139
  dara/core/visual/themes/light.py,sha256=dtHb6Q1HOb5r_AvJfe0vZajikVc-GnBEUrGsTcI5MHA,2022
137
- dara_core-1.24.3.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
138
- dara_core-1.24.3.dist-info/METADATA,sha256=TtNzBmIbp6RS5z31VTbcs79Q9VXy1nQq7F6Y3sVB6Z4,7523
139
- dara_core-1.24.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
140
- dara_core-1.24.3.dist-info/entry_points.txt,sha256=nAT9o1kJCmTK1saDh29PFGFD6cbxDDDjTj31HDEDwfU,197
141
- dara_core-1.24.3.dist-info/RECORD,,
140
+ dara_core-1.25.0.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
141
+ dara_core-1.25.0.dist-info/METADATA,sha256=IJIVP_H8n7KDDql7fQQoK49XrIPU8JwiCK60A-ubO7E,7523
142
+ dara_core-1.25.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
143
+ dara_core-1.25.0.dist-info/entry_points.txt,sha256=nAT9o1kJCmTK1saDh29PFGFD6cbxDDDjTj31HDEDwfU,197
144
+ dara_core-1.25.0.dist-info/RECORD,,