dara-core 1.20.0__py3-none-any.whl → 1.20.1a1__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.
Files changed (82) hide show
  1. dara/core/__init__.py +0 -3
  2. dara/core/actions.py +2 -1
  3. dara/core/auth/basic.py +16 -22
  4. dara/core/auth/definitions.py +2 -2
  5. dara/core/auth/routes.py +5 -5
  6. dara/core/auth/utils.py +5 -5
  7. dara/core/base_definitions.py +64 -22
  8. dara/core/cli.py +7 -8
  9. dara/core/configuration.py +2 -5
  10. dara/core/css.py +2 -1
  11. dara/core/data_utils.py +19 -18
  12. dara/core/defaults.py +7 -6
  13. dara/core/definitions.py +19 -50
  14. dara/core/http.py +3 -7
  15. dara/core/interactivity/__init__.py +0 -6
  16. dara/core/interactivity/actions.py +50 -52
  17. dara/core/interactivity/any_data_variable.py +134 -7
  18. dara/core/interactivity/any_variable.py +8 -5
  19. dara/core/interactivity/data_variable.py +266 -8
  20. dara/core/interactivity/derived_data_variable.py +290 -7
  21. dara/core/interactivity/derived_variable.py +176 -416
  22. dara/core/interactivity/filtering.py +27 -46
  23. dara/core/interactivity/loop_variable.py +2 -2
  24. dara/core/interactivity/non_data_variable.py +68 -5
  25. dara/core/interactivity/plain_variable.py +15 -89
  26. dara/core/interactivity/switch_variable.py +19 -19
  27. dara/core/interactivity/url_variable.py +90 -10
  28. dara/core/internal/cache_store/base_impl.py +1 -2
  29. dara/core/internal/cache_store/cache_store.py +25 -22
  30. dara/core/internal/cache_store/keep_all.py +1 -4
  31. dara/core/internal/cache_store/lru.py +1 -5
  32. dara/core/internal/cache_store/ttl.py +1 -4
  33. dara/core/internal/cgroup.py +1 -1
  34. dara/core/internal/dependency_resolution.py +66 -60
  35. dara/core/internal/devtools.py +5 -12
  36. dara/core/internal/download.py +4 -13
  37. dara/core/internal/encoder_registry.py +7 -7
  38. dara/core/internal/execute_action.py +13 -13
  39. dara/core/internal/hashing.py +3 -1
  40. dara/core/internal/import_discovery.py +4 -3
  41. dara/core/internal/normalization.py +18 -9
  42. dara/core/internal/pandas_utils.py +5 -107
  43. dara/core/internal/pool/definitions.py +1 -1
  44. dara/core/internal/pool/task_pool.py +16 -25
  45. dara/core/internal/pool/utils.py +18 -21
  46. dara/core/internal/pool/worker.py +2 -3
  47. dara/core/internal/port_utils.py +1 -1
  48. dara/core/internal/registries.py +6 -12
  49. dara/core/internal/registry.py +2 -4
  50. dara/core/internal/registry_lookup.py +5 -11
  51. dara/core/internal/routing.py +145 -109
  52. dara/core/internal/scheduler.py +8 -13
  53. dara/core/internal/settings.py +2 -2
  54. dara/core/internal/store.py +29 -2
  55. dara/core/internal/tasks.py +195 -379
  56. dara/core/internal/utils.py +13 -36
  57. dara/core/internal/websocket.py +20 -21
  58. dara/core/js_tooling/js_utils.py +26 -28
  59. dara/core/js_tooling/templates/vite.config.template.ts +3 -12
  60. dara/core/logging.py +12 -13
  61. dara/core/main.py +11 -14
  62. dara/core/metrics/cache.py +1 -1
  63. dara/core/metrics/utils.py +3 -3
  64. dara/core/persistence.py +5 -27
  65. dara/core/umd/dara.core.umd.js +55428 -59098
  66. dara/core/visual/components/__init__.py +2 -2
  67. dara/core/visual/components/fallback.py +4 -30
  68. dara/core/visual/components/for_cmp.py +1 -4
  69. dara/core/visual/css/__init__.py +31 -30
  70. dara/core/visual/dynamic_component.py +28 -31
  71. dara/core/visual/progress_updater.py +3 -4
  72. {dara_core-1.20.0.dist-info → dara_core-1.20.1a1.dist-info}/METADATA +11 -12
  73. dara_core-1.20.1a1.dist-info/RECORD +114 -0
  74. dara/core/interactivity/client_variable.py +0 -71
  75. dara/core/interactivity/server_variable.py +0 -325
  76. dara/core/interactivity/state_variable.py +0 -69
  77. dara/core/interactivity/tabular_variable.py +0 -94
  78. dara/core/internal/multi_resource_lock.py +0 -70
  79. dara_core-1.20.0.dist-info/RECORD +0 -119
  80. {dara_core-1.20.0.dist-info → dara_core-1.20.1a1.dist-info}/LICENSE +0 -0
  81. {dara_core-1.20.0.dist-info → dara_core-1.20.1a1.dist-info}/WHEEL +0 -0
  82. {dara_core-1.20.0.dist-info → dara_core-1.20.1a1.dist-info}/entry_points.txt +0 -0
dara/core/__init__.py CHANGED
@@ -14,8 +14,6 @@ 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
-
19
17
  from importlib.metadata import version
20
18
 
21
19
  from pydantic import BaseModel
@@ -41,7 +39,6 @@ __all__ = [
41
39
  'DerivedVariable',
42
40
  'DerivedDataVariable',
43
41
  'DataVariable',
44
- 'ServerVariable',
45
42
  'UrlVariable',
46
43
  'Cache',
47
44
  'CacheType',
dara/core/actions.py CHANGED
@@ -16,7 +16,8 @@ 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
- from dara.core.interactivity import ( # noqa: F401
19
+ # pylint: disable=unused-import
20
+ from dara.core.interactivity import (
20
21
  DownloadContent,
21
22
  DownloadContentImpl,
22
23
  DownloadVariable,
dara/core/auth/basic.py CHANGED
@@ -25,6 +25,7 @@ from dara.core.auth.definitions import (
25
25
  EXPIRED_TOKEN_ERROR,
26
26
  INVALID_CREDENTIALS_ERROR,
27
27
  INVALID_TOKEN_ERROR,
28
+ JWT_ALGO,
28
29
  SESSION_ID,
29
30
  USER,
30
31
  AuthError,
@@ -34,6 +35,7 @@ from dara.core.auth.definitions import (
34
35
  UserData,
35
36
  )
36
37
  from dara.core.auth.utils import decode_token, sign_jwt
38
+ from dara.core.internal.settings import get_settings
37
39
 
38
40
  DefaultAuthLogin = AuthComponent(js_module='@darajs/core', py_module='dara.core', js_name='DefaultAuthLogin')
39
41
 
@@ -67,7 +69,7 @@ class BaseBasicAuthConfig(BaseAuthConfig):
67
69
 
68
70
  return {
69
71
  'token': sign_jwt(
70
- identity_id=body.username,
72
+ identity_id=None,
71
73
  identity_name=body.username,
72
74
  identity_email=None,
73
75
  groups=[],
@@ -85,15 +87,14 @@ class BaseBasicAuthConfig(BaseAuthConfig):
85
87
  SESSION_ID.set(decoded.session_id)
86
88
  USER.set(
87
89
  UserData(
88
- identity_id=decoded.identity_name,
89
90
  identity_name=decoded.identity_name,
90
91
  )
91
92
  )
92
93
  return decoded
93
- except jwt.ExpiredSignatureError as e:
94
- raise AuthError(EXPIRED_TOKEN_ERROR, 401) from e
95
- except jwt.DecodeError as e:
96
- raise AuthError(INVALID_TOKEN_ERROR, 401) from e
94
+ except jwt.ExpiredSignatureError:
95
+ raise AuthError(EXPIRED_TOKEN_ERROR, 401)
96
+ except jwt.DecodeError:
97
+ raise AuthError(INVALID_TOKEN_ERROR, 401)
97
98
 
98
99
 
99
100
  class BasicAuthConfig(BaseBasicAuthConfig):
@@ -120,13 +121,13 @@ class DefaultAuthConfig(BaseAuthConfig):
120
121
  logout=BasicAuthLogout,
121
122
  )
122
123
 
123
- def get_token(self, body: SessionRequestBody) -> TokenResponse:
124
+ def get_token(self, _: SessionRequestBody) -> TokenResponse:
124
125
  """
125
126
  Get a session token.
126
127
 
127
128
  In default auth a new token is returned every time.
128
129
  """
129
- token = sign_jwt(identity_id='user', identity_name='user', identity_email=None, groups=[])
130
+ token = sign_jwt(identity_id=None, identity_name='user', identity_email=None, groups=[])
130
131
  return {'token': token}
131
132
 
132
133
  def verify_token(self, token: str) -> TokenData:
@@ -138,17 +139,10 @@ class DefaultAuthConfig(BaseAuthConfig):
138
139
  :param token: the token to verify
139
140
  """
140
141
  try:
141
- decoded = decode_token(token)
142
- SESSION_ID.set(decoded.session_id)
143
- # Implicit auth assumes used by one user so all users are the same
144
- USER.set(
145
- UserData(
146
- identity_id=decoded.identity_id,
147
- identity_name=decoded.identity_id,
148
- )
149
- )
150
- return decoded
151
- except jwt.ExpiredSignatureError as e:
152
- raise AuthError(EXPIRED_TOKEN_ERROR, 401) from e
153
- except jwt.DecodeError as e:
154
- raise AuthError(INVALID_TOKEN_ERROR, 401) from e
142
+ decoded = jwt.decode(token, get_settings().jwt_secret, algorithms=[JWT_ALGO])
143
+ SESSION_ID.set(decoded.get('session_id'))
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)
@@ -39,7 +39,7 @@ class TokenData(BaseModel):
39
39
 
40
40
  session_id: str
41
41
  exp: Union[float, int, datetime]
42
- identity_id: str
42
+ identity_id: Optional[str] = None
43
43
  identity_name: str
44
44
  identity_email: Optional[str] = None
45
45
  id_token: Optional[str] = None
@@ -56,7 +56,7 @@ class UserData(BaseModel):
56
56
  :param groups: list of groups user belongs to
57
57
  """
58
58
 
59
- identity_id: str
59
+ identity_id: Optional[str] = None
60
60
  identity_name: str
61
61
  identity_email: Optional[str] = None
62
62
  groups: Optional[List[str]] = []
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) from e
78
+ raise HTTPException(status_code=401, detail=EXPIRED_TOKEN_ERROR)
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) from e
81
+ raise HTTPException(status_code=401, detail=INVALID_TOKEN_ERROR)
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) from err
84
+ raise HTTPException(status_code=err.code, detail=err.detail)
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) from e
165
+ raise HTTPException(status_code=401, detail=EXPIRED_TOKEN_ERROR, headers=headers)
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) from e
169
+ raise HTTPException(status_code=401, detail=INVALID_TOKEN_ERROR, headers=headers)
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,14 +44,14 @@ 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 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
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)
51
51
 
52
52
 
53
53
  def sign_jwt(
54
- identity_id: str,
54
+ identity_id: Optional[str],
55
55
  identity_name: str,
56
56
  identity_email: Optional[str],
57
57
  groups: List[str],
@@ -21,16 +21,17 @@ 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
25
24
  from enum import Enum
26
25
  from typing import (
27
26
  TYPE_CHECKING,
28
27
  Annotated,
29
28
  Any,
29
+ Awaitable,
30
30
  Callable,
31
31
  ClassVar,
32
32
  Dict,
33
33
  List,
34
+ Mapping,
34
35
  Optional,
35
36
  Tuple,
36
37
  Union,
@@ -66,7 +67,7 @@ def annotation_has_base_model(typ: Any) -> bool:
66
67
  type_args = get_args(typ)
67
68
  if len(type_args) > 0:
68
69
  return any(annotation_has_base_model(arg) for arg in type_args)
69
- except: # noqa: E722
70
+ except: # pylint: disable=bare-except
70
71
  # canot get arguments, should be a simple type
71
72
  pass
72
73
 
@@ -78,7 +79,9 @@ def annotation_has_base_model(typ: Any) -> bool:
78
79
  # It works by adding SerializeAsAny to all fields of the model.
79
80
  # See https://github.com/pydantic/pydantic/issues/6381
80
81
  class SerializeAsAnyMeta(ModelMetaclass):
81
- def __new__(cls, name: str, bases: Tuple[type], namespaces: Dict[str, Any], **kwargs):
82
+ def __new__(
83
+ self, name: str, bases: Tuple[type], namespaces: Dict[str, Any], **kwargs
84
+ ): # pylint: disable=bad-mcs-classmethod-argument
82
85
  annotations: dict = namespaces.get('__annotations__', {}).copy()
83
86
 
84
87
  for base in bases:
@@ -94,11 +97,11 @@ class SerializeAsAnyMeta(ModelMetaclass):
94
97
  if isinstance(annotation, str) or annotation is ClassVar:
95
98
  continue
96
99
  if annotation_has_base_model(annotation):
97
- annotations[field] = SerializeAsAny[annotation] # type: ignore
100
+ annotations[field] = SerializeAsAny[annotation] # type: ignore
98
101
 
99
102
  namespaces['__annotations__'] = annotations
100
103
 
101
- return super().__new__(cls, name, bases, namespaces, **kwargs)
104
+ return super().__new__(self, name, bases, namespaces, **kwargs)
102
105
 
103
106
 
104
107
  class DaraBaseModel(BaseModel, metaclass=SerializeAsAnyMeta):
@@ -132,17 +135,16 @@ class DaraBaseModel(BaseModel, metaclass=SerializeAsAnyMeta):
132
135
  and annotation_has_base_model(field_info.annotation)
133
136
  ):
134
137
  # Skip if it has metadata that is already annotated with SerializeAsAny
135
- if any(isinstance(x, SerializeAsAny) for x in field_info.metadata): # type: ignore
138
+ if any(isinstance(x, SerializeAsAny) for x in field_info.metadata): # type: ignore
136
139
  continue
137
140
  # Skip if the type is already annotated with SerializeAsAny
138
141
  if get_origin(field_info.annotation) is Annotated and any(
139
- isinstance(arg, SerializeAsAny) # pyright: ignore[reportArgumentType]
140
- for arg in field_info.annotation.__metadata__ # type: ignore
142
+ isinstance(arg, SerializeAsAny) for arg in field_info.annotation.__metadata__ # type: ignore
141
143
  ):
142
144
  continue
143
145
 
144
- field_info.annotation = SerializeAsAny[field_info.annotation] # type: ignore
145
- field_info.metadata = list(field_info.annotation.__metadata__) # type: ignore
146
+ field_info.annotation = SerializeAsAny[field_info.annotation] # type: ignore
147
+ field_info.metadata = list(field_info.annotation.__metadata__) # type: ignore
146
148
 
147
149
  # Rebuild again with force to ensure we rebuild the schema with new annotations
148
150
  return super().model_rebuild(
@@ -253,8 +255,10 @@ class Cache:
253
255
  if isinstance(arg, Cache.Type):
254
256
  return LruCachePolicy(cache_type=arg)
255
257
 
256
- if isinstance(arg, str) and (typ := Cache.Type.get_member(arg)):
257
- return LruCachePolicy(cache_type=typ)
258
+ if isinstance(arg, str):
259
+ # Check that the string is one of allowed cache members
260
+ if typ := Cache.Type.get_member(arg):
261
+ return LruCachePolicy(cache_type=typ)
258
262
 
259
263
  raise ValueError(
260
264
  f'Invalid cache argument: {arg}. Please provide a Cache.Policy object or one of Cache.Type members'
@@ -304,12 +308,6 @@ class CachedRegistryEntry(BaseModel):
304
308
  return f'{self.__class__.__name__}(cache={self.cache}, uid={self.uid})'
305
309
 
306
310
 
307
- class NonTabularDataError(Exception):
308
- """
309
- Raised when trying to interpret a non-tabular variable as tabular
310
- """
311
-
312
-
313
311
  class BaseTaskMessage(BaseModel):
314
312
  task_id: str
315
313
 
@@ -351,10 +349,12 @@ class BaseTask(abc.ABC):
351
349
  super().__init__()
352
350
 
353
351
  @abc.abstractmethod
354
- async def run(self, send_stream: Optional[MemoryObjectSendStream[TaskMessage]] = None) -> Any: ...
352
+ async def run(self, send_stream: Optional[MemoryObjectSendStream[TaskMessage]] = None) -> Any:
353
+ ...
355
354
 
356
355
  @abc.abstractmethod
357
- async def cancel(self): ...
356
+ async def cancel(self):
357
+ ...
358
358
 
359
359
 
360
360
  class PendingTask(BaseTask):
@@ -413,7 +413,7 @@ class PendingTask(BaseTask):
413
413
  self.cancel_scope.cancel()
414
414
  await self.task_def.cancel()
415
415
 
416
- self.error = anyio.get_cancelled_exc_class()()
416
+ self.error = Exception('Task was cancelled')
417
417
  self.event.set()
418
418
 
419
419
  def add_subscriber(self):
@@ -438,6 +438,47 @@ class PendingTask(BaseTask):
438
438
  return self.result
439
439
 
440
440
 
441
+ class PendingValue:
442
+ """
443
+ An internal class that's used to represent a pending value. Holds a future object that can be awaited by
444
+ multiple consumers.
445
+ """
446
+
447
+ def __init__(self):
448
+ self.event = anyio.Event()
449
+ self._value = None
450
+ self._error = None
451
+
452
+ async def wait(self):
453
+ """
454
+ Wait for the underlying event to be set
455
+ """
456
+ # Waiting in chunks as otherwise Jupyter blocks the event loop
457
+ while not self.event.is_set():
458
+ await anyio.sleep(0.01)
459
+ if self._error:
460
+ raise self._error
461
+ return self._value
462
+
463
+ def resolve(self, value: Any):
464
+ """
465
+ Resolve the pending state and send values to the waiting code
466
+
467
+ :param value: the value to resolve as the result
468
+ """
469
+ self._value = value
470
+ self.event.set()
471
+
472
+ def error(self, exc: Exception):
473
+ """
474
+ Resolve the pending state with an error and send it to the waiting code
475
+
476
+ :param exc: exception to resolve as the result
477
+ """
478
+ self._error = exc
479
+ self.event.set()
480
+
481
+
441
482
  class AnnotatedAction(BaseModel):
442
483
  """
443
484
  Represents a single call to an @action-annotated action.
@@ -463,11 +504,12 @@ class AnnotatedAction(BaseModel):
463
504
  dynamic_kwargs: Mapping[str, Any]
464
505
  """Dynamic kwargs of the action; uid -> variable instance"""
465
506
 
466
- loading: Variable # type: ignore # noqa: F821
507
+ loading: 'Variable' # type: ignore
467
508
  """Loading Variable instance"""
468
509
 
469
510
  def __init__(self, **data):
470
511
  # Resolve the circular dependency to add a loading Variable to the model upon creation
512
+ # pylint: disable-next=import-error, import-outside-toplevel
471
513
  from dara.core.interactivity.plain_variable import Variable
472
514
 
473
515
  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 uvicorn
25
-
26
24
  import click
25
+ import uvicorn
27
26
  from click.exceptions import UsageError
27
+
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') # nosec B104 # default for local dev
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,11 +214,10 @@ def dev():
214
214
  stderr=subprocess.STDOUT,
215
215
  shell=True,
216
216
  ) as vite_process:
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)
217
+ for line in vite_process.stdout:
218
+ decoded_line = line.decode('utf-8').strip()
219
+ if decoded_line != '':
220
+ print(decoded_line) # pylint: disable=bad-builtin
222
221
 
223
222
 
224
223
  @cli.command()
@@ -14,7 +14,6 @@ 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
17
  import os
19
18
  import pathlib
20
19
  from inspect import isclass, isfunction
@@ -387,7 +386,7 @@ class ConfigurationBuilder:
387
386
  icon: Optional[str] = None,
388
387
  route: Optional[str] = None,
389
388
  include_in_menu: Optional[bool] = True,
390
- reset_vars_on_load: Optional[List[AnyVariable]] = None,
389
+ reset_vars_on_load: Optional[List[AnyVariable]] = [],
391
390
  on_load: Optional[Action] = None,
392
391
  ):
393
392
  """
@@ -403,8 +402,6 @@ class ConfigurationBuilder:
403
402
  :param on_load: optional action to execute upon visiting the page
404
403
  """
405
404
  # Backwards compatibility - deprecated
406
- if reset_vars_on_load is None:
407
- reset_vars_on_load = []
408
405
  if reset_vars_on_load is not None and len(reset_vars_on_load) > 0:
409
406
  if on_load is not None:
410
407
  raise ValueError('reset_vars_on_load and on_load cannot be used together')
@@ -488,7 +485,7 @@ class ConfigurationBuilder:
488
485
  if len(options) > 0:
489
486
  dev_logger.warning(f'Options provided for a function middleware {middleware}, but they will be ignored')
490
487
  elif isclass(middleware):
491
- constructed_middleware = Middleware(middleware, **options) # type: ignore
488
+ constructed_middleware = Middleware(middleware, **options) # type: ignore
492
489
  else:
493
490
  raise ValueError(f'Invalid middleware type: {type(middleware)}')
494
491
 
dara/core/css.py CHANGED
@@ -18,7 +18,8 @@ limitations under the License.
18
18
  from typing import Literal, Optional
19
19
 
20
20
  # Re-export CSSProperties for easier importing
21
- from dara.core.visual.css import CSSProperties # noqa: F401
21
+ # pylint: disable=unused-import
22
+ from dara.core.visual.css import CSSProperties
22
23
 
23
24
  IconStyle = Literal['solid', 'regular', 'brands']
24
25
  IconSize = Literal['1x', '2x', '3x', '4x', '5x', '6x', '7x', '8x', '9x', '10x', '2xs', 'xs', 'sm', 'lg', 'xl', '2xl']
dara/core/data_utils.py CHANGED
@@ -25,9 +25,10 @@ from pandas import DataFrame
25
25
  from dara.core.base_definitions import CacheType
26
26
  from dara.core.base_definitions import DaraBaseModel as BaseModel
27
27
  from dara.core.interactivity import (
28
- ClientVariable,
28
+ DerivedDataVariable,
29
29
  DerivedVariable,
30
30
  DownloadContent,
31
+ NonDataVariable,
31
32
  SideEffect,
32
33
  Variable,
33
34
  )
@@ -115,7 +116,7 @@ class FileStore(BaseModel):
115
116
  if not os.path.exists(file_path):
116
117
  return None
117
118
 
118
- return open(file_path, 'rb')
119
+ return io.open(file_path, 'rb')
119
120
 
120
121
  def write_file(self, cache_type: CacheType, name: str) -> io.BufferedWriter:
121
122
  """
@@ -129,7 +130,7 @@ class FileStore(BaseModel):
129
130
  """
130
131
  scope_path = self.get_scoped_path(cache_type)
131
132
  os.makedirs(scope_path, exist_ok=True)
132
- return open(os.path.join(scope_path, name), 'wb')
133
+ return io.open(os.path.join(scope_path, name), 'wb')
133
134
 
134
135
  def delete_file(self, cache_type: CacheType, name: str) -> None:
135
136
  """
@@ -171,7 +172,7 @@ class DataFactory(BaseModel):
171
172
 
172
173
  def list_datasets_var(
173
174
  self,
174
- cache: Union[CacheType, ClientVariable] = CacheType.GLOBAL,
175
+ cache: Union[CacheType, NonDataVariable] = CacheType.GLOBAL,
175
176
  polling_interval: Optional[int] = None,
176
177
  ) -> DerivedVariable[List[str]]:
177
178
  """
@@ -180,7 +181,7 @@ class DataFactory(BaseModel):
180
181
  :param cache: cache type to get the list of datasets for
181
182
  :param polling_interval: optional polling_interval in seconds for the derived variable
182
183
  """
183
- cache_var = cache if isinstance(cache, ClientVariable) else Variable(cache)
184
+ cache_var = cache if isinstance(cache, NonDataVariable) else Variable(cache)
184
185
 
185
186
  return DerivedVariable(
186
187
  lambda cache_val: self.file_store.list_files(cache_val or CacheType.GLOBAL),
@@ -288,21 +289,21 @@ class DataFactory(BaseModel):
288
289
 
289
290
  def read_dataset_var(
290
291
  self,
291
- name: Union[str, ClientVariable],
292
- cache: Union[CacheType, ClientVariable] = CacheType.GLOBAL,
292
+ name: Union[str, NonDataVariable],
293
+ cache: Union[CacheType, NonDataVariable] = CacheType.GLOBAL,
293
294
  polling_interval: Optional[int] = None,
294
- ) -> DerivedVariable:
295
+ ) -> DerivedDataVariable:
295
296
  """
296
- Create a DerivedVariable which reads a specific dataset from disk
297
+ Create a DerivedDataVariable which reads a specific dataset from disk
297
298
 
298
299
  :param name: name of the dataset
299
300
  :param cache: cache to get the dataset for
300
301
  :param polling_interval: optional polling interval in seconds for the derived variable
301
302
  """
302
- name_var = name if isinstance(name, ClientVariable) else Variable(name)
303
- cache_var = cache if isinstance(cache, ClientVariable) else Variable(cache)
303
+ name_var = name if isinstance(name, NonDataVariable) else Variable(name)
304
+ cache_var = cache if isinstance(cache, NonDataVariable) else Variable(cache)
304
305
 
305
- return DerivedVariable(
306
+ return DerivedDataVariable(
306
307
  self.read_dataset,
307
308
  variables=[name_var, cache_var],
308
309
  cache=CacheType.SESSION,
@@ -319,7 +320,7 @@ class DataFactory(BaseModel):
319
320
  self.file_store.delete_file(cache, name)
320
321
 
321
322
  def delete_dataset_action(
322
- self, name: Union[str, ClientVariable], cache: Union[CacheType, ClientVariable] = CacheType.GLOBAL
323
+ self, name: Union[str, NonDataVariable], cache: Union[CacheType, NonDataVariable] = CacheType.GLOBAL
323
324
  ):
324
325
  """
325
326
  Get a SideEffect action which deletes a given dataset
@@ -327,13 +328,13 @@ class DataFactory(BaseModel):
327
328
  :param name: name of the dataset
328
329
  :param cache: cache to remove the daatset for
329
330
  """
330
- name_var = name if isinstance(name, ClientVariable) else Variable(name)
331
- cache_var = cache if isinstance(cache, ClientVariable) else Variable(cache)
331
+ name_var = name if isinstance(name, NonDataVariable) else Variable(name)
332
+ cache_var = cache if isinstance(cache, NonDataVariable) else Variable(cache)
332
333
 
333
334
  return SideEffect(lambda ctx: self.delete_dataset(ctx.extras[0], ctx.extras[1]), extras=[name_var, cache_var])
334
335
 
335
336
  def download_dataset_action(
336
- self, name: Union[str, ClientVariable], cache: Union[CacheType, ClientVariable] = CacheType.GLOBAL
337
+ self, name: Union[str, NonDataVariable], cache: Union[CacheType, NonDataVariable] = CacheType.GLOBAL
337
338
  ):
338
339
  """
339
340
  Get a DownloadContent action which downloads a dataset with a given name as a .csv
@@ -341,8 +342,8 @@ class DataFactory(BaseModel):
341
342
  :param name: name of the dataset to download
342
343
  :param cache: cache to download dataset for
343
344
  """
344
- name_var = name if isinstance(name, ClientVariable) else Variable(name)
345
- cache_var = cache if isinstance(cache, ClientVariable) else Variable(cache)
345
+ name_var = name if isinstance(name, NonDataVariable) else Variable(name)
346
+ cache_var = cache if isinstance(cache, NonDataVariable) else Variable(cache)
346
347
 
347
348
  def _resolver(ctx: DownloadContent.Ctx): # type: ignore
348
349
  ds_name, sel_cache = ctx.extras # type: ignore
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, cast
20
+ from typing import TYPE_CHECKING, Dict
21
21
 
22
22
  from dara.core.base_definitions import ActionDef
23
23
  from dara.core.interactivity.actions import (
@@ -53,7 +53,6 @@ from dara.core.visual.components import (
53
53
  TopBarFrame,
54
54
  TopBarFrameDef,
55
55
  )
56
- from dara.core.visual.components.fallback import CustomFallbackDef
57
56
  from dara.core.visual.template import TemplateBuilder
58
57
 
59
58
  if TYPE_CHECKING:
@@ -78,9 +77,8 @@ CORE_COMPONENTS: Dict[str, ComponentTypeAnnotation] = {
78
77
  RouterContent.__name__: RouterContentDef,
79
78
  SideBarFrame.__name__: SideBarFrameDef,
80
79
  TopBarFrame.__name__: TopBarFrameDef,
81
- cast(str, Fallback.Default.py_component): DefaultFallbackDef,
82
- cast(str, Fallback.Row.py_component): RowFallbackDef,
83
- cast(str, Fallback.Custom.py_component): CustomFallbackDef,
80
+ Fallback.Default.py_component: DefaultFallbackDef,
81
+ Fallback.Row.py_component: RowFallbackDef,
84
82
  For.__name__: ForDef,
85
83
  }
86
84
 
@@ -95,9 +93,9 @@ CORE_ACTIONS: Dict[str, ActionDef] = {
95
93
  Notify.__name__: NotifyDef,
96
94
  }
97
95
 
98
-
99
96
  # Define a default layout template
100
97
  def default_template(config: Configuration) -> Template:
98
+
101
99
  template = TemplateBuilder(name='default')
102
100
 
103
101
  router = template.add_router_from_pages(list(config.pages.values()))
@@ -109,6 +107,7 @@ def default_template(config: Configuration) -> Template:
109
107
 
110
108
  # Define a blank template
111
109
  def blank_template(config: Configuration) -> Template:
110
+
112
111
  template = TemplateBuilder(name='default')
113
112
 
114
113
  router = template.add_router_from_pages(list(config.pages.values()))
@@ -120,6 +119,7 @@ def blank_template(config: Configuration) -> Template:
120
119
 
121
120
  # Define a top layout template
122
121
  def top_template(config: Configuration) -> Template:
122
+
123
123
  template = TemplateBuilder(name='default')
124
124
 
125
125
  router = template.add_router_from_pages(list(config.pages.values()))
@@ -131,6 +131,7 @@ def top_template(config: Configuration) -> Template:
131
131
 
132
132
  # Define a top layout template with menu
133
133
  def top_menu_template(config: Configuration) -> Template:
134
+
134
135
  template = TemplateBuilder(name='default')
135
136
 
136
137
  router = template.add_router_from_pages(list(config.pages.values()))