dara-core 1.16.23__py3-none-any.whl → 1.17.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.
@@ -428,6 +428,15 @@ class PendingTask(BaseTask):
428
428
  """
429
429
  self.subscribers -= 1
430
430
 
431
+ async def value(self):
432
+ """
433
+ Wait for the task to complete and return the result
434
+ """
435
+ await self.event.wait()
436
+ if self.error:
437
+ raise self.error
438
+ return self.result
439
+
431
440
 
432
441
  class PendingValue:
433
442
  """
@@ -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.switch_variable import SwitchVariable
45
46
  from dara.core.interactivity.url_variable import UrlVariable
46
47
 
47
48
  __all__ = [
@@ -52,6 +53,7 @@ __all__ = [
52
53
  'DataVariable',
53
54
  'NonDataVariable',
54
55
  'Variable',
56
+ 'SwitchVariable',
55
57
  'DerivedVariable',
56
58
  'DerivedDataVariable',
57
59
  'UrlVariable',
@@ -1247,15 +1247,7 @@ class ActionCtx:
1247
1247
 
1248
1248
  task = Task(func=func, args=args, kwargs=kwargs, on_progress=on_progress)
1249
1249
  pending_task = await task_mgr.run_task(task)
1250
-
1251
- # Run until completion
1252
- await pending_task.event.wait()
1253
-
1254
- # Raise exception if there was one
1255
- if pending_task.error:
1256
- raise pending_task.error
1257
-
1258
- return pending_task.result
1250
+ return await pending_task.value()
1259
1251
 
1260
1252
  async def execute_action(self, action: ActionImpl):
1261
1253
  """
@@ -20,6 +20,8 @@ from __future__ import annotations
20
20
  from enum import Enum
21
21
  from typing import TYPE_CHECKING, ClassVar, Union
22
22
 
23
+ from pydantic import SerializerFunctionWrapHandler, model_serializer
24
+
23
25
  from dara.core.base_definitions import DaraBaseModel as BaseModel
24
26
 
25
27
  # Type-only imports
@@ -46,3 +48,9 @@ class Condition(BaseModel):
46
48
  variable: AnyVariable
47
49
 
48
50
  Operator: ClassVar[OperatorType] = Operator
51
+
52
+ @model_serializer(mode='wrap')
53
+ def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
54
+ parent_dict = nxt(self)
55
+
56
+ return {**parent_dict, '__typename': 'Condition'}
@@ -37,20 +37,6 @@ from dara.core.internal.utils import call_async
37
37
  from dara.core.logging import dev_logger
38
38
  from dara.core.persistence import PersistenceStore
39
39
 
40
-
41
- def _is_subclass_safe(value: type, base: type) -> bool:
42
- """
43
- Check if a class is a subclass of another class. Returns False if the value is not a class.
44
-
45
- :param value: the class to check
46
- :param base: the class to check against
47
- """
48
- try:
49
- return issubclass(value, base)
50
- except TypeError:
51
- return False
52
-
53
-
54
40
  VARIABLE_INIT_OVERRIDE = ContextVar[Optional[Callable[[dict], dict]]]('VARIABLE_INIT_OVERRIDE', default=None)
55
41
 
56
42
  VariableType = TypeVar('VariableType')
@@ -0,0 +1,416 @@
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 Any, Dict, Optional, Union
21
+
22
+ from pydantic import SerializerFunctionWrapHandler, field_validator, model_serializer
23
+
24
+ from dara.core.interactivity.condition import Condition
25
+ from dara.core.interactivity.non_data_variable import NonDataVariable
26
+
27
+
28
+ class SwitchVariable(NonDataVariable):
29
+ """
30
+ A SwitchVariable represents a conditional value that switches between
31
+ different values based on a condition or variable value.
32
+
33
+ SwitchVariable provides a clean way to create conditional logic in your
34
+ application by mapping input values or conditions to output values. It
35
+ supports boolean conditions, value mappings, and default fallbacks.
36
+
37
+ There are three main patterns for creating SwitchVariables:
38
+
39
+ 1. **Boolean Conditions**: Switch between two values based on a true/false condition
40
+ 2. **Value Mapping**: Map specific input values to corresponding output values
41
+ 3. **Mixed Usage**: Combine conditions and mappings with default fallbacks
42
+
43
+ Examples:
44
+ Basic boolean switching:
45
+
46
+ ```python
47
+ from dara.core import ConfigurationBuilder, Variable, SwitchVariable
48
+ from dara.components import Stack, Text, Button, Card
49
+
50
+ config = ConfigurationBuilder()
51
+ is_admin = Variable(default=False)
52
+
53
+ # Show different UI based on admin status
54
+ ui_mode = SwitchVariable.when(
55
+ condition=is_admin,
56
+ true_value='Admin Panel',
57
+ false_value='User Panel'
58
+ )
59
+
60
+ # Complete component usage
61
+ page_content = Card(
62
+ Stack(
63
+ Text('Current Mode:'),
64
+ Text(text=ui_mode),
65
+ Button('Toggle Admin', onclick=is_admin.toggle())
66
+ ),
67
+ title='Admin Panel Demo'
68
+ )
69
+
70
+ config.add_page('Admin Demo', content=page_content)
71
+ ```
72
+
73
+ Value mapping with defaults:
74
+
75
+ ```python
76
+ from dara.core import ConfigurationBuilder, Variable, SwitchVariable
77
+ from dara.components import Stack, Text, Select, Item, Card
78
+
79
+ config = ConfigurationBuilder()
80
+ user_role = Variable(default='guest')
81
+
82
+ # Map user roles to permission levels
83
+ permissions = SwitchVariable.match(
84
+ value=user_role,
85
+ mapping={
86
+ 'admin': 'Full Access',
87
+ 'editor': 'Write Access',
88
+ 'viewer': 'Read Access'
89
+ },
90
+ default='No Access' # for unknown roles
91
+ )
92
+
93
+ # Complete component usage
94
+ page_content = Card(
95
+ Stack(
96
+ Text('Select Role:'),
97
+ Select(
98
+ value=user_role,
99
+ items=[
100
+ Item(label='Guest', value='guest'),
101
+ Item(label='Viewer', value='viewer'),
102
+ Item(label='Editor', value='editor'),
103
+ Item(label='Admin', value='admin')
104
+ ]
105
+ ),
106
+ Text('Permissions:'),
107
+ Text(text=permissions)
108
+ ),
109
+ title='Role Permissions'
110
+ )
111
+
112
+ config.add_page('Permissions Demo', content=page_content)
113
+ ```
114
+
115
+ Complex conditions:
116
+
117
+ ```python
118
+ from dara.core import Variable, SwitchVariable
119
+ from dara.components import Stack, Text, Input, Card
120
+
121
+ score = Variable(default=85)
122
+
123
+ # Grade based on score ranges
124
+ grade = SwitchVariable.when(
125
+ condition=score >= 90,
126
+ true_value='A Grade',
127
+ false_value='B Grade'
128
+ )
129
+
130
+ # Complete component usage
131
+ grade_component = Card(
132
+ Stack(
133
+ Text('Enter Score:'),
134
+ Input(value=score, type='number'),
135
+ Text('Grade (>=90 = A, <90 = B):'),
136
+ Text(text=grade)
137
+ ),
138
+ title='Grade Calculator'
139
+ )
140
+ ```
141
+
142
+ Switching on computed conditions:
143
+
144
+ ```python
145
+ from dara.core import Variable, SwitchVariable
146
+ from dara.components import Stack, Text, Input, Card
147
+
148
+ temperature = Variable(default=20)
149
+
150
+ # Weather advice based on temperature
151
+ advice = SwitchVariable.when(
152
+ condition=temperature > 25,
153
+ true_value='Wear light clothes - it\'s warm!',
154
+ false_value='Wear warm clothes - it\'s cool!'
155
+ )
156
+
157
+ # Complete component usage
158
+ weather_app = Card(
159
+ Stack(
160
+ Text('Temperature (°C):'),
161
+ Input(value=temperature, type='number'),
162
+ Text('Advice:'),
163
+ Text(text=advice)
164
+ ),
165
+ title='Weather Advisor'
166
+ )
167
+ ```
168
+
169
+ Using with other variables:
170
+
171
+ ```python
172
+ from dara.core import Variable, SwitchVariable
173
+ from dara.components import Stack, Text, Select, Item, Card
174
+
175
+ user_preference = Variable(default='auto')
176
+ mapping_variable = Variable({
177
+ 'auto': 'System Theme',
178
+ 'light': 'Light Theme',
179
+ 'dark': 'Dark Theme'
180
+ })
181
+
182
+ # Theme selection with user preference override
183
+ active_theme = SwitchVariable.match(
184
+ value=user_preference,
185
+ mapping=mapping_variable,
186
+ default='Default Theme'
187
+ )
188
+
189
+ # Complete component usage
190
+ theme_selector = Card(
191
+ Stack(
192
+ Text('Theme Preference:'),
193
+ Select(
194
+ value=user_preference,
195
+ items=[
196
+ Item(label='Auto', value='auto'),
197
+ Item(label='Light', value='light'),
198
+ Item(label='Dark', value='dark')
199
+ ]
200
+ ),
201
+ Text('Active Theme:'),
202
+ Text(text=active_theme)
203
+ ),
204
+ title='Theme Selector'
205
+ )
206
+ ```
207
+
208
+ Note:
209
+ - Value can be a condition, raw value, or variable
210
+ - Value map can be a variable or a dict
211
+ - Default can be a variable or a raw value
212
+ - The switch evaluation happens reactively when underlying variables change
213
+ - Default values are only used in mapping scenarios when the switch value
214
+ doesn't match any key in the mapping
215
+
216
+ Key Serialization:
217
+ When using mappings with SwitchVariable, be aware that JavaScript object keys
218
+ are always strings. The system automatically converts lookup keys to strings:
219
+ - Python: {True: 'admin', False: 'user'}
220
+ - JavaScript: {"true": "admin", "false": "user"}
221
+ - Boolean values are converted to lowercase strings ("true"/"false")
222
+ - Other values use standard string conversion to match JavaScript's String() behavior
223
+ """
224
+
225
+ value: Optional[Union[Condition, NonDataVariable, Any]] = None
226
+ # must be typed as any, otherwise pydantic is trying to instantiate the variables incorrectly
227
+ value_map: Optional[Any] = None
228
+ default: Optional[Any] = None
229
+
230
+ def __init__(
231
+ self,
232
+ value: Union[Condition, NonDataVariable, Any],
233
+ value_map: Dict[Any, Any] | NonDataVariable,
234
+ default: Optional[Any] = None,
235
+ uid: Optional[str] = None,
236
+ ):
237
+ """
238
+ Create a SwitchVariable with a mapping of values.
239
+
240
+ :param value: Variable, condition, or value to switch on
241
+ :param value_map: Dict mapping switch values to return values
242
+ :param default: Default value when switch value not in mapping
243
+ :param uid: Unique identifier for this variable
244
+ """
245
+ super().__init__(
246
+ uid=uid,
247
+ value=value,
248
+ value_map=value_map,
249
+ default=default,
250
+ )
251
+
252
+ @field_validator('value_map')
253
+ @classmethod
254
+ def validate_value_map(cls, v):
255
+ """
256
+ Validate that value_map is either a dict or a NonDataVariable.
257
+
258
+ :param v: The value to validate
259
+ :return: The validated value
260
+ :raises ValueError: If value_map is not a dict or NonDataVariable
261
+ """
262
+ if v is None:
263
+ return v
264
+ if isinstance(v, dict):
265
+ return v
266
+ if isinstance(v, NonDataVariable):
267
+ return v
268
+ raise ValueError(f'value_map must be a dict or NonDataVariable, got {type(v)}')
269
+
270
+ @classmethod
271
+ def when(
272
+ cls,
273
+ condition: Union[Condition, NonDataVariable, Any],
274
+ true_value: Union[Any, NonDataVariable],
275
+ false_value: Union[Any, NonDataVariable],
276
+ uid: Optional[str] = None,
277
+ ) -> 'SwitchVariable':
278
+ """
279
+ Create a SwitchVariable for boolean conditions.
280
+
281
+ This is the most common pattern for simple if/else logic. The condition
282
+ is evaluated and returns true_value if truthy, false_value otherwise.
283
+
284
+ :param condition: Condition to evaluate (Variable, Condition object, or any value)
285
+ :param true_value: Value to return when condition evaluates to True
286
+ :param false_value: Value to return when condition evaluates to False
287
+ :param uid: Unique identifier for this variable
288
+ :return: SwitchVariable configured for boolean switching
289
+
290
+ Example with variable condition:
291
+ ```python
292
+ from dara.core import Variable, SwitchVariable
293
+ from dara.components import Stack, Text, Button
294
+
295
+ is_loading = Variable(default=True)
296
+
297
+ # Show spinner while loading, content when done
298
+ display = SwitchVariable.when(
299
+ condition=is_loading,
300
+ true_value='Loading...',
301
+ false_value='Content loaded!'
302
+ )
303
+
304
+ # Use in a complete component
305
+ page_content = Stack(
306
+ Text(text=display),
307
+ Button(
308
+ text='Toggle Loading',
309
+ onclick=is_loading.toggle()
310
+ )
311
+ )
312
+ ```
313
+
314
+ Example with a condition object:
315
+
316
+ ```python
317
+ from dara.core import Variable, SwitchVariable
318
+ from dara.components import Stack, Text, Input, Card
319
+
320
+ # Temperature-based clothing advice
321
+ temperature = Variable(default=20)
322
+ advice = SwitchVariable.when(
323
+ condition=temperature > 25,
324
+ true_value='Wear light clothes - it\'s warm!',
325
+ false_value='Wear warm clothes - it\'s cool!'
326
+ )
327
+
328
+ # Complete weather advice component
329
+ weather_component = Card(
330
+ Stack(
331
+ Text('Enter temperature (°C):'),
332
+ Input(value=temperature, type='number'),
333
+ Text('Advice:'),
334
+ Text(text=advice)
335
+ ),
336
+ title='Weather Advice'
337
+ )
338
+ ```
339
+ """
340
+ return cls(
341
+ value=condition,
342
+ value_map={True: true_value, False: false_value},
343
+ uid=uid,
344
+ )
345
+
346
+ @classmethod
347
+ def match(
348
+ cls,
349
+ value: Union[NonDataVariable, Any],
350
+ mapping: Union[Dict[Any, Any], NonDataVariable],
351
+ default: Optional[Union[Any, NonDataVariable]] = None,
352
+ uid: Optional[str] = None,
353
+ ) -> 'SwitchVariable':
354
+ """
355
+ Create a SwitchVariable with a custom mapping.
356
+
357
+ This pattern is useful when you have multiple specific values to map to
358
+ different outputs, similar to a switch statement in other languages.
359
+
360
+ :param value: Variable or value to switch on
361
+ :param mapping: Dict mapping switch values to return values
362
+ :param default: Default value when switch value not found in mapping
363
+ :param uid: Unique identifier for this variable
364
+ :return: SwitchVariable configured with the provided mapping
365
+
366
+ Example:
367
+ ```python
368
+ from dara.core import Variable, SwitchVariable
369
+ from dara.components import Stack, Text, Select, Item
370
+
371
+ status = Variable(default='pending')
372
+
373
+ # Map status codes to user-friendly messages
374
+ message = SwitchVariable.match(
375
+ value=status,
376
+ mapping={
377
+ 'pending': 'Please wait...',
378
+ 'success': 'Operation completed!',
379
+ 'error': 'Something went wrong',
380
+ 'cancelled': 'Operation was cancelled'
381
+ },
382
+ default='Unknown status'
383
+ )
384
+
385
+ # Use in a complete component
386
+ page_content = Stack(
387
+ Select(
388
+ value=status,
389
+ items=[
390
+ Item(label='Pending', value='pending'),
391
+ Item(label='Success', value='success'),
392
+ Item(label='Error', value='error'),
393
+ Item(label='Cancelled', value='cancelled')
394
+ ]
395
+ ),
396
+ Text(text=message)
397
+ )
398
+ ```
399
+ """
400
+ return cls(
401
+ value=value,
402
+ value_map=mapping,
403
+ default=default,
404
+ uid=uid,
405
+ )
406
+
407
+ @model_serializer(mode='wrap')
408
+ def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
409
+ """
410
+ Serialize the SwitchVariable model with additional metadata.
411
+
412
+ :param nxt: The next serializer function in the chain
413
+ :return: Serialized dictionary with __typename and uid fields
414
+ """
415
+ parent_dict = nxt(self)
416
+ return {**parent_dict, '__typename': 'SwitchVariable', 'uid': str(parent_dict['uid'])}
@@ -19,12 +19,14 @@ from typing import Any, List, Literal, Optional, Union
19
19
 
20
20
  from typing_extensions import TypedDict, TypeGuard
21
21
 
22
+ from dara.core.base_definitions import BaseTask, PendingTask
22
23
  from dara.core.interactivity import DataVariable, DerivedDataVariable, DerivedVariable
23
24
  from dara.core.interactivity.filtering import FilterQuery
24
25
  from dara.core.internal.cache_store import CacheStore
25
26
  from dara.core.internal.pandas_utils import remove_index
26
27
  from dara.core.internal.registry_lookup import RegistryLookup
27
28
  from dara.core.internal.tasks import TaskManager
29
+ from dara.core.logging import dev_logger
28
30
 
29
31
 
30
32
  class ResolvedDerivedVariable(TypedDict):
@@ -50,6 +52,14 @@ class ResolvedDataVariable(TypedDict):
50
52
  uid: str
51
53
 
52
54
 
55
+ class ResolvedSwitchVariable(TypedDict):
56
+ type: Literal['switch']
57
+ uid: str
58
+ value: Any
59
+ value_map: Any
60
+ default: Any
61
+
62
+
53
63
  def is_resolved_derived_variable(obj: Any) -> TypeGuard[ResolvedDerivedVariable]:
54
64
  return isinstance(obj, dict) and 'uid' in obj and obj.get('type') == 'derived'
55
65
 
@@ -62,8 +72,14 @@ def is_resolved_data_variable(obj: Any) -> TypeGuard[ResolvedDataVariable]:
62
72
  return isinstance(obj, dict) and 'uid' in obj and obj.get('type') == 'data'
63
73
 
64
74
 
75
+ def is_resolved_switch_variable(obj: Any) -> TypeGuard[ResolvedSwitchVariable]:
76
+ return isinstance(obj, dict) and 'uid' in obj and obj.get('type') == 'switch'
77
+
78
+
65
79
  async def resolve_dependency(
66
- entry: Union[ResolvedDerivedDataVariable, ResolvedDataVariable, ResolvedDerivedVariable, Any],
80
+ entry: Union[
81
+ ResolvedDerivedDataVariable, ResolvedDataVariable, ResolvedDerivedVariable, ResolvedSwitchVariable, Any
82
+ ],
67
83
  store: CacheStore,
68
84
  task_mgr: TaskManager,
69
85
  ):
@@ -84,6 +100,9 @@ async def resolve_dependency(
84
100
  if is_resolved_data_variable(entry):
85
101
  return await _resolve_data_var(entry, store)
86
102
 
103
+ if is_resolved_switch_variable(entry):
104
+ return await _resolve_switch_var(entry, store, task_mgr)
105
+
87
106
  return entry
88
107
 
89
108
 
@@ -149,3 +168,121 @@ async def _resolve_data_var(data_variable_entry: ResolvedDataVariable, store: Ca
149
168
  var = await registry_mgr.get(data_variable_registry, str(data_variable_entry.get('uid')))
150
169
  result = await DataVariable.get_value(var, store, data_variable_entry.get('filters', None))
151
170
  return remove_index(result)
171
+
172
+
173
+ def _normalize_lookup_key(value: Any) -> str:
174
+ """
175
+ Normalize a value to a string key that matches JavaScript object key serialization.
176
+ This ensures consistent lookup between Python backend and JavaScript frontend.
177
+
178
+ JavaScript's String() conversion rules:
179
+ - String(true) -> "true", String(false) -> "false"
180
+ - String(null) -> "null", String(undefined) -> "undefined"
181
+ - Numbers and other types are converted to their string representation
182
+
183
+ :param value: The value to normalize as a lookup key
184
+ :return: String representation suitable for object key lookup
185
+ """
186
+ if isinstance(value, bool):
187
+ # JavaScript String(true) -> "true", String(false) -> "false"
188
+ return str(value).lower()
189
+ elif value is None:
190
+ # JavaScript String(null) -> "null"
191
+ return 'null'
192
+ else:
193
+ # For numbers, strings, and other types, use standard string conversion
194
+ return str(value)
195
+
196
+
197
+ def _evaluate_condition(condition: dict) -> bool:
198
+ """
199
+ Evaluate a condition object and return the boolean result.
200
+
201
+ :param condition: condition dict with 'variable', 'operator', and 'other' keys
202
+ :return: boolean result of the condition evaluation
203
+ """
204
+ variable_value = condition['variable']
205
+ operator = condition['operator']
206
+ other_value = condition['other']
207
+
208
+ if operator == 'equal':
209
+ return variable_value == other_value
210
+ elif operator == 'truthy':
211
+ return bool(variable_value)
212
+ elif operator == 'not_equal':
213
+ return variable_value != other_value
214
+ else:
215
+ # strictly numeric comparisons, ensure they're numbers just in case
216
+ val_a = float(variable_value)
217
+ val_b = float(other_value)
218
+
219
+ if operator == 'greater_than':
220
+ return val_a > val_b
221
+ elif operator == 'greater_equal':
222
+ return val_a >= val_b
223
+ elif operator == 'less_than':
224
+ return val_a < val_b
225
+ elif operator == 'less_equal':
226
+ return val_a <= val_b
227
+ else:
228
+ raise ValueError(f'Unknown condition operator: {operator}')
229
+
230
+
231
+ async def _resolve_switch_var(switch_variable_entry: ResolvedSwitchVariable, store: CacheStore, task_mgr: TaskManager):
232
+ """
233
+ Resolve a switch variable by evaluating its constituent parts and returning the appropriate value.
234
+
235
+ :param switch_variable_entry: switch variable entry
236
+ :param store: store instance to use for caching
237
+ :param task_mgr: task manager instance
238
+ """
239
+
240
+ async def _resolve_maybe_task(value: Any) -> Any:
241
+ if isinstance(value, BaseTask):
242
+ task_result = await task_mgr.run_task(value)
243
+ if isinstance(task_result, PendingTask):
244
+ return await task_result.value()
245
+ return task_result
246
+ return value
247
+
248
+ # resolve the constituent parts
249
+ resolved_value = await resolve_dependency(switch_variable_entry.get('value'), store, task_mgr)
250
+ resolved_value = await _resolve_maybe_task(resolved_value)
251
+
252
+ resolved_value_map = await resolve_dependency(switch_variable_entry.get('value_map'), store, task_mgr)
253
+ resolved_value_map = await _resolve_maybe_task(resolved_value_map)
254
+
255
+ resolved_default = await resolve_dependency(switch_variable_entry.get('default'), store, task_mgr)
256
+ resolved_default = await _resolve_maybe_task(resolved_default)
257
+
258
+ # The frontend should have already evaluated conditions and sent the resolved value
259
+ # For switch variables, we just need to look up the value in the mapping
260
+ if isinstance(resolved_value_map, dict):
261
+ # value could be a condition object
262
+ if isinstance(resolved_value, dict) and resolved_value.get('__typename') == 'Condition':
263
+ # Evaluate the condition and use the boolean result as the lookup key
264
+ try:
265
+ # First, resolve any nested dependencies in the condition's 'other' field
266
+ condition_copy = resolved_value.copy()
267
+ condition_copy['other'] = await resolve_dependency(condition_copy.get('other'), store, task_mgr)
268
+ condition_copy['other'] = await _resolve_maybe_task(condition_copy['other'])
269
+
270
+ # Also resolve the variable field in case it's a dependency
271
+ condition_copy['variable'] = await resolve_dependency(condition_copy.get('variable'), store, task_mgr)
272
+ condition_copy['variable'] = await _resolve_maybe_task(condition_copy['variable'])
273
+
274
+ # Evaluate the condition
275
+ resolved_value = _evaluate_condition(condition_copy)
276
+ except Exception as e:
277
+ # If condition evaluation fails, log and use default
278
+ dev_logger.error('Error evaluating condition', error=e)
279
+ return resolved_default
280
+
281
+ # Normalize the lookup key to match JavaScript object key serialization
282
+ resolved_value = _normalize_lookup_key(resolved_value)
283
+
284
+ # Try to get the value from the mapping, fall back to default
285
+ return resolved_value_map.get(resolved_value, resolved_default)
286
+
287
+ # If value_map is not a dict (shouldn't happen in normal cases), return default
288
+ return resolved_default
dara/core/main.py CHANGED
@@ -314,6 +314,9 @@ def _start_application(config: Configuration):
314
314
  template_registry.register(name, renderer(config))
315
315
 
316
316
  except Exception as e:
317
+ import traceback
318
+
319
+ traceback.print_exc()
317
320
  dev_logger.error(
318
321
  'Something went wrong when building application template, there is most likely an issue in the application logic',
319
322
  e,
@@ -50158,6 +50158,9 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
50158
50158
  function isLoopVariable(variable) {
50159
50159
  return variable && typeof variable == "object" && variable.hasOwnProperty("uid") && variable.hasOwnProperty("__typename") && variable.__typename === "LoopVariable";
50160
50160
  }
50161
+ function isSwitchVariable(variable) {
50162
+ return isVariable(variable) && variable.__typename === "SwitchVariable";
50163
+ }
50161
50164
  function isResolvedDerivedVariable(value) {
50162
50165
  return value && typeof value === "object" && "values" in value && "type" in value && value.type === "derived" && "uid" in value;
50163
50166
  }
@@ -50167,6 +50170,9 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
50167
50170
  function isResolvedDerivedDataVariable(value) {
50168
50171
  return value && typeof value === "object" && "filters" in value && "values" in value && "type" in value && value.type === "derived-data" && "uid" in value;
50169
50172
  }
50173
+ function isResolvedSwitchVariable(value) {
50174
+ return value && typeof value === "object" && "type" in value && value.type === "switch" && "uid" in value && "value_map" in value;
50175
+ }
50170
50176
  function isActionImpl(action) {
50171
50177
  return action && typeof action === "object" && action.__typename === "ActionImpl";
50172
50178
  }
@@ -50174,6 +50180,9 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
50174
50180
  return action && "uid" in action && "definition_uid" in action && "dynamic_kwargs" in action;
50175
50181
  }
50176
50182
  const isPyComponent = (value) => isObject_1(value) && "props" in value && isObject_1(value.props) && "func_name" in value.props && "dynamic_kwargs" in value.props;
50183
+ function isCondition(value) {
50184
+ return value && typeof value === "object" && value.__typename === "Condition";
50185
+ }
50177
50186
  function commonjsRequire(path) {
50178
50187
  throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
50179
50188
  }
@@ -51103,11 +51112,16 @@ You must set sticky: 'left' | 'right' for the '${bugWithUnderColumnsSticky.Heade
51103
51112
  ...lookup,
51104
51113
  ...nestedLookup
51105
51114
  };
51106
- } else {
51115
+ } else if (isResolvedSwitchVariable(val)) {
51107
51116
  const varDef = def.variables[key];
51108
51117
  const identifier = getIdentifier(varDef);
51109
51118
  lookup[identifier] = val;
51110
51119
  normalizedValues.push({ __ref: identifier });
51120
+ } else {
51121
+ const varDef = def.variables[key];
51122
+ const identifier = getIdentifier(varDef);
51123
+ lookup[identifier] = val === void 0 ? null : val;
51124
+ normalizedValues.push({ __ref: identifier });
51111
51125
  }
51112
51126
  }
51113
51127
  return {
@@ -52935,6 +52949,14 @@ Inferred class string: "${iconClasses}."`
52935
52949
  values: formatDerivedVariableRequest(nestedValues)
52936
52950
  };
52937
52951
  }
52952
+ if (isResolvedSwitchVariable(val)) {
52953
+ return {
52954
+ ...val,
52955
+ value: formatDerivedVariableRequest([val.value])[0],
52956
+ value_map: formatDerivedVariableRequest([val.value_map])[0],
52957
+ default: formatDerivedVariableRequest([val.default])[0]
52958
+ };
52959
+ }
52938
52960
  return val;
52939
52961
  });
52940
52962
  }
@@ -53007,9 +53029,23 @@ Inferred class string: "${iconClasses}."`
53007
53029
  values: resolvedValues
53008
53030
  };
53009
53031
  }
53032
+ if (isResolvedSwitchVariable(value)) {
53033
+ return {
53034
+ ...value,
53035
+ value: resolveValue(value.value, getter),
53036
+ value_map: resolveValue(value.value_map, getter),
53037
+ default: resolveValue(value.default, getter)
53038
+ };
53039
+ }
53010
53040
  if (isResolvedDataVariable(value)) {
53011
53041
  return value;
53012
53042
  }
53043
+ if (isCondition(value)) {
53044
+ return {
53045
+ ...value,
53046
+ variable: resolveValue(value.variable, getter)
53047
+ };
53048
+ }
53013
53049
  if (Recoil_index_2(value)) {
53014
53050
  return getter(value);
53015
53051
  }
@@ -53020,7 +53056,13 @@ Inferred class string: "${iconClasses}."`
53020
53056
  if (deps && !deps.includes(idx)) {
53021
53057
  return [];
53022
53058
  }
53023
- return isResolvedDerivedVariable(val) || isResolvedDerivedDataVariable(val) ? getDeps(val.values, val.deps).flat() : val;
53059
+ if (isResolvedDerivedVariable(val) || isResolvedDerivedDataVariable(val)) {
53060
+ return getDeps(val.values, val.deps).flat();
53061
+ }
53062
+ if (isResolvedSwitchVariable(val)) {
53063
+ return getDeps([val.value, val.value_map, val.default], [0, 1, 2]).flat();
53064
+ }
53065
+ return val;
53024
53066
  });
53025
53067
  }
53026
53068
  function useTriggerIndex(variable) {
@@ -53365,6 +53407,24 @@ Inferred class string: "${iconClasses}."`
53365
53407
  if (isUrlVariable(variable)) {
53366
53408
  return resolver(getOrRegisterUrlVariable(variable));
53367
53409
  }
53410
+ if (isSwitchVariable(variable)) {
53411
+ let resolvedValue = isVariable(variable.value) ? resolveVariable(variable.value, client, taskContext, extras, resolver) : variable.value;
53412
+ if (isCondition(resolvedValue)) {
53413
+ resolvedValue = {
53414
+ ...resolvedValue,
53415
+ variable: resolveVariable(resolvedValue.variable, client, taskContext, extras, resolver)
53416
+ };
53417
+ }
53418
+ const resolvedValueMap = isVariable(variable.value_map) ? resolveVariable(variable.value_map, client, taskContext, extras, resolver) : variable.value_map;
53419
+ const resolvedDefault = isVariable(variable.default) ? resolveVariable(variable.default, client, taskContext, extras, resolver) : variable.default;
53420
+ return {
53421
+ type: "switch",
53422
+ uid: variable.uid,
53423
+ value: resolvedValue,
53424
+ value_map: resolvedValueMap,
53425
+ default: resolvedDefault
53426
+ };
53427
+ }
53368
53428
  return resolver(getOrRegisterPlainVariable(variable, client, taskContext, extras));
53369
53429
  }
53370
53430
  /*!
@@ -54257,6 +54317,46 @@ Inferred class string: "${iconClasses}."`
54257
54317
  function StoreProviders({ children }) {
54258
54318
  return /* @__PURE__ */ React__namespace.createElement(BackendStoreSync, null, /* @__PURE__ */ React__namespace.createElement(BrowserStoreSync, null, children));
54259
54319
  }
54320
+ function isConditionTrue(operator, value, other) {
54321
+ if (operator === ConditionOperator.EQUAL) {
54322
+ return value === other;
54323
+ }
54324
+ if (operator === ConditionOperator.NOT_EQUAL) {
54325
+ return value !== other;
54326
+ }
54327
+ if (operator === ConditionOperator.GREATER_EQUAL) {
54328
+ return value >= other;
54329
+ }
54330
+ if (operator === ConditionOperator.GREATER_THAN) {
54331
+ return value > other;
54332
+ }
54333
+ if (operator === ConditionOperator.LESS_EQUAL) {
54334
+ return value <= other;
54335
+ }
54336
+ if (operator === ConditionOperator.LESS_THAN) {
54337
+ return value < other;
54338
+ }
54339
+ if (operator === ConditionOperator.TRUTHY) {
54340
+ return !!value;
54341
+ }
54342
+ throw new Error(`Unexpected operator ${String(operator)} passed to conditional (If) component`);
54343
+ }
54344
+ function useConditionOrVariable(arg) {
54345
+ if (isCondition(arg)) {
54346
+ const value = useVariable(arg.variable)[0];
54347
+ const other = useVariable(arg.other)[0];
54348
+ return isConditionTrue(arg.operator, value, other);
54349
+ }
54350
+ return useVariable(arg)[0];
54351
+ }
54352
+ function useSwitchVariable(variable) {
54353
+ var _a;
54354
+ const value = useConditionOrVariable(variable.value);
54355
+ const [valueMap] = useVariable(variable.value_map);
54356
+ const [defaultValue] = useVariable(variable.default);
54357
+ const lookupKey = String(value);
54358
+ return (_a = valueMap[lookupKey]) != null ? _a : defaultValue;
54359
+ }
54260
54360
  function warnUpdateOnDerivedState() {
54261
54361
  console.warn("You tried to call update on variable with derived state, this is a noop and will be ignored.");
54262
54362
  }
@@ -54302,6 +54402,9 @@ Inferred class string: "${iconClasses}."`
54302
54402
  }, [urlValue]);
54303
54403
  return [urlValue, setUrlValue];
54304
54404
  }
54405
+ if (isSwitchVariable(variable)) {
54406
+ return [useSwitchVariable(variable), warnUpdateOnDerivedState];
54407
+ }
54305
54408
  const recoilState = getOrRegisterPlainVariable(variable, wsClient, taskContext, extras);
54306
54409
  if (!isDerivedVariable(variable.default)) {
54307
54410
  const [value, setValue] = Recoil_index_22(recoilState);
@@ -54907,6 +55010,9 @@ Inferred class string: "${iconClasses}."`
54907
55010
  ctx.extras,
54908
55011
  (v2) => ctx.snapshot.getLoadable(v2).getValue()
54909
55012
  );
55013
+ if (isResolvedSwitchVariable(resolved)) {
55014
+ throw new UserError("Switch variables are not supported in this context");
55015
+ }
54910
55016
  if (!shouldFetchVariable || !isDerivedVariable(variable) && !isDataVariable(variable) && !isDerivedDataVariable(variable)) {
54911
55017
  return resolved;
54912
55018
  }
@@ -55257,6 +55363,14 @@ Inferred class string: "${iconClasses}."`
55257
55363
  values: cleanedValues
55258
55364
  };
55259
55365
  }
55366
+ if (isResolvedSwitchVariable(value)) {
55367
+ return {
55368
+ ...value,
55369
+ value: cleanValue(value.value, force),
55370
+ value_map: cleanValue(value.value_map, force),
55371
+ default: cleanValue(value.default, force)
55372
+ };
55373
+ }
55260
55374
  return value;
55261
55375
  }
55262
55376
  function cleanKwargs(kwargs, force) {
@@ -56790,6 +56904,8 @@ Inferred class string: "${iconClasses}."`
56790
56904
  ctx.eventBus.publish("URL_VARIABLE_LOADED", { variable, value: variable.default });
56791
56905
  } else if (isDataVariable(variable))
56792
56906
  ;
56907
+ else if (isSwitchVariable(variable))
56908
+ ;
56793
56909
  else {
56794
56910
  const plainAtom = getOrRegisterPlainVariable(variable, ctx.wsClient, ctx.taskCtx, ctx.extras);
56795
56911
  ctx.reset(plainAtom);
@@ -82580,6 +82696,8 @@ Inferred class string: "${iconClasses}."`
82580
82696
  exports.injectCss = injectCss;
82581
82697
  exports.isActionImpl = isActionImpl;
82582
82698
  exports.isAnnotatedAction = isAnnotatedAction;
82699
+ exports.isCondition = isCondition;
82700
+ exports.isConditionTrue = isConditionTrue;
82583
82701
  exports.isDataVariable = isDataVariable;
82584
82702
  exports.isDerivedDataVariable = isDerivedDataVariable;
82585
82703
  exports.isDerivedVariable = isDerivedVariable;
@@ -82592,6 +82710,8 @@ Inferred class string: "${iconClasses}."`
82592
82710
  exports.isResolvedDataVariable = isResolvedDataVariable;
82593
82711
  exports.isResolvedDerivedDataVariable = isResolvedDerivedDataVariable;
82594
82712
  exports.isResolvedDerivedVariable = isResolvedDerivedVariable;
82713
+ exports.isResolvedSwitchVariable = isResolvedSwitchVariable;
82714
+ exports.isSwitchVariable = isSwitchVariable;
82595
82715
  exports.isTaskResponse = isTaskResponse$1;
82596
82716
  exports.isUrlVariable = isUrlVariable;
82597
82717
  exports.isVariable = isVariable;
@@ -82620,6 +82740,7 @@ Inferred class string: "${iconClasses}."`
82620
82740
  exports.useComponentRegistry = useComponentRegistry;
82621
82741
  exports.useComponentStyles = useComponentStyles;
82622
82742
  exports.useComponents = useComponents;
82743
+ exports.useConditionOrVariable = useConditionOrVariable;
82623
82744
  exports.useConfig = useConfig;
82624
82745
  exports.useDataVariable = useDataVariable;
82625
82746
  exports.useDeferLoadable = useDeferLoadable;
@@ -82632,6 +82753,7 @@ Inferred class string: "${iconClasses}."`
82632
82753
  exports.useRequestExtras = useRequestExtras;
82633
82754
  exports.useSession = useSession;
82634
82755
  exports.useSessionToken = useSessionToken;
82756
+ exports.useSwitchVariable = useSwitchVariable;
82635
82757
  exports.useTaskContext = useTaskContext;
82636
82758
  exports.useTemplate = useTemplate;
82637
82759
  exports.useTriggerIndex = useTriggerIndex;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dara-core
3
- Version: 1.16.23
3
+ Version: 1.17.0
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)
23
+ Requires-Dist: create-dara-app (==1.17.0)
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.23) ; extra == "all"
26
+ Requires-Dist: dara-components (==1.17.0) ; 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)
@@ -54,7 +54,7 @@ Description-Content-Type: text/markdown
54
54
 
55
55
  # Dara Application Framework
56
56
 
57
- <img src="https://github.com/causalens/dara/blob/v1.16.23/img/dara_light.svg?raw=true">
57
+ <img src="https://github.com/causalens/dara/blob/v1.17.0/img/dara_light.svg?raw=true">
58
58
 
59
59
  ![Master tests](https://github.com/causalens/dara/actions/workflows/tests.yml/badge.svg?branch=master)
60
60
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
@@ -99,7 +99,7 @@ source .venv/bin/activate
99
99
  dara start
100
100
  ```
101
101
 
102
- ![Dara App](https://github.com/causalens/dara/blob/v1.16.23/img/components_gallery.png?raw=true)
102
+ ![Dara App](https://github.com/causalens/dara/blob/v1.17.0/img/components_gallery.png?raw=true)
103
103
 
104
104
  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:
105
105
 
@@ -116,9 +116,9 @@ Explore some of our favorite apps - a great way of getting started and getting t
116
116
 
117
117
  | Dara App | Description |
118
118
  | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
119
- | ![Large Language Model](https://github.com/causalens/dara/blob/v1.16.23/img/llm.png?raw=true) | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
120
- | ![Plot Interactivity](https://github.com/causalens/dara/blob/v1.16.23/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 |
121
- | ![Graph Editor](https://github.com/causalens/dara/blob/v1.16.23/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. |
119
+ | ![Large Language Model](https://github.com/causalens/dara/blob/v1.17.0/img/llm.png?raw=true) | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
120
+ | ![Plot Interactivity](https://github.com/causalens/dara/blob/v1.17.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 |
121
+ | ![Graph Editor](https://github.com/causalens/dara/blob/v1.17.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. |
122
122
 
123
123
  Check out our [App Gallery](https://dara.causalens.com/gallery) for more inspiration!
124
124
 
@@ -145,9 +145,9 @@ And the supporting UI packages and tools.
145
145
  - `ui-utils` - miscellaneous utility functions
146
146
  - `ui-widgets` - widget components
147
147
 
148
- More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.16.23/CONTRIBUTING.md) file.
148
+ More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.17.0/CONTRIBUTING.md) file.
149
149
 
150
150
  ## License
151
151
 
152
- Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.16.23/LICENSE).
152
+ Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.17.0/LICENSE).
153
153
 
@@ -6,7 +6,7 @@ dara/core/auth/basic.py,sha256=IMkoC1OeeRmnmjIqPHpybs8zSdbLlNKYLRvj08ajirg,4692
6
6
  dara/core/auth/definitions.py,sha256=7SoUBlYQhccop4Rl0XlHjSBjBU41ZbCZHS7Y-fL9g84,3501
7
7
  dara/core/auth/routes.py,sha256=1gOe1Z2Vv5MtpW5uvUYvyYWTJ_BDoLRllji1Plk4nss,7180
8
8
  dara/core/auth/utils.py,sha256=sFQWbkclDi2842mhauTeLh5g5RsSrzS7AxlKSSnzhYM,7320
9
- dara/core/base_definitions.py,sha256=6PFJpeHADl518TgkFHrQ7U2442S2RM5fTKkpm6_pAcY,17839
9
+ dara/core/base_definitions.py,sha256=wNYPDUFHHhjE4gH2fm1z2wba04Lrl3-7JDua01j-zCs,18062
10
10
  dara/core/cli.py,sha256=hgzy8zrEB_JR2GF_nBBfs9d2I9LmjJt0OQJYOOCc284,8158
11
11
  dara/core/configuration.py,sha256=bWD9oL_lqCEq88I2Zr39cPt_8jZIwq5vUAyLFrsmVYQ,22297
12
12
  dara/core/css.py,sha256=KWCJTPpx4tTezP9VJO3k-RSVhWwomJr-4USoLz9vNAs,1771
@@ -14,18 +14,19 @@ dara/core/data_utils.py,sha256=5GGz4vk6srLO8HMySiAEk_xZCvmf0SeTTgVi-N4qaKY,12636
14
14
  dara/core/defaults.py,sha256=UkjWwIbo8GLyn8RZRoD3cJe174-3wHZYzvMB0hQCnMg,4150
15
15
  dara/core/definitions.py,sha256=KmJdLWwurLBRZonPz94N9947cG1jMrs-wzqtof6ubK0,17762
16
16
  dara/core/http.py,sha256=LR1Kr5Hca-Z6klNl-M8R8Q1eOfFh3hLrjVS3kVrRsKA,4658
17
- dara/core/interactivity/__init__.py,sha256=zZp4wO6nHZ_-IzDtlv8D5mjlutn4bckqIiSCaPoCYZw,2326
18
- dara/core/interactivity/actions.py,sha256=yMw8S11wj4J2HVmDsmedh9D0IZFL9hxw6WGNj7X2f6M,48198
17
+ dara/core/interactivity/__init__.py,sha256=4gD69m5nRf6lefKdNx9K_fRJOqmsAwjALZqKiPtvutk,2415
18
+ dara/core/interactivity/actions.py,sha256=niNMt-IE_YXTtLpYrueTNCT60KelmNCGpUn7-WTVDQ4,48020
19
19
  dara/core/interactivity/any_data_variable.py,sha256=1dLLxLuDErRsgaFPSTXxZHvpVucKKty90twQE6N-_NI,5286
20
20
  dara/core/interactivity/any_variable.py,sha256=LOGhbDdYffujlRxF4LR9ZuWdai03R-EXuGsTEJAwfo0,13544
21
- dara/core/interactivity/condition.py,sha256=q_RDDt-DtZEUQL054Mc7zHyJIJIGACljJ2gOFygCHQc,1309
21
+ dara/core/interactivity/condition.py,sha256=69tdxgLqNbU-AlwHTWFdp-hvxGDugNnb_McDrz0mDtE,1575
22
22
  dara/core/interactivity/data_variable.py,sha256=pvPOx6SMxHWDxoo5Ea5xqLwrBTrWN68x8lnBiblYSGg,11760
23
23
  dara/core/interactivity/derived_data_variable.py,sha256=u2HOts5rtmzK3D1K383YfYYQnb4pHZF3rTu1lfwMpPA,15323
24
24
  dara/core/interactivity/derived_variable.py,sha256=x_vdt8hvgubDEISX6GfpR5CS7n2Vxq3oZIkVd8Daiw0,21927
25
25
  dara/core/interactivity/filtering.py,sha256=BZsWsQvXPhn6WUoAFpAtgN6hXlGDudPbi4h97Qao2ic,9197
26
26
  dara/core/interactivity/loop_variable.py,sha256=EqZX3bMCwKmI2Yu4pQ7TJG9hf3PY2AlAIBLxEzHFbTw,2998
27
27
  dara/core/interactivity/non_data_variable.py,sha256=IMH5cNce2O6RUbu4HB_VLT-BBnDnGHr3lp09XU_lWa4,2378
28
- dara/core/interactivity/plain_variable.py,sha256=yCm-gBoxNiWsJ43KS4Fr7WCPXIOaChn8FCjXh8IWMUA,9592
28
+ dara/core/interactivity/plain_variable.py,sha256=ZIg9LhgfKEY1mfcWwysAXzeMRbVoFF61OYHt_od-9n0,9248
29
+ dara/core/interactivity/switch_variable.py,sha256=mC91kBoZON3JGBczv-YPvA5A6zY0mKqEr_7d3Sa_YMg,14194
29
30
  dara/core/interactivity/url_variable.py,sha256=1ZPFHO3gq7ZuATHt4S9x5ijmk4GBVlUv5KJN6DkM49w,4357
30
31
  dara/core/internal/__init__.py,sha256=QN0wbG9HPQ_vXh8BO8DnBXeYLIENVTNtRmYzZf1lx7c,577
31
32
  dara/core/internal/cache_store/__init__.py,sha256=7JCmQwNIMzfyLZGnsk0BbT1EdDFO_PRZz86E9ATcC6c,139
@@ -36,7 +37,7 @@ dara/core/internal/cache_store/lru.py,sha256=V0KkpsU9oJ_vJ2j7iKFTxxihF1zCxVTvKBf
36
37
  dara/core/internal/cache_store/ttl.py,sha256=7zcVYR9a2M5qOTWuhor-TgKBKERPn6qsZsDnoUyazfM,4755
37
38
  dara/core/internal/cgroup.py,sha256=o1Qqn5LV3TOPK7lQ0xbQj01zYT1pv-X_edGx8VhFvEw,3218
38
39
  dara/core/internal/custom_response.py,sha256=aSPonh8AdRjioTk1MaPwdaJHkO4R4aG_osGAGCIHbtg,1341
39
- dara/core/internal/dependency_resolution.py,sha256=y0CUop-RNNhwZLbafeqjhNEiTY549mLvDyNMTfvEl2Q,5456
40
+ dara/core/internal/dependency_resolution.py,sha256=YoDPvnEM5HXSJ1asrX8zrgceqj6EM1o5YVSWaFE8zUc,11161
40
41
  dara/core/internal/devtools.py,sha256=YmJdYKEqfxrpXFYAM5sJ2wUtUhfvaCMbb9PuDcqFdt4,2441
41
42
  dara/core/internal/download.py,sha256=2_fNIWDsV9f0BptTQRhBuIFXOO4th9pcYaIB7lsPcJU,3183
42
43
  dara/core/internal/encoder_registry.py,sha256=tq2ka9RjStEFsxTdsWF6kSOU7rycggEq8YfDlzSyzKk,11277
@@ -76,13 +77,13 @@ dara/core/js_tooling/templates/dara.config.json,sha256=RZG_R_xJv_5rYIoz2QZulcG49
76
77
  dara/core/js_tooling/templates/vite.config.template.ts,sha256=-_GgbqPAcg_rTL2JsWjqYqZa4w6p2fKa2nyqGnjqj18,888
77
78
  dara/core/log_configs/logging.yaml,sha256=YJyD18psAmSVz6587dcEOyoulLuRFFu1g8yMXl1ylM0,706
78
79
  dara/core/logging.py,sha256=HiEbksFvlsSunlJd7wZrYrhKzR0Y4il0982rIwg5wOo,13099
79
- dara/core/main.py,sha256=7bsmIPzjhnJx6RjcOaFTMKfxRBvnx8RjLjsH5xaQMQc,18326
80
+ dara/core/main.py,sha256=LxWLueYonVMnstz8JnpiGACEvB8LXl75JGqfUCnmSEM,18382
80
81
  dara/core/metrics/__init__.py,sha256=2UqpWHv-Ie58QLJIHJ9Szfjq8xifAuwy5FYGUIFwWtI,823
81
82
  dara/core/metrics/cache.py,sha256=bGXwjO_rSc-FkS3PnPi1mvIZf1x-hvmG113dAUk1g-Y,2616
82
83
  dara/core/metrics/runtime.py,sha256=YP-6Dz0GeI9_Yr7bUk_-OqShyFySGH_AKpDO126l6es,1833
83
84
  dara/core/metrics/utils.py,sha256=aKaa_hskV3M3h4xOGZYvegDLq_OWOEUlslkQKrrPQiI,2281
84
85
  dara/core/persistence.py,sha256=-lnksbGfHwabeRjbJkceoFd0kHfCu4gK2fud8dkeDTg,18538
85
- dara/core/umd/dara.core.umd.js,sha256=dHJvK0B3dARTocGdCvCuOVZAx4qHwwviYwX4EaxrwT4,4725934
86
+ dara/core/umd/dara.core.umd.js,sha256=M-6KfeAB33xEd0BzlZInksx5Ch0IK6B5cXwSg1E-gPc,4730555
86
87
  dara/core/umd/style.css,sha256=YQtQ4veiSktnyONl0CU1iU1kKfcQhreH4iASi1MP7Ak,4095007
87
88
  dara/core/visual/__init__.py,sha256=QN0wbG9HPQ_vXh8BO8DnBXeYLIENVTNtRmYzZf1lx7c,577
88
89
  dara/core/visual/components/__init__.py,sha256=eg6M2YcH1o-I2H3IqLaD32ExfZcXhKuH5KTM52Ow6RM,2359
@@ -106,8 +107,8 @@ dara/core/visual/themes/__init__.py,sha256=aM4mgoIYo2neBSw5FRzswsht7PUKjLthiHLmF
106
107
  dara/core/visual/themes/dark.py,sha256=UQGDooOc8ric73eHs9E0ltYP4UCrwqQ3QxqN_fb4PwY,1942
107
108
  dara/core/visual/themes/definitions.py,sha256=nS_gQvOzCt5hTmj74d0_siq_9QWuj6wNuir4VCHy0Dk,2779
108
109
  dara/core/visual/themes/light.py,sha256=-Tviq8oEwGbdFULoDOqPuHO0UpAZGsBy8qFi0kAGolQ,1944
109
- dara_core-1.16.23.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
110
- dara_core-1.16.23.dist-info/METADATA,sha256=nkpL_PljY5X2YcGsCF4jMTmvKJ8iIcfD2tbrjlsD6Rk,7507
111
- dara_core-1.16.23.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
112
- dara_core-1.16.23.dist-info/entry_points.txt,sha256=H__D5sNIGuPIhVam0DChNL-To5k8Y7nY7TAFz9Mz6cc,139
113
- dara_core-1.16.23.dist-info/RECORD,,
110
+ dara_core-1.17.0.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
111
+ dara_core-1.17.0.dist-info/METADATA,sha256=EKazJEN18CnxMiVy52kYaZwHlRCYVDRKse9RQmDpT4U,7497
112
+ dara_core-1.17.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
113
+ dara_core-1.17.0.dist-info/entry_points.txt,sha256=H__D5sNIGuPIhVam0DChNL-To5k8Y7nY7TAFz9Mz6cc,139
114
+ dara_core-1.17.0.dist-info/RECORD,,