dara-core 1.17.5__py3-none-any.whl → 1.18.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 +2 -0
- dara/core/actions.py +1 -2
- dara/core/auth/basic.py +9 -9
- dara/core/auth/routes.py +5 -5
- dara/core/auth/utils.py +4 -4
- dara/core/base_definitions.py +15 -22
- dara/core/cli.py +8 -7
- dara/core/configuration.py +5 -2
- dara/core/css.py +1 -2
- dara/core/data_utils.py +2 -2
- dara/core/defaults.py +4 -7
- dara/core/definitions.py +6 -9
- dara/core/http.py +7 -3
- dara/core/interactivity/actions.py +28 -30
- dara/core/interactivity/any_data_variable.py +6 -5
- dara/core/interactivity/any_variable.py +4 -7
- dara/core/interactivity/data_variable.py +1 -1
- dara/core/interactivity/derived_data_variable.py +7 -6
- dara/core/interactivity/derived_variable.py +93 -33
- dara/core/interactivity/filtering.py +19 -27
- dara/core/interactivity/plain_variable.py +3 -2
- dara/core/interactivity/switch_variable.py +4 -4
- dara/core/internal/cache_store/base_impl.py +2 -1
- dara/core/internal/cache_store/cache_store.py +17 -5
- dara/core/internal/cache_store/keep_all.py +4 -1
- dara/core/internal/cache_store/lru.py +5 -1
- dara/core/internal/cache_store/ttl.py +4 -1
- dara/core/internal/cgroup.py +1 -1
- dara/core/internal/dependency_resolution.py +46 -10
- dara/core/internal/devtools.py +2 -2
- dara/core/internal/download.py +4 -3
- dara/core/internal/encoder_registry.py +7 -7
- dara/core/internal/execute_action.py +4 -10
- dara/core/internal/hashing.py +1 -3
- dara/core/internal/import_discovery.py +3 -4
- dara/core/internal/normalization.py +9 -13
- dara/core/internal/pandas_utils.py +3 -3
- dara/core/internal/pool/task_pool.py +16 -10
- dara/core/internal/pool/utils.py +5 -7
- dara/core/internal/pool/worker.py +3 -2
- dara/core/internal/port_utils.py +1 -1
- dara/core/internal/registries.py +9 -4
- dara/core/internal/registry.py +3 -1
- dara/core/internal/registry_lookup.py +7 -3
- dara/core/internal/routing.py +77 -44
- dara/core/internal/scheduler.py +13 -8
- dara/core/internal/settings.py +2 -2
- dara/core/internal/tasks.py +8 -14
- dara/core/internal/utils.py +11 -10
- dara/core/internal/websocket.py +18 -19
- dara/core/js_tooling/js_utils.py +23 -24
- dara/core/logging.py +3 -6
- dara/core/main.py +14 -11
- dara/core/metrics/cache.py +1 -1
- dara/core/metrics/utils.py +3 -3
- dara/core/persistence.py +1 -1
- dara/core/umd/dara.core.umd.js +149 -128
- dara/core/visual/components/__init__.py +2 -2
- dara/core/visual/components/fallback.py +3 -3
- dara/core/visual/css/__init__.py +30 -31
- dara/core/visual/dynamic_component.py +10 -11
- dara/core/visual/progress_updater.py +4 -3
- {dara_core-1.17.5.dist-info → dara_core-1.18.0.dist-info}/METADATA +10 -10
- dara_core-1.18.0.dist-info/RECORD +114 -0
- dara_core-1.17.5.dist-info/RECORD +0 -114
- {dara_core-1.17.5.dist-info → dara_core-1.18.0.dist-info}/LICENSE +0 -0
- {dara_core-1.17.5.dist-info → dara_core-1.18.0.dist-info}/WHEEL +0 -0
- {dara_core-1.17.5.dist-info → dara_core-1.18.0.dist-info}/entry_points.txt +0 -0
dara/core/__init__.py
CHANGED
|
@@ -14,6 +14,8 @@ 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
|
+
# ruff: noqa: F403, F405
|
|
18
|
+
|
|
17
19
|
from importlib.metadata import version
|
|
18
20
|
|
|
19
21
|
from pydantic import BaseModel
|
dara/core/actions.py
CHANGED
|
@@ -16,8 +16,7 @@ limitations under the License.
|
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
# Re-export actions so users can import from dara.core.actions instead of dara_core.interactivity
|
|
19
|
-
#
|
|
20
|
-
from dara.core.interactivity import (
|
|
19
|
+
from dara.core.interactivity import ( # noqa: F401
|
|
21
20
|
DownloadContent,
|
|
22
21
|
DownloadContentImpl,
|
|
23
22
|
DownloadVariable,
|
dara/core/auth/basic.py
CHANGED
|
@@ -91,10 +91,10 @@ class BaseBasicAuthConfig(BaseAuthConfig):
|
|
|
91
91
|
)
|
|
92
92
|
)
|
|
93
93
|
return decoded
|
|
94
|
-
except jwt.ExpiredSignatureError:
|
|
95
|
-
raise AuthError(EXPIRED_TOKEN_ERROR, 401)
|
|
96
|
-
except jwt.DecodeError:
|
|
97
|
-
raise AuthError(INVALID_TOKEN_ERROR, 401)
|
|
94
|
+
except jwt.ExpiredSignatureError as e:
|
|
95
|
+
raise AuthError(EXPIRED_TOKEN_ERROR, 401) from e
|
|
96
|
+
except jwt.DecodeError as e:
|
|
97
|
+
raise AuthError(INVALID_TOKEN_ERROR, 401) from e
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
class BasicAuthConfig(BaseBasicAuthConfig):
|
|
@@ -121,7 +121,7 @@ class DefaultAuthConfig(BaseAuthConfig):
|
|
|
121
121
|
logout=BasicAuthLogout,
|
|
122
122
|
)
|
|
123
123
|
|
|
124
|
-
def get_token(self,
|
|
124
|
+
def get_token(self, body: SessionRequestBody) -> TokenResponse:
|
|
125
125
|
"""
|
|
126
126
|
Get a session token.
|
|
127
127
|
|
|
@@ -142,7 +142,7 @@ class DefaultAuthConfig(BaseAuthConfig):
|
|
|
142
142
|
decoded = jwt.decode(token, get_settings().jwt_secret, algorithms=[JWT_ALGO])
|
|
143
143
|
SESSION_ID.set(decoded.get('session_id'))
|
|
144
144
|
return TokenData.parse_obj(decoded)
|
|
145
|
-
except jwt.ExpiredSignatureError:
|
|
146
|
-
raise AuthError(EXPIRED_TOKEN_ERROR, 401)
|
|
147
|
-
except jwt.DecodeError:
|
|
148
|
-
raise AuthError(INVALID_TOKEN_ERROR, 401)
|
|
145
|
+
except jwt.ExpiredSignatureError as e:
|
|
146
|
+
raise AuthError(EXPIRED_TOKEN_ERROR, 401) from e
|
|
147
|
+
except jwt.DecodeError as e:
|
|
148
|
+
raise AuthError(INVALID_TOKEN_ERROR, 401) from e
|
dara/core/auth/routes.py
CHANGED
|
@@ -75,13 +75,13 @@ async def verify_session(
|
|
|
75
75
|
return SESSION_ID.get()
|
|
76
76
|
except jwt.ExpiredSignatureError as e:
|
|
77
77
|
dev_logger.error('Expired Token Signature', error=e)
|
|
78
|
-
raise HTTPException(status_code=401, detail=EXPIRED_TOKEN_ERROR)
|
|
78
|
+
raise HTTPException(status_code=401, detail=EXPIRED_TOKEN_ERROR) from e
|
|
79
79
|
except jwt.PyJWTError as e:
|
|
80
80
|
dev_logger.error('Invalid Token', error=e)
|
|
81
|
-
raise HTTPException(status_code=401, detail=INVALID_TOKEN_ERROR)
|
|
81
|
+
raise HTTPException(status_code=401, detail=INVALID_TOKEN_ERROR) from e
|
|
82
82
|
except AuthError as err:
|
|
83
83
|
dev_logger.error('Auth Error', error=err)
|
|
84
|
-
raise HTTPException(status_code=err.code, detail=err.detail)
|
|
84
|
+
raise HTTPException(status_code=err.code, detail=err.detail) from err
|
|
85
85
|
raise HTTPException(status_code=400, detail=BAD_REQUEST_ERROR('No auth credentials passed'))
|
|
86
86
|
|
|
87
87
|
|
|
@@ -162,11 +162,11 @@ async def handle_refresh_token(
|
|
|
162
162
|
# Explicitly handle expired signature error
|
|
163
163
|
if isinstance(e, jwt.ExpiredSignatureError):
|
|
164
164
|
dev_logger.error('Expired Token Signature', error=e)
|
|
165
|
-
raise HTTPException(status_code=401, detail=EXPIRED_TOKEN_ERROR, headers=headers)
|
|
165
|
+
raise HTTPException(status_code=401, detail=EXPIRED_TOKEN_ERROR, headers=headers) from e
|
|
166
166
|
|
|
167
167
|
# Otherwise show a generic invalid token error
|
|
168
168
|
dev_logger.error('Invalid Token', error=cast(Exception, e))
|
|
169
|
-
raise HTTPException(status_code=401, detail=INVALID_TOKEN_ERROR, headers=headers)
|
|
169
|
+
raise HTTPException(status_code=401, detail=INVALID_TOKEN_ERROR, headers=headers) from e
|
|
170
170
|
|
|
171
171
|
|
|
172
172
|
# Request to retrieve a session token from the backend. The app does this on startup.
|
dara/core/auth/utils.py
CHANGED
|
@@ -44,10 +44,10 @@ def decode_token(token: str, **kwargs) -> TokenData:
|
|
|
44
44
|
"""
|
|
45
45
|
try:
|
|
46
46
|
return TokenData.parse_obj(jwt.decode(token, get_settings().jwt_secret, algorithms=[JWT_ALGO], **kwargs))
|
|
47
|
-
except jwt.ExpiredSignatureError:
|
|
48
|
-
raise AuthError(code=401, detail=EXPIRED_TOKEN_ERROR)
|
|
49
|
-
except jwt.DecodeError:
|
|
50
|
-
raise AuthError(code=401, detail=INVALID_TOKEN_ERROR)
|
|
47
|
+
except jwt.ExpiredSignatureError as e:
|
|
48
|
+
raise AuthError(code=401, detail=EXPIRED_TOKEN_ERROR) from e
|
|
49
|
+
except jwt.DecodeError as e:
|
|
50
|
+
raise AuthError(code=401, detail=INVALID_TOKEN_ERROR) from e
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
def sign_jwt(
|
dara/core/base_definitions.py
CHANGED
|
@@ -21,17 +21,16 @@ 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
25
|
from enum import Enum
|
|
25
26
|
from typing import (
|
|
26
27
|
TYPE_CHECKING,
|
|
27
28
|
Annotated,
|
|
28
29
|
Any,
|
|
29
|
-
Awaitable,
|
|
30
30
|
Callable,
|
|
31
31
|
ClassVar,
|
|
32
32
|
Dict,
|
|
33
33
|
List,
|
|
34
|
-
Mapping,
|
|
35
34
|
Optional,
|
|
36
35
|
Tuple,
|
|
37
36
|
Union,
|
|
@@ -67,7 +66,7 @@ def annotation_has_base_model(typ: Any) -> bool:
|
|
|
67
66
|
type_args = get_args(typ)
|
|
68
67
|
if len(type_args) > 0:
|
|
69
68
|
return any(annotation_has_base_model(arg) for arg in type_args)
|
|
70
|
-
except:
|
|
69
|
+
except: # noqa: E722
|
|
71
70
|
# canot get arguments, should be a simple type
|
|
72
71
|
pass
|
|
73
72
|
|
|
@@ -79,9 +78,7 @@ def annotation_has_base_model(typ: Any) -> bool:
|
|
|
79
78
|
# It works by adding SerializeAsAny to all fields of the model.
|
|
80
79
|
# See https://github.com/pydantic/pydantic/issues/6381
|
|
81
80
|
class SerializeAsAnyMeta(ModelMetaclass):
|
|
82
|
-
def __new__(
|
|
83
|
-
self, name: str, bases: Tuple[type], namespaces: Dict[str, Any], **kwargs
|
|
84
|
-
): # pylint: disable=bad-mcs-classmethod-argument
|
|
81
|
+
def __new__(cls, name: str, bases: Tuple[type], namespaces: Dict[str, Any], **kwargs):
|
|
85
82
|
annotations: dict = namespaces.get('__annotations__', {}).copy()
|
|
86
83
|
|
|
87
84
|
for base in bases:
|
|
@@ -97,11 +94,11 @@ class SerializeAsAnyMeta(ModelMetaclass):
|
|
|
97
94
|
if isinstance(annotation, str) or annotation is ClassVar:
|
|
98
95
|
continue
|
|
99
96
|
if annotation_has_base_model(annotation):
|
|
100
|
-
annotations[field] = SerializeAsAny[annotation]
|
|
97
|
+
annotations[field] = SerializeAsAny[annotation] # type: ignore
|
|
101
98
|
|
|
102
99
|
namespaces['__annotations__'] = annotations
|
|
103
100
|
|
|
104
|
-
return super().__new__(
|
|
101
|
+
return super().__new__(cls, name, bases, namespaces, **kwargs)
|
|
105
102
|
|
|
106
103
|
|
|
107
104
|
class DaraBaseModel(BaseModel, metaclass=SerializeAsAnyMeta):
|
|
@@ -135,16 +132,17 @@ class DaraBaseModel(BaseModel, metaclass=SerializeAsAnyMeta):
|
|
|
135
132
|
and annotation_has_base_model(field_info.annotation)
|
|
136
133
|
):
|
|
137
134
|
# Skip if it has metadata that is already annotated with SerializeAsAny
|
|
138
|
-
if any(isinstance(x, SerializeAsAny) for x in field_info.metadata):
|
|
135
|
+
if any(isinstance(x, SerializeAsAny) for x in field_info.metadata): # type: ignore
|
|
139
136
|
continue
|
|
140
137
|
# Skip if the type is already annotated with SerializeAsAny
|
|
141
138
|
if get_origin(field_info.annotation) is Annotated and any(
|
|
142
|
-
isinstance(arg, SerializeAsAny)
|
|
139
|
+
isinstance(arg, SerializeAsAny) # pyright: ignore[reportArgumentType]
|
|
140
|
+
for arg in field_info.annotation.__metadata__ # type: ignore
|
|
143
141
|
):
|
|
144
142
|
continue
|
|
145
143
|
|
|
146
|
-
field_info.annotation = SerializeAsAny[field_info.annotation]
|
|
147
|
-
field_info.metadata = list(field_info.annotation.__metadata__)
|
|
144
|
+
field_info.annotation = SerializeAsAny[field_info.annotation] # type: ignore
|
|
145
|
+
field_info.metadata = list(field_info.annotation.__metadata__) # type: ignore
|
|
148
146
|
|
|
149
147
|
# Rebuild again with force to ensure we rebuild the schema with new annotations
|
|
150
148
|
return super().model_rebuild(
|
|
@@ -255,10 +253,8 @@ class Cache:
|
|
|
255
253
|
if isinstance(arg, Cache.Type):
|
|
256
254
|
return LruCachePolicy(cache_type=arg)
|
|
257
255
|
|
|
258
|
-
if isinstance(arg, str):
|
|
259
|
-
|
|
260
|
-
if typ := Cache.Type.get_member(arg):
|
|
261
|
-
return LruCachePolicy(cache_type=typ)
|
|
256
|
+
if isinstance(arg, str) and (typ := Cache.Type.get_member(arg)):
|
|
257
|
+
return LruCachePolicy(cache_type=typ)
|
|
262
258
|
|
|
263
259
|
raise ValueError(
|
|
264
260
|
f'Invalid cache argument: {arg}. Please provide a Cache.Policy object or one of Cache.Type members'
|
|
@@ -349,12 +345,10 @@ class BaseTask(abc.ABC):
|
|
|
349
345
|
super().__init__()
|
|
350
346
|
|
|
351
347
|
@abc.abstractmethod
|
|
352
|
-
async def run(self, send_stream: Optional[MemoryObjectSendStream[TaskMessage]] = None) -> Any:
|
|
353
|
-
...
|
|
348
|
+
async def run(self, send_stream: Optional[MemoryObjectSendStream[TaskMessage]] = None) -> Any: ...
|
|
354
349
|
|
|
355
350
|
@abc.abstractmethod
|
|
356
|
-
async def cancel(self):
|
|
357
|
-
...
|
|
351
|
+
async def cancel(self): ...
|
|
358
352
|
|
|
359
353
|
|
|
360
354
|
class PendingTask(BaseTask):
|
|
@@ -504,12 +498,11 @@ class AnnotatedAction(BaseModel):
|
|
|
504
498
|
dynamic_kwargs: Mapping[str, Any]
|
|
505
499
|
"""Dynamic kwargs of the action; uid -> variable instance"""
|
|
506
500
|
|
|
507
|
-
loading:
|
|
501
|
+
loading: Variable # type: ignore # noqa: F821
|
|
508
502
|
"""Loading Variable instance"""
|
|
509
503
|
|
|
510
504
|
def __init__(self, **data):
|
|
511
505
|
# Resolve the circular dependency to add a loading Variable to the model upon creation
|
|
512
|
-
# pylint: disable-next=import-error, import-outside-toplevel
|
|
513
506
|
from dara.core.interactivity.plain_variable import Variable
|
|
514
507
|
|
|
515
508
|
self.model_rebuild()
|
dara/core/cli.py
CHANGED
|
@@ -21,10 +21,10 @@ import pathlib
|
|
|
21
21
|
import subprocess
|
|
22
22
|
from typing import List, Optional
|
|
23
23
|
|
|
24
|
-
import click
|
|
25
24
|
import uvicorn
|
|
26
|
-
from click.exceptions import UsageError
|
|
27
25
|
|
|
26
|
+
import click
|
|
27
|
+
from click.exceptions import UsageError
|
|
28
28
|
from dara.core.internal.port_utils import find_available_port
|
|
29
29
|
from dara.core.internal.settings import generate_env_file
|
|
30
30
|
from dara.core.internal.utils import find_module_path
|
|
@@ -52,7 +52,7 @@ def cli():
|
|
|
52
52
|
@click.option('--port', help='The port to run on', type=int)
|
|
53
53
|
@click.option('--metrics-port', help='The port for the metrics server to run on', type=int)
|
|
54
54
|
@click.option('--disable-metrics', is_flag=True, help='Whether to disable the metrics server')
|
|
55
|
-
@click.option('--host', default='0.0.0.0', help='The host to run on')
|
|
55
|
+
@click.option('--host', default='0.0.0.0', help='The host to run on') # nosec B104 # default for local dev
|
|
56
56
|
@click.option('--rebuild', is_flag=True, help='Whether to force a rebuild of the app')
|
|
57
57
|
@click.option('--require-sso', is_flag=True, help='Whether to enforce that an SSO auth config is used')
|
|
58
58
|
@click.option('--docker', is_flag=True, help='Whether to run in Docker mode - assumes assets are prebuilt')
|
|
@@ -214,10 +214,11 @@ def dev():
|
|
|
214
214
|
stderr=subprocess.STDOUT,
|
|
215
215
|
shell=True,
|
|
216
216
|
) as vite_process:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
217
|
+
if vite_process.stdout is not None:
|
|
218
|
+
for line in vite_process.stdout:
|
|
219
|
+
decoded_line = line.decode('utf-8').strip()
|
|
220
|
+
if decoded_line != '':
|
|
221
|
+
print(decoded_line)
|
|
221
222
|
|
|
222
223
|
|
|
223
224
|
@cli.command()
|
dara/core/configuration.py
CHANGED
|
@@ -14,6 +14,7 @@ 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
|
+
|
|
17
18
|
import os
|
|
18
19
|
import pathlib
|
|
19
20
|
from inspect import isclass, isfunction
|
|
@@ -386,7 +387,7 @@ class ConfigurationBuilder:
|
|
|
386
387
|
icon: Optional[str] = None,
|
|
387
388
|
route: Optional[str] = None,
|
|
388
389
|
include_in_menu: Optional[bool] = True,
|
|
389
|
-
reset_vars_on_load: Optional[List[AnyVariable]] =
|
|
390
|
+
reset_vars_on_load: Optional[List[AnyVariable]] = None,
|
|
390
391
|
on_load: Optional[Action] = None,
|
|
391
392
|
):
|
|
392
393
|
"""
|
|
@@ -402,6 +403,8 @@ class ConfigurationBuilder:
|
|
|
402
403
|
:param on_load: optional action to execute upon visiting the page
|
|
403
404
|
"""
|
|
404
405
|
# Backwards compatibility - deprecated
|
|
406
|
+
if reset_vars_on_load is None:
|
|
407
|
+
reset_vars_on_load = []
|
|
405
408
|
if reset_vars_on_load is not None and len(reset_vars_on_load) > 0:
|
|
406
409
|
if on_load is not None:
|
|
407
410
|
raise ValueError('reset_vars_on_load and on_load cannot be used together')
|
|
@@ -485,7 +488,7 @@ class ConfigurationBuilder:
|
|
|
485
488
|
if len(options) > 0:
|
|
486
489
|
dev_logger.warning(f'Options provided for a function middleware {middleware}, but they will be ignored')
|
|
487
490
|
elif isclass(middleware):
|
|
488
|
-
constructed_middleware = Middleware(middleware, **options)
|
|
491
|
+
constructed_middleware = Middleware(middleware, **options) # type: ignore
|
|
489
492
|
else:
|
|
490
493
|
raise ValueError(f'Invalid middleware type: {type(middleware)}')
|
|
491
494
|
|
dara/core/css.py
CHANGED
|
@@ -18,8 +18,7 @@ limitations under the License.
|
|
|
18
18
|
from typing import Literal, Optional
|
|
19
19
|
|
|
20
20
|
# Re-export CSSProperties for easier importing
|
|
21
|
-
#
|
|
22
|
-
from dara.core.visual.css import CSSProperties
|
|
21
|
+
from dara.core.visual.css import CSSProperties # noqa: F401
|
|
23
22
|
|
|
24
23
|
IconStyle = Literal['solid', 'regular', 'brands']
|
|
25
24
|
IconSize = Literal['1x', '2x', '3x', '4x', '5x', '6x', '7x', '8x', '9x', '10x', '2xs', 'xs', 'sm', 'lg', 'xl', '2xl']
|
dara/core/data_utils.py
CHANGED
|
@@ -116,7 +116,7 @@ class FileStore(BaseModel):
|
|
|
116
116
|
if not os.path.exists(file_path):
|
|
117
117
|
return None
|
|
118
118
|
|
|
119
|
-
return
|
|
119
|
+
return open(file_path, 'rb')
|
|
120
120
|
|
|
121
121
|
def write_file(self, cache_type: CacheType, name: str) -> io.BufferedWriter:
|
|
122
122
|
"""
|
|
@@ -130,7 +130,7 @@ class FileStore(BaseModel):
|
|
|
130
130
|
"""
|
|
131
131
|
scope_path = self.get_scoped_path(cache_type)
|
|
132
132
|
os.makedirs(scope_path, exist_ok=True)
|
|
133
|
-
return
|
|
133
|
+
return open(os.path.join(scope_path, name), 'wb')
|
|
134
134
|
|
|
135
135
|
def delete_file(self, cache_type: CacheType, name: str) -> None:
|
|
136
136
|
"""
|
dara/core/defaults.py
CHANGED
|
@@ -17,7 +17,7 @@ limitations under the License.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
from typing import TYPE_CHECKING, Dict
|
|
20
|
+
from typing import TYPE_CHECKING, Dict, cast
|
|
21
21
|
|
|
22
22
|
from dara.core.base_definitions import ActionDef
|
|
23
23
|
from dara.core.interactivity.actions import (
|
|
@@ -77,8 +77,8 @@ CORE_COMPONENTS: Dict[str, ComponentTypeAnnotation] = {
|
|
|
77
77
|
RouterContent.__name__: RouterContentDef,
|
|
78
78
|
SideBarFrame.__name__: SideBarFrameDef,
|
|
79
79
|
TopBarFrame.__name__: TopBarFrameDef,
|
|
80
|
-
Fallback.Default.py_component: DefaultFallbackDef,
|
|
81
|
-
Fallback.Row.py_component: RowFallbackDef,
|
|
80
|
+
cast(str, Fallback.Default.py_component): DefaultFallbackDef,
|
|
81
|
+
cast(str, Fallback.Row.py_component): RowFallbackDef,
|
|
82
82
|
For.__name__: ForDef,
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -93,9 +93,9 @@ CORE_ACTIONS: Dict[str, ActionDef] = {
|
|
|
93
93
|
Notify.__name__: NotifyDef,
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
|
|
96
97
|
# Define a default layout template
|
|
97
98
|
def default_template(config: Configuration) -> Template:
|
|
98
|
-
|
|
99
99
|
template = TemplateBuilder(name='default')
|
|
100
100
|
|
|
101
101
|
router = template.add_router_from_pages(list(config.pages.values()))
|
|
@@ -107,7 +107,6 @@ def default_template(config: Configuration) -> Template:
|
|
|
107
107
|
|
|
108
108
|
# Define a blank template
|
|
109
109
|
def blank_template(config: Configuration) -> Template:
|
|
110
|
-
|
|
111
110
|
template = TemplateBuilder(name='default')
|
|
112
111
|
|
|
113
112
|
router = template.add_router_from_pages(list(config.pages.values()))
|
|
@@ -119,7 +118,6 @@ def blank_template(config: Configuration) -> Template:
|
|
|
119
118
|
|
|
120
119
|
# Define a top layout template
|
|
121
120
|
def top_template(config: Configuration) -> Template:
|
|
122
|
-
|
|
123
121
|
template = TemplateBuilder(name='default')
|
|
124
122
|
|
|
125
123
|
router = template.add_router_from_pages(list(config.pages.values()))
|
|
@@ -131,7 +129,6 @@ def top_template(config: Configuration) -> Template:
|
|
|
131
129
|
|
|
132
130
|
# Define a top layout template with menu
|
|
133
131
|
def top_menu_template(config: Configuration) -> Template:
|
|
134
|
-
|
|
135
132
|
template = TemplateBuilder(name='default')
|
|
136
133
|
|
|
137
134
|
router = template.add_router_from_pages(list(config.pages.values()))
|
dara/core/definitions.py
CHANGED
|
@@ -19,15 +19,14 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import json
|
|
21
21
|
import uuid
|
|
22
|
+
from collections.abc import Awaitable, Mapping
|
|
22
23
|
from enum import Enum
|
|
23
24
|
from typing import (
|
|
24
25
|
Any,
|
|
25
|
-
Awaitable,
|
|
26
26
|
Callable,
|
|
27
27
|
ClassVar,
|
|
28
28
|
List,
|
|
29
29
|
Literal,
|
|
30
|
-
Mapping,
|
|
31
30
|
Optional,
|
|
32
31
|
Protocol,
|
|
33
32
|
Type,
|
|
@@ -264,8 +263,7 @@ class CallableClassComponent(Protocol):
|
|
|
264
263
|
Callable class component protocol. Describes any class with a __call__ instance method returning a component instance.
|
|
265
264
|
"""
|
|
266
265
|
|
|
267
|
-
def __call__(self) -> ComponentInstance:
|
|
268
|
-
...
|
|
266
|
+
def __call__(self) -> ComponentInstance: ...
|
|
269
267
|
|
|
270
268
|
|
|
271
269
|
DiscoverTarget = Union[Callable[..., ComponentInstance], ComponentInstance, Type[CallableClassComponent]]
|
|
@@ -279,7 +277,7 @@ def discover(outer_obj: DiscoverT) -> DiscoverT:
|
|
|
279
277
|
Will make sure to statically register all encountered dependencies of marked functional component or component class.
|
|
280
278
|
Should not be necessary in most cases, mainly useful when creating component libraries.
|
|
281
279
|
"""
|
|
282
|
-
outer_obj.__wrapped_by__ = discover
|
|
280
|
+
outer_obj.__wrapped_by__ = discover # type: ignore
|
|
283
281
|
return outer_obj
|
|
284
282
|
|
|
285
283
|
|
|
@@ -419,9 +417,8 @@ class BaseFallback(StyledComponentInstance):
|
|
|
419
417
|
@field_validator('suspend_render')
|
|
420
418
|
@classmethod
|
|
421
419
|
def validate_suspend_render(cls, value):
|
|
422
|
-
if isinstance(value, int):
|
|
423
|
-
|
|
424
|
-
raise ValueError('suspend_render must be a positive integer')
|
|
420
|
+
if isinstance(value, int) and value < 0:
|
|
421
|
+
raise ValueError('suspend_render must be a positive integer')
|
|
425
422
|
|
|
426
423
|
return value
|
|
427
424
|
|
|
@@ -505,7 +502,7 @@ class Page(BaseModel):
|
|
|
505
502
|
icon: Optional[str] = None
|
|
506
503
|
content: ComponentInstanceType
|
|
507
504
|
name: str
|
|
508
|
-
sub_pages: Optional[List[
|
|
505
|
+
sub_pages: Optional[List[Page]] = []
|
|
509
506
|
url_safe_name: str
|
|
510
507
|
include_in_menu: Optional[bool] = None
|
|
511
508
|
on_load: Optional[Action] = None
|
dara/core/http.py
CHANGED
|
@@ -18,7 +18,7 @@ limitations under the License.
|
|
|
18
18
|
import inspect
|
|
19
19
|
from collections import OrderedDict
|
|
20
20
|
from functools import wraps
|
|
21
|
-
from typing import Callable, Dict, List, Type
|
|
21
|
+
from typing import Callable, Dict, List, Type, Union
|
|
22
22
|
|
|
23
23
|
from fastapi import Depends
|
|
24
24
|
from fastapi.params import Depends as DependsType
|
|
@@ -54,7 +54,10 @@ def _get_config_instances(annotations: Dict[str, type]) -> Dict[str, EndpointCon
|
|
|
54
54
|
def _method_decorator(method: HttpMethod):
|
|
55
55
|
"""Create a decorator for a given HTTP method"""
|
|
56
56
|
|
|
57
|
-
def _decorator(url: str, dependencies: List[DependsType] =
|
|
57
|
+
def _decorator(url: str, dependencies: Union[List[DependsType], None] = None, authenticated: bool = True):
|
|
58
|
+
if dependencies is None:
|
|
59
|
+
dependencies = []
|
|
60
|
+
|
|
58
61
|
def _inner(func: Callable):
|
|
59
62
|
# Make sure we're using a copy of the dependencies list
|
|
60
63
|
final_dependencies = dependencies[:]
|
|
@@ -104,7 +107,8 @@ def _method_decorator(method: HttpMethod):
|
|
|
104
107
|
|
|
105
108
|
new_handler.__annotations__ = new_annotations
|
|
106
109
|
new_handler.__signature__ = inspect.Signature( # type: ignore
|
|
107
|
-
parameters=list(params.values()),
|
|
110
|
+
parameters=list(params.values()),
|
|
111
|
+
return_annotation=sig.return_annotation, # type: ignore
|
|
108
112
|
)
|
|
109
113
|
|
|
110
114
|
return ApiRoute(
|
|
@@ -17,16 +17,17 @@ limitations under the License.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
+
import contextlib
|
|
20
21
|
import inspect
|
|
21
22
|
import math
|
|
22
23
|
import uuid
|
|
24
|
+
from collections.abc import Awaitable
|
|
23
25
|
from contextvars import ContextVar
|
|
24
26
|
from enum import Enum
|
|
25
27
|
from functools import partial, update_wrapper
|
|
26
28
|
from typing import (
|
|
27
29
|
TYPE_CHECKING,
|
|
28
30
|
Any,
|
|
29
|
-
Awaitable,
|
|
30
31
|
Callable,
|
|
31
32
|
ClassVar,
|
|
32
33
|
Dict,
|
|
@@ -50,9 +51,9 @@ from dara.core.base_definitions import (
|
|
|
50
51
|
ActionImpl,
|
|
51
52
|
ActionResolverDef,
|
|
52
53
|
AnnotatedAction,
|
|
54
|
+
TaskProgressUpdate,
|
|
53
55
|
)
|
|
54
56
|
from dara.core.base_definitions import DaraBaseModel as BaseModel
|
|
55
|
-
from dara.core.base_definitions import TaskProgressUpdate
|
|
56
57
|
from dara.core.interactivity.data_variable import DataVariable
|
|
57
58
|
from dara.core.internal.download import generate_download_code
|
|
58
59
|
from dara.core.internal.registry_lookup import RegistryLookup
|
|
@@ -97,7 +98,7 @@ class ComponentActionContext(ActionContext):
|
|
|
97
98
|
ActionContext for actions that only require component value
|
|
98
99
|
"""
|
|
99
100
|
|
|
100
|
-
inputs: ComponentActionInputs
|
|
101
|
+
inputs: ComponentActionInputs # type: ignore
|
|
101
102
|
|
|
102
103
|
|
|
103
104
|
class UpdateVariableImpl(ActionImpl):
|
|
@@ -162,7 +163,7 @@ class UpdateVariableInputs(ActionInputs):
|
|
|
162
163
|
|
|
163
164
|
|
|
164
165
|
class UpdateVariableContext(ActionContext):
|
|
165
|
-
inputs: UpdateVariableInputs
|
|
166
|
+
inputs: UpdateVariableInputs # type: ignore
|
|
166
167
|
|
|
167
168
|
|
|
168
169
|
@deprecated('Use @action or `UpdateVariableImpl` for simple cases')
|
|
@@ -254,8 +255,8 @@ class UpdateVariable(AnnotatedAction):
|
|
|
254
255
|
:param extras: any extra variables to resolve and pass to the resolution function context
|
|
255
256
|
"""
|
|
256
257
|
|
|
257
|
-
async def _update(ctx: action.Ctx, **kwargs):
|
|
258
|
-
ctx = cast(ActionCtx, ctx)
|
|
258
|
+
async def _update(ctx: action.Ctx, **kwargs): # type: ignore
|
|
259
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
259
260
|
old = kwargs.pop('old')
|
|
260
261
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
261
262
|
old_ctx = UpdateVariableContext(inputs=UpdateVariableInputs(old=old, new=ctx.input), extras=extras)
|
|
@@ -271,7 +272,7 @@ class UpdateVariable(AnnotatedAction):
|
|
|
271
272
|
for idx in range(len(extras or []))
|
|
272
273
|
],
|
|
273
274
|
]
|
|
274
|
-
_update.__signature__ = inspect.Signature(params)
|
|
275
|
+
_update.__signature__ = inspect.Signature(params) # type: ignore
|
|
275
276
|
|
|
276
277
|
# Pass in variable and extras as kwargs
|
|
277
278
|
kwargs = {f'kwarg_{idx}': value for idx, value in enumerate(extras or [])}
|
|
@@ -428,11 +429,11 @@ def NavigateTo(
|
|
|
428
429
|
return NavigateToImpl(url=url, new_tab=new_tab)
|
|
429
430
|
|
|
430
431
|
# Otherwise create a new @action with the provided resolver
|
|
431
|
-
async def _navigate(ctx: action.Ctx, **kwargs):
|
|
432
|
-
ctx = cast(ActionCtx, ctx)
|
|
432
|
+
async def _navigate(ctx: action.Ctx, **kwargs): # type: ignore
|
|
433
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
433
434
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
434
435
|
old_ctx = ComponentActionContext(inputs=ComponentActionInputs(value=ctx.input), extras=extras)
|
|
435
|
-
result = await run_user_handler(url, args=(old_ctx,))
|
|
436
|
+
result = await run_user_handler(url, args=(old_ctx,)) # type: ignore
|
|
436
437
|
# Navigate to resulting url
|
|
437
438
|
await ctx.navigate(result, new_tab)
|
|
438
439
|
|
|
@@ -444,14 +445,14 @@ def NavigateTo(
|
|
|
444
445
|
for idx in range(len(extras or []))
|
|
445
446
|
],
|
|
446
447
|
]
|
|
447
|
-
_navigate.__signature__ = inspect.Signature(params)
|
|
448
|
+
_navigate.__signature__ = inspect.Signature(params) # type: ignore
|
|
448
449
|
|
|
449
450
|
# Pass in variable and extras as kwargs
|
|
450
451
|
kwargs = {f'kwarg_{idx}': value for idx, value in enumerate(extras or [])}
|
|
451
452
|
return action(_navigate)(**kwargs)
|
|
452
453
|
|
|
453
454
|
|
|
454
|
-
NavigateTo.Ctx = ComponentActionContext
|
|
455
|
+
NavigateTo.Ctx = ComponentActionContext # type: ignore
|
|
455
456
|
|
|
456
457
|
|
|
457
458
|
def Logout():
|
|
@@ -663,8 +664,8 @@ def DownloadContent(
|
|
|
663
664
|
```
|
|
664
665
|
"""
|
|
665
666
|
|
|
666
|
-
async def _download(ctx: action.Ctx, **kwargs):
|
|
667
|
-
ctx = cast(ActionCtx, ctx)
|
|
667
|
+
async def _download(ctx: action.Ctx, **kwargs): # type: ignore
|
|
668
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
668
669
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
669
670
|
old_ctx = ComponentActionContext(inputs=ComponentActionInputs(value=ctx.input), extras=extras)
|
|
670
671
|
result = await run_user_handler(resolver, args=(old_ctx,))
|
|
@@ -678,7 +679,7 @@ def DownloadContent(
|
|
|
678
679
|
for idx in range(len(extras or []))
|
|
679
680
|
],
|
|
680
681
|
]
|
|
681
|
-
_download.__signature__ = inspect.Signature(params)
|
|
682
|
+
_download.__signature__ = inspect.Signature(params) # type: ignore
|
|
682
683
|
|
|
683
684
|
# Pass in extras as kwargs
|
|
684
685
|
kwargs = {f'kwarg_{idx}': value for idx, value in enumerate(extras or [])}
|
|
@@ -686,7 +687,7 @@ def DownloadContent(
|
|
|
686
687
|
return action(_download)(**kwargs)
|
|
687
688
|
|
|
688
689
|
|
|
689
|
-
DownloadContent.Ctx = ComponentActionContext
|
|
690
|
+
DownloadContent.Ctx = ComponentActionContext # type: ignore
|
|
690
691
|
"""@deprecated retained for backwards compatibility, to be removed in 2.0"""
|
|
691
692
|
|
|
692
693
|
DownloadVariableDef = ActionDef(name='DownloadVariable', js_module='@darajs/core', py_module='dara.core')
|
|
@@ -769,8 +770,8 @@ def SideEffect(
|
|
|
769
770
|
```
|
|
770
771
|
"""
|
|
771
772
|
|
|
772
|
-
async def _effect(ctx: action.Ctx, **kwargs):
|
|
773
|
-
ctx = cast(ActionCtx, ctx)
|
|
773
|
+
async def _effect(ctx: action.Ctx, **kwargs): # type: ignore
|
|
774
|
+
ctx = cast(ActionCtx, ctx) # type: ignore
|
|
774
775
|
extras = [kwargs[f'kwarg_{idx}'] for idx in range(len(kwargs))]
|
|
775
776
|
old_ctx = ComponentActionContext(inputs=ComponentActionInputs(value=ctx.input), extras=extras)
|
|
776
777
|
# Simply run the user handler
|
|
@@ -784,7 +785,7 @@ def SideEffect(
|
|
|
784
785
|
for idx in range(len(extras or []))
|
|
785
786
|
],
|
|
786
787
|
]
|
|
787
|
-
_effect.__signature__ = inspect.Signature(params)
|
|
788
|
+
_effect.__signature__ = inspect.Signature(params) # type: ignore
|
|
788
789
|
|
|
789
790
|
# Pass in extras as kwargs
|
|
790
791
|
kwargs = {f'kwarg_{idx}': value for idx, value in enumerate(extras or [])}
|
|
@@ -792,7 +793,7 @@ def SideEffect(
|
|
|
792
793
|
return action(_effect)(**kwargs)
|
|
793
794
|
|
|
794
795
|
|
|
795
|
-
SideEffect.Ctx = ComponentActionContext
|
|
796
|
+
SideEffect.Ctx = ComponentActionContext # type: ignore
|
|
796
797
|
"""@deprecated retained for backwards compatibility, to be removed in 2.0"""
|
|
797
798
|
|
|
798
799
|
VariableT = TypeVar('VariableT')
|
|
@@ -825,12 +826,10 @@ class ActionCtx:
|
|
|
825
826
|
self._on_action = _on_action
|
|
826
827
|
|
|
827
828
|
@overload
|
|
828
|
-
async def update(self, variable: DataVariable, value: Optional[DataFrame]):
|
|
829
|
-
...
|
|
829
|
+
async def update(self, variable: DataVariable, value: Optional[DataFrame]): ...
|
|
830
830
|
|
|
831
831
|
@overload
|
|
832
|
-
async def update(self, variable: Union[Variable[VariableT], UrlVariable[VariableT]], value: VariableT):
|
|
833
|
-
...
|
|
832
|
+
async def update(self, variable: Union[Variable[VariableT], UrlVariable[VariableT]], value: VariableT): ...
|
|
834
833
|
|
|
835
834
|
async def update(self, variable: Union[Variable, UrlVariable, DataVariable], value: Any):
|
|
836
835
|
"""
|
|
@@ -1389,11 +1388,10 @@ class action:
|
|
|
1389
1388
|
return bound_f
|
|
1390
1389
|
|
|
1391
1390
|
@overload
|
|
1392
|
-
def __call__(self, ctx: ActionCtx, *args: Any, **kwargs: Any) -> Any:
|
|
1393
|
-
...
|
|
1391
|
+
def __call__(self, ctx: ActionCtx, *args: Any, **kwargs: Any) -> Any: ...
|
|
1394
1392
|
|
|
1395
1393
|
@overload
|
|
1396
|
-
def __call__(self, *args: Any, **kwargs: Any) -> AnnotatedAction:
|
|
1394
|
+
def __call__(self, *args: Any, **kwargs: Any) -> AnnotatedAction: # type: ignore
|
|
1397
1395
|
...
|
|
1398
1396
|
|
|
1399
1397
|
def __call__(self, *args, **kwargs) -> Union[AnnotatedAction, Any]:
|
|
@@ -1444,10 +1442,10 @@ class action:
|
|
|
1444
1442
|
for key, value in all_kwargs.items():
|
|
1445
1443
|
if key in self.func.__annotations__:
|
|
1446
1444
|
valid_value = True
|
|
1447
|
-
|
|
1445
|
+
# The type is either not set or something tricky to verify, e.g. union
|
|
1446
|
+
with contextlib.suppress(Exception):
|
|
1448
1447
|
valid_value = isinstance(value, (self.func.__annotations__[key], AnyVariable))
|
|
1449
|
-
|
|
1450
|
-
pass # The type is either not set or something tricky to verify, e.g. union
|
|
1448
|
+
|
|
1451
1449
|
if not valid_value:
|
|
1452
1450
|
raise TypeError(
|
|
1453
1451
|
f'Argument: {key} was passed as a {type(value)}, but it should be '
|