dara-core 1.19.0__py3-none-any.whl → 1.20.0a1__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 +1 -0
- dara/core/auth/basic.py +13 -7
- dara/core/auth/definitions.py +2 -2
- dara/core/auth/utils.py +1 -1
- dara/core/base_definitions.py +7 -42
- dara/core/data_utils.py +16 -17
- dara/core/definitions.py +8 -8
- dara/core/interactivity/__init__.py +6 -0
- dara/core/interactivity/actions.py +26 -22
- dara/core/interactivity/any_data_variable.py +7 -135
- dara/core/interactivity/any_variable.py +1 -1
- dara/core/interactivity/client_variable.py +71 -0
- dara/core/interactivity/data_variable.py +8 -266
- dara/core/interactivity/derived_data_variable.py +6 -290
- dara/core/interactivity/derived_variable.py +379 -199
- dara/core/interactivity/filtering.py +29 -2
- dara/core/interactivity/loop_variable.py +2 -2
- dara/core/interactivity/non_data_variable.py +5 -68
- dara/core/interactivity/plain_variable.py +87 -14
- dara/core/interactivity/server_variable.py +325 -0
- dara/core/interactivity/state_variable.py +69 -0
- dara/core/interactivity/switch_variable.py +15 -15
- dara/core/interactivity/tabular_variable.py +94 -0
- dara/core/interactivity/url_variable.py +10 -90
- dara/core/internal/cache_store/cache_store.py +5 -20
- dara/core/internal/dependency_resolution.py +27 -69
- dara/core/internal/devtools.py +10 -3
- dara/core/internal/execute_action.py +9 -3
- dara/core/internal/multi_resource_lock.py +70 -0
- dara/core/internal/normalization.py +0 -5
- dara/core/internal/pandas_utils.py +105 -3
- dara/core/internal/pool/definitions.py +1 -1
- dara/core/internal/pool/task_pool.py +9 -6
- dara/core/internal/pool/utils.py +19 -14
- dara/core/internal/registries.py +3 -2
- dara/core/internal/registry.py +1 -1
- dara/core/internal/registry_lookup.py +5 -3
- dara/core/internal/routing.py +52 -121
- dara/core/internal/store.py +2 -29
- dara/core/internal/tasks.py +372 -182
- dara/core/internal/utils.py +25 -3
- dara/core/internal/websocket.py +1 -1
- dara/core/js_tooling/js_utils.py +2 -0
- dara/core/logging.py +10 -6
- dara/core/persistence.py +26 -4
- dara/core/umd/dara.core.umd.js +1082 -1464
- dara/core/visual/dynamic_component.py +17 -13
- {dara_core-1.19.0.dist-info → dara_core-1.20.0a1.dist-info}/METADATA +11 -11
- {dara_core-1.19.0.dist-info → dara_core-1.20.0a1.dist-info}/RECORD +52 -47
- {dara_core-1.19.0.dist-info → dara_core-1.20.0a1.dist-info}/LICENSE +0 -0
- {dara_core-1.19.0.dist-info → dara_core-1.20.0a1.dist-info}/WHEEL +0 -0
- {dara_core-1.19.0.dist-info → dara_core-1.20.0a1.dist-info}/entry_points.txt +0 -0
dara/core/internal/utils.py
CHANGED
|
@@ -33,13 +33,16 @@ from typing import (
|
|
|
33
33
|
Literal,
|
|
34
34
|
Optional,
|
|
35
35
|
Tuple,
|
|
36
|
+
Type,
|
|
37
|
+
TypeVar,
|
|
36
38
|
Union,
|
|
37
39
|
)
|
|
38
40
|
|
|
39
41
|
import anyio
|
|
40
42
|
from anyio import from_thread
|
|
41
|
-
from exceptiongroup import ExceptionGroup
|
|
43
|
+
from exceptiongroup import BaseExceptionGroup, ExceptionGroup
|
|
42
44
|
from starlette.concurrency import run_in_threadpool
|
|
45
|
+
from typing_extensions import ParamSpec
|
|
43
46
|
|
|
44
47
|
from dara.core.auth.definitions import SESSION_ID, USER
|
|
45
48
|
from dara.core.base_definitions import CacheType
|
|
@@ -176,7 +179,11 @@ def enforce_sso(conf: ConfigurationBuilder):
|
|
|
176
179
|
) from err
|
|
177
180
|
|
|
178
181
|
|
|
179
|
-
|
|
182
|
+
P = ParamSpec('P')
|
|
183
|
+
T = TypeVar('T')
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def async_dedupe(fn: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
|
|
180
187
|
"""
|
|
181
188
|
Decorator to deduplicate concurrent calls to asynchronous functions based on their arguments.
|
|
182
189
|
|
|
@@ -194,7 +201,7 @@ def async_dedupe(fn: Callable[..., Awaitable]):
|
|
|
194
201
|
is_method = 'self' in inspect.signature(fn).parameters
|
|
195
202
|
|
|
196
203
|
@wraps(fn)
|
|
197
|
-
async def wrapped(*args, **kwargs):
|
|
204
|
+
async def wrapped(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
198
205
|
non_self_args = args[1:] if is_method else args
|
|
199
206
|
key = (non_self_args, frozenset(kwargs.items()))
|
|
200
207
|
lock = locks.get(key)
|
|
@@ -234,3 +241,18 @@ def resolve_exception_group(error: Any):
|
|
|
234
241
|
return resolve_exception_group(error.exceptions[0])
|
|
235
242
|
|
|
236
243
|
return error
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def exception_group_contains(err_type: Type[BaseException], group: BaseExceptionGroup) -> bool:
|
|
247
|
+
"""
|
|
248
|
+
Check if an ExceptionGroup contains an error of a given type, recursively
|
|
249
|
+
|
|
250
|
+
:param err_type: The type of error to check for
|
|
251
|
+
:param group: The ExceptionGroup to check
|
|
252
|
+
"""
|
|
253
|
+
for exc in group.exceptions:
|
|
254
|
+
if isinstance(exc, err_type):
|
|
255
|
+
return True
|
|
256
|
+
if isinstance(exc, BaseExceptionGroup):
|
|
257
|
+
return exception_group_contains(err_type, exc)
|
|
258
|
+
return False
|
dara/core/internal/websocket.py
CHANGED
|
@@ -474,7 +474,7 @@ async def ws_handler(websocket: WebSocket, token: Optional[str] = Query(default=
|
|
|
474
474
|
if pending_tokens_registry.has(token):
|
|
475
475
|
pending_tokens_registry.remove(token)
|
|
476
476
|
|
|
477
|
-
user_identifier = token_content.identity_id
|
|
477
|
+
user_identifier = token_content.identity_id
|
|
478
478
|
|
|
479
479
|
# Add the new session ID to known sessions for that user
|
|
480
480
|
if sessions_registry.has(user_identifier):
|
dara/core/js_tooling/js_utils.py
CHANGED
|
@@ -544,11 +544,13 @@ def bundle_js(build_cache: BuildCache, copy_js: bool = False):
|
|
|
544
544
|
|
|
545
545
|
cwd = os.getcwd()
|
|
546
546
|
os.chdir(build_cache.static_files_dir)
|
|
547
|
+
dev_logger.info('Installing JS dependencies...')
|
|
547
548
|
exit_code = os.system(f'{package_manager} install') # nosec B605 # package_manager is validated
|
|
548
549
|
if exit_code > 0:
|
|
549
550
|
raise SystemError(
|
|
550
551
|
"Failed to install the JS dependencies - there's likely a connection issue or a broken package"
|
|
551
552
|
)
|
|
553
|
+
dev_logger.info('JS dependencies installed successfully')
|
|
552
554
|
|
|
553
555
|
# Load entry template as a string
|
|
554
556
|
with open(entry_template, encoding='utf-8') as f:
|
dara/core/logging.py
CHANGED
|
@@ -74,7 +74,7 @@ class Logger:
|
|
|
74
74
|
|
|
75
75
|
self._logger.warning(payload, extra={'content': extra})
|
|
76
76
|
|
|
77
|
-
def error(self, title: str, error:
|
|
77
|
+
def error(self, title: str, error: BaseException, extra: Optional[Dict[str, Any]] = None):
|
|
78
78
|
"""
|
|
79
79
|
Log a message at the ERROR level
|
|
80
80
|
|
|
@@ -171,11 +171,14 @@ class LoggingMiddleware(BaseHTTPMiddleware):
|
|
|
171
171
|
return response
|
|
172
172
|
|
|
173
173
|
|
|
174
|
-
def _print_stacktrace():
|
|
174
|
+
def _print_stacktrace(err: Optional[BaseException] = None) -> str:
|
|
175
175
|
"""
|
|
176
176
|
Prints out the current stack trace whilst unwinding all the logging calls on the way so it just shows the relevant
|
|
177
177
|
parts. Will also extract any exceptions and print them at the end.
|
|
178
178
|
"""
|
|
179
|
+
if err is not None:
|
|
180
|
+
return ''.join(traceback.format_exception(type(err), err, err.__traceback__))
|
|
181
|
+
|
|
179
182
|
exc = sys.exc_info()[0]
|
|
180
183
|
stack = traceback.extract_stack()[:-1]
|
|
181
184
|
if exc is not None:
|
|
@@ -215,8 +218,9 @@ class DaraProdFormatter(logging.Formatter):
|
|
|
215
218
|
}
|
|
216
219
|
|
|
217
220
|
if record.levelname == 'ERROR':
|
|
218
|
-
|
|
219
|
-
payload['
|
|
221
|
+
err = payload.pop('error')
|
|
222
|
+
payload['error'] = str(err)
|
|
223
|
+
payload['stacktrace'] = _print_stacktrace(err if isinstance(err, BaseException) else None)
|
|
220
224
|
|
|
221
225
|
return payload
|
|
222
226
|
|
|
@@ -281,8 +285,8 @@ class DaraDevFormatter(logging.Formatter):
|
|
|
281
285
|
|
|
282
286
|
if record.levelname == 'ERROR':
|
|
283
287
|
error = ''
|
|
284
|
-
if payload.get('error'):
|
|
285
|
-
error = _print_stacktrace()
|
|
288
|
+
if err := payload.get('error'):
|
|
289
|
+
error = _print_stacktrace(err if isinstance(err, BaseException) else None)
|
|
286
290
|
content = base_msg
|
|
287
291
|
if record.__dict__.get('content'):
|
|
288
292
|
content = content + '\r\n' + self.extra_template % (spacer, record.__dict__['content'])
|
dara/core/persistence.py
CHANGED
|
@@ -255,7 +255,7 @@ class BackendStore(PersistenceStore):
|
|
|
255
255
|
user = USER.get()
|
|
256
256
|
|
|
257
257
|
if user:
|
|
258
|
-
user_key = user.identity_id
|
|
258
|
+
user_key = user.identity_id
|
|
259
259
|
|
|
260
260
|
# Make sure the store is initialized
|
|
261
261
|
if user_key not in self.initialized_scopes:
|
|
@@ -277,7 +277,7 @@ class BackendStore(PersistenceStore):
|
|
|
277
277
|
if key == 'global':
|
|
278
278
|
return None
|
|
279
279
|
|
|
280
|
-
# otherwise key is a user identity_id
|
|
280
|
+
# otherwise key is a user identity_id
|
|
281
281
|
return key
|
|
282
282
|
|
|
283
283
|
def _register(self):
|
|
@@ -374,7 +374,7 @@ class BackendStore(PersistenceStore):
|
|
|
374
374
|
if not user:
|
|
375
375
|
return
|
|
376
376
|
|
|
377
|
-
user_identifier = user.identity_id
|
|
377
|
+
user_identifier = user.identity_id
|
|
378
378
|
return await self._notify_user(user_identifier, value=value, ignore_channel=ignore_channel)
|
|
379
379
|
|
|
380
380
|
async def _notify_patches(self, patches: List[Dict[str, Any]]):
|
|
@@ -393,7 +393,7 @@ class BackendStore(PersistenceStore):
|
|
|
393
393
|
if not user:
|
|
394
394
|
return
|
|
395
395
|
|
|
396
|
-
user_identifier = user.identity_id
|
|
396
|
+
user_identifier = user.identity_id
|
|
397
397
|
return await self._notify_user(user_identifier, patches=patches)
|
|
398
398
|
|
|
399
399
|
async def init(self, variable: 'Variable'):
|
|
@@ -540,3 +540,25 @@ class BackendStoreEntry(BaseModel):
|
|
|
540
540
|
uid: str
|
|
541
541
|
store: BackendStore
|
|
542
542
|
"""Store instance"""
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
class BrowserStore(PersistenceStore):
|
|
546
|
+
"""
|
|
547
|
+
Persistence store implementation that uses browser local storage
|
|
548
|
+
"""
|
|
549
|
+
|
|
550
|
+
async def init(self, variable: 'Variable'):
|
|
551
|
+
# noop
|
|
552
|
+
pass
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
class QueryParamStore(PersistenceStore):
|
|
556
|
+
"""
|
|
557
|
+
Persistence store implementation that uses a URL query parameter
|
|
558
|
+
"""
|
|
559
|
+
|
|
560
|
+
query: str
|
|
561
|
+
|
|
562
|
+
async def init(self, variable: 'Variable'):
|
|
563
|
+
# noop
|
|
564
|
+
pass
|