dara-core 1.20.3__py3-none-any.whl → 1.21.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dara/core/__init__.py +8 -42
- dara/core/configuration.py +33 -4
- dara/core/defaults.py +7 -0
- dara/core/definitions.py +22 -35
- dara/core/interactivity/actions.py +29 -28
- dara/core/interactivity/plain_variable.py +6 -2
- dara/core/interactivity/switch_variable.py +2 -2
- dara/core/internal/execute_action.py +75 -6
- dara/core/internal/routing.py +526 -354
- dara/core/internal/tasks.py +1 -1
- dara/core/jinja/index.html +97 -1
- dara/core/jinja/index_autojs.html +116 -10
- dara/core/js_tooling/js_utils.py +35 -14
- dara/core/main.py +137 -89
- dara/core/persistence.py +6 -2
- dara/core/router/__init__.py +5 -0
- dara/core/router/compat.py +77 -0
- dara/core/router/components.py +143 -0
- dara/core/router/dependency_graph.py +62 -0
- dara/core/router/router.py +887 -0
- dara/core/umd/{dara.core.umd.js → dara.core.umd.cjs} +62588 -46966
- dara/core/umd/style.css +52 -9
- dara/core/visual/components/__init__.py +16 -11
- dara/core/visual/components/menu.py +4 -0
- dara/core/visual/components/menu_link.py +1 -0
- dara/core/visual/components/powered_by_causalens.py +9 -0
- dara/core/visual/components/sidebar_frame.py +1 -0
- dara/core/visual/dynamic_component.py +1 -1
- {dara_core-1.20.3.dist-info → dara_core-1.21.1.dist-info}/METADATA +10 -10
- {dara_core-1.20.3.dist-info → dara_core-1.21.1.dist-info}/RECORD +33 -26
- {dara_core-1.20.3.dist-info → dara_core-1.21.1.dist-info}/LICENSE +0 -0
- {dara_core-1.20.3.dist-info → dara_core-1.21.1.dist-info}/WHEEL +0 -0
- {dara_core-1.20.3.dist-info → dara_core-1.21.1.dist-info}/entry_points.txt +0 -0
dara/core/__init__.py
CHANGED
|
@@ -16,61 +16,27 @@ limitations under the License.
|
|
|
16
16
|
"""
|
|
17
17
|
# ruff: noqa: F403, F405
|
|
18
18
|
|
|
19
|
+
import inspect
|
|
19
20
|
from importlib.metadata import version
|
|
20
21
|
|
|
21
22
|
from pydantic import BaseModel
|
|
22
23
|
|
|
23
24
|
from dara.core.base_definitions import *
|
|
24
|
-
from dara.core.configuration import
|
|
25
|
-
from dara.core.css import
|
|
25
|
+
from dara.core.configuration import *
|
|
26
|
+
from dara.core.css import *
|
|
26
27
|
from dara.core.definitions import *
|
|
27
28
|
from dara.core.interactivity import *
|
|
28
|
-
from dara.core.
|
|
29
|
-
from dara.core.visual.
|
|
30
|
-
from dara.core.visual.
|
|
29
|
+
from dara.core.router import *
|
|
30
|
+
from dara.core.visual.components import *
|
|
31
|
+
from dara.core.visual.dynamic_component import *
|
|
32
|
+
from dara.core.visual.progress_updater import *
|
|
31
33
|
|
|
32
34
|
__version__ = version('dara-core')
|
|
33
35
|
|
|
34
36
|
|
|
35
|
-
# Top-level imports for most commonly used APIs for ease of use
|
|
36
|
-
|
|
37
|
-
__all__ = [
|
|
38
|
-
'action',
|
|
39
|
-
'ActionCtx',
|
|
40
|
-
'ConfigurationBuilder',
|
|
41
|
-
'DerivedVariable',
|
|
42
|
-
'DerivedDataVariable',
|
|
43
|
-
'DataVariable',
|
|
44
|
-
'ServerVariable',
|
|
45
|
-
'UrlVariable',
|
|
46
|
-
'Cache',
|
|
47
|
-
'CacheType',
|
|
48
|
-
'Variable',
|
|
49
|
-
'py_component',
|
|
50
|
-
'DownloadVariable',
|
|
51
|
-
'DownloadContent',
|
|
52
|
-
'NavigateTo',
|
|
53
|
-
'Notify',
|
|
54
|
-
'ResetVariables',
|
|
55
|
-
'SideEffect',
|
|
56
|
-
'TriggerVariable',
|
|
57
|
-
'UpdateVariable',
|
|
58
|
-
'get_icon',
|
|
59
|
-
'CSSProperties',
|
|
60
|
-
'ProgressUpdater',
|
|
61
|
-
'track_progress',
|
|
62
|
-
'ComponentInstance',
|
|
63
|
-
'StyledComponentInstance',
|
|
64
|
-
'ErrorHandlingConfig',
|
|
65
|
-
'Fallback',
|
|
66
|
-
'UpdateVariableImpl',
|
|
67
|
-
'DownloadContentImpl',
|
|
68
|
-
'NavigateToImpl',
|
|
69
|
-
]
|
|
70
|
-
|
|
71
37
|
for symbol in list(globals().values()):
|
|
72
38
|
try:
|
|
73
|
-
if issubclass(symbol, BaseModel) and symbol is not BaseModel:
|
|
39
|
+
if inspect.isclass(symbol) and issubclass(symbol, BaseModel) and symbol is not BaseModel:
|
|
74
40
|
symbol.model_rebuild()
|
|
75
41
|
except Exception as e:
|
|
76
42
|
from dara.core.logging import dev_logger
|
dara/core/configuration.py
CHANGED
|
@@ -36,6 +36,7 @@ from typing import (
|
|
|
36
36
|
from fastapi.middleware import Middleware
|
|
37
37
|
from pydantic import ConfigDict
|
|
38
38
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
39
|
+
from typing_extensions import deprecated
|
|
39
40
|
|
|
40
41
|
from dara.core.auth.base import BaseAuthConfig
|
|
41
42
|
from dara.core.auth.basic import DefaultAuthConfig
|
|
@@ -63,6 +64,7 @@ from dara.core.internal.import_discovery import (
|
|
|
63
64
|
from dara.core.internal.registry_lookup import CustomRegistryLookup
|
|
64
65
|
from dara.core.internal.scheduler import ScheduledJob, ScheduledJobFactory
|
|
65
66
|
from dara.core.logging import dev_logger
|
|
67
|
+
from dara.core.router import Router
|
|
66
68
|
from dara.core.visual.components import RawString
|
|
67
69
|
from dara.core.visual.themes import BaseTheme, ThemeDef
|
|
68
70
|
|
|
@@ -80,6 +82,7 @@ class Configuration(BaseModel):
|
|
|
80
82
|
module_dependencies: Dict[str, str]
|
|
81
83
|
live_reload: bool
|
|
82
84
|
powered_by_causalens: bool
|
|
85
|
+
router: Router
|
|
83
86
|
pages: Dict[str, Page]
|
|
84
87
|
routes: Set[ApiRoute]
|
|
85
88
|
scheduled_jobs: List[Tuple[Union[ScheduledJob, ScheduledJobFactory], Callable, Optional[List[Any]]]] = []
|
|
@@ -157,12 +160,13 @@ class ConfigurationBuilder:
|
|
|
157
160
|
_custom_encoders: Dict[Type[Any], Encoder]
|
|
158
161
|
_middlewares: List[Middleware]
|
|
159
162
|
routes: Set[ApiRoute]
|
|
163
|
+
router: Router
|
|
160
164
|
static_files_dir: str
|
|
161
165
|
scheduled_jobs: List[Tuple[Union[ScheduledJob, ScheduledJobFactory], Callable, Optional[List[Any]]]] = []
|
|
162
166
|
startup_functions: List[Callable]
|
|
163
167
|
context_components: List[ComponentInstance]
|
|
164
168
|
task_module: Optional[str]
|
|
165
|
-
|
|
169
|
+
_template: str
|
|
166
170
|
theme: BaseTheme
|
|
167
171
|
title: str
|
|
168
172
|
|
|
@@ -175,13 +179,14 @@ class ConfigurationBuilder:
|
|
|
175
179
|
self._errors = []
|
|
176
180
|
self.enable_devtools = False
|
|
177
181
|
self.live_reload = False
|
|
178
|
-
self.
|
|
182
|
+
self._powered_by_causalens = False
|
|
179
183
|
self._package_tags_processors = []
|
|
180
184
|
self._template_extra_js = ''
|
|
181
185
|
self._pages = {}
|
|
182
186
|
self._template_renderers = {}
|
|
183
187
|
self._endpoint_configurations = []
|
|
184
188
|
self.routes = set()
|
|
189
|
+
self.router = Router()
|
|
185
190
|
self.static_files_dir = os.path.join(pathlib.Path().parent.parent.absolute(), 'dist')
|
|
186
191
|
self._static_folders = []
|
|
187
192
|
self._middlewares = []
|
|
@@ -193,10 +198,31 @@ class ConfigurationBuilder:
|
|
|
193
198
|
self._custom_ws_handlers = {}
|
|
194
199
|
self._custom_encoders = {}
|
|
195
200
|
|
|
196
|
-
self.
|
|
201
|
+
self._template = 'default'
|
|
197
202
|
self.theme = BaseTheme(main='light')
|
|
198
203
|
self.title = 'decisionApp'
|
|
199
204
|
|
|
205
|
+
@property
|
|
206
|
+
@deprecated('Use `config.router` instead and set a root layout route.')
|
|
207
|
+
def template(self):
|
|
208
|
+
return self._template
|
|
209
|
+
|
|
210
|
+
@template.setter
|
|
211
|
+
@deprecated('Use `config.router` instead and set a root layout route.')
|
|
212
|
+
def template(self, value):
|
|
213
|
+
self._template = value
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def powered_by_causalens(self):
|
|
217
|
+
return self._powered_by_causalens
|
|
218
|
+
|
|
219
|
+
@powered_by_causalens.setter
|
|
220
|
+
@deprecated(
|
|
221
|
+
'Pass `powered_by_causalens=` kwarg to SideBarFrame directly, or use the `dara.core.visual.components.PoweredByCausalens` component'
|
|
222
|
+
)
|
|
223
|
+
def powered_by_causalens(self, value):
|
|
224
|
+
self._powered_by_causalens = value
|
|
225
|
+
|
|
200
226
|
def add_action(self, action: Type[ActionImpl], local: bool = False):
|
|
201
227
|
"""
|
|
202
228
|
Register an Action with the application.
|
|
@@ -380,6 +406,7 @@ class ConfigurationBuilder:
|
|
|
380
406
|
"""
|
|
381
407
|
self._package_tags_processors.append(processor)
|
|
382
408
|
|
|
409
|
+
@deprecated('Use `config.router.add_page` instead.')
|
|
383
410
|
def add_page(
|
|
384
411
|
self,
|
|
385
412
|
name: str,
|
|
@@ -436,6 +463,7 @@ class ConfigurationBuilder:
|
|
|
436
463
|
self._pages[name] = page
|
|
437
464
|
return page
|
|
438
465
|
|
|
466
|
+
@deprecated('Use `config.router.add_layout` and set a root layout route instead.')
|
|
439
467
|
def add_template_renderer(self, name: str, template_renderer: Callable[..., Template]) -> str:
|
|
440
468
|
"""
|
|
441
469
|
Add a new template renderer that can be selected by name as part of the configuration. By default calling this
|
|
@@ -570,10 +598,11 @@ class ConfigurationBuilder:
|
|
|
570
598
|
enable_devtools=self.enable_devtools,
|
|
571
599
|
module_dependencies=self._module_dependencies,
|
|
572
600
|
live_reload=self.live_reload,
|
|
601
|
+
pages=self._pages,
|
|
573
602
|
powered_by_causalens=self.powered_by_causalens,
|
|
574
603
|
package_tag_processors=self._package_tags_processors,
|
|
575
|
-
pages=self._pages,
|
|
576
604
|
routes=self.routes,
|
|
605
|
+
router=self.router,
|
|
577
606
|
static_files_dir=self.static_files_dir,
|
|
578
607
|
scheduled_jobs=self.scheduled_jobs,
|
|
579
608
|
startup_functions=self.startup_functions,
|
dara/core/defaults.py
CHANGED
|
@@ -34,6 +34,7 @@ from dara.core.interactivity.actions import (
|
|
|
34
34
|
UpdateVariableDef,
|
|
35
35
|
)
|
|
36
36
|
from dara.core.internal.cache_store import CacheStore
|
|
37
|
+
from dara.core.router import Link, LinkDef, MenuLink, MenuLinkDef, Outlet, OutletDef
|
|
37
38
|
from dara.core.visual.components import (
|
|
38
39
|
DefaultFallbackDef,
|
|
39
40
|
DynamicComponent,
|
|
@@ -43,6 +44,8 @@ from dara.core.visual.components import (
|
|
|
43
44
|
ForDef,
|
|
44
45
|
Menu,
|
|
45
46
|
MenuDef,
|
|
47
|
+
PoweredByCausalens,
|
|
48
|
+
PoweredByCausalensDef,
|
|
46
49
|
ProgressTracker,
|
|
47
50
|
ProgressTrackerDef,
|
|
48
51
|
RouterContent,
|
|
@@ -74,6 +77,7 @@ INITIAL_CORE_INTERNALS = {'Store': _store}
|
|
|
74
77
|
CORE_COMPONENTS: Dict[str, ComponentTypeAnnotation] = {
|
|
75
78
|
DynamicComponent.__name__: DynamicComponentDef,
|
|
76
79
|
Menu.__name__: MenuDef,
|
|
80
|
+
MenuLink.__name__: MenuLinkDef,
|
|
77
81
|
ProgressTracker.__name__: ProgressTrackerDef,
|
|
78
82
|
RouterContent.__name__: RouterContentDef,
|
|
79
83
|
SideBarFrame.__name__: SideBarFrameDef,
|
|
@@ -82,6 +86,9 @@ CORE_COMPONENTS: Dict[str, ComponentTypeAnnotation] = {
|
|
|
82
86
|
cast(str, Fallback.Row.py_component): RowFallbackDef,
|
|
83
87
|
cast(str, Fallback.Custom.py_component): CustomFallbackDef,
|
|
84
88
|
For.__name__: ForDef,
|
|
89
|
+
Link.__name__: LinkDef,
|
|
90
|
+
Outlet.__name__: OutletDef,
|
|
91
|
+
PoweredByCausalens.__name__: PoweredByCausalensDef,
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
# These actions are provided by the core JS of this module
|
dara/core/definitions.py
CHANGED
|
@@ -38,12 +38,14 @@ from typing import (
|
|
|
38
38
|
from fastapi.encoders import jsonable_encoder
|
|
39
39
|
from fastapi.params import Depends
|
|
40
40
|
from pydantic import (
|
|
41
|
+
BeforeValidator,
|
|
41
42
|
ConfigDict,
|
|
42
43
|
Field,
|
|
43
44
|
SerializerFunctionWrapHandler,
|
|
44
45
|
field_validator,
|
|
45
46
|
model_serializer,
|
|
46
47
|
)
|
|
48
|
+
from typing_extensions import Annotated
|
|
47
49
|
|
|
48
50
|
from dara.core.base_definitions import Action, ComponentType
|
|
49
51
|
from dara.core.base_definitions import DaraBaseModel as BaseModel
|
|
@@ -74,6 +76,22 @@ def _kebab_to_camel(string: str):
|
|
|
74
76
|
return chunks[0] + ''.join([chunk[0].upper() + chunk[1:].lower() for chunk in chunks[1:]])
|
|
75
77
|
|
|
76
78
|
|
|
79
|
+
def transform_raw_css(value: Any):
|
|
80
|
+
"""
|
|
81
|
+
Transform and validate a raw_css value.
|
|
82
|
+
"""
|
|
83
|
+
from dara.core.interactivity.client_variable import ClientVariable
|
|
84
|
+
|
|
85
|
+
if value is None:
|
|
86
|
+
return None
|
|
87
|
+
if isinstance(value, (str, ClientVariable, CSSProperties)):
|
|
88
|
+
return value
|
|
89
|
+
if isinstance(value, dict):
|
|
90
|
+
return {_kebab_to_camel(k): v for k, v in value.items()}
|
|
91
|
+
|
|
92
|
+
raise ValueError(f'raw_css must be a CSSProperties, dict, str, None or ClientVariable, got {type(value)}')
|
|
93
|
+
|
|
94
|
+
|
|
77
95
|
class ErrorHandlingConfig(BaseModel):
|
|
78
96
|
title: str = DEFAULT_ERROR_TITLE
|
|
79
97
|
"""Title to display in the error boundary"""
|
|
@@ -81,26 +99,12 @@ class ErrorHandlingConfig(BaseModel):
|
|
|
81
99
|
description: str = DEFAULT_ERROR_DESCRIPTION
|
|
82
100
|
"""Description to display in the error boundary"""
|
|
83
101
|
|
|
84
|
-
raw_css: Optional[Any] = None
|
|
102
|
+
raw_css: Annotated[Optional[Any], BeforeValidator(transform_raw_css)] = None
|
|
85
103
|
"""
|
|
86
104
|
Raw styling to apply to the displayed error boundary.
|
|
87
105
|
Accepts a CSSProperties, dict, str, or ClientVariable.
|
|
88
106
|
"""
|
|
89
107
|
|
|
90
|
-
@field_validator('raw_css', mode='before')
|
|
91
|
-
@classmethod
|
|
92
|
-
def validate_raw_css(cls, value):
|
|
93
|
-
from dara.core.interactivity.client_variable import ClientVariable
|
|
94
|
-
|
|
95
|
-
if value is None:
|
|
96
|
-
return None
|
|
97
|
-
if isinstance(value, (str, ClientVariable, CSSProperties)):
|
|
98
|
-
return value
|
|
99
|
-
if isinstance(value, dict):
|
|
100
|
-
return {_kebab_to_camel(k): v for k, v in value.items()}
|
|
101
|
-
|
|
102
|
-
raise ValueError(f'raw_css must be a CSSProperties, dict, str, None or ClientVariable, got {type(value)}')
|
|
103
|
-
|
|
104
108
|
def model_dump(self, *args, **kwargs):
|
|
105
109
|
result = super().model_dump(*args, **kwargs)
|
|
106
110
|
|
|
@@ -119,7 +123,7 @@ class ComponentInstance(BaseModel):
|
|
|
119
123
|
Definition of a Component Instance
|
|
120
124
|
"""
|
|
121
125
|
|
|
122
|
-
uid:
|
|
126
|
+
uid: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
123
127
|
|
|
124
128
|
js_module: ClassVar[Optional[str]] = None
|
|
125
129
|
"""
|
|
@@ -141,7 +145,7 @@ class ComponentInstance(BaseModel):
|
|
|
141
145
|
required_routes: ClassVar[List[ApiRoute]] = []
|
|
142
146
|
"""List of routes the component depends on. Will be implicitly added to the app if this component is used"""
|
|
143
147
|
|
|
144
|
-
raw_css: Optional[Any] = None
|
|
148
|
+
raw_css: Annotated[Optional[Any], BeforeValidator(transform_raw_css)] = None
|
|
145
149
|
"""
|
|
146
150
|
Raw styling to apply to the component.
|
|
147
151
|
Can be an dict/CSSProperties instance representing the `styles` tag, a string injected directly into the CSS of the wrapping component,
|
|
@@ -183,7 +187,7 @@ class ComponentInstance(BaseModel):
|
|
|
183
187
|
"""
|
|
184
188
|
An optional unique identifier for the component, defaults to None
|
|
185
189
|
|
|
186
|
-
This
|
|
190
|
+
This is intended to help identify components with human-readable names in the serialized trees, and is also set as the `id` attribute of the DOM element
|
|
187
191
|
"""
|
|
188
192
|
|
|
189
193
|
for_: Optional[str] = None
|
|
@@ -213,23 +217,6 @@ class ComponentInstance(BaseModel):
|
|
|
213
217
|
|
|
214
218
|
raise ValueError(f'fallback must be a BaseFallback or ComponentInstance, got {type(fallback)}')
|
|
215
219
|
|
|
216
|
-
@field_validator('raw_css', mode='before')
|
|
217
|
-
@classmethod
|
|
218
|
-
def parse_css(cls, css: Optional[Any]):
|
|
219
|
-
from dara.core.interactivity.client_variable import ClientVariable
|
|
220
|
-
|
|
221
|
-
if css is None:
|
|
222
|
-
return None
|
|
223
|
-
|
|
224
|
-
# If it's a plain dict, change kebab case to camel case
|
|
225
|
-
if isinstance(css, dict):
|
|
226
|
-
return {_kebab_to_camel(k): v for k, v in css.items()}
|
|
227
|
-
|
|
228
|
-
if isinstance(css, (ClientVariable, CSSProperties, str)):
|
|
229
|
-
return css
|
|
230
|
-
|
|
231
|
-
raise ValueError(f'raw_css must be a CSSProperties, dict, str, None or ClientVariable, got {type(css)}')
|
|
232
|
-
|
|
233
220
|
@classmethod
|
|
234
221
|
def isinstance(cls, obj: Any) -> bool:
|
|
235
222
|
return isinstance(obj, cls)
|
|
@@ -325,7 +325,7 @@ class TriggerVariable(ActionImpl):
|
|
|
325
325
|
)
|
|
326
326
|
|
|
327
327
|
|
|
328
|
-
config.add_page(
|
|
328
|
+
config.router.add_page(path='trigger', content=test_page)
|
|
329
329
|
|
|
330
330
|
```
|
|
331
331
|
"""
|
|
@@ -364,8 +364,8 @@ class NavigateToImpl(ActionImpl):
|
|
|
364
364
|
)
|
|
365
365
|
|
|
366
366
|
|
|
367
|
-
config.add_page(
|
|
368
|
-
config.add_page(
|
|
367
|
+
config.router.add_page(path='test-page', content=test_page)
|
|
368
|
+
config.router.add_page(path='another-page', content=another_page)
|
|
369
369
|
|
|
370
370
|
```
|
|
371
371
|
"""
|
|
@@ -373,7 +373,7 @@ class NavigateToImpl(ActionImpl):
|
|
|
373
373
|
py_name = 'NavigateTo'
|
|
374
374
|
|
|
375
375
|
url: Optional[str] = None
|
|
376
|
-
new_tab: bool
|
|
376
|
+
new_tab: bool = False
|
|
377
377
|
|
|
378
378
|
|
|
379
379
|
@deprecated('Use @action or `NavigateToImpl` for simple cases')
|
|
@@ -417,8 +417,8 @@ def NavigateTo(
|
|
|
417
417
|
)
|
|
418
418
|
|
|
419
419
|
|
|
420
|
-
config.add_page(
|
|
421
|
-
config.add_page(
|
|
420
|
+
config.router.add_page(path='test-page', content=test_page)
|
|
421
|
+
config.router.add_page(path='another-page', content=another_page)
|
|
422
422
|
|
|
423
423
|
```
|
|
424
424
|
"""
|
|
@@ -473,7 +473,7 @@ def Logout():
|
|
|
473
473
|
return Stack(Button('Logout', onclick=Logout()))
|
|
474
474
|
|
|
475
475
|
|
|
476
|
-
config.add_page(
|
|
476
|
+
config.router.add_page(path='logout-page', content=test_page)
|
|
477
477
|
|
|
478
478
|
```
|
|
479
479
|
"""
|
|
@@ -511,7 +511,7 @@ class ResetVariables(ActionImpl):
|
|
|
511
511
|
)
|
|
512
512
|
|
|
513
513
|
|
|
514
|
-
config.add_page(
|
|
514
|
+
config.router.add_page(path='reset-variable', content=test_page)
|
|
515
515
|
|
|
516
516
|
```
|
|
517
517
|
|
|
@@ -565,7 +565,7 @@ class Notify(ActionImpl):
|
|
|
565
565
|
)
|
|
566
566
|
|
|
567
567
|
|
|
568
|
-
config.add_page(
|
|
568
|
+
config.router.add_page(path='notify-example', content=test_page)
|
|
569
569
|
|
|
570
570
|
```
|
|
571
571
|
"""
|
|
@@ -600,7 +600,7 @@ class DownloadContentImpl(ActionImpl):
|
|
|
600
600
|
)
|
|
601
601
|
|
|
602
602
|
|
|
603
|
-
config.add_page(
|
|
603
|
+
config.router.add_page(path='download-content', content=test_page)
|
|
604
604
|
|
|
605
605
|
```
|
|
606
606
|
"""
|
|
@@ -650,14 +650,14 @@ def DownloadContent(
|
|
|
650
650
|
|
|
651
651
|
|
|
652
652
|
def test_page():
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
653
|
+
return Stack(
|
|
654
|
+
Button(
|
|
655
|
+
'Download File', onclick=DownloadContent(resolver=return_csv, extras=[my_var], cleanup_file=False)
|
|
656
|
+
),
|
|
657
|
+
)
|
|
658
658
|
|
|
659
659
|
|
|
660
|
-
config.add_page(
|
|
660
|
+
config.router.add_page(path='download-content', content=test_page)
|
|
661
661
|
|
|
662
662
|
```
|
|
663
663
|
"""
|
|
@@ -715,9 +715,9 @@ class DownloadVariable(ActionImpl):
|
|
|
715
715
|
)
|
|
716
716
|
|
|
717
717
|
|
|
718
|
-
config.add_page(
|
|
718
|
+
config.router.add_page(path='download-variable', content=test_page)
|
|
719
719
|
|
|
720
|
-
|
|
720
|
+
```
|
|
721
721
|
"""
|
|
722
722
|
|
|
723
723
|
variable: AnyVariable
|
|
@@ -764,7 +764,8 @@ def SideEffect(
|
|
|
764
764
|
return Stack(Select(value=Variable(3), items=[3, 4, 5], onchange=SideEffect(side_effect, extras=[x, y, z])))
|
|
765
765
|
|
|
766
766
|
|
|
767
|
-
config.add_page(
|
|
767
|
+
config.router.add_page(path='side-effect', content=test_page)
|
|
768
|
+
|
|
768
769
|
```
|
|
769
770
|
"""
|
|
770
771
|
|
|
@@ -937,7 +938,7 @@ class ActionCtx:
|
|
|
937
938
|
)
|
|
938
939
|
|
|
939
940
|
|
|
940
|
-
config.add_page(
|
|
941
|
+
config.router.add_page(path='trigger-variable', content=test_page)
|
|
941
942
|
|
|
942
943
|
```
|
|
943
944
|
|
|
@@ -983,8 +984,8 @@ class ActionCtx:
|
|
|
983
984
|
)
|
|
984
985
|
|
|
985
986
|
|
|
986
|
-
config.add_page(
|
|
987
|
-
config.add_page(
|
|
987
|
+
config.router.add_page(path='test-page', content=test_page)
|
|
988
|
+
config.router.add_page(path='another-page', content=another_page)
|
|
988
989
|
|
|
989
990
|
```
|
|
990
991
|
|
|
@@ -1014,7 +1015,7 @@ class ActionCtx:
|
|
|
1014
1015
|
return Stack(Button('Logout', onclick=logout()))
|
|
1015
1016
|
|
|
1016
1017
|
|
|
1017
|
-
config.add_page(
|
|
1018
|
+
config.router.add_page(path='logout-page', content=test_page)
|
|
1018
1019
|
|
|
1019
1020
|
```
|
|
1020
1021
|
"""
|
|
@@ -1052,7 +1053,7 @@ class ActionCtx:
|
|
|
1052
1053
|
)
|
|
1053
1054
|
|
|
1054
1055
|
|
|
1055
|
-
config.add_page(
|
|
1056
|
+
config.router.add_page(path='notify-example', content=test_page)
|
|
1056
1057
|
|
|
1057
1058
|
```
|
|
1058
1059
|
|
|
@@ -1097,7 +1098,7 @@ class ActionCtx:
|
|
|
1097
1098
|
)
|
|
1098
1099
|
|
|
1099
1100
|
|
|
1100
|
-
config.add_page(
|
|
1101
|
+
config.router.add_page(path='reset-variable', content=test_page)
|
|
1101
1102
|
|
|
1102
1103
|
```
|
|
1103
1104
|
|
|
@@ -1141,7 +1142,7 @@ class ActionCtx:
|
|
|
1141
1142
|
)
|
|
1142
1143
|
|
|
1143
1144
|
|
|
1144
|
-
config.add_page(
|
|
1145
|
+
config.router.add_page(path='download-content', content=test_page)
|
|
1145
1146
|
|
|
1146
1147
|
```
|
|
1147
1148
|
|
|
@@ -1180,7 +1181,7 @@ class ActionCtx:
|
|
|
1180
1181
|
)
|
|
1181
1182
|
|
|
1182
1183
|
|
|
1183
|
-
config.add_page(
|
|
1184
|
+
config.router.add_page(path='download-variable', content=test_page)
|
|
1184
1185
|
|
|
1185
1186
|
```
|
|
1186
1187
|
|
|
@@ -1229,7 +1230,7 @@ class ActionCtx:
|
|
|
1229
1230
|
def task_page():
|
|
1230
1231
|
return Stack(Text('Status display:'), Text(text=status), Button('Run', onclick=my_task()))
|
|
1231
1232
|
|
|
1232
|
-
config.add_page(
|
|
1233
|
+
config.router.add_page(path='task', content=task_page)
|
|
1233
1234
|
```
|
|
1234
1235
|
|
|
1235
1236
|
:param func: the function to run as a task
|
|
@@ -284,7 +284,7 @@ class Variable(ClientVariable, Generic[VariableType]):
|
|
|
284
284
|
assert isinstance(self.store, BackendStore), 'This method can only be used with a BackendStore'
|
|
285
285
|
return await self.store.write(value, notify=notify, ignore_channel=ignore_channel)
|
|
286
286
|
|
|
287
|
-
async def write_partial(self, data: Union[List[Dict[str, Any]], Any], notify: bool = True):
|
|
287
|
+
async def write_partial(self, data: Union[List[Dict[str, Any]], Any], notify: bool = True, in_place: bool = False):
|
|
288
288
|
"""
|
|
289
289
|
Apply partial updates to the variable's BackendStore using JSON Patch operations or automatic diffing.
|
|
290
290
|
Raises an error if the variable does not have a BackendStore attached.
|
|
@@ -294,9 +294,13 @@ class Variable(ClientVariable, Generic[VariableType]):
|
|
|
294
294
|
|
|
295
295
|
:param data: Either a list of JSON patch operations (RFC 6902) or a full object to diff against current value
|
|
296
296
|
:param notify: whether to broadcast the patches to clients
|
|
297
|
+
:param in_place: whether to apply the patches in-place or return a new value.
|
|
298
|
+
When set to True, the value will be mutated when applying the patches rather than deep-cloned.
|
|
299
|
+
This is recommended when the updated value is large and deep-cloning it can be expensive; however, users should exercise
|
|
300
|
+
caution when using the option as previous results retrieved from `variable.read()` will potentially be mutated, depending on the backend used.
|
|
297
301
|
"""
|
|
298
302
|
assert isinstance(self.store, BackendStore), 'This method can only be used with a BackendStore'
|
|
299
|
-
return await self.store.write_partial(data, notify=notify)
|
|
303
|
+
return await self.store.write_partial(data, notify=notify, in_place=in_place)
|
|
300
304
|
|
|
301
305
|
async def read(self):
|
|
302
306
|
"""
|
|
@@ -67,7 +67,7 @@ class SwitchVariable(ClientVariable):
|
|
|
67
67
|
title='Admin Panel Demo'
|
|
68
68
|
)
|
|
69
69
|
|
|
70
|
-
config.add_page('
|
|
70
|
+
config.router.add_page(path='admin', content=page_content)
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
Value mapping with defaults:
|
|
@@ -109,7 +109,7 @@ class SwitchVariable(ClientVariable):
|
|
|
109
109
|
title='Role Permissions'
|
|
110
110
|
)
|
|
111
111
|
|
|
112
|
-
config.add_page('
|
|
112
|
+
config.router.add_page(path='permissions', content=page_content)
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
Complex conditions:
|