fractal-server 2.13.0__py3-none-any.whl → 2.14.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 (127) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/__main__.py +3 -1
  3. fractal_server/app/models/linkusergroup.py +6 -2
  4. fractal_server/app/models/v2/__init__.py +11 -1
  5. fractal_server/app/models/v2/accounting.py +35 -0
  6. fractal_server/app/models/v2/dataset.py +1 -11
  7. fractal_server/app/models/v2/history.py +78 -0
  8. fractal_server/app/models/v2/job.py +10 -3
  9. fractal_server/app/models/v2/task_group.py +2 -2
  10. fractal_server/app/models/v2/workflow.py +1 -1
  11. fractal_server/app/models/v2/workflowtask.py +1 -1
  12. fractal_server/app/routes/admin/v2/__init__.py +4 -0
  13. fractal_server/app/routes/admin/v2/accounting.py +98 -0
  14. fractal_server/app/routes/admin/v2/impersonate.py +35 -0
  15. fractal_server/app/routes/admin/v2/job.py +5 -13
  16. fractal_server/app/routes/admin/v2/task.py +1 -1
  17. fractal_server/app/routes/admin/v2/task_group.py +4 -29
  18. fractal_server/app/routes/api/__init__.py +1 -1
  19. fractal_server/app/routes/api/v2/__init__.py +8 -2
  20. fractal_server/app/routes/api/v2/_aux_functions.py +66 -0
  21. fractal_server/app/routes/api/v2/_aux_functions_history.py +166 -0
  22. fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +3 -3
  23. fractal_server/app/routes/api/v2/dataset.py +0 -17
  24. fractal_server/app/routes/api/v2/history.py +544 -0
  25. fractal_server/app/routes/api/v2/images.py +31 -43
  26. fractal_server/app/routes/api/v2/job.py +30 -0
  27. fractal_server/app/routes/api/v2/project.py +1 -53
  28. fractal_server/app/routes/api/v2/{status.py → status_legacy.py} +6 -6
  29. fractal_server/app/routes/api/v2/submit.py +17 -14
  30. fractal_server/app/routes/api/v2/task.py +3 -10
  31. fractal_server/app/routes/api/v2/task_collection_custom.py +4 -9
  32. fractal_server/app/routes/api/v2/task_group.py +2 -22
  33. fractal_server/app/routes/api/v2/verify_image_types.py +61 -0
  34. fractal_server/app/routes/api/v2/workflow.py +28 -69
  35. fractal_server/app/routes/api/v2/workflowtask.py +53 -50
  36. fractal_server/app/routes/auth/group.py +0 -16
  37. fractal_server/app/routes/auth/oauth.py +5 -3
  38. fractal_server/app/routes/aux/__init__.py +0 -20
  39. fractal_server/app/routes/pagination.py +47 -0
  40. fractal_server/app/runner/components.py +0 -3
  41. fractal_server/app/runner/compress_folder.py +57 -29
  42. fractal_server/app/runner/exceptions.py +4 -0
  43. fractal_server/app/runner/executors/base_runner.py +157 -0
  44. fractal_server/app/runner/{v2/_local/_local_config.py → executors/local/get_local_config.py} +7 -9
  45. fractal_server/app/runner/executors/local/runner.py +248 -0
  46. fractal_server/app/runner/executors/{slurm → slurm_common}/_batching.py +1 -1
  47. fractal_server/app/runner/executors/{slurm → slurm_common}/_slurm_config.py +9 -7
  48. fractal_server/app/runner/executors/slurm_common/base_slurm_runner.py +868 -0
  49. fractal_server/app/runner/{v2/_slurm_common → executors/slurm_common}/get_slurm_config.py +48 -17
  50. fractal_server/app/runner/executors/{slurm → slurm_common}/remote.py +36 -47
  51. fractal_server/app/runner/executors/slurm_common/slurm_job_task_models.py +134 -0
  52. fractal_server/app/runner/executors/slurm_ssh/runner.py +268 -0
  53. fractal_server/app/runner/executors/slurm_sudo/__init__.py +0 -0
  54. fractal_server/app/runner/executors/{slurm/sudo → slurm_sudo}/_subprocess_run_as_user.py +2 -83
  55. fractal_server/app/runner/executors/slurm_sudo/runner.py +193 -0
  56. fractal_server/app/runner/extract_archive.py +1 -3
  57. fractal_server/app/runner/task_files.py +134 -87
  58. fractal_server/app/runner/v2/__init__.py +0 -395
  59. fractal_server/app/runner/v2/_local.py +88 -0
  60. fractal_server/app/runner/v2/{_slurm_ssh/__init__.py → _slurm_ssh.py} +22 -19
  61. fractal_server/app/runner/v2/{_slurm_sudo/__init__.py → _slurm_sudo.py} +19 -15
  62. fractal_server/app/runner/v2/db_tools.py +119 -0
  63. fractal_server/app/runner/v2/runner.py +219 -98
  64. fractal_server/app/runner/v2/runner_functions.py +491 -189
  65. fractal_server/app/runner/v2/runner_functions_low_level.py +40 -43
  66. fractal_server/app/runner/v2/submit_workflow.py +358 -0
  67. fractal_server/app/runner/v2/task_interface.py +31 -0
  68. fractal_server/app/schemas/_validators.py +13 -24
  69. fractal_server/app/schemas/user.py +10 -7
  70. fractal_server/app/schemas/user_settings.py +9 -21
  71. fractal_server/app/schemas/v2/__init__.py +10 -1
  72. fractal_server/app/schemas/v2/accounting.py +18 -0
  73. fractal_server/app/schemas/v2/dataset.py +12 -94
  74. fractal_server/app/schemas/v2/dumps.py +26 -9
  75. fractal_server/app/schemas/v2/history.py +80 -0
  76. fractal_server/app/schemas/v2/job.py +15 -8
  77. fractal_server/app/schemas/v2/manifest.py +14 -7
  78. fractal_server/app/schemas/v2/project.py +9 -7
  79. fractal_server/app/schemas/v2/status_legacy.py +35 -0
  80. fractal_server/app/schemas/v2/task.py +72 -77
  81. fractal_server/app/schemas/v2/task_collection.py +14 -32
  82. fractal_server/app/schemas/v2/task_group.py +10 -9
  83. fractal_server/app/schemas/v2/workflow.py +10 -11
  84. fractal_server/app/schemas/v2/workflowtask.py +2 -21
  85. fractal_server/app/security/__init__.py +3 -3
  86. fractal_server/app/security/signup_email.py +2 -2
  87. fractal_server/config.py +91 -90
  88. fractal_server/images/tools.py +23 -0
  89. fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +50 -0
  90. fractal_server/migrations/versions/9db60297b8b2_set_ondelete.py +250 -0
  91. fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +57 -0
  92. fractal_server/migrations/versions/c90a7c76e996_job_id_in_history_run.py +41 -0
  93. fractal_server/migrations/versions/e81103413827_add_job_type_filters.py +36 -0
  94. fractal_server/migrations/versions/f37aceb45062_make_historyunit_logfile_required.py +39 -0
  95. fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +120 -0
  96. fractal_server/ssh/_fabric.py +28 -14
  97. fractal_server/tasks/v2/local/collect.py +2 -2
  98. fractal_server/tasks/v2/ssh/collect.py +2 -2
  99. fractal_server/tasks/v2/templates/2_pip_install.sh +1 -1
  100. fractal_server/tasks/v2/templates/4_pip_show.sh +1 -1
  101. fractal_server/tasks/v2/utils_background.py +1 -20
  102. fractal_server/tasks/v2/utils_database.py +30 -17
  103. fractal_server/tasks/v2/utils_templates.py +6 -0
  104. {fractal_server-2.13.0.dist-info → fractal_server-2.14.0.dist-info}/METADATA +4 -4
  105. {fractal_server-2.13.0.dist-info → fractal_server-2.14.0.dist-info}/RECORD +114 -99
  106. {fractal_server-2.13.0.dist-info → fractal_server-2.14.0.dist-info}/WHEEL +1 -1
  107. fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -126
  108. fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -116
  109. fractal_server/app/runner/executors/slurm/ssh/executor.py +0 -1386
  110. fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -71
  111. fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -130
  112. fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -1281
  113. fractal_server/app/runner/v2/_local/__init__.py +0 -129
  114. fractal_server/app/runner/v2/_local/_submit_setup.py +0 -52
  115. fractal_server/app/runner/v2/_local/executor.py +0 -100
  116. fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -83
  117. fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -83
  118. fractal_server/app/runner/v2/handle_failed_job.py +0 -59
  119. fractal_server/app/schemas/v2/status.py +0 -16
  120. /fractal_server/app/{runner/executors/slurm → history}/__init__.py +0 -0
  121. /fractal_server/app/runner/executors/{slurm/ssh → local}/__init__.py +0 -0
  122. /fractal_server/app/runner/executors/{slurm/sudo → slurm_common}/__init__.py +0 -0
  123. /fractal_server/app/runner/executors/{_job_states.py → slurm_common/_job_states.py} +0 -0
  124. /fractal_server/app/runner/executors/{slurm → slurm_common}/utils_executors.py +0 -0
  125. /fractal_server/app/runner/{v2/_slurm_common → executors/slurm_ssh}/__init__.py +0 -0
  126. {fractal_server-2.13.0.dist-info → fractal_server-2.14.0.dist-info}/LICENSE +0 -0
  127. {fractal_server-2.13.0.dist-info → fractal_server-2.14.0.dist-info}/entry_points.txt +0 -0
@@ -9,38 +9,64 @@ from pydantic import field_validator
9
9
  from pydantic import HttpUrl
10
10
  from pydantic import model_validator
11
11
 
12
+ from .._validators import cant_set_none
13
+ from fractal_server.app.schemas._validators import NonEmptyString
12
14
  from fractal_server.app.schemas._validators import val_unique_list
13
15
  from fractal_server.app.schemas._validators import valdict_keys
14
- from fractal_server.app.schemas._validators import valstr
16
+ from fractal_server.logger import set_logger
15
17
  from fractal_server.string_tools import validate_cmd
16
18
 
19
+ TaskTypeType = Literal[
20
+ "compound",
21
+ "converter_compound",
22
+ "non_parallel",
23
+ "converter_non_parallel",
24
+ "parallel",
25
+ ]
26
+
27
+
28
+ logger = set_logger(__name__)
29
+
17
30
 
18
31
  class TaskCreateV2(BaseModel):
19
32
  model_config = ConfigDict(extra="forbid")
20
33
 
21
- name: str
34
+ name: NonEmptyString
22
35
 
23
- command_non_parallel: Optional[str] = None
24
- command_parallel: Optional[str] = None
36
+ command_non_parallel: Optional[NonEmptyString] = None
37
+ command_parallel: Optional[NonEmptyString] = None
25
38
 
26
39
  meta_non_parallel: Optional[dict[str, Any]] = None
27
40
  meta_parallel: Optional[dict[str, Any]] = None
28
- version: Optional[str] = None
41
+ version: Optional[NonEmptyString] = None
29
42
  args_schema_non_parallel: Optional[dict[str, Any]] = None
30
43
  args_schema_parallel: Optional[dict[str, Any]] = None
31
- args_schema_version: Optional[str] = None
44
+ args_schema_version: Optional[NonEmptyString] = None
32
45
  docs_info: Optional[str] = None
33
46
  docs_link: Optional[str] = None
34
47
 
35
48
  input_types: dict[str, bool] = Field(default={})
36
49
  output_types: dict[str, bool] = Field(default={})
37
50
 
38
- category: Optional[str] = None
39
- modality: Optional[str] = None
40
- tags: list[str] = Field(default_factory=list)
41
- authors: Optional[str] = None
51
+ category: Optional[NonEmptyString] = None
52
+ modality: Optional[NonEmptyString] = None
53
+ tags: list[NonEmptyString] = Field(default_factory=list)
54
+ authors: Optional[NonEmptyString] = None
55
+
56
+ type: Optional[TaskTypeType] = None
42
57
 
43
58
  # Validators
59
+
60
+ @field_validator(
61
+ "command_non_parallel",
62
+ "command_parallel",
63
+ "version",
64
+ "args_schema_version",
65
+ )
66
+ @classmethod
67
+ def _cant_set_none(cls, v):
68
+ return cant_set_none(v)
69
+
44
70
  @model_validator(mode="after")
45
71
  def validate_commands(self):
46
72
  command_parallel = self.command_parallel
@@ -57,14 +83,22 @@ class TaskCreateV2(BaseModel):
57
83
 
58
84
  return self
59
85
 
60
- _name = field_validator("name")(classmethod(valstr("name")))
61
- _command_non_parallel = field_validator("command_non_parallel")(
62
- classmethod(valstr("command_non_parallel"))
63
- )
64
- _command_parallel = field_validator("command_parallel")(
65
- classmethod(valstr("command_parallel"))
66
- )
67
- _version = field_validator("version")(classmethod(valstr("version")))
86
+ @model_validator(mode="after")
87
+ def set_task_type(self):
88
+ if self.type is None:
89
+ logger.warning(
90
+ f"Task type is not set for task '{self.name}', "
91
+ "which will be deprecated in a future version. "
92
+ "Please move to `fractal-task-tools`."
93
+ )
94
+ if self.command_non_parallel is None:
95
+ self.type = "parallel"
96
+ elif self.command_parallel is None:
97
+ self.type = "non_parallel"
98
+ else:
99
+ self.type = "compound"
100
+
101
+ return self
68
102
 
69
103
  _meta_non_parallel = field_validator("meta_non_parallel")(
70
104
  classmethod(valdict_keys("meta_non_parallel"))
@@ -78,9 +112,6 @@ class TaskCreateV2(BaseModel):
78
112
  _args_schema_parallel = field_validator("args_schema_parallel")(
79
113
  classmethod(valdict_keys("args_schema_parallel"))
80
114
  )
81
- _args_schema_version = field_validator("args_schema_version")(
82
- classmethod(valstr("args_schema_version"))
83
- )
84
115
  _input_types = field_validator("input_types")(
85
116
  classmethod(valdict_keys("input_types"))
86
117
  )
@@ -88,21 +119,9 @@ class TaskCreateV2(BaseModel):
88
119
  classmethod(valdict_keys("output_types"))
89
120
  )
90
121
 
91
- _category = field_validator("category")(
92
- classmethod(valstr("category", accept_none=True))
93
- )
94
- _modality = field_validator("modality")(
95
- classmethod(valstr("modality", accept_none=True))
96
- )
97
- _authors = field_validator("authors")(
98
- classmethod(valstr("authors", accept_none=True))
99
- )
100
-
101
122
  @field_validator("tags")
102
123
  @classmethod
103
124
  def validate_list_of_strings(cls, value):
104
- for i, tag in enumerate(value):
105
- value[i] = valstr(f"tags[{i}]")(cls, tag)
106
125
  return val_unique_list("tags")(cls, value)
107
126
 
108
127
  @field_validator("docs_link", mode="after")
@@ -116,7 +135,7 @@ class TaskCreateV2(BaseModel):
116
135
  class TaskReadV2(BaseModel):
117
136
  id: int
118
137
  name: str
119
- type: Literal["parallel", "non_parallel", "compound"]
138
+ type: TaskTypeType
120
139
  source: Optional[str] = None
121
140
  version: Optional[str] = None
122
141
 
@@ -143,17 +162,23 @@ class TaskReadV2(BaseModel):
143
162
  class TaskUpdateV2(BaseModel):
144
163
  model_config = ConfigDict(extra="forbid")
145
164
 
146
- command_parallel: Optional[str] = None
147
- command_non_parallel: Optional[str] = None
165
+ command_parallel: Optional[NonEmptyString] = None
166
+ command_non_parallel: Optional[NonEmptyString] = None
148
167
  input_types: Optional[dict[str, bool]] = None
149
168
  output_types: Optional[dict[str, bool]] = None
150
169
 
151
- category: Optional[str] = None
152
- modality: Optional[str] = None
153
- authors: Optional[str] = None
154
- tags: Optional[list[str]] = None
170
+ category: Optional[NonEmptyString] = None
171
+ modality: Optional[NonEmptyString] = None
172
+ authors: Optional[NonEmptyString] = None
173
+ tags: Optional[list[NonEmptyString]] = None
155
174
 
156
175
  # Validators
176
+
177
+ @field_validator("command_parallel", "command_non_parallel")
178
+ @classmethod
179
+ def _cant_set_none(cls, v):
180
+ return cant_set_none(v)
181
+
157
182
  @field_validator("input_types", "output_types")
158
183
  @classmethod
159
184
  def val_is_dict(cls, v):
@@ -161,12 +186,6 @@ class TaskUpdateV2(BaseModel):
161
186
  raise ValueError
162
187
  return v
163
188
 
164
- _command_parallel = field_validator("command_parallel")(
165
- classmethod(valstr("command_parallel"))
166
- )
167
- _command_non_parallel = field_validator("command_non_parallel")(
168
- classmethod(valstr("command_non_parallel"))
169
- )
170
189
  _input_types = field_validator("input_types")(
171
190
  classmethod(valdict_keys("input_types"))
172
191
  )
@@ -174,49 +193,25 @@ class TaskUpdateV2(BaseModel):
174
193
  classmethod(valdict_keys("output_types"))
175
194
  )
176
195
 
177
- _category = field_validator("category")(
178
- classmethod(valstr("category", accept_none=True))
179
- )
180
- _modality = field_validator("modality")(
181
- classmethod(valstr("modality", accept_none=True))
182
- )
183
- _authors = field_validator("authors")(
184
- classmethod(valstr("authors", accept_none=True))
185
- )
186
-
187
196
  @field_validator("tags")
188
197
  @classmethod
189
198
  def validate_tags(cls, value):
190
- for i, tag in enumerate(value):
191
- value[i] = valstr(f"tags[{i}]")(cls, tag)
192
199
  return val_unique_list("tags")(cls, value)
193
200
 
194
201
 
195
202
  class TaskImportV2(BaseModel):
196
203
  model_config = ConfigDict(extra="forbid")
197
204
 
198
- pkg_name: str
199
- version: Optional[str] = None
200
- name: str
201
- _pkg_name = field_validator("pkg_name")(classmethod(valstr("pkg_name")))
202
- _version = field_validator("version")(
203
- classmethod(valstr("version", accept_none=True))
204
- )
205
- _name = field_validator("name")(classmethod(valstr("name")))
205
+ pkg_name: NonEmptyString
206
+ version: Optional[NonEmptyString] = None
207
+ name: NonEmptyString
206
208
 
207
209
 
208
210
  class TaskImportV2Legacy(BaseModel):
209
- source: str
210
- _source = field_validator("source")(classmethod(valstr("source")))
211
+ source: NonEmptyString
211
212
 
212
213
 
213
214
  class TaskExportV2(BaseModel):
214
- pkg_name: str
215
- version: Optional[str] = None
216
- name: str
217
-
218
- _pkg_name = field_validator("pkg_name")(classmethod(valstr("pkg_name")))
219
- _version = field_validator("version")(
220
- classmethod(valstr("version", accept_none=True))
221
- )
222
- _name = field_validator("name")(classmethod(valstr("name")))
215
+ pkg_name: NonEmptyString
216
+ version: Optional[NonEmptyString] = None
217
+ name: NonEmptyString
@@ -7,7 +7,7 @@ from pydantic import ConfigDict
7
7
  from pydantic import field_validator
8
8
  from pydantic import model_validator
9
9
 
10
- from .._validators import valstr
10
+ from .._validators import NonEmptyString
11
11
  from fractal_server.app.schemas.v2 import ManifestV2
12
12
  from fractal_server.string_tools import validate_cmd
13
13
 
@@ -46,9 +46,9 @@ class TaskCollectPipV2(BaseModel):
46
46
  """
47
47
 
48
48
  model_config = ConfigDict(extra="forbid")
49
- package: Optional[str] = None
50
- package_version: Optional[str] = None
51
- package_extras: Optional[str] = None
49
+ package: Optional[NonEmptyString] = None
50
+ package_version: Optional[NonEmptyString] = None
51
+ package_extras: Optional[NonEmptyString] = None
52
52
  python_version: Optional[Literal["3.9", "3.10", "3.11", "3.12"]] = None
53
53
  pinned_package_versions: Optional[dict[str, str]] = None
54
54
 
@@ -57,7 +57,6 @@ class TaskCollectPipV2(BaseModel):
57
57
  def package_validator(cls, value: Optional[str]) -> Optional[str]:
58
58
  if value is None:
59
59
  return value
60
- value = valstr("package")(cls, value)
61
60
  validate_cmd(value, attribute_name="package")
62
61
  return value
63
62
 
@@ -66,7 +65,6 @@ class TaskCollectPipV2(BaseModel):
66
65
  def package_version_validator(cls, value: Optional[str]) -> Optional[str]:
67
66
  if value is None:
68
67
  return value
69
- value = valstr("package_version")(cls, value)
70
68
  validate_cmd(value, attribute_name="package_version")
71
69
  return value
72
70
 
@@ -75,11 +73,11 @@ class TaskCollectPipV2(BaseModel):
75
73
  def pinned_package_versions_validator(cls, value):
76
74
  if value is None:
77
75
  return value
76
+
78
77
  old_keys = list(value.keys())
79
- new_keys = [
80
- valstr(f"pinned_package_versions[{key}]")(cls, key)
81
- for key in old_keys
82
- ]
78
+ new_keys = [key.strip() for key in old_keys]
79
+ if any(k == "" for k in new_keys):
80
+ raise ValueError(f"Empty string in {new_keys}.")
83
81
  if len(new_keys) != len(set(new_keys)):
84
82
  raise ValueError(
85
83
  f"Dictionary contains multiple identical keys: {value}."
@@ -87,6 +85,7 @@ class TaskCollectPipV2(BaseModel):
87
85
  for old_key, new_key in zip(old_keys, new_keys):
88
86
  if new_key != old_key:
89
87
  value[new_key] = value.pop(old_key)
88
+
90
89
  for pkg, version in value.items():
91
90
  validate_cmd(pkg)
92
91
  validate_cmd(version)
@@ -97,7 +96,6 @@ class TaskCollectPipV2(BaseModel):
97
96
  def package_extras_validator(cls, value: Optional[str]) -> Optional[str]:
98
97
  if value is None:
99
98
  return value
100
- value = valstr("package_extras")(cls, value)
101
99
  validate_cmd(value, attribute_name="package_extras")
102
100
  return value
103
101
 
@@ -122,26 +120,11 @@ class TaskCollectCustomV2(BaseModel):
122
120
 
123
121
  model_config = ConfigDict(extra="forbid")
124
122
  manifest: ManifestV2
125
- python_interpreter: str
126
- label: str
127
- package_root: Optional[str] = None
128
- package_name: Optional[str] = None
129
- version: Optional[str] = None
130
-
131
- # Valstr
132
- _python_interpreter = field_validator("python_interpreter")(
133
- classmethod(valstr("python_interpreter"))
134
- )
135
- _label = field_validator("label")(classmethod(valstr("label")))
136
- _package_root = field_validator("package_root")(
137
- classmethod(valstr("package_root", accept_none=True))
138
- )
139
- _package_name = field_validator("package_name")(
140
- classmethod(valstr("package_name", accept_none=True))
141
- )
142
- _version = field_validator("version")(
143
- classmethod(valstr("version", accept_none=True))
144
- )
123
+ python_interpreter: NonEmptyString
124
+ label: NonEmptyString
125
+ package_root: Optional[NonEmptyString] = None
126
+ package_name: Optional[NonEmptyString] = None
127
+ version: Optional[NonEmptyString] = None
145
128
 
146
129
  @model_validator(mode="before")
147
130
  @classmethod
@@ -165,7 +148,6 @@ class TaskCollectCustomV2(BaseModel):
165
148
  """
166
149
  if value is not None:
167
150
  validate_cmd(value)
168
- value = valstr("package_name")(cls, value)
169
151
  value = value.replace(" ", "")
170
152
  return value
171
153
 
@@ -9,9 +9,10 @@ from pydantic import field_serializer
9
9
  from pydantic import field_validator
10
10
  from pydantic.types import AwareDatetime
11
11
 
12
+ from .._validators import cant_set_none
13
+ from .._validators import NonEmptyString
12
14
  from .._validators import val_absolute_path
13
15
  from .._validators import valdict_keys
14
- from .._validators import valstr
15
16
  from .task import TaskReadV2
16
17
 
17
18
 
@@ -42,15 +43,21 @@ class TaskGroupCreateV2(BaseModel):
42
43
  origin: TaskGroupV2OriginEnum
43
44
  pkg_name: str
44
45
  version: Optional[str] = None
45
- python_version: Optional[str] = None
46
+ python_version: Optional[NonEmptyString] = None
46
47
  path: Optional[str] = None
47
48
  venv_path: Optional[str] = None
48
49
  wheel_path: Optional[str] = None
49
- pip_extras: Optional[str] = None
50
+ pip_extras: Optional[NonEmptyString] = None
50
51
  pip_freeze: Optional[str] = None
51
52
  pinned_package_versions: dict[str, str] = Field(default_factory=dict)
52
53
 
53
54
  # Validators
55
+
56
+ @field_validator("python_version", "pip_extras")
57
+ @classmethod
58
+ def _cant_set_none(cls, v):
59
+ return cant_set_none(v)
60
+
54
61
  _path = field_validator("path")(classmethod(val_absolute_path("path")))
55
62
  _venv_path = field_validator("venv_path")(
56
63
  classmethod(val_absolute_path("venv_path"))
@@ -61,12 +68,6 @@ class TaskGroupCreateV2(BaseModel):
61
68
  _pinned_package_versions = field_validator("pinned_package_versions")(
62
69
  valdict_keys("pinned_package_versions")
63
70
  )
64
- _pip_extras = field_validator("pip_extras")(
65
- classmethod(valstr("pip_extras"))
66
- )
67
- _python_version = field_validator("python_version")(
68
- classmethod(valstr("python_version"))
69
- )
70
71
 
71
72
 
72
73
  class TaskGroupCreateV2Strict(TaskGroupCreateV2):
@@ -7,7 +7,8 @@ from pydantic import field_serializer
7
7
  from pydantic import field_validator
8
8
  from pydantic.types import AwareDatetime
9
9
 
10
- from .._validators import valstr
10
+ from .._validators import cant_set_none
11
+ from .._validators import NonEmptyString
11
12
  from .project import ProjectReadV2
12
13
  from .workflowtask import WorkflowTaskExportV2
13
14
  from .workflowtask import WorkflowTaskImportV2
@@ -19,10 +20,7 @@ class WorkflowCreateV2(BaseModel):
19
20
 
20
21
  model_config = ConfigDict(extra="forbid")
21
22
 
22
- name: str
23
-
24
- # Validators
25
- _name = field_validator("name")(classmethod(valstr("name")))
23
+ name: NonEmptyString
26
24
 
27
25
 
28
26
  class WorkflowReadV2(BaseModel):
@@ -47,11 +45,15 @@ class WorkflowUpdateV2(BaseModel):
47
45
 
48
46
  model_config = ConfigDict(extra="forbid")
49
47
 
50
- name: Optional[str] = None
48
+ name: Optional[NonEmptyString] = None
51
49
  reordered_workflowtask_ids: Optional[list[int]] = None
52
50
 
53
51
  # Validators
54
- _name = field_validator("name")(classmethod(valstr("name")))
52
+
53
+ @field_validator("name")
54
+ @classmethod
55
+ def _cant_set_none(cls, v):
56
+ return cant_set_none(v)
55
57
 
56
58
  @field_validator("reordered_workflowtask_ids")
57
59
  @classmethod
@@ -72,12 +74,9 @@ class WorkflowImportV2(BaseModel):
72
74
  """
73
75
 
74
76
  model_config = ConfigDict(extra="forbid")
75
- name: str
77
+ name: NonEmptyString
76
78
  task_list: list[WorkflowTaskImportV2]
77
79
 
78
- # Validators
79
- _name = field_validator("name")(classmethod(valstr("name")))
80
-
81
80
 
82
81
  class WorkflowExportV2(BaseModel):
83
82
  """
@@ -1,4 +1,3 @@
1
- from enum import Enum
2
1
  from typing import Any
3
2
  from typing import Optional
4
3
  from typing import Union
@@ -16,29 +15,11 @@ from .task import TaskExportV2
16
15
  from .task import TaskImportV2
17
16
  from .task import TaskImportV2Legacy
18
17
  from .task import TaskReadV2
18
+ from .task import TaskTypeType
19
19
 
20
20
  RESERVED_ARGUMENTS = {"zarr_dir", "zarr_url", "zarr_urls", "init_args"}
21
21
 
22
22
 
23
- class WorkflowTaskStatusTypeV2(str, Enum):
24
- """
25
- Define the available values for the status of a `WorkflowTask`.
26
-
27
- This model is used within the `Dataset.history` attribute, which is
28
- constructed in the runner and then used in the API (e.g. in the
29
- `api/v2/project/{project_id}/dataset/{dataset_id}/status` endpoint).
30
-
31
- Attributes:
32
- SUBMITTED: The `WorkflowTask` is part of a running job.
33
- DONE: The most-recent execution of this `WorkflowTask` was successful.
34
- FAILED: The most-recent execution of this `WorkflowTask` failed.
35
- """
36
-
37
- SUBMITTED = "submitted"
38
- DONE = "done"
39
- FAILED = "failed"
40
-
41
-
42
23
  class WorkflowTaskCreateV2(BaseModel):
43
24
  model_config = ConfigDict(extra="forbid")
44
25
 
@@ -113,7 +94,7 @@ class WorkflowTaskReadV2(BaseModel):
113
94
 
114
95
  type_filters: dict[str, bool]
115
96
 
116
- task_type: str
97
+ task_type: TaskTypeType
117
98
  task_id: int
118
99
  task: TaskReadV2
119
100
 
@@ -24,7 +24,7 @@ FastAPIUsers with Barer Token and cookie transports and register local routes.
24
24
  Then, for each OAuth client defined in the Fractal Settings configuration, it
25
25
  registers the client and the relative routes.
26
26
 
27
- All routes are registerd under the `auth/` prefix.
27
+ All routes are registered under the `auth/` prefix.
28
28
  """
29
29
  import contextlib
30
30
  from typing import Any
@@ -296,7 +296,7 @@ async def _create_first_user(
296
296
  Private method to create the first fractal-server user
297
297
 
298
298
  Create a user with the given default arguments and return a message with
299
- the relevant informations. If the user alredy exists, for example after a
299
+ the relevant information. If the user already exists, for example after a
300
300
  restart, it returns a message to inform that user already exists.
301
301
 
302
302
  **WARNING**: This function is only meant to create the first user, and then
@@ -312,7 +312,7 @@ async def _create_first_user(
312
312
  email: New user's email
313
313
  password: New user's password
314
314
  is_superuser: `True` if the new user is a superuser
315
- is_verified: `True` if the new user is verifie
315
+ is_verified: `True` if the new user is verified
316
316
  username:
317
317
  """
318
318
  function_logger = set_logger("fractal_server.create_first_user")
@@ -36,8 +36,8 @@ def mail_new_oauth_signup(msg: str, email_settings: MailSettings):
36
36
  server.ehlo()
37
37
  if email_settings.use_login:
38
38
  password = (
39
- Fernet(email_settings.encryption_key)
40
- .decrypt(email_settings.encrypted_password)
39
+ Fernet(email_settings.encryption_key.get_secret_value())
40
+ .decrypt(email_settings.encrypted_password.get_secret_value())
41
41
  .decode("utf-8")
42
42
  )
43
43
  server.login(user=email_settings.sender, password=password)