dara-core 1.21.15__py3-none-any.whl → 1.21.17__py3-none-any.whl

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