dara-core 1.15.6a1__py3-none-any.whl → 1.16.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.
- dara/core/__init__.py +16 -27
- dara/core/auth/base.py +3 -2
- dara/core/auth/definitions.py +0 -3
- dara/core/auth/utils.py +1 -1
- dara/core/base_definitions.py +122 -65
- dara/core/cli.py +12 -0
- dara/core/configuration.py +5 -8
- dara/core/defaults.py +0 -3
- dara/core/definitions.py +95 -231
- dara/core/interactivity/__init__.py +12 -18
- dara/core/interactivity/actions.py +22 -19
- dara/core/interactivity/any_data_variable.py +2 -4
- dara/core/interactivity/any_variable.py +10 -2
- dara/core/interactivity/condition.py +7 -10
- dara/core/interactivity/data_variable.py +11 -12
- dara/core/interactivity/derived_data_variable.py +7 -7
- dara/core/interactivity/derived_variable.py +20 -17
- dara/core/interactivity/filtering.py +1 -1
- dara/core/interactivity/plain_variable.py +53 -6
- dara/core/interactivity/url_variable.py +7 -6
- dara/core/internal/download.py +1 -1
- dara/core/internal/encoder_registry.py +14 -0
- dara/core/internal/hashing.py +1 -1
- dara/core/internal/normalization.py +0 -24
- dara/core/internal/routing.py +10 -10
- dara/core/internal/scheduler.py +3 -2
- dara/core/internal/settings.py +2 -4
- dara/core/internal/store.py +0 -3
- dara/core/internal/tasks.py +2 -2
- dara/core/internal/websocket.py +29 -20
- dara/core/js_tooling/js_utils.py +1 -1
- dara/core/main.py +2 -2
- dara/core/persistence.py +12 -4
- dara/core/umd/dara.core.umd.js +13 -277
- dara/core/visual/components/__init__.py +0 -3
- dara/core/visual/components/fallback.py +3 -3
- dara/core/visual/components/invalid_component.py +3 -3
- dara/core/visual/components/menu.py +3 -3
- dara/core/visual/components/progress_tracker.py +3 -2
- dara/core/visual/components/raw_string.py +3 -3
- dara/core/visual/components/router_content.py +3 -3
- dara/core/visual/components/sidebar_frame.py +3 -3
- dara/core/visual/components/topbar_frame.py +3 -3
- dara/core/visual/css/__init__.py +2 -6
- dara/core/visual/dynamic_component.py +3 -6
- {dara_core-1.15.6a1.dist-info → dara_core-1.16.0.dist-info}/METADATA +13 -12
- {dara_core-1.15.6a1.dist-info → dara_core-1.16.0.dist-info}/RECORD +50 -51
- dara/core/visual/components/for_cmp.py +0 -150
- {dara_core-1.15.6a1.dist-info → dara_core-1.16.0.dist-info}/LICENSE +0 -0
- {dara_core-1.15.6a1.dist-info → dara_core-1.16.0.dist-info}/WHEEL +0 -0
- {dara_core-1.15.6a1.dist-info → dara_core-1.16.0.dist-info}/entry_points.txt +0 -0
dara/core/definitions.py
CHANGED
|
@@ -25,8 +25,8 @@ from typing import (
|
|
|
25
25
|
Awaitable,
|
|
26
26
|
Callable,
|
|
27
27
|
ClassVar,
|
|
28
|
-
Generic,
|
|
29
28
|
List,
|
|
29
|
+
Literal,
|
|
30
30
|
Mapping,
|
|
31
31
|
Optional,
|
|
32
32
|
Protocol,
|
|
@@ -38,15 +38,16 @@ from typing import (
|
|
|
38
38
|
|
|
39
39
|
from fastapi.encoders import jsonable_encoder
|
|
40
40
|
from fastapi.params import Depends
|
|
41
|
-
from pydantic import
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
DaraBaseModel,
|
|
48
|
-
TemplateMarker,
|
|
41
|
+
from pydantic import (
|
|
42
|
+
ConfigDict,
|
|
43
|
+
Field,
|
|
44
|
+
SerializerFunctionWrapHandler,
|
|
45
|
+
field_validator,
|
|
46
|
+
model_serializer,
|
|
49
47
|
)
|
|
48
|
+
|
|
49
|
+
from dara.core.base_definitions import Action, ComponentType
|
|
50
|
+
from dara.core.base_definitions import DaraBaseModel as BaseModel
|
|
50
51
|
from dara.core.css import CSSProperties
|
|
51
52
|
from dara.core.interactivity import AnyVariable
|
|
52
53
|
|
|
@@ -62,7 +63,7 @@ class HttpMethod(Enum):
|
|
|
62
63
|
|
|
63
64
|
|
|
64
65
|
class Session(BaseModel):
|
|
65
|
-
session_id: Optional[str]
|
|
66
|
+
session_id: Optional[str] = None
|
|
66
67
|
|
|
67
68
|
|
|
68
69
|
DEFAULT_ERROR_TITLE = 'Unexpected error occurred'
|
|
@@ -79,74 +80,25 @@ class ErrorHandlingConfig(BaseModel):
|
|
|
79
80
|
raw_css: Optional[Union[CSSProperties, dict, str]] = None
|
|
80
81
|
"""Raw styling to apply to the displayed error boundary"""
|
|
81
82
|
|
|
82
|
-
def
|
|
83
|
-
result = super().
|
|
83
|
+
def model_dump(self, *args, **kwargs):
|
|
84
|
+
result = super().model_dump(*args, **kwargs)
|
|
84
85
|
|
|
85
86
|
# Exclude raw_css if not set
|
|
86
87
|
if 'raw_css' in result and result.get('raw_css') is None:
|
|
87
88
|
result.pop('raw_css')
|
|
88
89
|
elif isinstance(self.raw_css, CSSProperties):
|
|
89
90
|
# If it's an instance of CSSProperties, serialize but exclude none
|
|
90
|
-
result['raw_css'] = self.raw_css.
|
|
91
|
+
result['raw_css'] = self.raw_css.model_dump(exclude_none=True)
|
|
91
92
|
|
|
92
93
|
return result
|
|
93
94
|
|
|
94
95
|
|
|
95
|
-
class
|
|
96
|
-
"""
|
|
97
|
-
Creates a TemplateMarker instance for a given field name when accessing
|
|
98
|
-
any attribute on the instance. Should not be used standalone, it is injected
|
|
99
|
-
into function parameters when using the @template decorator.
|
|
100
|
-
|
|
101
|
-
Example
|
|
102
|
-
|
|
103
|
-
```python
|
|
104
|
-
from dara.core.definitions import TemplateMarkerCreator
|
|
105
|
-
|
|
106
|
-
creator = TemplateMarkerCreator()
|
|
107
|
-
creator.foo
|
|
108
|
-
# <TemplateMarker field_name='foo'>
|
|
109
|
-
```
|
|
110
|
-
"""
|
|
111
|
-
|
|
112
|
-
def __getattribute__(self, __name: str) -> Any:
|
|
113
|
-
return TemplateMarker(field_name=__name)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
class BaseFallback(DaraBaseModel):
|
|
117
|
-
suspend_render: Union[bool, int] = 200
|
|
118
|
-
"""
|
|
119
|
-
:param suspend_render: bool or int, optional
|
|
120
|
-
Determines the suspense behavior of the component during state updates.
|
|
121
|
-
|
|
122
|
-
- If True, the component will always use suspense during state updates.
|
|
123
|
-
This means the component will suspend rendering and show a fallback UI until the new state is ready.
|
|
124
|
-
|
|
125
|
-
- If False, the component will always show the previous state while loading the new state.
|
|
126
|
-
This means the component will never suspend during state updates. The fallback UI will only
|
|
127
|
-
be shown on the first render.
|
|
128
|
-
|
|
129
|
-
- If a positive integer (default is 200), this denotes the threshold in milliseconds.
|
|
130
|
-
The component will show the previous state while loading the new state,
|
|
131
|
-
but will suspend and show a fallback UI after the given timeout if the new state is not ready.
|
|
132
|
-
"""
|
|
133
|
-
|
|
134
|
-
@validator('suspend_render')
|
|
135
|
-
@classmethod
|
|
136
|
-
def validate_suspend_render(cls, value):
|
|
137
|
-
if isinstance(value, int):
|
|
138
|
-
if value < 0:
|
|
139
|
-
raise ValueError('suspend_render must be a positive integer')
|
|
140
|
-
|
|
141
|
-
return value
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
class ComponentInstance(DaraBaseModel):
|
|
96
|
+
class ComponentInstance(BaseModel):
|
|
145
97
|
"""
|
|
146
98
|
Definition of a Component Instance
|
|
147
99
|
"""
|
|
148
100
|
|
|
149
|
-
uid: Optional[str] =
|
|
101
|
+
uid: Optional[str] = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
150
102
|
|
|
151
103
|
js_module: ClassVar[Optional[str]] = None
|
|
152
104
|
"""
|
|
@@ -191,9 +143,6 @@ class ComponentInstance(DaraBaseModel):
|
|
|
191
143
|
```
|
|
192
144
|
"""
|
|
193
145
|
|
|
194
|
-
templated: bool = False
|
|
195
|
-
"""Whether the component is templated, created by the @template decorator"""
|
|
196
|
-
|
|
197
146
|
track_progress: Optional[bool] = False
|
|
198
147
|
"""Whether to use ProgressTracker to display progress updates from a task the component is subscribed to"""
|
|
199
148
|
|
|
@@ -219,18 +168,10 @@ class ComponentInstance(DaraBaseModel):
|
|
|
219
168
|
This has no runtime effect and are intended to help identify components with human-readable names in the serialized trees, not in the DOM
|
|
220
169
|
"""
|
|
221
170
|
|
|
222
|
-
def __init__(self, *args, **kwargs):
|
|
223
|
-
uid = kwargs.get('uid', None)
|
|
224
|
-
if uid is None:
|
|
225
|
-
uid = str(uuid.uuid4())
|
|
226
|
-
kwargs = {**kwargs, 'uid': uid}
|
|
227
|
-
|
|
228
|
-
super().__init__(*args, **kwargs)
|
|
229
|
-
|
|
230
171
|
def __repr__(self):
|
|
231
172
|
return '__dara__' + json.dumps(jsonable_encoder(self))
|
|
232
173
|
|
|
233
|
-
@
|
|
174
|
+
@field_validator('raw_css', mode='before')
|
|
234
175
|
@classmethod
|
|
235
176
|
def parse_css(cls, css: Optional[Union[CSSProperties, dict, str]]):
|
|
236
177
|
# If it's a plain dict, change kebab case to camel case
|
|
@@ -244,8 +185,14 @@ class ComponentInstance(DaraBaseModel):
|
|
|
244
185
|
|
|
245
186
|
return css
|
|
246
187
|
|
|
247
|
-
|
|
248
|
-
|
|
188
|
+
@classmethod
|
|
189
|
+
def isinstance(cls, obj: Any) -> bool:
|
|
190
|
+
return isinstance(obj, cls)
|
|
191
|
+
|
|
192
|
+
@model_serializer(mode='wrap')
|
|
193
|
+
def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
|
|
194
|
+
props = nxt(self)
|
|
195
|
+
|
|
249
196
|
props.pop('uid')
|
|
250
197
|
|
|
251
198
|
# Exclude raw_css if not set
|
|
@@ -253,7 +200,7 @@ class ComponentInstance(DaraBaseModel):
|
|
|
253
200
|
props.pop('raw_css')
|
|
254
201
|
elif isinstance(self.raw_css, CSSProperties):
|
|
255
202
|
# If it's an instance of CSSProperties, serialize but exclude none
|
|
256
|
-
props['raw_css'] = self.raw_css.
|
|
203
|
+
props['raw_css'] = self.raw_css.model_dump(exclude_none=True)
|
|
257
204
|
|
|
258
205
|
# Exclude track_progress if not set
|
|
259
206
|
if 'track_progress' in props and props.get('track_progress') is False:
|
|
@@ -263,10 +210,6 @@ class ComponentInstance(DaraBaseModel):
|
|
|
263
210
|
if 'error_handler' in props and props.get('error_handler') is None:
|
|
264
211
|
props.pop('error_handler')
|
|
265
212
|
|
|
266
|
-
# Exclude template if not set
|
|
267
|
-
if 'templated' in props and props.get('templated') is False:
|
|
268
|
-
props.pop('templated')
|
|
269
|
-
|
|
270
213
|
# Exclude fallback if not set
|
|
271
214
|
if 'fallback' in props and props.get('fallback') is None:
|
|
272
215
|
props.pop('fallback')
|
|
@@ -311,6 +254,26 @@ def discover(outer_obj: DiscoverT) -> DiscoverT:
|
|
|
311
254
|
return outer_obj
|
|
312
255
|
|
|
313
256
|
|
|
257
|
+
AlignItems = Literal[
|
|
258
|
+
'-moz-initial',
|
|
259
|
+
'baseline',
|
|
260
|
+
'center',
|
|
261
|
+
'end',
|
|
262
|
+
'flex-end',
|
|
263
|
+
'flex-start',
|
|
264
|
+
'inherit',
|
|
265
|
+
'initial',
|
|
266
|
+
'normal',
|
|
267
|
+
'revert',
|
|
268
|
+
'self-end',
|
|
269
|
+
'self-start',
|
|
270
|
+
'start',
|
|
271
|
+
'stretch',
|
|
272
|
+
'unset',
|
|
273
|
+
None,
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
|
|
314
277
|
class StyledComponentInstance(ComponentInstance):
|
|
315
278
|
"""
|
|
316
279
|
Base class for a component implementing the common styling props
|
|
@@ -341,13 +304,13 @@ class StyledComponentInstance(ComponentInstance):
|
|
|
341
304
|
:param width: the width of the component, can be an number, which will be converted to pixels, or a string
|
|
342
305
|
"""
|
|
343
306
|
|
|
344
|
-
align: Optional[
|
|
307
|
+
align: Optional[AlignItems] = None
|
|
345
308
|
background: Optional[str] = None
|
|
346
309
|
basis: Optional[Union[int, str, bool]] = None
|
|
347
310
|
bold: bool = False
|
|
348
311
|
border: Optional[str] = None
|
|
349
312
|
border_radius: Optional[Union[float, int, str]] = None
|
|
350
|
-
children: Optional[List[
|
|
313
|
+
children: Optional[List[ComponentInstance]] = None
|
|
351
314
|
color: Optional[str] = None
|
|
352
315
|
font: Optional[str] = None
|
|
353
316
|
font_size: Optional[str] = None
|
|
@@ -368,10 +331,7 @@ class StyledComponentInstance(ComponentInstance):
|
|
|
368
331
|
underline: bool = False
|
|
369
332
|
width: Optional[Union[float, int, str]] = None
|
|
370
333
|
|
|
371
|
-
|
|
372
|
-
smart_union = True
|
|
373
|
-
|
|
374
|
-
@validator(
|
|
334
|
+
@field_validator(
|
|
375
335
|
'height',
|
|
376
336
|
'basis',
|
|
377
337
|
'border_radius',
|
|
@@ -383,7 +343,7 @@ class StyledComponentInstance(ComponentInstance):
|
|
|
383
343
|
'min_height',
|
|
384
344
|
'padding',
|
|
385
345
|
'width',
|
|
386
|
-
|
|
346
|
+
mode='before',
|
|
387
347
|
)
|
|
388
348
|
@classmethod
|
|
389
349
|
def validate_dimension(cls, value):
|
|
@@ -392,7 +352,7 @@ class StyledComponentInstance(ComponentInstance):
|
|
|
392
352
|
|
|
393
353
|
return value
|
|
394
354
|
|
|
395
|
-
@
|
|
355
|
+
@field_validator('grow', 'shrink', mode='before')
|
|
396
356
|
@classmethod
|
|
397
357
|
def validate_flex(cls, value):
|
|
398
358
|
if isinstance(value, (bool)):
|
|
@@ -400,7 +360,7 @@ class StyledComponentInstance(ComponentInstance):
|
|
|
400
360
|
|
|
401
361
|
return value
|
|
402
362
|
|
|
403
|
-
@
|
|
363
|
+
@field_validator('children', mode='before')
|
|
404
364
|
@classmethod
|
|
405
365
|
def validate_children(cls, children):
|
|
406
366
|
# Filter out None children
|
|
@@ -409,13 +369,44 @@ class StyledComponentInstance(ComponentInstance):
|
|
|
409
369
|
return children
|
|
410
370
|
|
|
411
371
|
|
|
372
|
+
class BaseFallback(StyledComponentInstance):
|
|
373
|
+
suspend_render: Union[bool, int] = 200
|
|
374
|
+
"""
|
|
375
|
+
:param suspend_render: bool or int, optional
|
|
376
|
+
Determines the suspense behavior of the component during state updates.
|
|
377
|
+
|
|
378
|
+
- If True, the component will always use suspense during state updates.
|
|
379
|
+
This means the component will suspend rendering and show a fallback UI until the new state is ready.
|
|
380
|
+
|
|
381
|
+
- If False, the component will always show the previous state while loading the new state.
|
|
382
|
+
This means the component will never suspend during state updates. The fallback UI will only
|
|
383
|
+
be shown on the first render.
|
|
384
|
+
|
|
385
|
+
- If a positive integer (default is 200), this denotes the threshold in milliseconds.
|
|
386
|
+
The component will show the previous state while loading the new state,
|
|
387
|
+
but will suspend and show a fallback UI after the given timeout if the new state is not ready.
|
|
388
|
+
"""
|
|
389
|
+
|
|
390
|
+
@field_validator('suspend_render')
|
|
391
|
+
@classmethod
|
|
392
|
+
def validate_suspend_render(cls, value):
|
|
393
|
+
if isinstance(value, int):
|
|
394
|
+
if value < 0:
|
|
395
|
+
raise ValueError('suspend_render must be a positive integer')
|
|
396
|
+
|
|
397
|
+
return value
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
ComponentInstance.model_rebuild()
|
|
401
|
+
|
|
402
|
+
|
|
412
403
|
ComponentInstanceType = Union[ComponentInstance, Callable[..., ComponentInstance]]
|
|
413
404
|
|
|
414
405
|
|
|
415
406
|
class JsComponentDef(BaseModel):
|
|
416
407
|
"""Definition of a JS Component"""
|
|
417
408
|
|
|
418
|
-
js_module: Optional[str]
|
|
409
|
+
js_module: Optional[str] = None
|
|
419
410
|
"""
|
|
420
411
|
JS module where the component implementation lives.
|
|
421
412
|
|
|
@@ -433,13 +424,13 @@ class JsComponentDef(BaseModel):
|
|
|
433
424
|
name: str
|
|
434
425
|
"""Name of the component, must match the Python definition and JS implementation"""
|
|
435
426
|
|
|
436
|
-
type:
|
|
427
|
+
type: Literal[ComponentType.JS] = ComponentType.JS
|
|
437
428
|
|
|
438
429
|
|
|
439
430
|
class PyComponentDef(BaseModel):
|
|
440
431
|
"""Definition of a Python Component"""
|
|
441
432
|
|
|
442
|
-
func: Optional[Callable[..., Any]]
|
|
433
|
+
func: Optional[Callable[..., Any]] = None
|
|
443
434
|
name: str
|
|
444
435
|
dynamic_kwargs: Optional[Mapping[str, AnyVariable]] = None
|
|
445
436
|
fallback: Optional[BaseFallback] = None
|
|
@@ -447,126 +438,7 @@ class PyComponentDef(BaseModel):
|
|
|
447
438
|
render_component: Callable[..., Awaitable[Any]]
|
|
448
439
|
"""Handler to render the component. Defaults to dara.core.visual.dynamic_component.render_component"""
|
|
449
440
|
|
|
450
|
-
type:
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
ComponentT = TypeVar('ComponentT', bound=ComponentInstance)
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
class TemplateComponentDef(GenericModel, Generic[ComponentT]):
|
|
457
|
-
"""
|
|
458
|
-
Definition of a Template Component, as returned by the @template decorator.
|
|
459
|
-
When called, executes the decorated function with the marker creator injected and returns a component instance,
|
|
460
|
-
annotated with `templated=True`.
|
|
461
|
-
"""
|
|
462
|
-
|
|
463
|
-
func: Callable[..., ComponentT]
|
|
464
|
-
name: str
|
|
465
|
-
|
|
466
|
-
def __call__(self, *args, **kwds) -> ComponentT:
|
|
467
|
-
creator = TemplateMarkerCreator()
|
|
468
|
-
result = self.func(creator, *args, **kwds)
|
|
469
|
-
result.templated = True
|
|
470
|
-
return result
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
class template(Generic[ComponentT]):
|
|
474
|
-
"""
|
|
475
|
-
The @template decorator is used to define reusable components that can be
|
|
476
|
-
dynamically rendered based on input data. It allows you to create template
|
|
477
|
-
components with customizable properties that can be injected into certain
|
|
478
|
-
components equipped to handle such templates.
|
|
479
|
-
|
|
480
|
-
Usage:
|
|
481
|
-
------
|
|
482
|
-
|
|
483
|
-
1. Define a template component using the `@template` decorator and provide a
|
|
484
|
-
function that accepts a first argument of type `template.Value`.
|
|
485
|
-
This argument is automatically injected by the decorator, and its name can be
|
|
486
|
-
chosen by the user. Inside the function, use the first argument to access
|
|
487
|
-
properties that will be injected into the component when rendered.
|
|
488
|
-
|
|
489
|
-
Example:
|
|
490
|
-
|
|
491
|
-
```
|
|
492
|
-
from dara.core import template
|
|
493
|
-
from dara_dashboarding_extension import Card, Heading, Text, Button
|
|
494
|
-
|
|
495
|
-
@template
|
|
496
|
-
def ExperimentCard(data: template.Value):
|
|
497
|
-
return Card(
|
|
498
|
-
Heading(data.title, level=2),
|
|
499
|
-
Text(data.description),
|
|
500
|
-
Button(data.button_text, raw_css={'backgroundColor': data.button_color}),
|
|
501
|
-
...
|
|
502
|
-
)
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
2. Use the template component in certain components that can handle such
|
|
506
|
-
templates, like the `For` component.
|
|
507
|
-
|
|
508
|
-
Example:
|
|
509
|
-
|
|
510
|
-
```
|
|
511
|
-
from dara.core import Variable, For
|
|
512
|
-
|
|
513
|
-
var = Variable([
|
|
514
|
-
{
|
|
515
|
-
'title': 'Experiment 1',
|
|
516
|
-
'description': 'This is a description of experiment 1',
|
|
517
|
-
'button_text': 'Run Experiment 1',
|
|
518
|
-
'button_color': 'red',
|
|
519
|
-
},
|
|
520
|
-
...
|
|
521
|
-
])
|
|
522
|
-
|
|
523
|
-
For(
|
|
524
|
-
key_accessor='title',
|
|
525
|
-
data=var,
|
|
526
|
-
template=ExperimentCard(),
|
|
527
|
-
)
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
The `For` component will dynamically render elements according to the
|
|
531
|
-
template, using the properties specified by the `template.Value`. As opposed
|
|
532
|
-
to `py_component`, the decorated component only executes once, when called.
|
|
533
|
-
|
|
534
|
-
Note that only certain components are equipped to handle template components,
|
|
535
|
-
so refer to the component documentation for compatibility information.
|
|
536
|
-
"""
|
|
537
|
-
|
|
538
|
-
Value = TemplateMarkerCreator
|
|
539
|
-
"""
|
|
540
|
-
The `template.Value` is a special class used with the @template decorator to
|
|
541
|
-
define dynamic properties that will be injected into a template component
|
|
542
|
-
when it is rendered.
|
|
543
|
-
|
|
544
|
-
When defining a template component, use the `template.Value` as the type
|
|
545
|
-
for the first argument of the function. This argument is automatically
|
|
546
|
-
injected by the `@template` decorator, and its name can be chosen by the user.
|
|
547
|
-
Access the properties of the Value within the function to create
|
|
548
|
-
dynamic components based on the injected data.
|
|
549
|
-
|
|
550
|
-
Example usage:
|
|
551
|
-
|
|
552
|
-
```
|
|
553
|
-
from dara.core import template
|
|
554
|
-
from dara_dashboarding_extension import Card, Heading, Text, Button
|
|
555
|
-
|
|
556
|
-
@template
|
|
557
|
-
def ExperimentCard(data: template.Value):
|
|
558
|
-
return Card(
|
|
559
|
-
Heading(data.title, level=2),
|
|
560
|
-
Text(data.description),
|
|
561
|
-
Button(data.button_text, raw_css={'backgroundColor': data.button_color}),
|
|
562
|
-
...
|
|
563
|
-
)
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
"""
|
|
567
|
-
|
|
568
|
-
def __new__(cls, func: Callable[..., ComponentT]) -> TemplateComponentDef[ComponentT]: # type: ignore
|
|
569
|
-
return TemplateComponentDef(func=func, name=func.__name__)
|
|
441
|
+
type: Literal[ComponentType.PY] = ComponentType.PY
|
|
570
442
|
|
|
571
443
|
|
|
572
444
|
# Helper type annotation for working with components
|
|
@@ -593,9 +465,7 @@ class ApiRoute(BaseModel):
|
|
|
593
465
|
handler: Callable
|
|
594
466
|
method: HttpMethod
|
|
595
467
|
url: str
|
|
596
|
-
|
|
597
|
-
class Config:
|
|
598
|
-
arbitrary_types_allowed = True
|
|
468
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
599
469
|
|
|
600
470
|
def __hash__(self):
|
|
601
471
|
return hash((self.handler, self.method, self.url, tuple(self.dependencies)))
|
|
@@ -604,37 +474,31 @@ class ApiRoute(BaseModel):
|
|
|
604
474
|
class Page(BaseModel):
|
|
605
475
|
"""Definition of a Page"""
|
|
606
476
|
|
|
607
|
-
icon: Optional[str]
|
|
477
|
+
icon: Optional[str] = None
|
|
608
478
|
content: ComponentInstanceType
|
|
609
479
|
name: str
|
|
610
480
|
sub_pages: Optional[List['Page']] = []
|
|
611
481
|
url_safe_name: str
|
|
612
|
-
include_in_menu: Optional[bool]
|
|
482
|
+
include_in_menu: Optional[bool] = None
|
|
613
483
|
on_load: Optional[Action] = None
|
|
614
|
-
|
|
615
|
-
class Config:
|
|
616
|
-
extra = 'forbid'
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
# This is required by pydantic to support the self referential type subpages.
|
|
620
|
-
Page.update_forward_refs()
|
|
484
|
+
model_config = ConfigDict(extra='forbid')
|
|
621
485
|
|
|
622
486
|
|
|
623
487
|
class TemplateRoute(BaseModel):
|
|
624
488
|
"""Definition of a route for the TemplateRouter"""
|
|
625
489
|
|
|
626
490
|
content: ComponentInstance
|
|
627
|
-
icon: Optional[str]
|
|
491
|
+
icon: Optional[str] = None
|
|
628
492
|
name: str
|
|
629
493
|
route: str
|
|
630
|
-
include_in_menu: Optional[bool]
|
|
494
|
+
include_in_menu: Optional[bool] = None
|
|
631
495
|
on_load: Optional[Action] = None
|
|
632
496
|
|
|
633
497
|
|
|
634
498
|
class TemplateRouterLink(BaseModel):
|
|
635
499
|
"""Definition of a link for the TemplateRouter"""
|
|
636
500
|
|
|
637
|
-
icon: Optional[str]
|
|
501
|
+
icon: Optional[str] = None
|
|
638
502
|
name: str
|
|
639
503
|
route: str
|
|
640
504
|
|
|
@@ -645,7 +509,7 @@ class TemplateRouterContent(BaseModel):
|
|
|
645
509
|
content: ComponentInstance
|
|
646
510
|
route: str
|
|
647
511
|
on_load: Optional[Action] = None
|
|
648
|
-
name: Optional[str]
|
|
512
|
+
name: Optional[str] = None
|
|
649
513
|
|
|
650
514
|
|
|
651
515
|
class Template(BaseModel):
|
|
@@ -17,6 +17,8 @@ limitations under the License.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
+
from pydantic import BaseModel
|
|
21
|
+
|
|
20
22
|
from dara.core.interactivity.actions import (
|
|
21
23
|
DownloadContent,
|
|
22
24
|
DownloadContentImpl,
|
|
@@ -41,24 +43,6 @@ from dara.core.interactivity.non_data_variable import NonDataVariable
|
|
|
41
43
|
from dara.core.interactivity.plain_variable import Variable
|
|
42
44
|
from dara.core.interactivity.url_variable import UrlVariable
|
|
43
45
|
|
|
44
|
-
# Update references to variable types in these actions
|
|
45
|
-
refs = {
|
|
46
|
-
'AnyDataVariable': AnyDataVariable,
|
|
47
|
-
'DerivedVariable': DerivedVariable,
|
|
48
|
-
'Variable': Variable,
|
|
49
|
-
'AnyVariable': AnyVariable,
|
|
50
|
-
'DataVariable': DataVariable,
|
|
51
|
-
'UrlVariable': UrlVariable,
|
|
52
|
-
}
|
|
53
|
-
DownloadVariable.update_forward_refs(**refs)
|
|
54
|
-
ResetVariables.update_forward_refs(**refs)
|
|
55
|
-
TriggerVariable.update_forward_refs(**refs)
|
|
56
|
-
UpdateVariable.update_forward_refs(**refs)
|
|
57
|
-
UpdateVariableImpl.update_forward_refs(**refs)
|
|
58
|
-
Condition.update_forward_refs(**refs)
|
|
59
|
-
Notify.update_forward_refs(**refs)
|
|
60
|
-
|
|
61
|
-
|
|
62
46
|
__all__ = [
|
|
63
47
|
'action',
|
|
64
48
|
'AnyVariable',
|
|
@@ -78,7 +62,17 @@ __all__ = [
|
|
|
78
62
|
'ResetVariables',
|
|
79
63
|
'TriggerVariable',
|
|
80
64
|
'UpdateVariable',
|
|
65
|
+
'UpdateVariableImpl',
|
|
81
66
|
'SideEffect',
|
|
82
67
|
'Condition',
|
|
83
68
|
'Operator',
|
|
84
69
|
]
|
|
70
|
+
|
|
71
|
+
for symbol in list(globals().values()):
|
|
72
|
+
try:
|
|
73
|
+
if issubclass(symbol, BaseModel) and symbol is not BaseModel:
|
|
74
|
+
symbol.model_rebuild()
|
|
75
|
+
except Exception as e:
|
|
76
|
+
from dara.core.logging import dev_logger
|
|
77
|
+
|
|
78
|
+
dev_logger.warning(f'Error rebuilding model "{symbol}": {e}')
|