fractal-server 2.14.5__py3-none-any.whl → 2.14.7__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 (108) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/db/__init__.py +2 -2
  3. fractal_server/app/models/security.py +8 -8
  4. fractal_server/app/models/user_settings.py +8 -10
  5. fractal_server/app/models/v2/accounting.py +2 -3
  6. fractal_server/app/models/v2/dataset.py +1 -2
  7. fractal_server/app/models/v2/history.py +3 -4
  8. fractal_server/app/models/v2/job.py +10 -11
  9. fractal_server/app/models/v2/project.py +1 -2
  10. fractal_server/app/models/v2/task.py +13 -14
  11. fractal_server/app/models/v2/task_group.py +15 -16
  12. fractal_server/app/models/v2/workflow.py +1 -2
  13. fractal_server/app/models/v2/workflowtask.py +6 -7
  14. fractal_server/app/routes/admin/v2/accounting.py +3 -4
  15. fractal_server/app/routes/admin/v2/job.py +13 -14
  16. fractal_server/app/routes/admin/v2/project.py +2 -4
  17. fractal_server/app/routes/admin/v2/task.py +11 -13
  18. fractal_server/app/routes/admin/v2/task_group.py +15 -17
  19. fractal_server/app/routes/admin/v2/task_group_lifecycle.py +5 -8
  20. fractal_server/app/routes/api/v2/__init__.py +2 -0
  21. fractal_server/app/routes/api/v2/_aux_functions.py +7 -9
  22. fractal_server/app/routes/api/v2/_aux_functions_history.py +1 -1
  23. fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +1 -3
  24. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +5 -6
  25. fractal_server/app/routes/api/v2/dataset.py +6 -8
  26. fractal_server/app/routes/api/v2/history.py +5 -8
  27. fractal_server/app/routes/api/v2/images.py +2 -3
  28. fractal_server/app/routes/api/v2/job.py +5 -6
  29. fractal_server/app/routes/api/v2/pre_submission_checks.py +1 -3
  30. fractal_server/app/routes/api/v2/project.py +2 -4
  31. fractal_server/app/routes/api/v2/status_legacy.py +2 -4
  32. fractal_server/app/routes/api/v2/submit.py +3 -4
  33. fractal_server/app/routes/api/v2/task.py +6 -7
  34. fractal_server/app/routes/api/v2/task_collection.py +11 -13
  35. fractal_server/app/routes/api/v2/task_collection_custom.py +4 -4
  36. fractal_server/app/routes/api/v2/task_group.py +6 -8
  37. fractal_server/app/routes/api/v2/task_group_lifecycle.py +6 -9
  38. fractal_server/app/routes/api/v2/task_version_update.py +270 -0
  39. fractal_server/app/routes/api/v2/workflow.py +5 -6
  40. fractal_server/app/routes/api/v2/workflow_import.py +3 -5
  41. fractal_server/app/routes/api/v2/workflowtask.py +2 -114
  42. fractal_server/app/routes/auth/current_user.py +2 -2
  43. fractal_server/app/routes/pagination.py +2 -3
  44. fractal_server/app/runner/exceptions.py +15 -16
  45. fractal_server/app/runner/executors/base_runner.py +3 -3
  46. fractal_server/app/runner/executors/call_command_wrapper.py +1 -1
  47. fractal_server/app/runner/executors/local/get_local_config.py +2 -3
  48. fractal_server/app/runner/executors/local/runner.py +1 -1
  49. fractal_server/app/runner/executors/slurm_common/_batching.py +2 -3
  50. fractal_server/app/runner/executors/slurm_common/_slurm_config.py +27 -29
  51. fractal_server/app/runner/executors/slurm_common/base_slurm_runner.py +32 -14
  52. fractal_server/app/runner/executors/slurm_common/get_slurm_config.py +2 -3
  53. fractal_server/app/runner/executors/slurm_common/remote.py +2 -2
  54. fractal_server/app/runner/executors/slurm_common/slurm_job_task_models.py +2 -3
  55. fractal_server/app/runner/executors/slurm_ssh/run_subprocess.py +2 -3
  56. fractal_server/app/runner/executors/slurm_ssh/runner.py +5 -4
  57. fractal_server/app/runner/executors/slurm_sudo/_subprocess_run_as_user.py +1 -2
  58. fractal_server/app/runner/executors/slurm_sudo/runner.py +7 -8
  59. fractal_server/app/runner/set_start_and_last_task_index.py +2 -5
  60. fractal_server/app/runner/shutdown.py +5 -11
  61. fractal_server/app/runner/task_files.py +3 -5
  62. fractal_server/app/runner/v2/_local.py +3 -4
  63. fractal_server/app/runner/v2/_slurm_ssh.py +8 -7
  64. fractal_server/app/runner/v2/_slurm_sudo.py +8 -9
  65. fractal_server/app/runner/v2/runner.py +4 -5
  66. fractal_server/app/runner/v2/runner_functions.py +4 -5
  67. fractal_server/app/runner/v2/submit_workflow.py +12 -11
  68. fractal_server/app/runner/v2/task_interface.py +2 -3
  69. fractal_server/app/runner/versions.py +1 -2
  70. fractal_server/app/schemas/user.py +2 -4
  71. fractal_server/app/schemas/user_group.py +1 -2
  72. fractal_server/app/schemas/user_settings.py +19 -21
  73. fractal_server/app/schemas/v2/dataset.py +2 -3
  74. fractal_server/app/schemas/v2/dumps.py +13 -15
  75. fractal_server/app/schemas/v2/history.py +6 -7
  76. fractal_server/app/schemas/v2/job.py +17 -18
  77. fractal_server/app/schemas/v2/manifest.py +12 -13
  78. fractal_server/app/schemas/v2/status_legacy.py +2 -2
  79. fractal_server/app/schemas/v2/task.py +29 -30
  80. fractal_server/app/schemas/v2/task_collection.py +8 -9
  81. fractal_server/app/schemas/v2/task_group.py +22 -23
  82. fractal_server/app/schemas/v2/workflow.py +1 -2
  83. fractal_server/app/schemas/v2/workflowtask.py +27 -29
  84. fractal_server/app/security/__init__.py +10 -12
  85. fractal_server/config.py +32 -33
  86. fractal_server/images/models.py +2 -4
  87. fractal_server/images/tools.py +4 -7
  88. fractal_server/logger.py +3 -5
  89. fractal_server/ssh/_fabric.py +37 -12
  90. fractal_server/string_tools.py +2 -2
  91. fractal_server/syringe.py +1 -1
  92. fractal_server/tasks/v2/local/collect.py +2 -3
  93. fractal_server/tasks/v2/local/deactivate.py +1 -1
  94. fractal_server/tasks/v2/local/reactivate.py +1 -1
  95. fractal_server/tasks/v2/ssh/collect.py +256 -245
  96. fractal_server/tasks/v2/ssh/deactivate.py +210 -187
  97. fractal_server/tasks/v2/ssh/reactivate.py +154 -146
  98. fractal_server/tasks/v2/utils_background.py +2 -3
  99. fractal_server/types/__init__.py +1 -2
  100. fractal_server/types/validators/_filter_validators.py +1 -2
  101. fractal_server/utils.py +4 -5
  102. fractal_server/zip_tools.py +1 -1
  103. {fractal_server-2.14.5.dist-info → fractal_server-2.14.7.dist-info}/METADATA +2 -3
  104. {fractal_server-2.14.5.dist-info → fractal_server-2.14.7.dist-info}/RECORD +107 -107
  105. fractal_server/app/history/__init__.py +0 -0
  106. {fractal_server-2.14.5.dist-info → fractal_server-2.14.7.dist-info}/LICENSE +0 -0
  107. {fractal_server-2.14.5.dist-info → fractal_server-2.14.7.dist-info}/WHEEL +0 -0
  108. {fractal_server-2.14.5.dist-info → fractal_server-2.14.7.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,5 @@
1
1
  from datetime import datetime
2
- from enum import Enum
3
- from typing import Optional
2
+ from enum import StrEnum
4
3
 
5
4
  from pydantic import BaseModel
6
5
  from pydantic import ConfigDict
@@ -14,20 +13,20 @@ from fractal_server.types import DictStrStr
14
13
  from fractal_server.types import NonEmptyStr
15
14
 
16
15
 
17
- class TaskGroupV2OriginEnum(str, Enum):
16
+ class TaskGroupV2OriginEnum(StrEnum):
18
17
  PYPI = "pypi"
19
18
  WHEELFILE = "wheel-file"
20
19
  OTHER = "other"
21
20
 
22
21
 
23
- class TaskGroupActivityStatusV2(str, Enum):
22
+ class TaskGroupActivityStatusV2(StrEnum):
24
23
  PENDING = "pending"
25
24
  ONGOING = "ongoing"
26
25
  FAILED = "failed"
27
26
  OK = "OK"
28
27
 
29
28
 
30
- class TaskGroupActivityActionV2(str, Enum):
29
+ class TaskGroupActivityActionV2(StrEnum):
31
30
  COLLECT = "collect"
32
31
  DEACTIVATE = "deactivate"
33
32
  REACTIVATE = "reactivate"
@@ -36,17 +35,17 @@ class TaskGroupActivityActionV2(str, Enum):
36
35
  class TaskGroupCreateV2(BaseModel):
37
36
  model_config = ConfigDict(extra="forbid")
38
37
  user_id: int
39
- user_group_id: Optional[int] = None
38
+ user_group_id: int | None = None
40
39
  active: bool = True
41
40
  origin: TaskGroupV2OriginEnum
42
41
  pkg_name: str
43
- version: Optional[str] = None
42
+ version: str | None = None
44
43
  python_version: NonEmptyStr = None
45
44
  path: AbsolutePathStr = None
46
45
  venv_path: AbsolutePathStr = None
47
46
  wheel_path: AbsolutePathStr = None
48
47
  pip_extras: NonEmptyStr = None
49
- pip_freeze: Optional[str] = None
48
+ pip_freeze: str | None = None
50
49
  pinned_package_versions: DictStrStr = Field(default_factory=dict)
51
50
 
52
51
 
@@ -66,21 +65,21 @@ class TaskGroupReadV2(BaseModel):
66
65
  task_list: list[TaskReadV2]
67
66
 
68
67
  user_id: int
69
- user_group_id: Optional[int] = None
68
+ user_group_id: int | None = None
70
69
 
71
70
  origin: TaskGroupV2OriginEnum
72
71
  pkg_name: str
73
- version: Optional[str] = None
74
- python_version: Optional[str] = None
75
- path: Optional[str] = None
76
- venv_path: Optional[str] = None
77
- wheel_path: Optional[str] = None
78
- pip_freeze: Optional[str] = None
79
- pip_extras: Optional[str] = None
72
+ version: str | None = None
73
+ python_version: str | None = None
74
+ path: str | None = None
75
+ venv_path: str | None = None
76
+ wheel_path: str | None = None
77
+ pip_freeze: str | None = None
78
+ pip_extras: str | None = None
80
79
  pinned_package_versions: dict[str, str] = Field(default_factory=dict)
81
80
 
82
- venv_size_in_kB: Optional[int] = None
83
- venv_file_number: Optional[int] = None
81
+ venv_size_in_kB: int | None = None
82
+ venv_file_number: int | None = None
84
83
 
85
84
  active: bool
86
85
  timestamp_created: AwareDatetime
@@ -93,27 +92,27 @@ class TaskGroupReadV2(BaseModel):
93
92
 
94
93
  class TaskGroupUpdateV2(BaseModel):
95
94
  model_config = ConfigDict(extra="forbid")
96
- user_group_id: Optional[int] = None
95
+ user_group_id: int | None = None
97
96
 
98
97
 
99
98
  class TaskGroupActivityV2Read(BaseModel):
100
99
  id: int
101
100
  user_id: int
102
- taskgroupv2_id: Optional[int] = None
101
+ taskgroupv2_id: int | None = None
103
102
  timestamp_started: AwareDatetime
104
- timestamp_ended: Optional[AwareDatetime] = None
103
+ timestamp_ended: AwareDatetime | None = None
105
104
  pkg_name: str
106
105
  version: str
107
106
  status: TaskGroupActivityStatusV2
108
107
  action: TaskGroupActivityActionV2
109
- log: Optional[str] = None
108
+ log: str | None = None
110
109
 
111
110
  @field_serializer("timestamp_started")
112
111
  def serialize_datetime_start(v: datetime) -> str:
113
112
  return v.isoformat()
114
113
 
115
114
  @field_serializer("timestamp_ended")
116
- def serialize_datetime_end(v: Optional[datetime]) -> Optional[str]:
115
+ def serialize_datetime_end(v: datetime | None) -> str | None:
117
116
  if v is None:
118
117
  return None
119
118
  else:
@@ -1,5 +1,4 @@
1
1
  from datetime import datetime
2
- from typing import Optional
3
2
 
4
3
  from pydantic import BaseModel
5
4
  from pydantic import ConfigDict
@@ -47,7 +46,7 @@ class WorkflowUpdateV2(BaseModel):
47
46
  model_config = ConfigDict(extra="forbid")
48
47
 
49
48
  name: NonEmptyStr = None
50
- reordered_workflowtask_ids: Optional[ListUniqueNonNegativeInt] = None
49
+ reordered_workflowtask_ids: ListUniqueNonNegativeInt | None = None
51
50
 
52
51
 
53
52
  class WorkflowImportV2(BaseModel):
@@ -1,6 +1,4 @@
1
1
  from typing import Any
2
- from typing import Optional
3
- from typing import Union
4
2
 
5
3
  from pydantic import BaseModel
6
4
  from pydantic import ConfigDict
@@ -20,30 +18,30 @@ from fractal_server.types import WorkflowTaskArgument
20
18
  class WorkflowTaskCreateV2(BaseModel):
21
19
  model_config = ConfigDict(extra="forbid")
22
20
 
23
- meta_non_parallel: Optional[DictStrAny] = None
24
- meta_parallel: Optional[DictStrAny] = None
25
- args_non_parallel: Optional[WorkflowTaskArgument] = None
26
- args_parallel: Optional[WorkflowTaskArgument] = None
21
+ meta_non_parallel: DictStrAny | None = None
22
+ meta_parallel: DictStrAny | None = None
23
+ args_non_parallel: WorkflowTaskArgument | None = None
24
+ args_parallel: WorkflowTaskArgument | None = None
27
25
  type_filters: TypeFilters = Field(default_factory=dict)
28
26
 
29
27
 
30
28
  class WorkflowTaskReplaceV2(BaseModel):
31
29
  """Used by 'replace-task' endpoint"""
32
30
 
33
- args_non_parallel: Optional[dict[str, Any]] = None
34
- args_parallel: Optional[dict[str, Any]] = None
31
+ args_non_parallel: dict[str, Any] | None = None
32
+ args_parallel: dict[str, Any] | None = None
35
33
 
36
34
 
37
35
  class WorkflowTaskReadV2(BaseModel):
38
36
  id: int
39
37
 
40
38
  workflow_id: int
41
- order: Optional[int] = None
42
- meta_non_parallel: Optional[dict[str, Any]] = None
43
- meta_parallel: Optional[dict[str, Any]] = None
39
+ order: int | None = None
40
+ meta_non_parallel: dict[str, Any] | None = None
41
+ meta_parallel: dict[str, Any] | None = None
44
42
 
45
- args_non_parallel: Optional[dict[str, Any]] = None
46
- args_parallel: Optional[dict[str, Any]] = None
43
+ args_non_parallel: dict[str, Any] | None = None
44
+ args_parallel: dict[str, Any] | None = None
47
45
 
48
46
  type_filters: dict[str, bool]
49
47
 
@@ -53,30 +51,30 @@ class WorkflowTaskReadV2(BaseModel):
53
51
 
54
52
 
55
53
  class WorkflowTaskReadV2WithWarning(WorkflowTaskReadV2):
56
- warning: Optional[str] = None
54
+ warning: str | None = None
57
55
 
58
56
 
59
57
  class WorkflowTaskUpdateV2(BaseModel):
60
58
  model_config = ConfigDict(extra="forbid")
61
59
 
62
- meta_non_parallel: Optional[DictStrAny] = None
63
- meta_parallel: Optional[DictStrAny] = None
64
- args_non_parallel: Optional[WorkflowTaskArgument] = None
65
- args_parallel: Optional[WorkflowTaskArgument] = None
60
+ meta_non_parallel: DictStrAny | None = None
61
+ meta_parallel: DictStrAny | None = None
62
+ args_non_parallel: WorkflowTaskArgument | None = None
63
+ args_parallel: WorkflowTaskArgument | None = None
66
64
  type_filters: TypeFilters = None
67
65
 
68
66
 
69
67
  class WorkflowTaskImportV2(BaseModel):
70
68
  model_config = ConfigDict(extra="forbid")
71
69
 
72
- meta_non_parallel: Optional[DictStrAny] = None
73
- meta_parallel: Optional[DictStrAny] = None
74
- args_non_parallel: Optional[DictStrAny] = None
75
- args_parallel: Optional[DictStrAny] = None
76
- type_filters: Optional[TypeFilters] = None
77
- input_filters: Optional[dict[str, Any]] = None
70
+ meta_non_parallel: DictStrAny | None = None
71
+ meta_parallel: DictStrAny | None = None
72
+ args_non_parallel: DictStrAny | None = None
73
+ args_parallel: DictStrAny | None = None
74
+ type_filters: TypeFilters | None = None
75
+ input_filters: dict[str, Any] | None = None
78
76
 
79
- task: Union[TaskImportV2, TaskImportV2Legacy]
77
+ task: TaskImportV2 | TaskImportV2Legacy
80
78
 
81
79
  @model_validator(mode="before")
82
80
  @classmethod
@@ -108,10 +106,10 @@ class WorkflowTaskImportV2(BaseModel):
108
106
 
109
107
 
110
108
  class WorkflowTaskExportV2(BaseModel):
111
- meta_non_parallel: Optional[dict[str, Any]] = None
112
- meta_parallel: Optional[dict[str, Any]] = None
113
- args_non_parallel: Optional[dict[str, Any]] = None
114
- args_parallel: Optional[dict[str, Any]] = None
109
+ meta_non_parallel: dict[str, Any] | None = None
110
+ meta_parallel: dict[str, Any] | None = None
111
+ args_non_parallel: dict[str, Any] | None = None
112
+ args_parallel: dict[str, Any] | None = None
115
113
  type_filters: dict[str, bool] = Field(default_factory=dict)
116
114
 
117
115
  task: TaskExportV2
@@ -27,11 +27,9 @@ registers the client and the relative routes.
27
27
  All routes are registered under the `auth/` prefix.
28
28
  """
29
29
  import contextlib
30
+ from collections.abc import AsyncGenerator
30
31
  from typing import Any
31
- from typing import AsyncGenerator
32
32
  from typing import Generic
33
- from typing import Optional
34
- from typing import Type
35
33
 
36
34
  from fastapi import Depends
37
35
  from fastapi import Request
@@ -82,24 +80,24 @@ class SQLModelUserDatabaseAsync(Generic[UP, ID], BaseUserDatabase[UP, ID]):
82
80
  """
83
81
 
84
82
  session: AsyncSession
85
- user_model: Type[UP]
86
- oauth_account_model: Optional[Type[OAuthAccount]] = None
83
+ user_model: type[UP]
84
+ oauth_account_model: type[OAuthAccount] | None = None
87
85
 
88
86
  def __init__(
89
87
  self,
90
88
  session: AsyncSession,
91
- user_model: Type[UP],
92
- oauth_account_model: Optional[Type[OAuthAccount]] = None,
89
+ user_model: type[UP],
90
+ oauth_account_model: type[OAuthAccount] | None = None,
93
91
  ):
94
92
  self.session = session
95
93
  self.user_model = user_model
96
94
  self.oauth_account_model = oauth_account_model
97
95
 
98
- async def get(self, id: ID) -> Optional[UP]:
96
+ async def get(self, id: ID) -> UP | None:
99
97
  """Get a single user by id."""
100
98
  return await self.session.get(self.user_model, id)
101
99
 
102
- async def get_by_email(self, email: str) -> Optional[UP]:
100
+ async def get_by_email(self, email: str) -> UP | None:
103
101
  """Get a single user by email."""
104
102
  statement = select(self.user_model).where(
105
103
  func.lower(self.user_model.email) == func.lower(email)
@@ -112,7 +110,7 @@ class SQLModelUserDatabaseAsync(Generic[UP, ID], BaseUserDatabase[UP, ID]):
112
110
 
113
111
  async def get_by_oauth_account(
114
112
  self, oauth: str, account_id: str
115
- ) -> Optional[UP]: # noqa
113
+ ) -> UP | None: # noqa
116
114
  """Get a single user by OAuth account id."""
117
115
  if self.oauth_account_model is None:
118
116
  raise NotImplementedError()
@@ -212,7 +210,7 @@ class UserManager(IntegerIDMixin, BaseUserManager[UserOAuth, int]):
212
210
  )
213
211
 
214
212
  async def on_after_register(
215
- self, user: UserOAuth, request: Optional[Request] = None
213
+ self, user: UserOAuth, request: Request | None = None
216
214
  ):
217
215
  logger.info(
218
216
  f"New-user registration completed ({user.id=}, {user.email=})."
@@ -290,7 +288,7 @@ async def _create_first_user(
290
288
  password: str,
291
289
  is_superuser: bool = False,
292
290
  is_verified: bool = False,
293
- username: Optional[str] = None,
291
+ username: str | None = None,
294
292
  ) -> None:
295
293
  """
296
294
  Private method to create the first fractal-server user
fractal_server/config.py CHANGED
@@ -18,7 +18,6 @@ from os import environ
18
18
  from os import getenv
19
19
  from pathlib import Path
20
20
  from typing import Literal
21
- from typing import Optional
22
21
  from typing import TypeVar
23
22
 
24
23
  from cryptography.fernet import Fernet
@@ -56,8 +55,8 @@ class MailSettings(BaseModel):
56
55
  recipients: list[EmailStr] = Field(min_length=1)
57
56
  smtp_server: str
58
57
  port: int
59
- encrypted_password: Optional[SecretStr] = None
60
- encryption_key: Optional[SecretStr] = None
58
+ encrypted_password: SecretStr | None = None
59
+ encryption_key: SecretStr | None = None
61
60
  instance_name: str
62
61
  use_starttls: bool
63
62
  use_login: bool
@@ -100,8 +99,8 @@ class OAuthClientConfig(BaseModel):
100
99
  CLIENT_NAME: str
101
100
  CLIENT_ID: str
102
101
  CLIENT_SECRET: SecretStr
103
- OIDC_CONFIGURATION_ENDPOINT: Optional[str] = None
104
- REDIRECT_URL: Optional[str] = None
102
+ OIDC_CONFIGURATION_ENDPOINT: str | None = None
103
+ REDIRECT_URL: str | None = None
105
104
 
106
105
  @model_validator(mode="before")
107
106
  @classmethod
@@ -139,7 +138,7 @@ class Settings(BaseSettings):
139
138
  JWT token lifetime, in seconds.
140
139
  """
141
140
 
142
- JWT_SECRET_KEY: Optional[SecretStr] = None
141
+ JWT_SECRET_KEY: SecretStr | None = None
143
142
  """
144
143
  JWT secret
145
144
 
@@ -202,23 +201,23 @@ class Settings(BaseSettings):
202
201
  """
203
202
  If `True`, make database operations verbose.
204
203
  """
205
- POSTGRES_USER: Optional[str] = None
204
+ POSTGRES_USER: str | None = None
206
205
  """
207
206
  User to use when connecting to the PostgreSQL database.
208
207
  """
209
- POSTGRES_PASSWORD: Optional[SecretStr] = None
208
+ POSTGRES_PASSWORD: SecretStr | None = None
210
209
  """
211
210
  Password to use when connecting to the PostgreSQL database.
212
211
  """
213
- POSTGRES_HOST: Optional[str] = "localhost"
212
+ POSTGRES_HOST: str | None = "localhost"
214
213
  """
215
214
  URL to the PostgreSQL server or path to a UNIX domain socket.
216
215
  """
217
- POSTGRES_PORT: Optional[str] = "5432"
216
+ POSTGRES_PORT: str | None = "5432"
218
217
  """
219
218
  Port number to use when connecting to the PostgreSQL server.
220
219
  """
221
- POSTGRES_DB: Optional[str] = None
220
+ POSTGRES_DB: str | None = None
222
221
  """
223
222
  Name of the PostgreSQL database to connect to.
224
223
  """
@@ -275,13 +274,13 @@ class Settings(BaseSettings):
275
274
  default admin credentials.
276
275
  """
277
276
 
278
- FRACTAL_TASKS_DIR: Optional[Path] = None
277
+ FRACTAL_TASKS_DIR: Path | None = None
279
278
  """
280
279
  Directory under which all the tasks will be saved (either an absolute path
281
280
  or a path relative to current working directory).
282
281
  """
283
282
 
284
- FRACTAL_RUNNER_WORKING_BASE_DIR: Optional[Path] = None
283
+ FRACTAL_RUNNER_WORKING_BASE_DIR: Path | None = None
285
284
  """
286
285
  Base directory for job files (either an absolute path or a path relative to
287
286
  current working directory).
@@ -293,7 +292,7 @@ class Settings(BaseSettings):
293
292
  mode="after",
294
293
  )
295
294
  @classmethod
296
- def make_paths_absolute(cls, path: Optional[Path]) -> Optional[Path]:
295
+ def make_paths_absolute(cls, path: Path | None) -> Path | None:
297
296
  if path is None or path.is_absolute():
298
297
  return path
299
298
  else:
@@ -319,7 +318,7 @@ class Settings(BaseSettings):
319
318
  Only logs of with this level (or higher) will appear in the console logs.
320
319
  """
321
320
 
322
- FRACTAL_LOCAL_CONFIG_FILE: Optional[Path] = None
321
+ FRACTAL_LOCAL_CONFIG_FILE: Path | None = None
323
322
  """
324
323
  Path of JSON file with configuration for the local backend.
325
324
  """
@@ -335,27 +334,27 @@ class Settings(BaseSettings):
335
334
  Waiting time for the shutdown phase of executors
336
335
  """
337
336
 
338
- FRACTAL_SLURM_CONFIG_FILE: Optional[Path] = None
337
+ FRACTAL_SLURM_CONFIG_FILE: Path | None = None
339
338
  """
340
339
  Path of JSON file with configuration for the SLURM backend.
341
340
  """
342
341
 
343
- FRACTAL_SLURM_WORKER_PYTHON: Optional[AbsolutePathStr] = None
342
+ FRACTAL_SLURM_WORKER_PYTHON: AbsolutePathStr | None = None
344
343
  """
345
344
  Absolute path to Python interpreter that will run the jobs on the SLURM
346
345
  nodes. If not specified, the same interpreter that runs the server is used.
347
346
  """
348
347
 
349
- FRACTAL_TASKS_PYTHON_DEFAULT_VERSION: Optional[
348
+ FRACTAL_TASKS_PYTHON_DEFAULT_VERSION: None | (
350
349
  Literal["3.9", "3.10", "3.11", "3.12"]
351
- ] = None
350
+ ) = None
352
351
  """
353
352
  Default Python version to be used for task collection. Defaults to the
354
353
  current version. Requires the corresponding variable (e.g
355
354
  `FRACTAL_TASKS_PYTHON_3_10`) to be set.
356
355
  """
357
356
 
358
- FRACTAL_TASKS_PYTHON_3_9: Optional[str] = None
357
+ FRACTAL_TASKS_PYTHON_3_9: str | None = None
359
358
  """
360
359
  Absolute path to the Python 3.9 interpreter that serves as base for virtual
361
360
  environments tasks. Note that this interpreter must have the `venv` module
@@ -364,17 +363,17 @@ class Settings(BaseSettings):
364
363
  unset, `sys.executable` is used as a default.
365
364
  """
366
365
 
367
- FRACTAL_TASKS_PYTHON_3_10: Optional[str] = None
366
+ FRACTAL_TASKS_PYTHON_3_10: str | None = None
368
367
  """
369
368
  Same as `FRACTAL_TASKS_PYTHON_3_9`, for Python 3.10.
370
369
  """
371
370
 
372
- FRACTAL_TASKS_PYTHON_3_11: Optional[str] = None
371
+ FRACTAL_TASKS_PYTHON_3_11: str | None = None
373
372
  """
374
373
  Same as `FRACTAL_TASKS_PYTHON_3_9`, for Python 3.11.
375
374
  """
376
375
 
377
- FRACTAL_TASKS_PYTHON_3_12: Optional[str] = None
376
+ FRACTAL_TASKS_PYTHON_3_12: str | None = None
378
377
  """
379
378
  Same as `FRACTAL_TASKS_PYTHON_3_9`, for Python 3.12.
380
379
  """
@@ -460,7 +459,7 @@ class Settings(BaseSettings):
460
459
  running a task that produces multiple SLURM jobs.
461
460
  """
462
461
 
463
- FRACTAL_PIP_CACHE_DIR: Optional[AbsolutePathStr] = None
462
+ FRACTAL_PIP_CACHE_DIR: AbsolutePathStr | None = None
464
463
  """
465
464
  Absolute path to the cache directory for `pip`; if unset,
466
465
  `--no-cache-dir` is used.
@@ -507,7 +506,7 @@ class Settings(BaseSettings):
507
506
  viewer paths. Useful when vizarr viewer is not used.
508
507
  """
509
508
 
510
- FRACTAL_VIEWER_BASE_FOLDER: Optional[str] = None
509
+ FRACTAL_VIEWER_BASE_FOLDER: str | None = None
511
510
  """
512
511
  Base path to Zarr files that will be served by fractal-vizarr-viewer;
513
512
  This variable is required and used only when
@@ -518,31 +517,31 @@ class Settings(BaseSettings):
518
517
  # SMTP SERVICE
519
518
  ###########################################################################
520
519
 
521
- FRACTAL_EMAIL_SENDER: Optional[EmailStr] = None
520
+ FRACTAL_EMAIL_SENDER: EmailStr | None = None
522
521
  """
523
522
  Address of the OAuth-signup email sender.
524
523
  """
525
- FRACTAL_EMAIL_PASSWORD: Optional[SecretStr] = None
524
+ FRACTAL_EMAIL_PASSWORD: SecretStr | None = None
526
525
  """
527
526
  Password for the OAuth-signup email sender.
528
527
  """
529
- FRACTAL_EMAIL_PASSWORD_KEY: Optional[SecretStr] = None
528
+ FRACTAL_EMAIL_PASSWORD_KEY: SecretStr | None = None
530
529
  """
531
530
  Key value for `cryptography.fernet` decrypt
532
531
  """
533
- FRACTAL_EMAIL_SMTP_SERVER: Optional[str] = None
532
+ FRACTAL_EMAIL_SMTP_SERVER: str | None = None
534
533
  """
535
534
  SMTP server for the OAuth-signup emails.
536
535
  """
537
- FRACTAL_EMAIL_SMTP_PORT: Optional[int] = None
536
+ FRACTAL_EMAIL_SMTP_PORT: int | None = None
538
537
  """
539
538
  SMTP server port for the OAuth-signup emails.
540
539
  """
541
- FRACTAL_EMAIL_INSTANCE_NAME: Optional[str] = None
540
+ FRACTAL_EMAIL_INSTANCE_NAME: str | None = None
542
541
  """
543
542
  Fractal instance name, to be included in the OAuth-signup emails.
544
543
  """
545
- FRACTAL_EMAIL_RECIPIENTS: Optional[str] = None
544
+ FRACTAL_EMAIL_RECIPIENTS: str | None = None
546
545
  """
547
546
  Comma-separated list of recipients of the OAuth-signup emails.
548
547
  """
@@ -558,7 +557,7 @@ class Settings(BaseSettings):
558
557
  provided.
559
558
  Accepted values: 'true', 'false'.
560
559
  """
561
- email_settings: Optional[MailSettings] = None
560
+ email_settings: MailSettings | None = None
562
561
 
563
562
  @model_validator(mode="after")
564
563
  def validate_email_settings(self):
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from pydantic import BaseModel
4
2
  from pydantic import Field
5
3
 
@@ -23,7 +21,7 @@ class _SingleImageBase(BaseModel):
23
21
  """
24
22
 
25
23
  zarr_url: ZarrUrlStr
26
- origin: Optional[ZarrDirStr] = None
24
+ origin: ZarrDirStr | None = None
27
25
 
28
26
  attributes: DictStrAny = Field(default_factory=dict)
29
27
  types: ImageTypes = Field(default_factory=dict)
@@ -48,4 +46,4 @@ class SingleImage(_SingleImageBase):
48
46
  class SingleImageUpdate(BaseModel):
49
47
  zarr_url: ZarrUrlStr
50
48
  attributes: ImageAttributes = None
51
- types: Optional[ImageTypes] = None
49
+ types: ImageTypes | None = None
@@ -1,7 +1,6 @@
1
1
  from copy import copy
2
2
  from typing import Any
3
3
  from typing import Literal
4
- from typing import Optional
5
4
  from typing import Union
6
5
 
7
6
  from fractal_server.types import AttributeFilters
@@ -13,7 +12,7 @@ def find_image_by_zarr_url(
13
12
  *,
14
13
  images: list[dict[str, Any]],
15
14
  zarr_url: str,
16
- ) -> Optional[ImageSearch]:
15
+ ) -> ImageSearch | None:
17
16
  """
18
17
  Return a copy of the image with a given zarr_url, and its positional index.
19
18
 
@@ -65,8 +64,8 @@ def match_filter(
65
64
 
66
65
  def filter_image_list(
67
66
  images: list[dict[str, Any]],
68
- type_filters: Optional[dict[str, bool]] = None,
69
- attribute_filters: Optional[AttributeFilters] = None,
67
+ type_filters: dict[str, bool] | None = None,
68
+ attribute_filters: AttributeFilters | None = None,
70
69
  ) -> list[dict[str, Any]]:
71
70
  """
72
71
  Compute a sublist with images that match a filter set.
@@ -141,6 +140,4 @@ def aggregate_types(images: list[dict[str, Any]]) -> list[str]:
141
140
  """
142
141
  Given a list of images, this function returns a list of all image types.
143
142
  """
144
- return list(
145
- set(type for image in images for type in image["types"].keys())
146
- )
143
+ return list({type for image in images for type in image["types"].keys()})
fractal_server/logger.py CHANGED
@@ -14,8 +14,6 @@ This module provides logging utilities
14
14
  """
15
15
  import logging
16
16
  from pathlib import Path
17
- from typing import Optional
18
- from typing import Union
19
17
 
20
18
  from .config import get_settings
21
19
  from .syringe import Inject
@@ -25,7 +23,7 @@ LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
25
23
  LOG_FORMATTER = logging.Formatter(LOG_FORMAT)
26
24
 
27
25
 
28
- def get_logger(logger_name: Optional[str] = None) -> logging.Logger:
26
+ def get_logger(logger_name: str | None = None) -> logging.Logger:
29
27
  """
30
28
  Wrap the
31
29
  [`logging.getLogger`](https://docs.python.org/3/library/logging.html#logging.getLogger)
@@ -57,8 +55,8 @@ def get_logger(logger_name: Optional[str] = None) -> logging.Logger:
57
55
  def set_logger(
58
56
  logger_name: str,
59
57
  *,
60
- log_file_path: Optional[Union[str, Path]] = None,
61
- default_logging_level: Optional[int] = None,
58
+ log_file_path: str | Path | None = None,
59
+ default_logging_level: int | None = None,
62
60
  ) -> logging.Logger:
63
61
  """
64
62
  Set up a `fractal-server` logger