fractal-server 2.15.4__py3-none-any.whl → 2.15.6__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.15.4"
1
+ __VERSION__ = "2.15.6"
@@ -12,12 +12,15 @@ from sqlmodel import select
12
12
  from fractal_server.app.db import AsyncSession
13
13
  from fractal_server.app.db import get_async_db
14
14
  from fractal_server.app.models import UserOAuth
15
+ from fractal_server.app.models.v2 import HistoryRun
16
+ from fractal_server.app.models.v2 import HistoryUnit
15
17
  from fractal_server.app.models.v2 import JobV2
16
18
  from fractal_server.app.models.v2 import ProjectV2
17
19
  from fractal_server.app.routes.auth import current_active_superuser
18
20
  from fractal_server.app.routes.aux._job import _write_shutdown_file
19
21
  from fractal_server.app.routes.aux._runner import _check_shutdown_is_supported
20
22
  from fractal_server.app.runner.filenames import WORKFLOW_LOG_FILENAME
23
+ from fractal_server.app.schemas.v2 import HistoryUnitStatus
21
24
  from fractal_server.app.schemas.v2 import JobReadV2
22
25
  from fractal_server.app.schemas.v2 import JobStatusTypeV2
23
26
  from fractal_server.app.schemas.v2 import JobUpdateV2
@@ -148,6 +151,11 @@ async def update_job(
148
151
  status_code=status.HTTP_404_NOT_FOUND,
149
152
  detail=f"Job {job_id} not found",
150
153
  )
154
+ if job.status != JobStatusTypeV2.SUBMITTED:
155
+ raise HTTPException(
156
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
157
+ detail=f"Job {job_id} has status {job.status=} != 'submitted'.",
158
+ )
151
159
 
152
160
  if job_update.status != JobStatusTypeV2.FAILED:
153
161
  raise HTTPException(
@@ -164,6 +172,25 @@ async def update_job(
164
172
  f"{job.log or ''}\nThis job was manually marked as "
165
173
  f"'{JobStatusTypeV2.FAILED}' by an admin ({timestamp.isoformat()}).",
166
174
  )
175
+
176
+ res = await db.execute(
177
+ select(HistoryRun)
178
+ .where(HistoryRun.job_id == job_id)
179
+ .order_by(HistoryRun.timestamp_started.desc())
180
+ .limit(1)
181
+ )
182
+ latest_run = res.scalar_one_or_none()
183
+ if latest_run is not None:
184
+ setattr(latest_run, "status", HistoryUnitStatus.FAILED)
185
+ res = await db.execute(
186
+ select(HistoryUnit).where(
187
+ HistoryUnit.history_run_id == latest_run.id
188
+ )
189
+ )
190
+ history_units = res.scalars().all()
191
+ for history_unit in history_units:
192
+ setattr(history_unit, "status", HistoryUnitStatus.FAILED)
193
+
167
194
  await db.commit()
168
195
  await db.refresh(job)
169
196
  await db.close()
@@ -1,6 +1,3 @@
1
- import os
2
-
3
-
4
1
  class TaskExecutionError(RuntimeError):
5
2
  """
6
3
  Forwards errors occurred during the execution of a task
@@ -42,86 +39,27 @@ class TaskOutputValidationError(ValueError):
42
39
 
43
40
  class JobExecutionError(RuntimeError):
44
41
  """
45
- Forwards errors in the execution of a task that are due to external factors
46
-
47
- This error wraps and forwards errors occurred during the execution of
48
- tasks, but related to external factors like an error on the executor side.
49
-
50
- This error also adds information that is useful to track down and debug the
51
- failing task within a workflow.
42
+ JobExecutionError
52
43
 
53
44
  Attributes:
54
45
  info:
55
46
  A free field for additional information
56
- cmd_file:
57
- Path to the file of the command that was executed (e.g. a SLURM
58
- submission script).
59
- stdout_file:
60
- Path to the file with the command stdout
61
- stderr_file:
62
- Path to the file with the command stderr
63
47
  """
64
48
 
65
- cmd_file: str | None = None
66
- stdout_file: str | None = None
67
- stderr_file: str | None = None
68
49
  info: str | None = None
69
50
 
70
51
  def __init__(
71
52
  self,
72
53
  *args,
73
- cmd_file: str | None = None,
74
- stdout_file: str | None = None,
75
- stderr_file: str | None = None,
76
54
  info: str | None = None,
77
55
  ):
78
56
  super().__init__(*args)
79
- self.cmd_file = cmd_file
80
- self.stdout_file = stdout_file
81
- self.stderr_file = stderr_file
82
57
  self.info = info
83
58
 
84
- def _read_file(self, filepath: str) -> str:
85
- """
86
- Return the content of a text file, and handle the cases where it is
87
- empty or missing
88
- """
89
- if os.path.exists(filepath):
90
- with open(filepath) as f:
91
- content = f.read()
92
- if content:
93
- return f"Content of {filepath}:\n{content}"
94
- else:
95
- return f"File {filepath} is empty\n"
96
- else:
97
- return f"File {filepath} is missing\n"
98
-
99
59
  def assemble_error(self) -> str:
100
- """
101
- Read the files that are specified in attributes, and combine them in an
102
- error message.
103
- """
104
- if self.cmd_file:
105
- content = self._read_file(self.cmd_file)
106
- cmd_content = f"COMMAND:\n{content}\n\n"
107
- else:
108
- cmd_content = ""
109
- if self.stdout_file:
110
- content = self._read_file(self.stdout_file)
111
- out_content = f"STDOUT:\n{content}\n\n"
112
- else:
113
- out_content = ""
114
- if self.stderr_file:
115
- content = self._read_file(self.stderr_file)
116
- err_content = f"STDERR:\n{content}\n\n"
117
- else:
118
- err_content = ""
119
-
120
- content = f"{cmd_content}{out_content}{err_content}"
121
60
  if self.info:
122
- content = f"{content}ADDITIONAL INFO:\n{self.info}\n\n"
123
-
124
- if not content:
61
+ content = f"\n{self.info}\n\n"
62
+ else:
125
63
  content = str(self)
126
- message = f"JobExecutionError\n\n{content}"
64
+ message = f"JobExecutionError\n{content}"
127
65
  return message
@@ -8,6 +8,7 @@ from ._slurm_config import logger
8
8
  from ._slurm_config import SlurmConfig
9
9
  from ._slurm_config import SlurmConfigError
10
10
  from fractal_server.app.models.v2 import WorkflowTaskV2
11
+ from fractal_server.string_tools import interpret_as_bool
11
12
 
12
13
 
13
14
  def get_slurm_config_internal(
@@ -86,7 +87,7 @@ def get_slurm_config_internal(
86
87
  # 2. This block of definitions has lower priority than whatever comes next
87
88
  # (i.e. from WorkflowTask.meta_parallel).
88
89
  if wftask_meta is not None:
89
- needs_gpu = wftask_meta.get("needs_gpu", False)
90
+ needs_gpu = interpret_as_bool(wftask_meta.get("needs_gpu", False))
90
91
  else:
91
92
  needs_gpu = False
92
93
  logger.debug(f"[get_slurm_config] {needs_gpu=}")
@@ -25,7 +25,6 @@ from ...models.v2 import JobV2
25
25
  from ...models.v2 import WorkflowV2
26
26
  from ...schemas.v2 import JobStatusTypeV2
27
27
  from ..exceptions import JobExecutionError
28
- from ..exceptions import TaskExecutionError
29
28
  from ..filenames import WORKFLOW_LOG_FILENAME
30
29
  from ._local import process_workflow as local_process_workflow
31
30
  from ._slurm_ssh import process_workflow as slurm_ssh_process_workflow
@@ -311,19 +310,6 @@ def submit_workflow(
311
310
  db_sync.merge(job)
312
311
  db_sync.commit()
313
312
 
314
- except TaskExecutionError as e:
315
- logger.debug(f'FAILED workflow "{workflow.name}", TaskExecutionError.')
316
- logger.info(f'Workflow "{workflow.name}" failed (TaskExecutionError).')
317
-
318
- exception_args_string = "\n".join(e.args)
319
- log_msg = (
320
- f"TASK ERROR: "
321
- f"Task name: {e.task_name}, "
322
- f"position in Workflow: {e.workflow_task_order}\n"
323
- f"TRACEBACK:\n{exception_args_string}"
324
- )
325
- fail_job(db=db_sync, job=job, log_msg=log_msg, logger_name=logger_name)
326
-
327
313
  except JobExecutionError as e:
328
314
  logger.debug(f'FAILED workflow "{workflow.name}", JobExecutionError.')
329
315
  logger.info(f'Workflow "{workflow.name}" failed (JobExecutionError).')
@@ -60,3 +60,26 @@ def validate_cmd(
60
60
  f"'{forbidden}'\n"
61
61
  f"Provided {attribute_name.lower()}: '{command}'."
62
62
  )
63
+
64
+
65
+ def interpret_as_bool(value: bool | str) -> bool:
66
+ """
67
+ Interpret a boolean or a string representation of a boolean as a boolean
68
+ value.
69
+
70
+ Accepts either a boolean (`True` or `False`) or a case-insensitive string
71
+ representation of a boolean ("true" or "false").
72
+ Returns the corresponding boolean value.
73
+ """
74
+ if isinstance(value, bool):
75
+ return value
76
+ elif isinstance(value, str):
77
+ value_lower = value.lower()
78
+ if value_lower == "true":
79
+ return True
80
+ elif value_lower == "false":
81
+ return False
82
+ else:
83
+ raise ValueError("String must be 'true' or 'false'.")
84
+ else:
85
+ raise TypeError(f"Expected bool or str, got {type(value)}: '{value}'.")
@@ -26,6 +26,7 @@ def _customize_and_run_template(
26
26
  fractal_ssh: FractalSSH,
27
27
  script_dir_remote: str,
28
28
  logger_name: str,
29
+ login_shell: bool = False,
29
30
  ) -> str:
30
31
  """
31
32
  Customize one of the template bash scripts, transfer it to the remote host
@@ -38,6 +39,7 @@ def _customize_and_run_template(
38
39
  prefix: Prefix for the script filename.
39
40
  fractal_ssh: FractalSSH object
40
41
  script_dir_remote: Remote scripts directory
42
+ login_shell: If `True`, use `bash --login` for remote script execution.
41
43
  """
42
44
  logger = get_logger(logger_name=logger_name)
43
45
  logger.debug(f"_customize_and_run_template {template_filename} - START")
@@ -67,7 +69,8 @@ def _customize_and_run_template(
67
69
  )
68
70
 
69
71
  # Execute script remotely
70
- cmd = f"bash {script_path_remote}"
72
+ bash = "bash --login " if login_shell else "bash"
73
+ cmd = f"{bash} {script_path_remote}"
71
74
  logger.debug(f"Now run '{cmd}' over SSH.")
72
75
  stdout = fractal_ssh.run_command(cmd=cmd)
73
76
 
@@ -204,6 +204,7 @@ def collect_ssh_pixi(
204
204
  stdout = _customize_and_run_template(
205
205
  template_filename="pixi_2_install.sh",
206
206
  replacements=replacements,
207
+ login_shell=True,
207
208
  **common_args,
208
209
  )
209
210
  logger.debug(f"STDOUT: {stdout}")
@@ -190,6 +190,7 @@ def reactivate_ssh_pixi(
190
190
  stdout = _customize_and_run_template(
191
191
  template_filename="pixi_2_install.sh",
192
192
  replacements=replacements,
193
+ login_shell=True,
193
194
  **common_args,
194
195
  )
195
196
  logger.debug(f"STDOUT: {stdout}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fractal-server
3
- Version: 2.15.4
3
+ Version: 2.15.6
4
4
  Summary: Backend component of the Fractal analytics platform
5
5
  License: BSD-3-Clause
6
6
  Author: Tommaso Comparin
@@ -1,4 +1,4 @@
1
- fractal_server/__init__.py,sha256=oQyZUHcUuOnv7ZqsoVjwEhBSMPbobwbvTyb3GUD_ZT8,23
1
+ fractal_server/__init__.py,sha256=9b_JI6UfsPyLDqzYMe_YaGYPHyDEJP2xKvdlE-7QIqo,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
@@ -23,7 +23,7 @@ fractal_server/app/routes/admin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
23
23
  fractal_server/app/routes/admin/v2/__init__.py,sha256=_5lqb6-M8-fZqE1HRMep6pAFYRUKMxrvbZOKs-RXWkw,933
24
24
  fractal_server/app/routes/admin/v2/accounting.py,sha256=-eLzBirENvdhjhrCf6bs1pTriIui19DAr_k-zuJB7Y8,3593
25
25
  fractal_server/app/routes/admin/v2/impersonate.py,sha256=gc4lshfEPFR6W2asH7aKu6hqE6chzusdhAUVV9p51eU,1131
26
- fractal_server/app/routes/admin/v2/job.py,sha256=fl_1hWrriCA24OPlHZhxfnM1j0C4UL5hqjcG1ww8Fac,7584
26
+ fractal_server/app/routes/admin/v2/job.py,sha256=ysfrmsaLUwdo3_YbXOhh6j2zZqKrj5dF2s4gJf1ngtQ,8604
27
27
  fractal_server/app/routes/admin/v2/project.py,sha256=MA_LdoEuSuisSGRO43TapMuJ080y5iaUGSAUgKuuKOg,1188
28
28
  fractal_server/app/routes/admin/v2/task.py,sha256=8njjq_zcvNW-Ewxn7WfsRQbs_aV5h-7pgcRIAegzTnc,4308
29
29
  fractal_server/app/routes/admin/v2/task_group.py,sha256=JUCxKnwSNmOKyh6lpqfG5jT5BuRE_U2X6HqD5VVhWPg,7096
@@ -70,7 +70,7 @@ fractal_server/app/routes/aux/validate_user_settings.py,sha256=FLVi__8YFcm_6c_K5
70
70
  fractal_server/app/routes/pagination.py,sha256=IGy8Ll5lYr6ENYE18h3huH5s0GMX1DCs5VdCi6j1cdk,1174
71
71
  fractal_server/app/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
72
  fractal_server/app/runner/components.py,sha256=-Ii5l8d_V6f5DFOd-Zsr8VYmOsyqw0Hox9fEFQiuqxY,66
73
- fractal_server/app/runner/exceptions.py,sha256=tJxs7WCQ86kjezunFm4o_VAiUAyD70l3GiH6ht0waWA,3958
73
+ fractal_server/app/runner/exceptions.py,sha256=lzYefT6IpA2ajcQUzm9kT9pTxRl5UGIJKyLs6v1JczA,1731
74
74
  fractal_server/app/runner/executors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
75
  fractal_server/app/runner/executors/base_runner.py,sha256=7ujN6gN92X9sWAlDwkc07G3FA4Z_Zwew_uBYh2qv0uI,5978
76
76
  fractal_server/app/runner/executors/call_command_wrapper.py,sha256=1BHl-zbXoX2oGUWGAFprVZMmg5QjutPH0-VZJSIC0II,1419
@@ -82,7 +82,7 @@ fractal_server/app/runner/executors/slurm_common/_batching.py,sha256=gbHZIxt90Gj
82
82
  fractal_server/app/runner/executors/slurm_common/_job_states.py,sha256=nuV-Zba38kDrRESOVB3gaGbrSPZc4q7YGichQaeqTW0,238
83
83
  fractal_server/app/runner/executors/slurm_common/_slurm_config.py,sha256=U9BONnnwn8eDqDevwUtFSBcvIsxvNgDHirhcQGJ9t9E,15947
84
84
  fractal_server/app/runner/executors/slurm_common/base_slurm_runner.py,sha256=1Sh56lb7NERVtsBMvVs4K7nVHhMy_KDbwquPl1ub8vE,37937
85
- fractal_server/app/runner/executors/slurm_common/get_slurm_config.py,sha256=jhoFHauWJm55bIC_v7pFylbK8WgcRJemGu2OjUiRbpQ,7377
85
+ fractal_server/app/runner/executors/slurm_common/get_slurm_config.py,sha256=KhQQJrGQZLX5u8fsMcIx7FN8jUKSGEeG68yO7Ay_LXg,7454
86
86
  fractal_server/app/runner/executors/slurm_common/remote.py,sha256=LHK2Ram8X8q6jNSCxnnwKUwmSJMsyQyRem_VjH53qdw,3811
87
87
  fractal_server/app/runner/executors/slurm_common/slurm_job_task_models.py,sha256=K4SdJOKsUWzDlnkb8Ug_UmTx6nBMsTqn9_oKqwE4XDI,3520
88
88
  fractal_server/app/runner/executors/slurm_ssh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -105,7 +105,7 @@ fractal_server/app/runner/v2/deduplicate_list.py,sha256=IVTE4abBU1bUprFTkxrTfYKn
105
105
  fractal_server/app/runner/v2/merge_outputs.py,sha256=YOTKbGOM9s-uqY4KN2onoIxuHNm-v3hr5zv6Aa1KEtA,905
106
106
  fractal_server/app/runner/v2/runner.py,sha256=IsQhSxASVI-yPBO2G2uuVvZkRIoAlSY6Ev7gcSYmDOw,18989
107
107
  fractal_server/app/runner/v2/runner_functions.py,sha256=aEwEDzI2l-QvgfJSj-M2LGvqA89nOiJYIFVryKEq_3M,18988
108
- fractal_server/app/runner/v2/submit_workflow.py,sha256=AMnXdozwIGlXD55ch0_SNAG-ntKBO-QRhkbInrvsShU,13140
108
+ fractal_server/app/runner/v2/submit_workflow.py,sha256=L6WqQC3Vm-tLe_CJXHIMbYLAeulAN2no_4HZD4cbhR0,12554
109
109
  fractal_server/app/runner/v2/task_interface.py,sha256=BRSKpitGproY48JQdCbfrghbDonA-EqPP1yIopohpPo,2525
110
110
  fractal_server/app/runner/versions.py,sha256=4BW-8Et8RVgILgpFoUJLWkEnZz53pv8hv_2ucG480ns,398
111
111
  fractal_server/app/schemas/__init__.py,sha256=stURAU_t3AOBaH0HSUbV-GKhlPKngnnIMoqWc3orFyI,135
@@ -186,7 +186,7 @@ fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py,sha256=TDWC
186
186
  fractal_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
187
187
  fractal_server/ssh/__init__.py,sha256=sVUmzxf7_DuXG1xoLQ1_00fo5NPhi2LJipSmU5EAkPs,124
188
188
  fractal_server/ssh/_fabric.py,sha256=7fCxTYqkAOaTTm67trfYdYQenOsI4EfrRQoG6x3M5kk,25188
189
- fractal_server/string_tools.py,sha256=qLB5u6-4QxXPiZrUeWn_cEo47axj4OXFzDd47kNTIWw,1847
189
+ fractal_server/string_tools.py,sha256=UJFi8rhlI6QXxX5twycLjsvOQ6x4uG7L3JdxEVDhC5A,2592
190
190
  fractal_server/syringe.py,sha256=3YJeIALH-wibuJ9R5VMNYUWh7x1-MkWT0SqGcWG5MY8,2795
191
191
  fractal_server/tasks/__init__.py,sha256=kadmVUoIghl8s190_Tt-8f-WBqMi8u8oU4Pvw39NHE8,23
192
192
  fractal_server/tasks/utils.py,sha256=V7dj8o2AnoHhGSTYlqJHcRFhCIpmOrMOUhtiE_DvRVA,291
@@ -200,13 +200,13 @@ fractal_server/tasks/v2/local/deactivate_pixi.py,sha256=x9YCILEP1EjCrZccAcj5C5i0
200
200
  fractal_server/tasks/v2/local/reactivate.py,sha256=Q43DOadNeFyyfgNP67lUqaXmZsS6onv67XwxH_-5ANA,5756
201
201
  fractal_server/tasks/v2/local/reactivate_pixi.py,sha256=IuxDRaj8i6Rc582TIbc9HVKQ9pOvR4IepyXSaJx2PfQ,7565
202
202
  fractal_server/tasks/v2/ssh/__init__.py,sha256=vX5aIM9Hbn2T_cIP_LrZ5ekRqJzYm_GSfp-4Iv7kqeI,300
203
- fractal_server/tasks/v2/ssh/_utils.py,sha256=jMe3eg2duad8wtGS_RD6oOIvA0LyJEg5cn--qanGuwE,4954
203
+ fractal_server/tasks/v2/ssh/_utils.py,sha256=7rEyykRbaGblunLhoDf0A3IF0aWPBX4wTd5ES3R42SM,5121
204
204
  fractal_server/tasks/v2/ssh/collect.py,sha256=WesUBtNaax9ST7CtgqAD2qd-ZII4t1JFPzRzltEOuM8,14333
205
- fractal_server/tasks/v2/ssh/collect_pixi.py,sha256=PWv-5cQFLA3n4NeFsTU-zfE1xWEy8LoLvHy41gRtWW8,14169
205
+ fractal_server/tasks/v2/ssh/collect_pixi.py,sha256=unZXs3qjBoWYBIa2xWiIKz2CVMVBAhcloQ_9YJ97n5k,14211
206
206
  fractal_server/tasks/v2/ssh/deactivate.py,sha256=7gJ2mOah0wKwUnK0S9QpaXg08_WE95P0rC-oExAGhLE,12438
207
207
  fractal_server/tasks/v2/ssh/deactivate_pixi.py,sha256=K0yK_NPUqhFMj6cp6G_0Kfn0Yo7oQux4kT5dFPulnos,4748
208
208
  fractal_server/tasks/v2/ssh/reactivate.py,sha256=NJIgMNFKaXMhbvK0iZOsMwMtsms6Boj9f8N4L01X9Bo,8271
209
- fractal_server/tasks/v2/ssh/reactivate_pixi.py,sha256=JGP19nbmCCuE8Ya7sBfgrm78vORM0bF0tkt3bWakvF0,10281
209
+ fractal_server/tasks/v2/ssh/reactivate_pixi.py,sha256=UOlG01wOv-eQCtUxSwphEY-Ey7OoUm5PMCSP9J-i1rY,10323
210
210
  fractal_server/tasks/v2/templates/1_create_venv.sh,sha256=PK0jdHKtQpda1zULebBaVPORt4t6V17wa4N1ohcj5ac,548
211
211
  fractal_server/tasks/v2/templates/2_pip_install.sh,sha256=jMJPQJXHKznO6fxOOXtFXKPdCmTf1VLLWj_JL_ZdKxo,1644
212
212
  fractal_server/tasks/v2/templates/3_pip_freeze.sh,sha256=JldREScEBI4cD_qjfX4UK7V4aI-FnX9ZvVNxgpSOBFc,168
@@ -230,8 +230,8 @@ fractal_server/types/validators/_workflow_task_arguments_validators.py,sha256=HL
230
230
  fractal_server/urls.py,sha256=QjIKAC1a46bCdiPMu3AlpgFbcv6a4l3ABcd5xz190Og,471
231
231
  fractal_server/utils.py,sha256=Vn35lApt1T1J8nc09sAVqd10Cy0sa3dLipcljI-hkuk,2185
232
232
  fractal_server/zip_tools.py,sha256=H0w7wS5yE4ebj7hw1_77YQ959dl2c-L0WX6J_ro1TY4,4884
233
- fractal_server-2.15.4.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
234
- fractal_server-2.15.4.dist-info/METADATA,sha256=xtZoBMySmrfFIEf-RXzQHmqZUUvZr2Ud1cyR1rMLqoM,4283
235
- fractal_server-2.15.4.dist-info/WHEEL,sha256=7dDg4QLnNKTvwIDR9Ac8jJaAmBC_owJrckbC0jjThyA,88
236
- fractal_server-2.15.4.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
237
- fractal_server-2.15.4.dist-info/RECORD,,
233
+ fractal_server-2.15.6.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
234
+ fractal_server-2.15.6.dist-info/METADATA,sha256=2gU9d2hWDNEFSQLlfx119dh46y1qC8b8tmtWvX3k1pc,4283
235
+ fractal_server-2.15.6.dist-info/WHEEL,sha256=7dDg4QLnNKTvwIDR9Ac8jJaAmBC_owJrckbC0jjThyA,88
236
+ fractal_server-2.15.6.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
237
+ fractal_server-2.15.6.dist-info/RECORD,,