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/__init__.py
CHANGED
|
@@ -14,35 +14,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
14
14
|
See the License for the specific language governing permissions and
|
|
15
15
|
limitations under the License.
|
|
16
16
|
"""
|
|
17
|
-
|
|
18
|
-
from __future__ import annotations
|
|
19
|
-
|
|
20
17
|
from importlib.metadata import version
|
|
21
18
|
|
|
22
|
-
from
|
|
19
|
+
from pydantic import BaseModel
|
|
20
|
+
|
|
21
|
+
from dara.core.base_definitions import *
|
|
23
22
|
from dara.core.configuration import ConfigurationBuilder
|
|
24
23
|
from dara.core.css import CSSProperties, get_icon
|
|
25
|
-
from dara.core.definitions import
|
|
26
|
-
from dara.core.interactivity import
|
|
27
|
-
|
|
28
|
-
DerivedDataVariable,
|
|
29
|
-
DerivedVariable,
|
|
30
|
-
DownloadContent,
|
|
31
|
-
DownloadContentImpl,
|
|
32
|
-
DownloadVariable,
|
|
33
|
-
NavigateTo,
|
|
34
|
-
NavigateToImpl,
|
|
35
|
-
Notify,
|
|
36
|
-
ResetVariables,
|
|
37
|
-
SideEffect,
|
|
38
|
-
TriggerVariable,
|
|
39
|
-
UpdateVariable,
|
|
40
|
-
UpdateVariableImpl,
|
|
41
|
-
UrlVariable,
|
|
42
|
-
Variable,
|
|
43
|
-
action,
|
|
44
|
-
)
|
|
45
|
-
from dara.core.visual.components import Fallback, For
|
|
24
|
+
from dara.core.definitions import *
|
|
25
|
+
from dara.core.interactivity import *
|
|
26
|
+
from dara.core.visual.components import Fallback
|
|
46
27
|
from dara.core.visual.dynamic_component import py_component
|
|
47
28
|
from dara.core.visual.progress_updater import ProgressUpdater, track_progress
|
|
48
29
|
|
|
@@ -75,11 +56,19 @@ __all__ = [
|
|
|
75
56
|
'ProgressUpdater',
|
|
76
57
|
'track_progress',
|
|
77
58
|
'ComponentInstance',
|
|
59
|
+
'StyledComponentInstance',
|
|
78
60
|
'ErrorHandlingConfig',
|
|
79
|
-
'template',
|
|
80
|
-
'For',
|
|
81
61
|
'Fallback',
|
|
82
62
|
'UpdateVariableImpl',
|
|
83
63
|
'DownloadContentImpl',
|
|
84
64
|
'NavigateToImpl',
|
|
85
65
|
]
|
|
66
|
+
|
|
67
|
+
for symbol in list(globals().values()):
|
|
68
|
+
try:
|
|
69
|
+
if issubclass(symbol, BaseModel) and symbol is not BaseModel:
|
|
70
|
+
symbol.model_rebuild()
|
|
71
|
+
except Exception as e:
|
|
72
|
+
from dara.core.logging import dev_logger
|
|
73
|
+
|
|
74
|
+
dev_logger.warning(f'Error rebuilding model "{symbol}": {e}')
|
dara/core/auth/base.py
CHANGED
|
@@ -19,7 +19,7 @@ import abc
|
|
|
19
19
|
from typing import Any, ClassVar, Dict, Union
|
|
20
20
|
|
|
21
21
|
from fastapi import HTTPException, Response
|
|
22
|
-
from pydantic import BaseModel
|
|
22
|
+
from pydantic import BaseModel, model_serializer
|
|
23
23
|
from typing_extensions import TypedDict
|
|
24
24
|
|
|
25
25
|
from dara.core.auth.definitions import (
|
|
@@ -59,7 +59,8 @@ class AuthComponentConfig(BaseModel):
|
|
|
59
59
|
extra: Dict[str, AuthComponent] = {}
|
|
60
60
|
"""Extra components, map of route -> component"""
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
@model_serializer()
|
|
63
|
+
def ser_model(self) -> dict:
|
|
63
64
|
return {'login': self.login, 'logout': self.logout, **self.extra}
|
|
64
65
|
|
|
65
66
|
|
dara/core/auth/definitions.py
CHANGED
dara/core/auth/utils.py
CHANGED
dara/core/base_definitions.py
CHANGED
|
@@ -24,58 +24,133 @@ import uuid
|
|
|
24
24
|
from enum import Enum
|
|
25
25
|
from typing import (
|
|
26
26
|
TYPE_CHECKING,
|
|
27
|
+
Annotated,
|
|
27
28
|
Any,
|
|
28
29
|
Awaitable,
|
|
29
30
|
Callable,
|
|
30
31
|
ClassVar,
|
|
32
|
+
Dict,
|
|
31
33
|
List,
|
|
32
34
|
Mapping,
|
|
33
35
|
Optional,
|
|
36
|
+
Tuple,
|
|
34
37
|
Union,
|
|
38
|
+
get_args,
|
|
39
|
+
get_origin,
|
|
35
40
|
)
|
|
36
41
|
|
|
37
42
|
import anyio
|
|
38
43
|
from anyio.streams.memory import MemoryObjectSendStream
|
|
39
|
-
from pydantic import
|
|
44
|
+
from pydantic import (
|
|
45
|
+
BaseModel,
|
|
46
|
+
ConfigDict,
|
|
47
|
+
Field,
|
|
48
|
+
SerializeAsAny,
|
|
49
|
+
SerializerFunctionWrapHandler,
|
|
50
|
+
model_serializer,
|
|
51
|
+
)
|
|
52
|
+
from pydantic._internal._model_construction import ModelMetaclass
|
|
40
53
|
|
|
41
54
|
if TYPE_CHECKING:
|
|
42
55
|
from dara.core.interactivity.actions import ActionCtx
|
|
43
56
|
|
|
44
57
|
|
|
45
|
-
|
|
58
|
+
def annotation_has_base_model(typ: Any) -> bool:
|
|
59
|
+
"""
|
|
60
|
+
Check whether the given value is of the given type.
|
|
61
|
+
|
|
62
|
+
Handles Union and List types.
|
|
46
63
|
"""
|
|
47
|
-
|
|
48
|
-
|
|
64
|
+
try:
|
|
65
|
+
type_args = get_args(typ)
|
|
66
|
+
if len(type_args) > 0:
|
|
67
|
+
return any(annotation_has_base_model(arg) for arg in type_args)
|
|
68
|
+
except: # pylint: disable=bare-except
|
|
69
|
+
# canot get arguments, should be a simple type
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
# Handle simple types
|
|
73
|
+
return typ is BaseModel or issubclass(typ, BaseModel)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# This reverts v1->v2 change to make any BaseModel field duck-type serializable.
|
|
77
|
+
# It works by adding SerializeAsAny to all fields of the model.
|
|
78
|
+
# See https://github.com/pydantic/pydantic/issues/6381
|
|
79
|
+
class SerializeAsAnyMeta(ModelMetaclass):
|
|
80
|
+
def __new__(
|
|
81
|
+
self, name: str, bases: Tuple[type], namespaces: Dict[str, Any], **kwargs
|
|
82
|
+
): # pylint: disable=bad-mcs-classmethod-argument
|
|
83
|
+
annotations: dict = namespaces.get('__annotations__', {}).copy()
|
|
84
|
+
|
|
85
|
+
for base in bases:
|
|
86
|
+
for base_ in base.__mro__:
|
|
87
|
+
if base_ is BaseModel:
|
|
88
|
+
annotations.update(base_.__annotations__)
|
|
89
|
+
|
|
90
|
+
for field, annotation in annotations.items():
|
|
91
|
+
if not field.startswith('__'):
|
|
92
|
+
# Wrapping `ClassVar`s in `SerializeAsAny` breaks pydantic behaviour of making `ClassVar` fields
|
|
93
|
+
# accessible via the class itself
|
|
94
|
+
# NOTE: the annotation can be a string due to future annotations
|
|
95
|
+
if isinstance(annotation, str) or annotation is ClassVar:
|
|
96
|
+
continue
|
|
97
|
+
if annotation_has_base_model(annotation):
|
|
98
|
+
annotations[field] = SerializeAsAny[annotation] # type: ignore
|
|
99
|
+
|
|
100
|
+
namespaces['__annotations__'] = annotations
|
|
101
|
+
|
|
102
|
+
return super().__new__(self, name, bases, namespaces, **kwargs)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class DaraBaseModel(BaseModel, metaclass=SerializeAsAnyMeta):
|
|
106
|
+
"""
|
|
107
|
+
Custom BaseModel for dara internals.
|
|
49
108
|
"""
|
|
50
109
|
|
|
51
|
-
|
|
52
|
-
smart_union = True
|
|
53
|
-
extra = 'forbid'
|
|
110
|
+
model_config = ConfigDict(extra='forbid')
|
|
54
111
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
112
|
+
@classmethod
|
|
113
|
+
def model_rebuild(
|
|
114
|
+
cls, *, force: bool = False, raise_errors: bool = True, _parent_namespace_depth: int = 2, _types_namespace=None
|
|
115
|
+
) -> bool | None:
|
|
116
|
+
"""
|
|
117
|
+
Rebuild the model to re-apply the SerializeAsAny wrapper once we've resolved the string annotations
|
|
118
|
+
"""
|
|
119
|
+
# rebuild once to get all types sorted
|
|
120
|
+
super().model_rebuild(
|
|
121
|
+
force=force,
|
|
122
|
+
raise_errors=raise_errors,
|
|
123
|
+
_parent_namespace_depth=_parent_namespace_depth + 1,
|
|
124
|
+
_types_namespace=_types_namespace,
|
|
58
125
|
)
|
|
59
126
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
127
|
+
# Re-apply the SerializeAsAny wrapper to any fields that were originally strings
|
|
128
|
+
for field_info in cls.__pydantic_fields__.values():
|
|
129
|
+
# if the original was a str, we've just resolved it so we can re-apply the SerializeAsAny wrapper
|
|
130
|
+
if (
|
|
131
|
+
field_info.annotation is not SerializeAsAny
|
|
132
|
+
and field_info.annotation is not ClassVar
|
|
133
|
+
and annotation_has_base_model(field_info.annotation)
|
|
134
|
+
):
|
|
135
|
+
# Skip if it has metadata that is already annotated with SerializeAsAny
|
|
136
|
+
if any(isinstance(x, SerializeAsAny) for x in field_info.metadata): # type: ignore
|
|
137
|
+
continue
|
|
138
|
+
# Skip if the type is already annotated with SerializeAsAny
|
|
139
|
+
if get_origin(field_info.annotation) is Annotated and any(
|
|
140
|
+
isinstance(arg, SerializeAsAny) for arg in field_info.annotation.__metadata__ # type: ignore
|
|
141
|
+
):
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
field_info.annotation = SerializeAsAny[field_info.annotation] # type: ignore
|
|
145
|
+
field_info.metadata = list(field_info.annotation.__metadata__) # type: ignore
|
|
146
|
+
|
|
147
|
+
# Rebuild again with force to ensure we rebuild the schema with new annotations
|
|
148
|
+
return super().model_rebuild(
|
|
149
|
+
force=True,
|
|
150
|
+
raise_errors=raise_errors,
|
|
151
|
+
_parent_namespace_depth=_parent_namespace_depth + 1,
|
|
152
|
+
_types_namespace=_types_namespace,
|
|
153
|
+
)
|
|
79
154
|
|
|
80
155
|
|
|
81
156
|
class CacheType(str, Enum):
|
|
@@ -115,7 +190,7 @@ class LruCachePolicy(BaseCachePolicy):
|
|
|
115
190
|
depending on `cache_type` set in the policy
|
|
116
191
|
"""
|
|
117
192
|
|
|
118
|
-
policy: str = Field(
|
|
193
|
+
policy: str = Field(default='lru', frozen=True)
|
|
119
194
|
max_size: int = 10
|
|
120
195
|
|
|
121
196
|
|
|
@@ -124,8 +199,8 @@ class MostRecentCachePolicy(LruCachePolicy):
|
|
|
124
199
|
Most recent cache policy. Only keeps the most recent item in the cache.
|
|
125
200
|
"""
|
|
126
201
|
|
|
127
|
-
policy: str = Field(
|
|
128
|
-
max_size: int = Field(
|
|
202
|
+
policy: str = Field(default='most-recent', frozen=True)
|
|
203
|
+
max_size: int = Field(default=1, frozen=True)
|
|
129
204
|
|
|
130
205
|
|
|
131
206
|
class KeepAllCachePolicy(BaseCachePolicy):
|
|
@@ -135,7 +210,7 @@ class KeepAllCachePolicy(BaseCachePolicy):
|
|
|
135
210
|
Should be used with caution as it can lead to memory leaks.
|
|
136
211
|
"""
|
|
137
212
|
|
|
138
|
-
policy: str = Field(
|
|
213
|
+
policy: str = Field(default='keep-all', frozen=True)
|
|
139
214
|
|
|
140
215
|
|
|
141
216
|
class TTLCachePolicy(BaseCachePolicy):
|
|
@@ -146,7 +221,7 @@ class TTLCachePolicy(BaseCachePolicy):
|
|
|
146
221
|
:param ttl: time-to-live in seconds
|
|
147
222
|
"""
|
|
148
223
|
|
|
149
|
-
policy: str = Field(
|
|
224
|
+
policy: str = Field(default='ttl', frozen=True)
|
|
150
225
|
ttl: int
|
|
151
226
|
|
|
152
227
|
|
|
@@ -218,7 +293,7 @@ class CachedRegistryEntry(BaseModel):
|
|
|
218
293
|
via the cache policy.
|
|
219
294
|
"""
|
|
220
295
|
|
|
221
|
-
cache: Optional[BaseCachePolicy]
|
|
296
|
+
cache: Optional[BaseCachePolicy] = None
|
|
222
297
|
uid: str
|
|
223
298
|
|
|
224
299
|
def to_store_key(self):
|
|
@@ -242,17 +317,15 @@ class TaskProgressUpdate(BaseTaskMessage):
|
|
|
242
317
|
|
|
243
318
|
class TaskResult(BaseTaskMessage):
|
|
244
319
|
result: Any
|
|
245
|
-
cache_key: Optional[str]
|
|
246
|
-
reg_entry: Optional[CachedRegistryEntry]
|
|
320
|
+
cache_key: Optional[str] = None
|
|
321
|
+
reg_entry: Optional[CachedRegistryEntry] = None
|
|
247
322
|
|
|
248
323
|
|
|
249
324
|
class TaskError(BaseTaskMessage):
|
|
250
325
|
error: BaseException
|
|
251
|
-
cache_key: Optional[str]
|
|
252
|
-
reg_entry: Optional[CachedRegistryEntry]
|
|
253
|
-
|
|
254
|
-
class Config:
|
|
255
|
-
arbitrary_types_allowed = True
|
|
326
|
+
cache_key: Optional[str] = None
|
|
327
|
+
reg_entry: Optional[CachedRegistryEntry] = None
|
|
328
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
256
329
|
|
|
257
330
|
|
|
258
331
|
TaskMessage = Union[TaskProgressUpdate, TaskResult, TaskError]
|
|
@@ -288,9 +361,6 @@ class PendingTask(BaseTask):
|
|
|
288
361
|
Is associated to an underlying task definition.
|
|
289
362
|
"""
|
|
290
363
|
|
|
291
|
-
cache_key: None = None
|
|
292
|
-
reg_entry: None = None
|
|
293
|
-
|
|
294
364
|
def __init__(self, task_id: str, task_def: BaseTask, ws_channel: Optional[str] = None):
|
|
295
365
|
self.task_id = task_id
|
|
296
366
|
self.task_def = task_def
|
|
@@ -431,7 +501,7 @@ class AnnotatedAction(BaseModel):
|
|
|
431
501
|
# pylint: disable-next=import-error, import-outside-toplevel
|
|
432
502
|
from dara.core.interactivity.plain_variable import Variable
|
|
433
503
|
|
|
434
|
-
self.
|
|
504
|
+
self.model_rebuild()
|
|
435
505
|
super().__init__(**data, loading=Variable(False))
|
|
436
506
|
|
|
437
507
|
|
|
@@ -456,11 +526,12 @@ class ActionImpl(DaraBaseModel):
|
|
|
456
526
|
"""
|
|
457
527
|
await ctx._push_action(self)
|
|
458
528
|
|
|
459
|
-
|
|
529
|
+
@model_serializer(mode='wrap')
|
|
530
|
+
def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
|
|
460
531
|
"""
|
|
461
532
|
This structure is expected by the frontend, must match the JS implementation
|
|
462
533
|
"""
|
|
463
|
-
dict_form =
|
|
534
|
+
dict_form = nxt(self)
|
|
464
535
|
dict_form['name'] = self.__class__.py_name or self.__class__.__name__
|
|
465
536
|
dict_form['__typename'] = 'ActionImpl'
|
|
466
537
|
return dict_form
|
|
@@ -497,14 +568,14 @@ class ActionDef(BaseModel):
|
|
|
497
568
|
|
|
498
569
|
name: str
|
|
499
570
|
py_module: str
|
|
500
|
-
js_module: Optional[str]
|
|
571
|
+
js_module: Optional[str] = None
|
|
501
572
|
|
|
502
573
|
|
|
503
574
|
class ActionResolverDef(BaseModel):
|
|
504
575
|
uid: str
|
|
505
576
|
"""Unique id of the action definition"""
|
|
506
577
|
|
|
507
|
-
resolver: Optional[Callable]
|
|
578
|
+
resolver: Optional[Callable] = None
|
|
508
579
|
"""Resolver function for the action"""
|
|
509
580
|
|
|
510
581
|
execute_action: Callable[..., Awaitable[Any]]
|
|
@@ -512,7 +583,7 @@ class ActionResolverDef(BaseModel):
|
|
|
512
583
|
|
|
513
584
|
|
|
514
585
|
class UploadResolverDef(BaseModel):
|
|
515
|
-
resolver: Optional[Callable]
|
|
586
|
+
resolver: Optional[Callable] = None
|
|
516
587
|
"""Optional custom resolver function for the upload"""
|
|
517
588
|
upload: Callable
|
|
518
589
|
"""Upload handling function, default dara.core.interactivity.any_data_variable.upload"""
|
|
@@ -523,17 +594,3 @@ class ComponentType(Enum):
|
|
|
523
594
|
|
|
524
595
|
JS = 'js'
|
|
525
596
|
PY = 'py'
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
class TemplateMarker(BaseModel):
|
|
529
|
-
"""
|
|
530
|
-
Template marker used to mark fields that should be replaced with a data field on the client before being rendered.
|
|
531
|
-
See dara_core.definitions.template for details
|
|
532
|
-
"""
|
|
533
|
-
|
|
534
|
-
field_name: str
|
|
535
|
-
|
|
536
|
-
def dict(self, *args, **kwargs):
|
|
537
|
-
dict_form = super().dict(*args, **kwargs)
|
|
538
|
-
dict_form['__typename'] = 'TemplateMarker'
|
|
539
|
-
return dict_form
|
dara/core/cli.py
CHANGED
|
@@ -60,6 +60,11 @@ def cli():
|
|
|
60
60
|
@click.option('--log', default=lambda: os.environ.get('DARA_DEV_LOG_LEVEL', None), help='Dev logger level to use')
|
|
61
61
|
@click.option('--reload-dir', multiple=True, help='Directories to watch for reload')
|
|
62
62
|
@click.option('--skip-jsbuild', is_flag=True, help='Whether to skip building the JS assets')
|
|
63
|
+
@click.option(
|
|
64
|
+
'--base-url',
|
|
65
|
+
default=lambda: os.environ.get('DARA_BASE_URL', None),
|
|
66
|
+
help='An optional base_url for running a Dara app behind a proxy',
|
|
67
|
+
)
|
|
63
68
|
def start(
|
|
64
69
|
reload: bool,
|
|
65
70
|
enable_hmr: bool,
|
|
@@ -76,6 +81,7 @@ def start(
|
|
|
76
81
|
log: Optional[str],
|
|
77
82
|
reload_dir: Optional[List[str]],
|
|
78
83
|
skip_jsbuild: bool,
|
|
84
|
+
base_url: Optional[str],
|
|
79
85
|
):
|
|
80
86
|
if config is None:
|
|
81
87
|
folder_name = os.path.basename(os.getcwd()).replace('-', '_')
|
|
@@ -127,6 +133,10 @@ def start(
|
|
|
127
133
|
if skip_jsbuild:
|
|
128
134
|
os.environ['SKIP_JSBUILD'] = 'TRUE'
|
|
129
135
|
|
|
136
|
+
# Ensure the base_url is set as an env var as well
|
|
137
|
+
if base_url:
|
|
138
|
+
os.environ['DARA_BASE_URL'] = base_url
|
|
139
|
+
|
|
130
140
|
# Check that if production/dev mode is set, node is installed - unless we're in docker mode, or explicitly skipping jsbuild
|
|
131
141
|
if not docker and not skip_jsbuild and (production or enable_hmr):
|
|
132
142
|
exit_code = os.system('node -v')
|
|
@@ -176,6 +186,8 @@ def start(
|
|
|
176
186
|
log_config=logging_config,
|
|
177
187
|
limit_max_requests=limit_max_requests,
|
|
178
188
|
lifespan='on',
|
|
189
|
+
# This matches the default on the uvicorn cli
|
|
190
|
+
root_path='' if base_url is None else base_url,
|
|
179
191
|
)
|
|
180
192
|
|
|
181
193
|
|
dara/core/configuration.py
CHANGED
|
@@ -33,7 +33,7 @@ from typing import (
|
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
from fastapi.middleware import Middleware
|
|
36
|
-
from pydantic
|
|
36
|
+
from pydantic import BaseModel, ConfigDict
|
|
37
37
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
38
38
|
|
|
39
39
|
from dara.core.auth.base import BaseAuthConfig
|
|
@@ -65,7 +65,7 @@ from dara.core.visual.components import RawString
|
|
|
65
65
|
from dara.core.visual.themes import BaseTheme, ThemeDef
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
class Configuration(
|
|
68
|
+
class Configuration(BaseModel):
|
|
69
69
|
"""Definition of the main framework configuration"""
|
|
70
70
|
|
|
71
71
|
auth_config: BaseAuthConfig
|
|
@@ -85,7 +85,7 @@ class Configuration(GenericModel):
|
|
|
85
85
|
static_files_dir: str
|
|
86
86
|
package_tag_processors: List[Callable[[Dict[str, List[str]]], Dict[str, List[str]]]]
|
|
87
87
|
template_extra_js: str
|
|
88
|
-
task_module: Optional[str]
|
|
88
|
+
task_module: Optional[str] = None
|
|
89
89
|
template: str
|
|
90
90
|
template_renderers: Dict[str, Callable[..., Template]]
|
|
91
91
|
theme: Union[BaseTheme, str]
|
|
@@ -93,10 +93,7 @@ class Configuration(GenericModel):
|
|
|
93
93
|
ws_handlers: Dict[str, Callable[[str, Any], Any]]
|
|
94
94
|
encoders: Dict[Type[Any], Encoder]
|
|
95
95
|
middlewares: List[Middleware]
|
|
96
|
-
|
|
97
|
-
class Config:
|
|
98
|
-
extra = 'forbid'
|
|
99
|
-
arbitrary_types_allowed = True
|
|
96
|
+
model_config = ConfigDict(extra='forbid', arbitrary_types_allowed=True)
|
|
100
97
|
|
|
101
98
|
def get_package_map(self) -> Dict[str, str]:
|
|
102
99
|
"""
|
|
@@ -117,7 +114,7 @@ class Configuration(GenericModel):
|
|
|
117
114
|
packages[act_def.py_module] = act_def.js_module
|
|
118
115
|
|
|
119
116
|
# Handle auth components
|
|
120
|
-
for comp in self.auth_config.component_config.
|
|
117
|
+
for comp in self.auth_config.component_config.model_dump().values():
|
|
121
118
|
packages[comp['py_module']] = comp['js_module']
|
|
122
119
|
|
|
123
120
|
return packages
|
dara/core/defaults.py
CHANGED
|
@@ -39,8 +39,6 @@ from dara.core.visual.components import (
|
|
|
39
39
|
DynamicComponent,
|
|
40
40
|
DynamicComponentDef,
|
|
41
41
|
Fallback,
|
|
42
|
-
For,
|
|
43
|
-
ForDef,
|
|
44
42
|
Menu,
|
|
45
43
|
MenuDef,
|
|
46
44
|
ProgressTracker,
|
|
@@ -79,7 +77,6 @@ CORE_COMPONENTS: Dict[str, ComponentTypeAnnotation] = {
|
|
|
79
77
|
TopBarFrame.__name__: TopBarFrameDef,
|
|
80
78
|
Fallback.Default.py_component: DefaultFallbackDef,
|
|
81
79
|
Fallback.Row.py_component: RowFallbackDef,
|
|
82
|
-
For.__name__: ForDef,
|
|
83
80
|
}
|
|
84
81
|
|
|
85
82
|
# These actions are provided by the core JS of this module
|