fractal-server 2.16.2a0__py3-none-any.whl → 2.16.3__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.
@@ -1 +1 @@
1
- __VERSION__ = "2.16.2a0"
1
+ __VERSION__ = "2.16.3"
@@ -2,12 +2,18 @@ import os
2
2
  import time
3
3
  from pathlib import Path
4
4
 
5
+ from sqlalchemy.orm import Session
6
+
7
+ from fractal_server.app.models.v2 import TaskGroupActivityV2
5
8
  from fractal_server.config import get_settings
6
9
  from fractal_server.config import PixiSLURMConfig
7
10
  from fractal_server.logger import get_logger
8
11
  from fractal_server.ssh._fabric import FractalSSH
9
12
  from fractal_server.syringe import Inject
13
+ from fractal_server.tasks.v2.utils_background import add_commit_refresh
14
+ from fractal_server.tasks.v2.utils_background import get_current_log
10
15
 
16
+ FRACTAL_SQUEUE_ERROR_STATE = "__FRACTAL_SQUEUE_ERROR__"
11
17
 
12
18
  # https://slurm.schedmd.com/squeue.html#lbAG
13
19
  STATES_FINISHED = {
@@ -21,20 +27,130 @@ STATES_FINISHED = {
21
27
  "PREEMPTED",
22
28
  "SPECIAL_EXIT",
23
29
  "TIMEOUT",
30
+ FRACTAL_SQUEUE_ERROR_STATE,
24
31
  }
25
32
 
26
33
 
34
+ def _get_workdir_remote(script_paths: list[str]) -> str:
35
+ """
36
+ Check that there is one and only one `workdir`, and return it.
37
+
38
+ Note: The `is_absolute` check is to filter out a `chmod` command.
39
+ """
40
+ workdirs = [
41
+ Path(script_path).parent.as_posix()
42
+ for script_path in script_paths
43
+ if Path(script_path).is_absolute()
44
+ ]
45
+ if not len(set(workdirs)) == 1:
46
+ raise ValueError(f"Invalid {script_paths=}.")
47
+ return workdirs[0]
48
+
49
+
50
+ def _read_file_if_exists(
51
+ *,
52
+ fractal_ssh: FractalSSH,
53
+ path: str,
54
+ ) -> str:
55
+ """
56
+ Read a remote file if it exists, or return an empty string.
57
+ """
58
+ if fractal_ssh.remote_exists(path=path):
59
+ return fractal_ssh.read_remote_text_file(path)
60
+ else:
61
+ return ""
62
+
63
+
64
+ def _log_change_of_job_state(
65
+ *,
66
+ old_state: str | None,
67
+ new_state: str,
68
+ logger_name: str,
69
+ ) -> None:
70
+ """
71
+ Emit a log for state changes.
72
+
73
+ Args:
74
+ old_state:
75
+ new_state:
76
+ logger_name:
77
+ """
78
+ if new_state != old_state:
79
+ logger = get_logger(logger_name=logger_name)
80
+ logger.debug(
81
+ f"SLURM-job state changed from {old_state=} to {new_state=}."
82
+ )
83
+
84
+
85
+ def _run_squeue(
86
+ *,
87
+ fractal_ssh: FractalSSH,
88
+ squeue_cmd: str,
89
+ logger_name: str,
90
+ ) -> str:
91
+ """
92
+ Run a `squeue` command and handle exceptions.
93
+
94
+ Args:
95
+ fractal_ssh:
96
+ logger_name:
97
+ squeue_cmd:
98
+
99
+ Return:
100
+ state: The SLURM-job state.
101
+ """
102
+ try:
103
+ cmd_stdout = fractal_ssh.run_command(cmd=squeue_cmd)
104
+ state = cmd_stdout.strip().split()[1]
105
+ return state
106
+ except Exception as e:
107
+ logger = get_logger(logger_name=logger_name)
108
+ logger.info(f"`squeue` command failed (original error: {e})")
109
+ return FRACTAL_SQUEUE_ERROR_STATE
110
+
111
+
112
+ def _verify_success_file_exists(
113
+ *,
114
+ fractal_ssh: FractalSSH,
115
+ success_file_remote: str,
116
+ logger_name: str,
117
+ stderr_remote: str,
118
+ ) -> None:
119
+ """
120
+ Fail if the success sentinel file does not exist remotely.
121
+
122
+ Note: the `FractalSSH` methods in this function may fail, and such failures
123
+ are not handled in this function. Any such failure, however, will lead to
124
+ a "failed" task-group lifecycle activity (because it will raise an
125
+ exception from within `run_script_on_remote_slurm`, which will then be
126
+ handled at the calling-function level.
127
+ """
128
+ if not fractal_ssh.remote_exists(path=success_file_remote):
129
+ logger = get_logger(logger_name=logger_name)
130
+ error_msg = f"{success_file_remote=} missing."
131
+ logger.info(error_msg)
132
+
133
+ stderr = _read_file_if_exists(
134
+ fractal_ssh=fractal_ssh, path=stderr_remote
135
+ )
136
+ if stderr:
137
+ logger.info(f"SLURM-job stderr:\n{stderr}")
138
+ raise RuntimeError(error_msg)
139
+
140
+
27
141
  def run_script_on_remote_slurm(
28
142
  *,
29
- script_path: str,
143
+ script_paths: list[str],
30
144
  slurm_config: PixiSLURMConfig,
31
145
  fractal_ssh: FractalSSH,
32
146
  logger_name: str,
147
+ log_file_path: Path,
33
148
  prefix: str,
149
+ db: Session,
150
+ activity: TaskGroupActivityV2,
34
151
  ):
35
152
  """
36
- FIXME
37
-
153
+ Run a `pixi install` script as a SLURM job.
38
154
 
39
155
  NOTE: This is called from within a try/except, thus we can use exceptions
40
156
  as a mechanism to propagate failure/errors.
@@ -44,7 +160,7 @@ def run_script_on_remote_slurm(
44
160
  settings = Inject(get_settings)
45
161
 
46
162
  # (1) Prepare remote submission script
47
- workdir_remote = Path(script_path).parent.as_posix()
163
+ workdir_remote = _get_workdir_remote(script_paths)
48
164
  submission_script_remote = os.path.join(
49
165
  workdir_remote, f"{prefix}-submit.sh"
50
166
  )
@@ -61,55 +177,79 @@ def run_script_on_remote_slurm(
61
177
  f"#SBATCH --out={stdout_remote}",
62
178
  f"#SBATCH -D {workdir_remote}",
63
179
  "",
64
- f"bash {script_path}",
65
- f"touch {success_file_remote}",
66
- "",
67
180
  ]
181
+ for script_path in script_paths:
182
+ script_lines.append(f"bash {script_path}")
183
+ script_lines.append(f"touch {success_file_remote}")
184
+
68
185
  script_contents = "\n".join(script_lines)
69
186
  fractal_ssh.write_remote_file(
70
187
  path=submission_script_remote,
71
188
  content=script_contents,
72
189
  )
190
+ logger.debug(f"Written {submission_script_remote=}.")
191
+
192
+ activity.log = get_current_log(log_file_path)
193
+ activity = add_commit_refresh(obj=activity, db=db)
73
194
 
74
195
  # (2) Submit SLURM job
75
- sbatch_cmd = f"sbatch --parsable {submission_script_remote} "
196
+ logger.debug("Now submit SLURM job.")
197
+ sbatch_cmd = f"sbatch --parsable {submission_script_remote}"
76
198
  try:
77
199
  stdout = fractal_ssh.run_command(cmd=sbatch_cmd)
200
+ job_id = int(stdout)
201
+ logger.debug(f"SLURM-job submission successful ({job_id=}).")
78
202
  except Exception as e:
79
203
  logger.error(
80
- f"Submission of {submission_script_remote} failed. "
81
- f"Original error: {str(e)}"
204
+ (
205
+ f"Submission of {submission_script_remote} failed. "
206
+ f"Original error: {str(e)}"
207
+ )
82
208
  )
83
209
  raise e
84
- logger.debug(f"Now submit job {submission_script_remote} to SLURM.")
85
- job_id = int(stdout)
86
- logger.debug(f"SLURM-job submission successful ({job_id=}).")
210
+ finally:
211
+ activity.log = get_current_log(log_file_path)
212
+ activity = add_commit_refresh(obj=activity, db=db)
87
213
 
88
214
  # (3) Monitor job
89
215
  squeue_cmd = (
90
216
  f"squeue --noheader --format='%i %T' --states=all --jobs={job_id}"
91
217
  )
218
+ logger.debug(f"Start monitoring job with {squeue_cmd=}.")
219
+ old_state = None
92
220
  while True:
93
- try:
94
- stdout = fractal_ssh.run_command(cmd=squeue_cmd)
95
- except Exception as e:
96
- # FIXME: review this logic
97
- logger.info(
98
- f"`squeue` command failed (original error: {e}), "
99
- "consider the job as complete."
100
- )
101
- break
102
- state = stdout.strip().split()[1]
103
- logger.debug(f"Status of SLURM job {job_id}: {state}")
104
- if state in STATES_FINISHED:
105
- logger.debug(f"Exit retrieval loop ({state=}).")
221
+ new_state = _run_squeue(
222
+ fractal_ssh=fractal_ssh,
223
+ squeue_cmd=squeue_cmd,
224
+ logger_name=logger_name,
225
+ )
226
+ _log_change_of_job_state(
227
+ old_state=old_state,
228
+ new_state=new_state,
229
+ logger_name=logger_name,
230
+ )
231
+ activity.log = get_current_log(log_file_path)
232
+ activity = add_commit_refresh(obj=activity, db=db)
233
+ if new_state in STATES_FINISHED:
234
+ logger.debug(f"Exit retrieval loop (state={new_state}).")
106
235
  break
236
+ old_state = new_state
107
237
  time.sleep(settings.FRACTAL_SLURM_POLL_INTERVAL)
108
238
 
109
- if fractal_ssh.remote_exists(path=success_file_remote):
110
- logger.info(f"{success_file_remote=} exists.")
111
- else:
112
- raise RuntimeError(
113
- "SLURM job did not complete correctly "
114
- f"({success_file_remote=} missing)."
115
- )
239
+ _verify_success_file_exists(
240
+ fractal_ssh=fractal_ssh,
241
+ logger_name=logger_name,
242
+ success_file_remote=success_file_remote,
243
+ stderr_remote=stderr_remote,
244
+ )
245
+
246
+ stdout = _read_file_if_exists(
247
+ fractal_ssh=fractal_ssh,
248
+ path=stdout_remote,
249
+ )
250
+
251
+ logger.info("SLURM-job execution completed successfully, continue.")
252
+ activity.log = get_current_log(log_file_path)
253
+ activity = add_commit_refresh(obj=activity, db=db)
254
+
255
+ return stdout
@@ -203,12 +203,21 @@ def collect_ssh_pixi(
203
203
  pyproject_toml_path=pyproject_toml_path,
204
204
  )
205
205
 
206
- # Run script 2 - run pixi-install command
206
+ # Prepare scripts 2 and 3
207
207
  remote_script2_path = _customize_and_send_template(
208
208
  template_filename="pixi_2_install.sh",
209
209
  replacements=replacements,
210
210
  **common_args,
211
211
  )
212
+ remote_script3_path = _customize_and_send_template(
213
+ template_filename="pixi_3_post_install.sh",
214
+ replacements=replacements,
215
+ **common_args,
216
+ )
217
+ logger.debug(
218
+ "Post-installation script written to "
219
+ f"{remote_script3_path=}."
220
+ )
212
221
  logger.debug(
213
222
  "Installation script written to "
214
223
  f"{remote_script2_path=}."
@@ -216,26 +225,24 @@ def collect_ssh_pixi(
216
225
  activity.log = get_current_log(log_file_path)
217
226
  activity = add_commit_refresh(obj=activity, db=db)
218
227
 
219
- run_script_on_remote_slurm(
220
- script_path=remote_script2_path,
228
+ # Run scripts 2 and 3
229
+ stdout = run_script_on_remote_slurm(
230
+ script_paths=[
231
+ remote_script2_path,
232
+ remote_script3_path,
233
+ f"chmod -R 755 {source_dir}",
234
+ ],
221
235
  slurm_config=settings.pixi.SLURM_CONFIG,
222
236
  fractal_ssh=fractal_ssh,
223
237
  logger_name=LOGGER_NAME,
224
238
  prefix=common_args["prefix"],
239
+ db=db,
240
+ activity=activity,
241
+ log_file_path=log_file_path,
225
242
  )
226
243
  activity.log = get_current_log(log_file_path)
227
244
  activity = add_commit_refresh(obj=activity, db=db)
228
245
 
229
- # Run script 3 - post-install
230
- stdout = _customize_and_run_template(
231
- template_filename="pixi_3_post_install.sh",
232
- replacements=replacements,
233
- **common_args,
234
- )
235
- logger.debug(f"STDOUT: {stdout}")
236
- activity.log = get_current_log(log_file_path)
237
- activity = add_commit_refresh(obj=activity, db=db)
238
-
239
246
  # Parse stdout
240
247
  parsed_output = parse_collect_stdout(stdout)
241
248
  package_root_remote = parsed_output["package_root"]
@@ -245,8 +252,6 @@ def collect_ssh_pixi(
245
252
  "project_python_wrapper"
246
253
  ]
247
254
 
248
- fractal_ssh.run_command(cmd=f"chmod -R 755 {source_dir}")
249
-
250
255
  # Read and validate remote manifest file
251
256
  manifest_path_remote = (
252
257
  f"{package_root_remote}/__FRACTAL_MANIFEST__.json"
@@ -188,12 +188,21 @@ def reactivate_ssh_pixi(
188
188
  remote=pixi_lock_remote,
189
189
  )
190
190
 
191
- # Run script 2 - run pixi-install command
191
+ # Prepare scripts 2 and 3
192
192
  remote_script2_path = _customize_and_send_template(
193
193
  template_filename="pixi_2_install.sh",
194
194
  replacements=replacements,
195
195
  **common_args,
196
196
  )
197
+ remote_script3_path = _customize_and_send_template(
198
+ template_filename="pixi_3_post_install.sh",
199
+ replacements=replacements,
200
+ **common_args,
201
+ )
202
+ logger.debug(
203
+ "Post-installation script written to "
204
+ f"{remote_script3_path=}."
205
+ )
197
206
  logger.debug(
198
207
  "Installation script written to "
199
208
  f"{remote_script2_path=}."
@@ -201,28 +210,24 @@ def reactivate_ssh_pixi(
201
210
  activity.log = get_current_log(log_file_path)
202
211
  activity = add_commit_refresh(obj=activity, db=db)
203
212
 
204
- run_script_on_remote_slurm(
205
- script_path=remote_script2_path,
213
+ # Run scripts 2 and 3
214
+ stdout = run_script_on_remote_slurm(
215
+ script_paths=[
216
+ remote_script2_path,
217
+ remote_script3_path,
218
+ f"chmod -R 755 {source_dir}",
219
+ ],
206
220
  slurm_config=settings.pixi.SLURM_CONFIG,
207
221
  fractal_ssh=fractal_ssh,
208
222
  logger_name=LOGGER_NAME,
209
223
  prefix=common_args["prefix"],
224
+ db=db,
225
+ activity=activity,
226
+ log_file_path=log_file_path,
210
227
  )
211
228
  activity.log = get_current_log(log_file_path)
212
229
  activity = add_commit_refresh(obj=activity, db=db)
213
230
 
214
- # Run script 3 - post-install
215
- stdout = _customize_and_run_template(
216
- template_filename="pixi_3_post_install.sh",
217
- replacements=replacements,
218
- **common_args,
219
- )
220
- logger.debug(f"STDOUT: {stdout}")
221
- activity.log = get_current_log(log_file_path)
222
- activity = add_commit_refresh(obj=activity, db=db)
223
-
224
- fractal_ssh.run_command(cmd=f"chmod -R 755 {source_dir}")
225
-
226
231
  # Finalize (write metadata to DB)
227
232
  activity.status = TaskGroupActivityStatusV2.OK
228
233
  activity.timestamp_ended = get_timestamp()
@@ -143,6 +143,6 @@ def prepare_tasks_metadata(
143
143
  return task_list
144
144
 
145
145
 
146
- def get_current_log(logger_file_path: str) -> str:
146
+ def get_current_log(logger_file_path: str | Path) -> str:
147
147
  with open(logger_file_path) as f:
148
148
  return f.read()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fractal-server
3
- Version: 2.16.2a0
3
+ Version: 2.16.3
4
4
  Summary: Backend component of the Fractal analytics platform
5
5
  License: BSD-3-Clause
6
6
  Author: Tommaso Comparin
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Dist: alembic (>=1.13.1,<2.0.0)
15
- Requires-Dist: cryptography (>=45.0.3,<45.1.0)
15
+ Requires-Dist: cryptography (>=46.0.0,<47.0.0)
16
16
  Requires-Dist: fabric (>=3.2.2,<3.3.0)
17
17
  Requires-Dist: fastapi (>=0.116.0,<0.117.0)
18
18
  Requires-Dist: fastapi-users[oauth] (>=14,<15)
@@ -1,4 +1,4 @@
1
- fractal_server/__init__.py,sha256=ywvZY_b51fGszgBtc9wZwOVbz6O5w5tzfaYERx4Ui_s,25
1
+ fractal_server/__init__.py,sha256=XhckXI3NQgXzQFmbqjMIwxfxShIlVm8JhcoaOhWWvyY,23
2
2
  fractal_server/__main__.py,sha256=rkM8xjY1KeS3l63irB8yCrlVobR-73uDapC4wvrIlxI,6957
3
3
  fractal_server/alembic.ini,sha256=MWwi7GzjzawI9cCAK1LW7NxIBQDUqD12-ptJoq5JpP0,3153
4
4
  fractal_server/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -204,15 +204,15 @@ fractal_server/tasks/v2/local/delete.py,sha256=TbNcg0IhbJNXzXsO2Vgv6Dq4jWmFUVtSu
204
204
  fractal_server/tasks/v2/local/reactivate.py,sha256=Q43DOadNeFyyfgNP67lUqaXmZsS6onv67XwxH_-5ANA,5756
205
205
  fractal_server/tasks/v2/local/reactivate_pixi.py,sha256=IuxDRaj8i6Rc582TIbc9HVKQ9pOvR4IepyXSaJx2PfQ,7565
206
206
  fractal_server/tasks/v2/ssh/__init__.py,sha256=dPK6BtEZVh1GiFP05j1RKTEnZvjJez8o2KkMC2hWXaw,339
207
- fractal_server/tasks/v2/ssh/_pixi_slurm_ssh.py,sha256=P-QyoKlQ9rbq3DPuG0DjgnbJ9KOCBXqbFcJZ2qB4VzA,3484
207
+ fractal_server/tasks/v2/ssh/_pixi_slurm_ssh.py,sha256=24BX64KbnBr4Wd3k9KmSNz4WHzvLf9AF7Hh1-FDlC9w,7472
208
208
  fractal_server/tasks/v2/ssh/_utils.py,sha256=AjbH4o6-ENe-ZiHvE_pGe_yw8e2lapGMYtLX7VuBkA8,6111
209
209
  fractal_server/tasks/v2/ssh/collect.py,sha256=WesUBtNaax9ST7CtgqAD2qd-ZII4t1JFPzRzltEOuM8,14333
210
- fractal_server/tasks/v2/ssh/collect_pixi.py,sha256=5CRr_XiuRaFjydZqxgfoMMj3p1TfT7aqNOxHcVxrOQI,15020
210
+ fractal_server/tasks/v2/ssh/collect_pixi.py,sha256=i7_xqAy9kkF19AHHGSAmvs-QUEQIkPJvVxXUiau1WhU,15210
211
211
  fractal_server/tasks/v2/ssh/deactivate.py,sha256=7gJ2mOah0wKwUnK0S9QpaXg08_WE95P0rC-oExAGhLE,12438
212
212
  fractal_server/tasks/v2/ssh/deactivate_pixi.py,sha256=K0yK_NPUqhFMj6cp6G_0Kfn0Yo7oQux4kT5dFPulnos,4748
213
213
  fractal_server/tasks/v2/ssh/delete.py,sha256=9DCe5QXZ14pY8NRb_FCqpIr31r5Gboz6bnFrS8Oa044,4277
214
214
  fractal_server/tasks/v2/ssh/reactivate.py,sha256=NJIgMNFKaXMhbvK0iZOsMwMtsms6Boj9f8N4L01X9Bo,8271
215
- fractal_server/tasks/v2/ssh/reactivate_pixi.py,sha256=LZk7WaC2uCo3cHTwrpRcc4F_I7HUDA-8rE-RJ2agLZA,11020
215
+ fractal_server/tasks/v2/ssh/reactivate_pixi.py,sha256=yRoq7xTCpKgx8Fxc5RTpEwQI-L0-9Tjq7vnX_v3v7JE,11210
216
216
  fractal_server/tasks/v2/templates/1_create_venv.sh,sha256=PK0jdHKtQpda1zULebBaVPORt4t6V17wa4N1ohcj5ac,548
217
217
  fractal_server/tasks/v2/templates/2_pip_install.sh,sha256=vN9DX-eucJjB-XCuQuZmkuATGBzL4FlDQJTQdVewJG8,2155
218
218
  fractal_server/tasks/v2/templates/3_pip_freeze.sh,sha256=JldREScEBI4cD_qjfX4UK7V4aI-FnX9ZvVNxgpSOBFc,168
@@ -222,7 +222,7 @@ fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh,sha256=jHoALPagSC
222
222
  fractal_server/tasks/v2/templates/pixi_1_extract.sh,sha256=Jdy5OyKo2jxe_qIDB9Zi4a0FL0cMBysxvBPHlUrARQM,1099
223
223
  fractal_server/tasks/v2/templates/pixi_2_install.sh,sha256=h6-M101Q1AdAfZNZyPfSUc8AlZ-uS84Hae4vJdDSglY,1601
224
224
  fractal_server/tasks/v2/templates/pixi_3_post_install.sh,sha256=99J8KXkNeQk9utuEtUxfAZS6VCThC32X7I7HAp2gdTU,2501
225
- fractal_server/tasks/v2/utils_background.py,sha256=LhiJtlWSsXo_FIednOclhWpBjmIYwzs83hecWAfoGNo,4833
225
+ fractal_server/tasks/v2/utils_background.py,sha256=jjWxNbHPuvAkXNIQ9Bqs67X72xHSRqmY8_BCoj0HM3E,4840
226
226
  fractal_server/tasks/v2/utils_database.py,sha256=yi7793Uue32O59OBVUgomO42oUrVKdSKXoShBUNDdK0,1807
227
227
  fractal_server/tasks/v2/utils_package_names.py,sha256=RDg__xrvQs4ieeVzmVdMcEh95vGQYrv9Hfal-5EDBM8,2393
228
228
  fractal_server/tasks/v2/utils_pixi.py,sha256=tqCnxMdxs7KGWncWvk0alrdvDbX-w77P3fAot68Bqh4,3257
@@ -236,8 +236,8 @@ fractal_server/types/validators/_workflow_task_arguments_validators.py,sha256=HL
236
236
  fractal_server/urls.py,sha256=QjIKAC1a46bCdiPMu3AlpgFbcv6a4l3ABcd5xz190Og,471
237
237
  fractal_server/utils.py,sha256=Vn35lApt1T1J8nc09sAVqd10Cy0sa3dLipcljI-hkuk,2185
238
238
  fractal_server/zip_tools.py,sha256=H0w7wS5yE4ebj7hw1_77YQ959dl2c-L0WX6J_ro1TY4,4884
239
- fractal_server-2.16.2a0.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
240
- fractal_server-2.16.2a0.dist-info/METADATA,sha256=6x9kEFUU9jQkukimP25U3iu8UKP1JY2bCA67qTSxx2I,4336
241
- fractal_server-2.16.2a0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
242
- fractal_server-2.16.2a0.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
243
- fractal_server-2.16.2a0.dist-info/RECORD,,
239
+ fractal_server-2.16.3.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
240
+ fractal_server-2.16.3.dist-info/METADATA,sha256=pw_mNLXMPEkIRXLZGpIG5a4T_S4ehtX1HAuqTkKZrXQ,4334
241
+ fractal_server-2.16.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
242
+ fractal_server-2.16.3.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
243
+ fractal_server-2.16.3.dist-info/RECORD,,