dara-core 1.21.16__py3-none-any.whl → 1.21.17__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/auth/base.py +5 -5
- dara/core/auth/basic.py +3 -3
- dara/core/auth/definitions.py +13 -14
- dara/core/auth/routes.py +7 -5
- dara/core/auth/utils.py +11 -10
- dara/core/base_definitions.py +30 -36
- dara/core/cli.py +7 -8
- dara/core/configuration.py +51 -58
- dara/core/css.py +2 -2
- dara/core/data_utils.py +12 -17
- dara/core/defaults.py +3 -3
- dara/core/definitions.py +58 -63
- dara/core/http.py +4 -4
- dara/core/interactivity/actions.py +34 -42
- dara/core/interactivity/any_data_variable.py +1 -1
- dara/core/interactivity/any_variable.py +6 -5
- dara/core/interactivity/client_variable.py +1 -2
- dara/core/interactivity/condition.py +2 -2
- dara/core/interactivity/data_variable.py +2 -4
- dara/core/interactivity/derived_data_variable.py +7 -10
- dara/core/interactivity/derived_variable.py +45 -51
- dara/core/interactivity/filtering.py +19 -19
- dara/core/interactivity/loop_variable.py +2 -4
- dara/core/interactivity/non_data_variable.py +1 -1
- dara/core/interactivity/plain_variable.py +21 -18
- dara/core/interactivity/server_variable.py +13 -15
- dara/core/interactivity/state_variable.py +4 -5
- dara/core/interactivity/switch_variable.py +16 -16
- dara/core/interactivity/tabular_variable.py +3 -3
- dara/core/interactivity/url_variable.py +3 -3
- dara/core/internal/cache_store/cache_store.py +6 -6
- dara/core/internal/cache_store/keep_all.py +3 -3
- dara/core/internal/cache_store/lru.py +8 -8
- dara/core/internal/cache_store/ttl.py +4 -4
- dara/core/internal/custom_response.py +3 -3
- dara/core/internal/dependency_resolution.py +6 -10
- dara/core/internal/devtools.py +2 -3
- dara/core/internal/download.py +5 -6
- dara/core/internal/encoder_registry.py +7 -11
- dara/core/internal/execute_action.py +5 -5
- dara/core/internal/hashing.py +1 -2
- dara/core/internal/import_discovery.py +7 -9
- dara/core/internal/normalization.py +12 -15
- dara/core/internal/pandas_utils.py +6 -6
- dara/core/internal/pool/channel.py +3 -4
- dara/core/internal/pool/definitions.py +9 -9
- dara/core/internal/pool/task_pool.py +8 -8
- dara/core/internal/pool/utils.py +4 -3
- dara/core/internal/pool/worker.py +3 -3
- dara/core/internal/registries.py +4 -4
- dara/core/internal/registry.py +3 -3
- dara/core/internal/registry_lookup.py +4 -4
- dara/core/internal/routing.py +23 -22
- dara/core/internal/scheduler.py +8 -8
- dara/core/internal/settings.py +1 -2
- dara/core/internal/store.py +9 -9
- dara/core/internal/tasks.py +30 -30
- dara/core/internal/utils.py +9 -15
- dara/core/internal/websocket.py +18 -18
- dara/core/js_tooling/js_utils.py +19 -19
- dara/core/logging.py +13 -13
- dara/core/main.py +4 -5
- dara/core/metrics/cache.py +2 -4
- dara/core/persistence.py +19 -25
- dara/core/router/compat.py +1 -3
- dara/core/router/components.py +10 -10
- dara/core/router/dependency_graph.py +2 -4
- dara/core/router/router.py +43 -42
- dara/core/visual/components/dynamic_component.py +1 -3
- dara/core/visual/components/fallback.py +3 -3
- dara/core/visual/components/for_cmp.py +5 -5
- dara/core/visual/components/menu.py +1 -3
- dara/core/visual/components/router_content.py +1 -3
- dara/core/visual/components/sidebar_frame.py +8 -10
- dara/core/visual/components/theme_provider.py +3 -3
- dara/core/visual/components/topbar_frame.py +8 -10
- dara/core/visual/css/__init__.py +277 -277
- dara/core/visual/dynamic_component.py +18 -22
- dara/core/visual/progress_updater.py +1 -1
- dara/core/visual/template.py +10 -12
- dara/core/visual/themes/definitions.py +46 -46
- {dara_core-1.21.16.dist-info → dara_core-1.21.17.dist-info}/METADATA +12 -13
- dara_core-1.21.17.dist-info/RECORD +127 -0
- dara_core-1.21.16.dist-info/RECORD +0 -127
- {dara_core-1.21.16.dist-info → dara_core-1.21.17.dist-info}/LICENSE +0 -0
- {dara_core-1.21.16.dist-info → dara_core-1.21.17.dist-info}/WHEEL +0 -0
- {dara_core-1.21.16.dist-info → dara_core-1.21.17.dist-info}/entry_points.txt +0 -0
dara/core/auth/base.py
CHANGED
|
@@ -16,7 +16,7 @@ limitations under the License.
|
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
import abc
|
|
19
|
-
from typing import Any, ClassVar
|
|
19
|
+
from typing import Any, ClassVar
|
|
20
20
|
|
|
21
21
|
from fastapi import HTTPException, Response
|
|
22
22
|
from pydantic import model_serializer
|
|
@@ -57,7 +57,7 @@ class AuthComponentConfig(BaseModel):
|
|
|
57
57
|
logout: AuthComponent
|
|
58
58
|
"""Logout component"""
|
|
59
59
|
|
|
60
|
-
extra:
|
|
60
|
+
extra: dict[str, AuthComponent] = {}
|
|
61
61
|
"""Extra components, map of route -> component"""
|
|
62
62
|
|
|
63
63
|
@model_serializer()
|
|
@@ -72,7 +72,7 @@ class BaseAuthConfig(BaseModel, abc.ABC):
|
|
|
72
72
|
"""
|
|
73
73
|
|
|
74
74
|
@abc.abstractmethod
|
|
75
|
-
def get_token(self, body: SessionRequestBody) ->
|
|
75
|
+
def get_token(self, body: SessionRequestBody) -> TokenResponse | RedirectResponse:
|
|
76
76
|
"""
|
|
77
77
|
Get a session token.
|
|
78
78
|
|
|
@@ -82,7 +82,7 @@ class BaseAuthConfig(BaseModel, abc.ABC):
|
|
|
82
82
|
"""
|
|
83
83
|
|
|
84
84
|
@abc.abstractmethod
|
|
85
|
-
def verify_token(self, token: str) ->
|
|
85
|
+
def verify_token(self, token: str) -> Any | TokenData:
|
|
86
86
|
"""
|
|
87
87
|
Verify a session token.
|
|
88
88
|
|
|
@@ -105,7 +105,7 @@ class BaseAuthConfig(BaseModel, abc.ABC):
|
|
|
105
105
|
"""
|
|
106
106
|
raise HTTPException(400, f'Auth config {self.__class__.__name__} does not support token refresh')
|
|
107
107
|
|
|
108
|
-
def revoke_token(self, token: str, response: Response) ->
|
|
108
|
+
def revoke_token(self, token: str, response: Response) -> SuccessResponse | RedirectResponse:
|
|
109
109
|
"""
|
|
110
110
|
Revoke a session token.
|
|
111
111
|
|
dara/core/auth/basic.py
CHANGED
|
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
|
|
15
15
|
limitations under the License.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
from typing import Any, ClassVar
|
|
18
|
+
from typing import Any, ClassVar
|
|
19
19
|
|
|
20
20
|
import jwt
|
|
21
21
|
from fastapi import HTTPException
|
|
@@ -52,7 +52,7 @@ class BaseBasicAuthConfig(BaseAuthConfig):
|
|
|
52
52
|
login=BasicAuthLogin,
|
|
53
53
|
logout=BasicAuthLogout,
|
|
54
54
|
)
|
|
55
|
-
users:
|
|
55
|
+
users: dict[str, str]
|
|
56
56
|
|
|
57
57
|
def get_token(self, body: SessionRequestBody) -> TokenResponse:
|
|
58
58
|
"""
|
|
@@ -106,7 +106,7 @@ class BasicAuthConfig(BaseBasicAuthConfig):
|
|
|
106
106
|
class MultiBasicAuthConfig(BaseBasicAuthConfig):
|
|
107
107
|
"""Authenticate the dashboard with multiple users"""
|
|
108
108
|
|
|
109
|
-
def __init__(self, users:
|
|
109
|
+
def __init__(self, users: dict[str, str]):
|
|
110
110
|
super().__init__(users=users)
|
|
111
111
|
|
|
112
112
|
|
dara/core/auth/definitions.py
CHANGED
|
@@ -17,7 +17,6 @@ limitations under the License.
|
|
|
17
17
|
|
|
18
18
|
from contextvars import ContextVar
|
|
19
19
|
from datetime import datetime
|
|
20
|
-
from typing import List, Optional, Union
|
|
21
20
|
|
|
22
21
|
from typing_extensions import TypedDict
|
|
23
22
|
|
|
@@ -38,12 +37,12 @@ class TokenData(BaseModel):
|
|
|
38
37
|
"""
|
|
39
38
|
|
|
40
39
|
session_id: str
|
|
41
|
-
exp:
|
|
40
|
+
exp: float | int | datetime
|
|
42
41
|
identity_id: str
|
|
43
42
|
identity_name: str
|
|
44
|
-
identity_email:
|
|
45
|
-
id_token:
|
|
46
|
-
groups:
|
|
43
|
+
identity_email: str | None = None
|
|
44
|
+
id_token: str | None = None
|
|
45
|
+
groups: list[str] | None = []
|
|
47
46
|
|
|
48
47
|
|
|
49
48
|
class UserData(BaseModel):
|
|
@@ -58,8 +57,8 @@ class UserData(BaseModel):
|
|
|
58
57
|
|
|
59
58
|
identity_id: str
|
|
60
59
|
identity_name: str
|
|
61
|
-
identity_email:
|
|
62
|
-
groups:
|
|
60
|
+
identity_email: str | None = None
|
|
61
|
+
groups: list[str] | None = []
|
|
63
62
|
|
|
64
63
|
|
|
65
64
|
class TokenResponse(TypedDict):
|
|
@@ -75,15 +74,15 @@ class SuccessResponse(TypedDict):
|
|
|
75
74
|
|
|
76
75
|
|
|
77
76
|
class SessionRequestBody(BaseModel):
|
|
78
|
-
username:
|
|
79
|
-
password:
|
|
77
|
+
username: str | None = None
|
|
78
|
+
password: str | None = None
|
|
80
79
|
|
|
81
80
|
|
|
82
81
|
class AuthError(Exception):
|
|
83
|
-
detail:
|
|
82
|
+
detail: str | dict
|
|
84
83
|
code: int
|
|
85
84
|
|
|
86
|
-
def __init__(self, detail:
|
|
85
|
+
def __init__(self, detail: str | dict, code: int):
|
|
87
86
|
self.detail = detail
|
|
88
87
|
self.code = code
|
|
89
88
|
|
|
@@ -115,6 +114,6 @@ def BAD_REQUEST_ERROR(msg):
|
|
|
115
114
|
JWT_ALGO = 'HS256'
|
|
116
115
|
|
|
117
116
|
# Context
|
|
118
|
-
SESSION_ID: ContextVar[
|
|
119
|
-
USER: ContextVar[
|
|
120
|
-
ID_TOKEN: ContextVar[
|
|
117
|
+
SESSION_ID: ContextVar[str | None] = ContextVar('session_id', default=None)
|
|
118
|
+
USER: ContextVar[UserData | None] = ContextVar('user', default=None)
|
|
119
|
+
ID_TOKEN: ContextVar[str | None] = ContextVar('id_token', default=None)
|
dara/core/auth/routes.py
CHANGED
|
@@ -16,7 +16,7 @@ limitations under the License.
|
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
from inspect import iscoroutinefunction
|
|
19
|
-
from typing import
|
|
19
|
+
from typing import Annotated, cast
|
|
20
20
|
|
|
21
21
|
import jwt
|
|
22
22
|
from fastapi import APIRouter, Cookie, Depends, HTTPException, Request, Response
|
|
@@ -41,7 +41,7 @@ auth_router = APIRouter()
|
|
|
41
41
|
@auth_router.post('/verify-session')
|
|
42
42
|
async def verify_session(
|
|
43
43
|
req: Request,
|
|
44
|
-
credentials: HTTPAuthorizationCredentials
|
|
44
|
+
credentials: Annotated[HTTPAuthorizationCredentials, Depends(HTTPBearer(auto_error=False))],
|
|
45
45
|
):
|
|
46
46
|
"""
|
|
47
47
|
Helper to verify whether the user has a valid session JWT in the request they made. The function should be applied
|
|
@@ -86,7 +86,9 @@ async def verify_session(
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
@auth_router.post('/revoke-session')
|
|
89
|
-
async def _revoke_session(
|
|
89
|
+
async def _revoke_session(
|
|
90
|
+
response: Response, credentials: Annotated[HTTPAuthorizationCredentials, Depends(HTTPBearer())]
|
|
91
|
+
):
|
|
90
92
|
"""
|
|
91
93
|
Helper to revoke a session and its' associated token
|
|
92
94
|
"""
|
|
@@ -108,8 +110,8 @@ async def _revoke_session(response: Response, credentials: HTTPAuthorizationCred
|
|
|
108
110
|
@auth_router.post('/refresh-token')
|
|
109
111
|
async def handle_refresh_token(
|
|
110
112
|
response: Response,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
credentials: Annotated[HTTPAuthorizationCredentials, Depends(HTTPBearer())],
|
|
114
|
+
dara_refresh_token: Annotated[str | None, Cookie()] = None,
|
|
113
115
|
):
|
|
114
116
|
"""
|
|
115
117
|
Given a refresh token, issues a new session token and refresh token cookie.
|
dara/core/auth/utils.py
CHANGED
|
@@ -17,8 +17,9 @@ limitations under the License.
|
|
|
17
17
|
|
|
18
18
|
import asyncio
|
|
19
19
|
import uuid
|
|
20
|
+
from collections.abc import Callable
|
|
20
21
|
from datetime import datetime, timedelta, timezone
|
|
21
|
-
from typing import Any
|
|
22
|
+
from typing import Any
|
|
22
23
|
|
|
23
24
|
import jwt
|
|
24
25
|
from anyio import to_thread
|
|
@@ -53,11 +54,11 @@ def decode_token(token: str, **kwargs) -> TokenData:
|
|
|
53
54
|
def sign_jwt(
|
|
54
55
|
identity_id: str,
|
|
55
56
|
identity_name: str,
|
|
56
|
-
identity_email:
|
|
57
|
-
groups:
|
|
58
|
-
id_token:
|
|
59
|
-
exp:
|
|
60
|
-
session_id:
|
|
57
|
+
identity_email: str | None,
|
|
58
|
+
groups: list[str],
|
|
59
|
+
id_token: str | None = None,
|
|
60
|
+
exp: datetime | int | None = None,
|
|
61
|
+
session_id: str | None = None,
|
|
61
62
|
):
|
|
62
63
|
"""
|
|
63
64
|
Create a new Dara JWT token
|
|
@@ -115,8 +116,8 @@ class AsyncTokenRefreshCache:
|
|
|
115
116
|
"""
|
|
116
117
|
|
|
117
118
|
def __init__(self, ttl_seconds: int = 5):
|
|
118
|
-
self.cache:
|
|
119
|
-
self.locks:
|
|
119
|
+
self.cache: dict[str, tuple[Any, datetime]] = {}
|
|
120
|
+
self.locks: dict[str, asyncio.Lock] = {}
|
|
120
121
|
self.locks_lock = asyncio.Lock()
|
|
121
122
|
self.ttl = timedelta(seconds=ttl_seconds)
|
|
122
123
|
|
|
@@ -148,7 +149,7 @@ class AsyncTokenRefreshCache:
|
|
|
148
149
|
# We can modify self.locks here because we're always under an async lock when calling this
|
|
149
150
|
self.locks.pop(key, None)
|
|
150
151
|
|
|
151
|
-
def get_cached_value(self, key: str) ->
|
|
152
|
+
def get_cached_value(self, key: str) -> tuple[Any, bool]:
|
|
152
153
|
"""
|
|
153
154
|
Retrieve a value from the cache if it exists and hasn't expired.
|
|
154
155
|
|
|
@@ -186,7 +187,7 @@ Shared token refresh cache instance
|
|
|
186
187
|
|
|
187
188
|
|
|
188
189
|
async def cached_refresh_token(
|
|
189
|
-
do_refresh_token: Callable[[TokenData, str],
|
|
190
|
+
do_refresh_token: Callable[[TokenData, str], tuple[str, str]], old_token_data: TokenData, refresh_token: str
|
|
190
191
|
):
|
|
191
192
|
"""
|
|
192
193
|
A utility to run a token refresh method with caching to prevent multiple concurrent refreshes
|
dara/core/base_definitions.py
CHANGED
|
@@ -21,21 +21,15 @@ from __future__ import annotations
|
|
|
21
21
|
# between other parts of the framework
|
|
22
22
|
import abc
|
|
23
23
|
import uuid
|
|
24
|
-
from collections.abc import Awaitable, Mapping
|
|
24
|
+
from collections.abc import Awaitable, Callable, Mapping
|
|
25
25
|
from enum import Enum
|
|
26
26
|
from inspect import isclass
|
|
27
27
|
from typing import (
|
|
28
28
|
TYPE_CHECKING,
|
|
29
29
|
Annotated,
|
|
30
30
|
Any,
|
|
31
|
-
Callable,
|
|
32
31
|
ClassVar,
|
|
33
|
-
Dict,
|
|
34
|
-
List,
|
|
35
32
|
Literal,
|
|
36
|
-
Optional,
|
|
37
|
-
Tuple,
|
|
38
|
-
Union,
|
|
39
33
|
get_args,
|
|
40
34
|
get_origin,
|
|
41
35
|
)
|
|
@@ -84,7 +78,7 @@ def annotation_has_base_model(typ: Any) -> bool:
|
|
|
84
78
|
# It works by adding SerializeAsAny to all fields of the model.
|
|
85
79
|
# See https://github.com/pydantic/pydantic/issues/6381
|
|
86
80
|
class SerializeAsAnyMeta(ModelMetaclass):
|
|
87
|
-
def __new__(cls, name: str, bases:
|
|
81
|
+
def __new__(cls, name: str, bases: tuple[type], namespaces: dict[str, Any], **kwargs):
|
|
88
82
|
annotations: dict = namespaces.get('__annotations__', {}).copy()
|
|
89
83
|
|
|
90
84
|
for base in bases:
|
|
@@ -267,7 +261,7 @@ class Cache:
|
|
|
267
261
|
)
|
|
268
262
|
|
|
269
263
|
@classmethod
|
|
270
|
-
def from_dict(cls, arg: dict) ->
|
|
264
|
+
def from_dict(cls, arg: dict) -> BaseCachePolicy | None:
|
|
271
265
|
"""
|
|
272
266
|
Construct a cache policy from its serialized dict represetnation
|
|
273
267
|
"""
|
|
@@ -288,7 +282,7 @@ class Cache:
|
|
|
288
282
|
raise ValueError(f'Invalid cache policy: {arg}')
|
|
289
283
|
|
|
290
284
|
|
|
291
|
-
CacheArgType =
|
|
285
|
+
CacheArgType = CacheType | BaseCachePolicy | str
|
|
292
286
|
|
|
293
287
|
|
|
294
288
|
class CachedRegistryEntry(BaseModel):
|
|
@@ -297,7 +291,7 @@ class CachedRegistryEntry(BaseModel):
|
|
|
297
291
|
via the cache policy.
|
|
298
292
|
"""
|
|
299
293
|
|
|
300
|
-
cache:
|
|
294
|
+
cache: BaseCachePolicy | None = None
|
|
301
295
|
uid: str
|
|
302
296
|
|
|
303
297
|
def to_store_key(self):
|
|
@@ -327,18 +321,18 @@ class TaskProgressUpdate(BaseTaskMessage):
|
|
|
327
321
|
|
|
328
322
|
class TaskResult(BaseTaskMessage):
|
|
329
323
|
result: Any
|
|
330
|
-
cache_key:
|
|
331
|
-
reg_entry:
|
|
324
|
+
cache_key: str | None = None
|
|
325
|
+
reg_entry: CachedRegistryEntry | None = None
|
|
332
326
|
|
|
333
327
|
|
|
334
328
|
class TaskError(BaseTaskMessage):
|
|
335
329
|
error: BaseException
|
|
336
|
-
cache_key:
|
|
337
|
-
reg_entry:
|
|
330
|
+
cache_key: str | None = None
|
|
331
|
+
reg_entry: CachedRegistryEntry | None = None
|
|
338
332
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
339
333
|
|
|
340
334
|
|
|
341
|
-
TaskMessage =
|
|
335
|
+
TaskMessage = TaskProgressUpdate | TaskResult | TaskError
|
|
342
336
|
|
|
343
337
|
|
|
344
338
|
class BaseTask(abc.ABC):
|
|
@@ -346,18 +340,18 @@ class BaseTask(abc.ABC):
|
|
|
346
340
|
Generic BaseTask that can be used for type checking tasks
|
|
347
341
|
"""
|
|
348
342
|
|
|
349
|
-
cache_key:
|
|
350
|
-
reg_entry:
|
|
351
|
-
notify_channels:
|
|
343
|
+
cache_key: str | None
|
|
344
|
+
reg_entry: CachedRegistryEntry | None
|
|
345
|
+
notify_channels: list[str]
|
|
352
346
|
task_id: str
|
|
353
347
|
|
|
354
348
|
@abc.abstractmethod
|
|
355
|
-
def __init__(self, task_id:
|
|
349
|
+
def __init__(self, task_id: str | None = None) -> None:
|
|
356
350
|
self.task_id = str(uuid.uuid4()) if task_id is None else task_id
|
|
357
351
|
super().__init__()
|
|
358
352
|
|
|
359
353
|
@abc.abstractmethod
|
|
360
|
-
async def run(self, send_stream:
|
|
354
|
+
async def run(self, send_stream: MemoryObjectSendStream[TaskMessage] | None = None) -> Any: ...
|
|
361
355
|
|
|
362
356
|
@abc.abstractmethod
|
|
363
357
|
async def cancel(self): ...
|
|
@@ -369,18 +363,18 @@ class PendingTask(BaseTask):
|
|
|
369
363
|
Is associated to an underlying task definition.
|
|
370
364
|
"""
|
|
371
365
|
|
|
372
|
-
def __init__(self, task_id: str, task_def: BaseTask, ws_channel:
|
|
366
|
+
def __init__(self, task_id: str, task_def: BaseTask, ws_channel: str | None = None):
|
|
373
367
|
self.task_id = task_id
|
|
374
368
|
self.task_def = task_def
|
|
375
369
|
self.notify_channels = [ws_channel] if ws_channel else []
|
|
376
370
|
|
|
377
|
-
self.cancel_scope:
|
|
371
|
+
self.cancel_scope: anyio.CancelScope | None = None
|
|
378
372
|
self.event = anyio.Event()
|
|
379
|
-
self.result:
|
|
380
|
-
self.error:
|
|
373
|
+
self.result: Any | None = None
|
|
374
|
+
self.error: BaseException | None = None
|
|
381
375
|
self.subscribers = 1
|
|
382
376
|
|
|
383
|
-
async def run(self, send_stream:
|
|
377
|
+
async def run(self, send_stream: MemoryObjectSendStream[TaskMessage] | None = None):
|
|
384
378
|
"""
|
|
385
379
|
Wait for the task to complete
|
|
386
380
|
"""
|
|
@@ -445,17 +439,17 @@ class PendingTask(BaseTask):
|
|
|
445
439
|
|
|
446
440
|
|
|
447
441
|
class RouterPath(BaseModel):
|
|
448
|
-
pathname:
|
|
442
|
+
pathname: str | None = None
|
|
449
443
|
"""
|
|
450
444
|
A URL pathname, beginning with '/'.
|
|
451
445
|
"""
|
|
452
446
|
|
|
453
|
-
search:
|
|
447
|
+
search: str | None = None
|
|
454
448
|
"""
|
|
455
449
|
A URL search string, beginning with '?'.
|
|
456
450
|
"""
|
|
457
451
|
|
|
458
|
-
hash:
|
|
452
|
+
hash: str | None = None
|
|
459
453
|
"""
|
|
460
454
|
A URL hash string, beginning with '#'.
|
|
461
455
|
"""
|
|
@@ -542,8 +536,8 @@ class ActionImpl(DaraBaseModel):
|
|
|
542
536
|
Required for non-local actions which have a JS implementation.
|
|
543
537
|
"""
|
|
544
538
|
|
|
545
|
-
js_module: ClassVar[
|
|
546
|
-
py_name: ClassVar[
|
|
539
|
+
js_module: ClassVar[str | None] = None
|
|
540
|
+
py_name: ClassVar[str | None] = None
|
|
547
541
|
|
|
548
542
|
async def execute(self, ctx: ActionCtx) -> Any:
|
|
549
543
|
"""
|
|
@@ -570,13 +564,13 @@ class ActionImpl(DaraBaseModel):
|
|
|
570
564
|
return jsonable_encoder(value)
|
|
571
565
|
|
|
572
566
|
|
|
573
|
-
ActionInstance =
|
|
567
|
+
ActionInstance = ActionImpl | AnnotatedAction
|
|
574
568
|
"""
|
|
575
569
|
@deprecated alias for backwards compatibility
|
|
576
570
|
"""
|
|
577
571
|
|
|
578
572
|
# TODO: remove List[AnnotatedAction] support in 2.0
|
|
579
|
-
Action =
|
|
573
|
+
Action = ActionImpl | AnnotatedAction | list[AnnotatedAction | ActionImpl]
|
|
580
574
|
"""
|
|
581
575
|
Definition of an action that can be executed by the frontend.
|
|
582
576
|
Supports:
|
|
@@ -601,14 +595,14 @@ class ActionDef(BaseModel):
|
|
|
601
595
|
|
|
602
596
|
name: str
|
|
603
597
|
py_module: str
|
|
604
|
-
js_module:
|
|
598
|
+
js_module: str | None = None
|
|
605
599
|
|
|
606
600
|
|
|
607
601
|
class ActionResolverDef(BaseModel):
|
|
608
602
|
uid: str
|
|
609
603
|
"""Unique id of the action definition"""
|
|
610
604
|
|
|
611
|
-
resolver:
|
|
605
|
+
resolver: Callable | None = None
|
|
612
606
|
"""Resolver function for the action"""
|
|
613
607
|
|
|
614
608
|
execute_action: Callable[..., Awaitable[Any]]
|
|
@@ -616,7 +610,7 @@ class ActionResolverDef(BaseModel):
|
|
|
616
610
|
|
|
617
611
|
|
|
618
612
|
class UploadResolverDef(BaseModel):
|
|
619
|
-
resolver:
|
|
613
|
+
resolver: Callable | None = None
|
|
620
614
|
"""Optional custom resolver function for the upload"""
|
|
621
615
|
upload: Callable
|
|
622
616
|
"""Upload handling function, default dara.core.interactivity.any_data_variable.upload"""
|
dara/core/cli.py
CHANGED
|
@@ -19,7 +19,6 @@ import logging
|
|
|
19
19
|
import os
|
|
20
20
|
import pathlib
|
|
21
21
|
import subprocess
|
|
22
|
-
from typing import List, Optional
|
|
23
22
|
|
|
24
23
|
import uvicorn
|
|
25
24
|
|
|
@@ -69,19 +68,19 @@ def start(
|
|
|
69
68
|
reload: bool,
|
|
70
69
|
enable_hmr: bool,
|
|
71
70
|
production: bool,
|
|
72
|
-
config:
|
|
73
|
-
port:
|
|
74
|
-
metrics_port:
|
|
71
|
+
config: str | None,
|
|
72
|
+
port: int | None,
|
|
73
|
+
metrics_port: int | None,
|
|
75
74
|
disable_metrics: bool,
|
|
76
75
|
host: str,
|
|
77
76
|
rebuild: bool,
|
|
78
77
|
require_sso: bool,
|
|
79
78
|
docker: bool,
|
|
80
|
-
debug:
|
|
81
|
-
log:
|
|
82
|
-
reload_dir:
|
|
79
|
+
debug: str | None,
|
|
80
|
+
log: str | None,
|
|
81
|
+
reload_dir: list[str] | None,
|
|
83
82
|
skip_jsbuild: bool,
|
|
84
|
-
base_url:
|
|
83
|
+
base_url: str | None,
|
|
85
84
|
):
|
|
86
85
|
if config is None:
|
|
87
86
|
folder_name = os.path.basename(os.getcwd()).replace('-', '_')
|