dara-core 1.17.6__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.
Files changed (68) hide show
  1. dara/core/__init__.py +2 -0
  2. dara/core/actions.py +1 -2
  3. dara/core/auth/basic.py +9 -9
  4. dara/core/auth/routes.py +5 -5
  5. dara/core/auth/utils.py +4 -4
  6. dara/core/base_definitions.py +15 -22
  7. dara/core/cli.py +8 -7
  8. dara/core/configuration.py +5 -2
  9. dara/core/css.py +1 -2
  10. dara/core/data_utils.py +2 -2
  11. dara/core/defaults.py +4 -7
  12. dara/core/definitions.py +6 -9
  13. dara/core/http.py +7 -3
  14. dara/core/interactivity/actions.py +28 -30
  15. dara/core/interactivity/any_data_variable.py +6 -5
  16. dara/core/interactivity/any_variable.py +4 -7
  17. dara/core/interactivity/data_variable.py +1 -1
  18. dara/core/interactivity/derived_data_variable.py +7 -6
  19. dara/core/interactivity/derived_variable.py +93 -33
  20. dara/core/interactivity/filtering.py +19 -27
  21. dara/core/interactivity/plain_variable.py +3 -2
  22. dara/core/interactivity/switch_variable.py +4 -4
  23. dara/core/internal/cache_store/base_impl.py +2 -1
  24. dara/core/internal/cache_store/cache_store.py +17 -5
  25. dara/core/internal/cache_store/keep_all.py +4 -1
  26. dara/core/internal/cache_store/lru.py +5 -1
  27. dara/core/internal/cache_store/ttl.py +4 -1
  28. dara/core/internal/cgroup.py +1 -1
  29. dara/core/internal/dependency_resolution.py +46 -10
  30. dara/core/internal/devtools.py +2 -2
  31. dara/core/internal/download.py +4 -3
  32. dara/core/internal/encoder_registry.py +7 -7
  33. dara/core/internal/execute_action.py +4 -10
  34. dara/core/internal/hashing.py +1 -3
  35. dara/core/internal/import_discovery.py +3 -4
  36. dara/core/internal/normalization.py +9 -13
  37. dara/core/internal/pandas_utils.py +3 -3
  38. dara/core/internal/pool/task_pool.py +16 -10
  39. dara/core/internal/pool/utils.py +5 -7
  40. dara/core/internal/pool/worker.py +3 -2
  41. dara/core/internal/port_utils.py +1 -1
  42. dara/core/internal/registries.py +9 -4
  43. dara/core/internal/registry.py +3 -1
  44. dara/core/internal/registry_lookup.py +7 -3
  45. dara/core/internal/routing.py +77 -44
  46. dara/core/internal/scheduler.py +13 -8
  47. dara/core/internal/settings.py +2 -2
  48. dara/core/internal/tasks.py +8 -14
  49. dara/core/internal/utils.py +11 -10
  50. dara/core/internal/websocket.py +18 -19
  51. dara/core/js_tooling/js_utils.py +23 -24
  52. dara/core/logging.py +3 -6
  53. dara/core/main.py +14 -11
  54. dara/core/metrics/cache.py +1 -1
  55. dara/core/metrics/utils.py +3 -3
  56. dara/core/persistence.py +1 -1
  57. dara/core/umd/dara.core.umd.js +146 -128
  58. dara/core/visual/components/__init__.py +2 -2
  59. dara/core/visual/components/fallback.py +3 -3
  60. dara/core/visual/css/__init__.py +30 -31
  61. dara/core/visual/dynamic_component.py +10 -11
  62. dara/core/visual/progress_updater.py +4 -3
  63. {dara_core-1.17.6.dist-info → dara_core-1.18.0.dist-info}/METADATA +10 -10
  64. dara_core-1.18.0.dist-info/RECORD +114 -0
  65. dara_core-1.17.6.dist-info/RECORD +0 -114
  66. {dara_core-1.17.6.dist-info → dara_core-1.18.0.dist-info}/LICENSE +0 -0
  67. {dara_core-1.17.6.dist-info → dara_core-1.18.0.dist-info}/WHEEL +0 -0
  68. {dara_core-1.17.6.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
- # pylint: disable=unused-import
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, _: SessionRequestBody) -> TokenResponse:
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(
@@ -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: # pylint: disable=bare-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] # type: ignore
97
+ annotations[field] = SerializeAsAny[annotation] # type: ignore
101
98
 
102
99
  namespaces['__annotations__'] = annotations
103
100
 
104
- return super().__new__(self, name, bases, namespaces, **kwargs)
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): # type: ignore
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) for arg in field_info.annotation.__metadata__ # type: ignore
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] # type: ignore
147
- field_info.metadata = list(field_info.annotation.__metadata__) # type: ignore
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
- # Check that the string is one of allowed cache members
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: 'Variable' # type: ignore
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') # 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,10 +214,11 @@ def dev():
214
214
  stderr=subprocess.STDOUT,
215
215
  shell=True,
216
216
  ) as vite_process:
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
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()
@@ -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) # type: ignore
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
- # pylint: disable=unused-import
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 io.open(file_path, 'rb')
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 io.open(os.path.join(scope_path, name), 'wb')
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 # type: ignore
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
- if value < 0:
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['Page']] = []
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] = [], authenticated: bool = True):
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()), return_annotation=sig.return_annotation # type: ignore
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 # type: ignore
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 # type: ignore
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): # type: ignore
258
- ctx = cast(ActionCtx, ctx) # type: ignore
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) # type: ignore
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): # type: ignore
432
- ctx = cast(ActionCtx, ctx) # type: ignore
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,)) # type: ignore
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) # type: ignore
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 # type: ignore
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): # type: ignore
667
- ctx = cast(ActionCtx, ctx) # type: ignore
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) # type: ignore
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 # type: ignore
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): # type: ignore
773
- ctx = cast(ActionCtx, ctx) # type: ignore
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) # type: ignore
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 # type: ignore
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: # type: ignore
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
- try:
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
- except Exception:
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 '