fractal-server 2.7.1__py3-none-any.whl → 2.8.1__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 (36) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/models/user_settings.py +1 -0
  3. fractal_server/app/models/v2/task.py +15 -0
  4. fractal_server/app/routes/api/v2/dataset.py +39 -6
  5. fractal_server/app/routes/api/v2/task.py +2 -5
  6. fractal_server/app/routes/api/v2/task_collection.py +14 -42
  7. fractal_server/app/routes/api/v2/task_collection_custom.py +3 -3
  8. fractal_server/app/schemas/_validators.py +1 -1
  9. fractal_server/app/schemas/user_settings.py +18 -0
  10. fractal_server/app/schemas/v2/dataset.py +6 -4
  11. fractal_server/app/schemas/v2/task_collection.py +31 -12
  12. fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +39 -0
  13. fractal_server/string_tools.py +10 -3
  14. fractal_server/tasks/utils.py +0 -31
  15. fractal_server/tasks/v1/background_operations.py +11 -11
  16. fractal_server/tasks/v1/endpoint_operations.py +5 -5
  17. fractal_server/tasks/v1/utils.py +2 -2
  18. fractal_server/tasks/v2/collection_local.py +357 -0
  19. fractal_server/tasks/v2/{background_operations_ssh.py → collection_ssh.py} +108 -102
  20. fractal_server/tasks/v2/templates/_1_create_venv.sh +0 -8
  21. fractal_server/tasks/v2/templates/_2_preliminary_pip_operations.sh +2 -2
  22. fractal_server/tasks/v2/templates/_3_pip_install.sh +22 -1
  23. fractal_server/tasks/v2/templates/_5_pip_show.sh +5 -5
  24. fractal_server/tasks/v2/utils_background.py +209 -0
  25. fractal_server/tasks/v2/utils_package_names.py +77 -0
  26. fractal_server/tasks/v2/{utils.py → utils_python_interpreter.py} +0 -26
  27. fractal_server/tasks/v2/utils_templates.py +59 -0
  28. fractal_server/utils.py +48 -3
  29. {fractal_server-2.7.1.dist-info → fractal_server-2.8.1.dist-info}/METADATA +11 -8
  30. {fractal_server-2.7.1.dist-info → fractal_server-2.8.1.dist-info}/RECORD +34 -31
  31. fractal_server/tasks/v2/_venv_pip.py +0 -198
  32. fractal_server/tasks/v2/background_operations.py +0 -456
  33. /fractal_server/{tasks/v2/endpoint_operations.py → app/routes/api/v2/_aux_functions_task_collection.py} +0 -0
  34. {fractal_server-2.7.1.dist-info → fractal_server-2.8.1.dist-info}/LICENSE +0 -0
  35. {fractal_server-2.7.1.dist-info → fractal_server-2.8.1.dist-info}/WHEEL +0 -0
  36. {fractal_server-2.7.1.dist-info → fractal_server-2.8.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,77 @@
1
+ import re
2
+
3
+ from fractal_server.logger import get_logger
4
+
5
+
6
+ def _parse_wheel_filename(wheel_filename: str) -> dict[str, str]:
7
+ """
8
+ Extract distribution and version from a wheel filename.
9
+
10
+ The structure of a wheel filename is fixed, and it must start with
11
+ `{distribution}-{version}` (see
12
+ https://packaging.python.org/en/latest/specifications/binary-distribution-format
13
+ ).
14
+
15
+ Note that we transform exceptions in `ValueError`s, since this function is
16
+ also used within Pydantic validators.
17
+ """
18
+ if "/" in wheel_filename:
19
+ raise ValueError(
20
+ "[_parse_wheel_filename] Input must be a filename, not a full "
21
+ f"path (given: {wheel_filename})."
22
+ )
23
+ try:
24
+ parts = wheel_filename.split("-")
25
+ return dict(distribution=parts[0], version=parts[1])
26
+ except Exception as e:
27
+ raise ValueError(
28
+ f"Invalid {wheel_filename=}. Original error: {str(e)}."
29
+ )
30
+
31
+
32
+ def normalize_package_name(name: str) -> str:
33
+ """
34
+ Implement PyPa specifications for package-name normalization
35
+
36
+ The name should be lowercased with all runs of the characters `.`, `-`,
37
+ or `_` replaced with a single `-` character. This can be implemented in
38
+ Python with the re module.
39
+ (https://packaging.python.org/en/latest/specifications/name-normalization)
40
+
41
+ Args:
42
+ name: The non-normalized package name.
43
+
44
+ Returns:
45
+ The normalized package name.
46
+ """
47
+ return re.sub(r"[-_.]+", "-", name).lower()
48
+
49
+
50
+ def compare_package_names(
51
+ *,
52
+ pkg_name_pip_show: str,
53
+ pkg_name_task_group: str,
54
+ logger_name: str,
55
+ ) -> None:
56
+ """
57
+ Compare the package names from `pip show` and from the db.
58
+ """
59
+ logger = get_logger(logger_name)
60
+
61
+ if pkg_name_pip_show == pkg_name_task_group:
62
+ return
63
+
64
+ logger.warning(
65
+ f"Package name mismatch: "
66
+ f"{pkg_name_task_group=}, {pkg_name_pip_show=}."
67
+ )
68
+ normalized_pkg_name_pip = normalize_package_name(pkg_name_pip_show)
69
+ normalized_pkg_name_taskgroup = normalize_package_name(pkg_name_task_group)
70
+ if normalized_pkg_name_pip != normalized_pkg_name_taskgroup:
71
+ error_msg = (
72
+ f"Package name mismatch persists, after normalization: "
73
+ f"{pkg_name_task_group=}, "
74
+ f"{pkg_name_pip_show=}."
75
+ )
76
+ logger.error(error_msg)
77
+ raise ValueError(error_msg)
@@ -31,29 +31,3 @@ def get_python_interpreter_v2(
31
31
  if value is None:
32
32
  raise ValueError(f"Requested {python_version=}, but {key}={value}.")
33
33
  return value
34
-
35
-
36
- def _parse_wheel_filename(wheel_filename: str) -> dict[str, str]:
37
- """
38
- Extract distribution and version from a wheel filename.
39
-
40
- The structure of a wheel filename is fixed, and it must start with
41
- `{distribution}-{version}` (see
42
- https://packaging.python.org/en/latest/specifications/binary-distribution-format
43
- ).
44
-
45
- Note that we transform exceptions in `ValueError`s, since this function is
46
- also used within Pydantic validators.
47
- """
48
- if "/" in wheel_filename:
49
- raise ValueError(
50
- "[_parse_wheel_filename] Input must be a filename, not a full "
51
- f"path (given: {wheel_filename})."
52
- )
53
- try:
54
- parts = wheel_filename.split("-")
55
- return dict(distribution=parts[0], version=parts[1])
56
- except Exception as e:
57
- raise ValueError(
58
- f"Invalid {wheel_filename=}. Original error: {str(e)}."
59
- )
@@ -0,0 +1,59 @@
1
+ from pathlib import Path
2
+
3
+ TEMPLATES_DIR = Path(__file__).parent / "templates"
4
+
5
+
6
+ def customize_template(
7
+ *,
8
+ template_name: str,
9
+ replacements: list[tuple[str, str]],
10
+ script_path: str,
11
+ ) -> str:
12
+ """
13
+ Customize a bash-script template and write it to disk.
14
+
15
+ Args:
16
+ template_filename:
17
+ templates_folder:
18
+ replacements:
19
+ """
20
+ # Read template
21
+ template_path = TEMPLATES_DIR / template_name
22
+ with template_path.open("r") as f:
23
+ template_data = f.read()
24
+ # Customize template
25
+ script_data = template_data
26
+ for old_new in replacements:
27
+ script_data = script_data.replace(old_new[0], old_new[1])
28
+ # Write script locally
29
+ with open(script_path, "w") as f:
30
+ f.write(script_data)
31
+
32
+
33
+ def parse_script_5_stdout(stdout: str) -> dict[str, str]:
34
+ """
35
+ Parse standard output of template 5.
36
+ """
37
+ searches = [
38
+ ("Python interpreter:", "python_bin"),
39
+ ("Package name:", "package_name"),
40
+ ("Package version:", "package_version"),
41
+ ("Package parent folder:", "package_root_parent"),
42
+ ("Manifest absolute path:", "manifest_path"),
43
+ ]
44
+ stdout_lines = stdout.splitlines()
45
+ attributes = dict()
46
+ for search, attribute_name in searches:
47
+ matching_lines = [_line for _line in stdout_lines if search in _line]
48
+ if len(matching_lines) == 0:
49
+ raise ValueError(f"String '{search}' not found in stdout.")
50
+ elif len(matching_lines) > 1:
51
+ raise ValueError(
52
+ f"String '{search}' found too many times "
53
+ f"({len(matching_lines)})."
54
+ )
55
+ else:
56
+ actual_line = matching_lines[0]
57
+ attribute_value = actual_line.split(search)[-1].strip(" ")
58
+ attributes[attribute_name] = attribute_value
59
+ return attributes
fractal_server/utils.py CHANGED
@@ -14,13 +14,15 @@ This module provides general purpose utilities that are not specific to any
14
14
  subsystem.
15
15
  """
16
16
  import asyncio
17
+ import shlex
18
+ import subprocess # nosec
17
19
  from datetime import datetime
18
20
  from datetime import timezone
19
21
  from pathlib import Path
20
- from shlex import split as shlex_split
21
22
  from typing import Optional
22
23
 
23
24
  from .logger import get_logger
25
+ from .string_tools import validate_cmd
24
26
 
25
27
 
26
28
  def get_timestamp() -> datetime:
@@ -30,7 +32,7 @@ def get_timestamp() -> datetime:
30
32
  return datetime.now(tz=timezone.utc)
31
33
 
32
34
 
33
- async def execute_command(
35
+ async def execute_command_async(
34
36
  *,
35
37
  command: str,
36
38
  cwd: Optional[Path] = None,
@@ -56,7 +58,7 @@ async def execute_command(
56
58
  RuntimeError: if the process exited with non-zero status. The error
57
59
  string is set to the `stderr` of the process.
58
60
  """
59
- command_split = shlex_split(command)
61
+ command_split = shlex.split(command)
60
62
  cmd, *args = command_split
61
63
 
62
64
  logger = get_logger(logger_name)
@@ -75,3 +77,46 @@ async def execute_command(
75
77
  if proc.returncode != 0:
76
78
  raise RuntimeError(stderr.decode("utf-8"))
77
79
  return stdout.decode("utf-8")
80
+
81
+
82
+ def execute_command_sync(
83
+ *,
84
+ command: str,
85
+ logger_name: Optional[str] = None,
86
+ allow_char: Optional[str] = None,
87
+ ) -> str:
88
+ """
89
+ Execute arbitrary command
90
+
91
+ If the command returns a return code different from zero, a `RuntimeError`
92
+ is raised.
93
+
94
+ Arguments:
95
+ command: Command to be executed.
96
+ logger_name: Name of the logger.
97
+ allow_char: Argument propagated to `validate_cmd`.
98
+ """
99
+ logger = get_logger(logger_name)
100
+ logger.debug(f"START subprocess call to '{command}'")
101
+ validate_cmd(command=command, allow_char=allow_char)
102
+ res = subprocess.run( # nosec
103
+ shlex.split(command),
104
+ capture_output=True,
105
+ encoding="utf-8",
106
+ )
107
+ returncode = res.returncode
108
+ stdout = res.stdout
109
+ stderr = res.stderr
110
+ logger.debug(f"{returncode=}")
111
+ logger.debug(f"{stdout=}")
112
+ logger.debug(f"{stderr=}")
113
+ if res.returncode != 0:
114
+ logger.debug(f"ERROR in subprocess call to '{command}'")
115
+ raise RuntimeError(
116
+ f"Command {command} failed.\n"
117
+ f"returncode={res.returncode}\n"
118
+ f"{stdout=}\n"
119
+ f"{stderr=}\n"
120
+ )
121
+ logger.debug(f"END subprocess call to '{command}'")
122
+ return stdout
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fractal-server
3
- Version: 2.7.1
3
+ Version: 2.8.1
4
4
  Summary: Server component of the Fractal analytics platform
5
5
  Home-page: https://github.com/fractal-analytics-platform/fractal-server
6
6
  License: BSD-3-Clause
@@ -37,15 +37,20 @@ Description-Content-Type: text/markdown
37
37
 
38
38
  # Fractal Server
39
39
 
40
+ <p align="center">
41
+ <img src="https://github.com/user-attachments/assets/16e9cf11-d47d-4db8-a9b1-f5349e4175b7" alt="Fractal server" width="400">
42
+ </p>
43
+
40
44
  [![PyPI version](https://img.shields.io/pypi/v/fractal-server?color=gree)](https://pypi.org/project/fractal-server/)
45
+ [![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
41
46
  [![CI Status](https://github.com/fractal-analytics-platform/fractal-server/actions/workflows/ci.yml/badge.svg)](https://github.com/fractal-analytics-platform/fractal-server/actions/workflows/ci.yml?query=branch%3Amain)
42
47
  [![Coverage](https://raw.githubusercontent.com/fractal-analytics-platform/fractal-server/python-coverage-comment-action-data/badge.svg)](https://htmlpreview.github.io/?https://github.com/fractal-analytics-platform/fractal-server/blob/python-coverage-comment-action-data/htmlcov/index.html)
43
- [![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
48
+ [![Documentation Status](https://github.com/fractal-analytics-platform/fractal-server/actions/workflows/documentation.yaml/badge.svg)](https://fractal-analytics-platform.github.io/fractal-server)
44
49
  [![Benchmarks](https://img.shields.io/badge/Benchmarks-Done-blue)](https://htmlpreview.github.io/?https://github.com/fractal-analytics-platform/fractal-server/blob/benchmark-api/benchmarks/bench.html)
45
50
 
46
51
  [Fractal](https://fractal-analytics-platform.github.io/) is a framework developed at the [BioVisionCenter](https://www.biovisioncenter.uzh.ch/en.html) to process bioimaging data at scale in the OME-Zarr format and prepare the images for interactive visualization.
47
52
 
48
- ![Fractal_overview](https://github.com/user-attachments/assets/286122d9-08cf-48e8-996d-3cf53e0a81c6)
53
+ ![Fractal_overview](https://github.com/user-attachments/assets/666c8797-2594-4b8e-b1d2-b43fca66d1df)
49
54
 
50
55
  This is the server component of the fractal analytics platform.
51
56
  Find more information about Fractal in general and the other repositories at
@@ -58,14 +63,12 @@ See https://fractal-analytics-platform.github.io/fractal-server.
58
63
 
59
64
  # Contributors and license
60
65
 
61
- Unless otherwise stated in each individual module, all Fractal components are
62
- released according to a BSD 3-Clause License, and Copyright is with Friedrich
63
- Miescher Institute for Biomedical Research and University of Zurich.
66
+ Fractal was conceived in the Liberali Lab at the Friedrich Miescher Institute for Biomedical Research and in the Pelkmans Lab at the University of Zurich by [@jluethi](https://github.com/jluethi) and [@gusqgm](https://github.com/gusqgm). The Fractal project is now developed at the [BioVisionCenter](https://www.biovisioncenter.uzh.ch/en.html) at the University of Zurich and the project lead is with [@jluethi](https://github.com/jluethi). The core development is done under contract by [eXact lab S.r.l.](https://www.exact-lab.it).
67
+
68
+ Unless otherwise specified, Fractal components are released under the BSD 3-Clause License, and copyright is with the BioVisionCenter at the University of Zurich.
64
69
 
65
70
  The SLURM compatibility layer is based on
66
71
  [`clusterfutures`](https://github.com/sampsyo/clusterfutures), by
67
72
  [@sampsyo](https://github.com/sampsyo) and collaborators, and it is released
68
73
  under the terms of the MIT license.
69
74
 
70
- Fractal was conceived in the Liberali Lab at the Friedrich Miescher Institute for Biomedical Research and in the Pelkmans Lab at the University of Zurich by [@jluethi](https://github.com/jluethi) and [@gusqgm](https://github.com/gusqgm). The Fractal project is now developed at the [BioVisionCenter](https://www.biovisioncenter.uzh.ch/en.html) at the University of Zurich and the project lead is with [@jluethi](https://github.com/jluethi). The core development is done under contract by [eXact lab S.r.l.](https://www.exact-lab.it/).
71
-
@@ -1,4 +1,4 @@
1
- fractal_server/__init__.py,sha256=e5_mOoPeW4Rbnylz3g2qQviWIKwVN-scjNbLROol4Xc,22
1
+ fractal_server/__init__.py,sha256=_UadKzwkiZjaRG8tsoM0cHEnXlBYpVpNzh9Ld4g5XDg,22
2
2
  fractal_server/__main__.py,sha256=dEkCfzLLQrIlxsGC-HBfoR-RBMWnJDgNrxYTyzmE9c0,6146
3
3
  fractal_server/alembic.ini,sha256=MWwi7GzjzawI9cCAK1LW7NxIBQDUqD12-ptJoq5JpP0,3153
4
4
  fractal_server/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -7,7 +7,7 @@ fractal_server/app/models/__init__.py,sha256=aG7mf1zZbsgzDSp7GHEcZhdjHfW3TGPOLCI
7
7
  fractal_server/app/models/linkusergroup.py,sha256=LWTUfhH2uAnn_4moK7QdRUIHWtpw-hPZuW-5jClv_OE,610
8
8
  fractal_server/app/models/linkuserproject.py,sha256=eQaourbGRshvlMVlKzLYJKHEjfsW1CbWws9yW4eHXhA,567
9
9
  fractal_server/app/models/security.py,sha256=2npjgRKBZ7OAnhAXNbYxjtuOsSm1P4kak__qfk2SpeM,3770
10
- fractal_server/app/models/user_settings.py,sha256=0YXCAwoAVGqI2irRLdXgr9-JS0STtHhSaoFENigAnrk,1312
10
+ fractal_server/app/models/user_settings.py,sha256=3LodhERbRz3ajjmCnZiU1TOitduKu_9Lyv_Rgdnyusw,1350
11
11
  fractal_server/app/models/v1/__init__.py,sha256=hUI7dEbPaiZGN0IbHW4RSmSicyvtn_xeuevoX7zvUwI,466
12
12
  fractal_server/app/models/v1/dataset.py,sha256=99GDgt7njx8yYQApkImqp_7bHA5HH3ElvbR6Oyj9kVI,2017
13
13
  fractal_server/app/models/v1/job.py,sha256=QLGXcWdVRHaUHQNDapYYlLpEfw4K7QyD8TmcwhrWw2o,3304
@@ -20,7 +20,7 @@ fractal_server/app/models/v2/collection_state.py,sha256=Yx18ZbywjraOdlHFyRVlb3VP
20
20
  fractal_server/app/models/v2/dataset.py,sha256=-7sxHEw4IIAvF_uSan7tA3o8hvoakBkQ0SRvqS2iOQU,1455
21
21
  fractal_server/app/models/v2/job.py,sha256=ypJmN-qspkKBGhBG7Mt-HypSQqcQ2EmB4Bzzb2-y550,1535
22
22
  fractal_server/app/models/v2/project.py,sha256=rAHoh5KfYwIaW7rTX0_O0jvWmxEvfo1BafvmcXuSSRk,786
23
- fractal_server/app/models/v2/task.py,sha256=YIqPCQY4y0DbgWpDxYkZNApElFoHMEz_9GIP1nEGvUI,3295
23
+ fractal_server/app/models/v2/task.py,sha256=NmDGtSX5kGahAnsvFG__QEpyXCKxO9-E2Y_1lK6kKM8,3712
24
24
  fractal_server/app/models/v2/workflow.py,sha256=YBgFGCziUgU0aJ5EM3Svu9W2c46AewZO9VBlFCHiSps,1069
25
25
  fractal_server/app/models/v2/workflowtask.py,sha256=iDuJYk8kp4PNqGmbKRtGI7y-QsbjkNd_gDsbMzL4i-g,1274
26
26
  fractal_server/app/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -43,16 +43,17 @@ fractal_server/app/routes/api/v1/workflow.py,sha256=2T93DuEnSshaDCue-JPmjuvGCtbk
43
43
  fractal_server/app/routes/api/v1/workflowtask.py,sha256=OYYConwJbmNULDw5I3T-UbSJKrbbBiAHbbBeVcpoFKQ,5785
44
44
  fractal_server/app/routes/api/v2/__init__.py,sha256=jybEV-vrknPoQvbgKJl0QQvHDPHOJXbDUG5vatHeis4,1963
45
45
  fractal_server/app/routes/api/v2/_aux_functions.py,sha256=mb4R_qqFxeW0LAis2QJIIfVx8Sydv1jTYaRIMsMxnIk,11720
46
+ fractal_server/app/routes/api/v2/_aux_functions_task_collection.py,sha256=MtUoI0XWHuPSousDeH2IC2WU--AUKQVup6Q6AbHiNUA,4102
46
47
  fractal_server/app/routes/api/v2/_aux_functions_tasks.py,sha256=IVzSb7J4ls5qp1HI8WcjRLIq6nx3ffcMI3vUi_aM_gc,10565
47
- fractal_server/app/routes/api/v2/dataset.py,sha256=Eilf_BAGjicIhqUiVwI86jlW45ineA5sVzxXW4b2GoQ,8329
48
+ fractal_server/app/routes/api/v2/dataset.py,sha256=Y6uZz--YSEGgnPYu05rZ9sr1Ug08bNl2v1h3VeApBe8,9441
48
49
  fractal_server/app/routes/api/v2/images.py,sha256=JR1rR6qEs81nacjriOXAOBQjAbCXF4Ew7M7mkWdxBU0,7920
49
50
  fractal_server/app/routes/api/v2/job.py,sha256=Bga2Kz1OjvDIdxZObWaaXVhNIhC_5JKhKRjEH2_ayEE,5157
50
51
  fractal_server/app/routes/api/v2/project.py,sha256=eWYFJ7F2ZYQcpi-_n-rhPF-Q4gJhzYBsVGYFhHZZXAE,6653
51
52
  fractal_server/app/routes/api/v2/status.py,sha256=6N9DSZ4iFqbZImorWfEAPoyoFUgEruo4Hweqo0x0xXU,6435
52
53
  fractal_server/app/routes/api/v2/submit.py,sha256=tq-NGnUlpIcm_MRN47rJRHkRcIJ5HiL4Wj1wItJy3o8,8185
53
- fractal_server/app/routes/api/v2/task.py,sha256=hrYuVT6XDb3eFFUkQJrAf0O9eMDlE4bi0v5iJyQ5J5E,7293
54
- fractal_server/app/routes/api/v2/task_collection.py,sha256=URtHWrUy83v17uJyIciILYPuZXbGRryw-xO2FC5J9PQ,11581
55
- fractal_server/app/routes/api/v2/task_collection_custom.py,sha256=kWctxgNi20uwsfHx9l_mwhI50OIxWRESjU9UiCDVVFU,6217
54
+ fractal_server/app/routes/api/v2/task.py,sha256=K0ik33t7vL8BAK5S7fqyJDNdRK4stGqb_73bSa8tvPE,7159
55
+ fractal_server/app/routes/api/v2/task_collection.py,sha256=aCOg9zhRtGfJvRpukS_mSah19jhGEqXaE8hUDOMIZUs,10479
56
+ fractal_server/app/routes/api/v2/task_collection_custom.py,sha256=CTxRy0ghYZahlS6WNr6qRFa0xfAQbbU6xZh6F9VykAY,6212
56
57
  fractal_server/app/routes/api/v2/task_group.py,sha256=P32EUYbtGThexSWe5zI9WUFrgoOMof035fJBILTNnfQ,5580
57
58
  fractal_server/app/routes/api/v2/workflow.py,sha256=PyvkrUHHzFGUGZE5X0VW5u3DPQA7wtXXNcEpG7-N66I,8687
58
59
  fractal_server/app/routes/api/v2/workflow_import.py,sha256=rD26vZ-ztjehvglrERixTeHtXuzepAtgAuPiKRNz84Q,10981
@@ -131,10 +132,10 @@ fractal_server/app/runner/v2/runner_functions_low_level.py,sha256=1fWvQ6YZUUnDhO
131
132
  fractal_server/app/runner/v2/task_interface.py,sha256=hT3p-bRGsLNAR_dNv_PYFoqzIF_EQtSsGwl38j1haYA,1824
132
133
  fractal_server/app/runner/versions.py,sha256=dSaPRWqmFPHjg20kTCHmi_dmGNcCETflDtDLronNanU,852
133
134
  fractal_server/app/schemas/__init__.py,sha256=stURAU_t3AOBaH0HSUbV-GKhlPKngnnIMoqWc3orFyI,135
134
- fractal_server/app/schemas/_validators.py,sha256=XKEGEHxp3H6YSJewtFWXe_2Nh7SDdNtAXmlEmJO6Vb0,3606
135
+ fractal_server/app/schemas/_validators.py,sha256=3YBteFMxrEE9DOYlIHvIMsUOWTPSHEZlDL7dUQzBQjs,3616
135
136
  fractal_server/app/schemas/user.py,sha256=aUD8YAcfYTEO06TEUoTx4heVrXFiX7E2Mb8D2--4FsA,2130
136
137
  fractal_server/app/schemas/user_group.py,sha256=YwJvYgj-PI66LWy38CEd_FIZPsBV1_2N5zJPGFcFvBw,2143
137
- fractal_server/app/schemas/user_settings.py,sha256=UEST1MSmd9w2YypCji3SONSFlJcr2u4uG3bTczZy_Pk,3102
138
+ fractal_server/app/schemas/user_settings.py,sha256=TalISeEfCrtN8LgqbLx1Q8ZPoeiZnbksg5NYAVzkIqY,3527
138
139
  fractal_server/app/schemas/v1/__init__.py,sha256=CrBGgBhoemCvmZ70ZUchM-jfVAICnoa7AjZBAtL2UB0,1852
139
140
  fractal_server/app/schemas/v1/applyworkflow.py,sha256=uuIh7fHlHEL4yLqL-dePI6-nfCsqgBYATmht7w_KITw,4302
140
141
  fractal_server/app/schemas/v1/dataset.py,sha256=n71lNUO3JLy2K3IM9BZM2Fk1EnKQOTU7pm2s2rJ1FGY,3444
@@ -146,14 +147,14 @@ fractal_server/app/schemas/v1/task.py,sha256=7BxOZ_qoRQ8n3YbQpDvB7VMcxB5fSYQmR5R
146
147
  fractal_server/app/schemas/v1/task_collection.py,sha256=uvq9bcMaGD_qHsh7YtcpoSAkVAbw12eY4DocIO3MKOg,3057
147
148
  fractal_server/app/schemas/v1/workflow.py,sha256=tuOs5E5Q_ozA8if7YPZ07cQjzqB_QMkBS4u92qo4Ro0,4618
148
149
  fractal_server/app/schemas/v2/__init__.py,sha256=97y6QY0I4322CPXQCt3WO3QBWhVkmFgwLn8y2ZwWNR0,2382
149
- fractal_server/app/schemas/v2/dataset.py,sha256=865ia13E9mWu1DaYyppKW2csNYglaInrScrprdVYX7A,2552
150
+ fractal_server/app/schemas/v2/dataset.py,sha256=Jipcj9LiOOipAeM2Ew113wuNQ6CrbC1nf3KwnNApBco,2638
150
151
  fractal_server/app/schemas/v2/dumps.py,sha256=s6dg-pHZFui6t2Ktm0SMxjKDN-v-ZqBHz9iTsBQF3eU,1712
151
152
  fractal_server/app/schemas/v2/job.py,sha256=oYSLYkQ0HL83QyjEGIaggtZ117FndzFlONMKWd9sTXM,3270
152
153
  fractal_server/app/schemas/v2/manifest.py,sha256=Uqtd7DbyOkf9bxBOKkU7Sv7nToBIFGUcfjY7rd5iO7c,6981
153
154
  fractal_server/app/schemas/v2/project.py,sha256=UXEA0UUUe0bFFOVLLmVtvDFLBO5vmD1JVI7EeTIcwDo,756
154
155
  fractal_server/app/schemas/v2/status.py,sha256=SQaUpQkjFq5c5k5J4rOjNhuQaDOEg8lksPhkKmPU5VU,332
155
156
  fractal_server/app/schemas/v2/task.py,sha256=FFAbYwDlqowB8gVMdjFVPVHvAM0T89PYLixUth49xfQ,6870
156
- fractal_server/app/schemas/v2/task_collection.py,sha256=Ddw_7QaQ93kdEIwWQvzLQDu03gho_OHdhah3n0ioK3M,6296
157
+ fractal_server/app/schemas/v2/task_collection.py,sha256=56VWGwOZcgmfZ1bKz-_MSSO8HYwtMAKNBL5tfeQc7uQ,7000
157
158
  fractal_server/app/schemas/v2/task_group.py,sha256=oWy7NNsw8Co85qpLyK8FPNTgpAMvx0ZuXjIOVZuLEpM,2466
158
159
  fractal_server/app/schemas/v2/workflow.py,sha256=HSNQSrBRdoBzh8Igr76FUWCAWvVzykrqmUv1vGv-8og,2026
159
160
  fractal_server/app/schemas/v2/workflowtask.py,sha256=vDdMktYbHeYBgB5OuWSv6wRPRXWqvetkeqQ7IC5YtfA,5751
@@ -174,6 +175,7 @@ fractal_server/migrations/naming_convention.py,sha256=htbKrVdetx3pklowb_9Cdo5Rqe
174
175
  fractal_server/migrations/script.py.mako,sha256=oMXw9LC3zRbinWWPPDgeZ4z9FJrV2zhRWiYdS5YgNbI,526
175
176
  fractal_server/migrations/versions/034a469ec2eb_task_groups.py,sha256=vrPhC8hfFu1c4HmLHNZyCuqEfecFD8-bWc49bXMNes0,6199
176
177
  fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py,sha256=-BSS9AFTPcu3gYC-sYbawSy4MWQQx8TfMb5BW5EBKmQ,1450
178
+ fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py,sha256=Q1Gj1cJ0UrdLBJ5AXfFK9QpxTtmcv-4Z3NEGDnxOme4,961
177
179
  fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py,sha256=-wHe-fOffmYeAm0JXVl_lxZ7hhDkaEVqxgxpHkb_uL8,954
178
180
  fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py,sha256=Mob8McGYAcmgvrseyyYOa54E6Gsgr-4SiGdC-r9O4_A,1157
179
181
  fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py,sha256=5ROUgcoZOdjf8kMt6cxuvPhzHmV6xaCxvZEbhUEyZM4,3271
@@ -198,33 +200,34 @@ fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.p
198
200
  fractal_server/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
199
201
  fractal_server/ssh/__init__.py,sha256=sVUmzxf7_DuXG1xoLQ1_00fo5NPhi2LJipSmU5EAkPs,124
200
202
  fractal_server/ssh/_fabric.py,sha256=Pha-gRVUImj1cMsxulrJzaQa6Z60CmMYRAS4o22FcP0,19506
201
- fractal_server/string_tools.py,sha256=Z4qcleqXSG6RCG4hqS1emm0U-Bvv0sgTm_T87ZdYn7M,2395
203
+ fractal_server/string_tools.py,sha256=XtMNsr5R7GmgzmFi68zkKMedHs8vjGoVMMCXqWhIk9k,2568
202
204
  fractal_server/syringe.py,sha256=3qSMW3YaMKKnLdgnooAINOPxnCOxP7y2jeAQYB21Gdo,2786
203
205
  fractal_server/tasks/__init__.py,sha256=kadmVUoIghl8s190_Tt-8f-WBqMi8u8oU4Pvw39NHE8,23
204
- fractal_server/tasks/utils.py,sha256=8sbVCxvgfuzGxEhSfnLWRv36Rx4ZUwR7IAPP1TXfJFM,2178
206
+ fractal_server/tasks/utils.py,sha256=lJeaGX0xw4uE_dQGMnFAvmz03gUVh_nv8m47yuwFcKc,1340
205
207
  fractal_server/tasks/v1/_TaskCollectPip.py,sha256=ARq5AoHYXH0hziEsb-nFAqbsLA-VIddXOdXL38O6_zA,3746
206
208
  fractal_server/tasks/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
207
- fractal_server/tasks/v1/background_operations.py,sha256=zYhqAP3-hedHxF9AMHFf8Y1znoFvC7Lq1qhqJPNROfk,11781
208
- fractal_server/tasks/v1/endpoint_operations.py,sha256=YyMU1Y3Xt7D9WOKqaMLuwEoIaAqQP2Klz3I-ypAgOLI,5077
209
+ fractal_server/tasks/v1/background_operations.py,sha256=0Zm8TT_RV0BxJCXbruYJCu1eXOkEcHpqnWn_BEe7huw,11829
210
+ fractal_server/tasks/v1/endpoint_operations.py,sha256=NQYvgh-_qEI9YhsLiulfOFPDacCd-rgl3cCbPbkJUA0,5103
209
211
  fractal_server/tasks/v1/get_collection_data.py,sha256=5C22jp356rCH5IIC0J57wOu-DCC_kp3B6p68JooN7IM,508
210
- fractal_server/tasks/v1/utils.py,sha256=J9oKys-82OehBxOon5wWl3CxjVBgYWeVEEyWGVFnreI,1759
212
+ fractal_server/tasks/v1/utils.py,sha256=HYFyNAyZofmf--mVgdwGC5TJpGShIWIDaS01yRr4HxM,1771
211
213
  fractal_server/tasks/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
212
- fractal_server/tasks/v2/_venv_pip.py,sha256=EaauUkFIk4wa79B4OudhV8CYOzgWftTdfz8u4bEdjgo,6555
213
- fractal_server/tasks/v2/background_operations.py,sha256=jWmg_XkBmcXQfPKJu_eu0wtDL4sMp1QP2bIZ9QtMj_Y,15411
214
- fractal_server/tasks/v2/background_operations_ssh.py,sha256=_G0rOohkkHLGcvCQyfoMObnII-sxVwrLDW2PKz8IfCQ,13631
214
+ fractal_server/tasks/v2/collection_local.py,sha256=pRGF2NYIc5hwwNZOruyb7RSChWytRMxC2e8WSeMuYMI,13615
215
+ fractal_server/tasks/v2/collection_ssh.py,sha256=Yv8TgUK0yqiRjVo1mHXLbYV2NpTWE_nPzBFGdCPJX0E,13437
215
216
  fractal_server/tasks/v2/database_operations.py,sha256=6r56yyFPnEBrXl6ncmO6D76znzISQCFZqCYcD-Ummd4,1213
216
- fractal_server/tasks/v2/endpoint_operations.py,sha256=MtUoI0XWHuPSousDeH2IC2WU--AUKQVup6Q6AbHiNUA,4102
217
- fractal_server/tasks/v2/templates/_1_create_venv.sh,sha256=7tt-B6n8KRN-pannZ0enE6XSxyq-hKRYRGY63CvtINI,1151
218
- fractal_server/tasks/v2/templates/_2_preliminary_pip_operations.sh,sha256=NwkfDtNeT4YjUsqZuK4uN71bTe-wHIn2wmNhflimRo8,595
219
- fractal_server/tasks/v2/templates/_3_pip_install.sh,sha256=T9sabeB9iQzVZpLfuLkKGz9EpfHkUrJHKWO4HNij6yM,595
217
+ fractal_server/tasks/v2/templates/_1_create_venv.sh,sha256=bfpX3GJtrns1ogidyQHmYaAZjVdQLG-YYazc2GR4z5w,967
218
+ fractal_server/tasks/v2/templates/_2_preliminary_pip_operations.sh,sha256=nRDvTbGljCmJdzfRJz_w-RptyyJoeAtIMiP7NCwVNnU,625
219
+ fractal_server/tasks/v2/templates/_3_pip_install.sh,sha256=nTw8mGmZEVGc-WGDaIK3WVSsJRDuIS8X2l1T3gl34OM,1371
220
220
  fractal_server/tasks/v2/templates/_4_pip_freeze.sh,sha256=qHdDKu1svXi1VQKGePciEJK4_uEKuwAvwaDCcGxSvNk,274
221
- fractal_server/tasks/v2/templates/_5_pip_show.sh,sha256=GrJ19uHYQxANEy9JaeNJZVTquY9c8Ww9eCdnC7eLVr0,1754
222
- fractal_server/tasks/v2/utils.py,sha256=MnY6MhcxDRo4rPuXo2tQ252eWEPZF3OlCGe-p5MrG9U,1846
221
+ fractal_server/tasks/v2/templates/_5_pip_show.sh,sha256=OdPKVUu3gU0N9ygLoRWvW5rBe8_HO4YCCNX8ulsy9-M,1754
222
+ fractal_server/tasks/v2/utils_background.py,sha256=Q88PtbeyfYXfxupTU9d71EcNRurxKwCjuunXN567SC0,6569
223
+ fractal_server/tasks/v2/utils_package_names.py,sha256=RDg__xrvQs4ieeVzmVdMcEh95vGQYrv9Hfal-5EDBM8,2393
224
+ fractal_server/tasks/v2/utils_python_interpreter.py,sha256=-EWh3Y3VqHLDOWUO_wG_wknqmGqKAD0O2KTLhNjrZaI,948
225
+ fractal_server/tasks/v2/utils_templates.py,sha256=61uz9WSb4BDe6JUyJGiasw8BjVrPAmtc8pnNSJ51yS4,1840
223
226
  fractal_server/urls.py,sha256=5o_qq7PzKKbwq12NHSQZDmDitn5RAOeQ4xufu-2v9Zk,448
224
- fractal_server/utils.py,sha256=jrlCBPmC7F0ptBVcDac-EbZNsdYTLbHfX9oxkXthS5Q,2193
227
+ fractal_server/utils.py,sha256=BTvNYA8xG-2K42x_hhsKmxrludbSvUbPpbWPq-Dousg,3481
225
228
  fractal_server/zip_tools.py,sha256=xYpzBshysD2nmxkD5WLYqMzPYUcCRM3kYy-7n9bJL-U,4426
226
- fractal_server-2.7.1.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
227
- fractal_server-2.7.1.dist-info/METADATA,sha256=L3ZYv96IVgdninD7rHRXpJ6M7HqL_zh2KMG86cqHtlE,4303
228
- fractal_server-2.7.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
229
- fractal_server-2.7.1.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
230
- fractal_server-2.7.1.dist-info/RECORD,,
229
+ fractal_server-2.8.1.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
230
+ fractal_server-2.8.1.dist-info/METADATA,sha256=Kg5Q0Ww6lgtblwriHrTueIpkYnal0gCmO9F3JMvf7jc,4588
231
+ fractal_server-2.8.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
232
+ fractal_server-2.8.1.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
233
+ fractal_server-2.8.1.dist-info/RECORD,,
@@ -1,198 +0,0 @@
1
- from pathlib import Path
2
- from typing import Optional
3
-
4
- from ..utils import COLLECTION_FREEZE_FILENAME
5
- from fractal_server.app.models.v2 import TaskGroupV2
6
- from fractal_server.config import get_settings
7
- from fractal_server.logger import get_logger
8
- from fractal_server.syringe import Inject
9
- from fractal_server.tasks.v2.utils import get_python_interpreter_v2
10
- from fractal_server.utils import execute_command
11
-
12
-
13
- async def _init_venv_v2(
14
- *,
15
- venv_path: Path,
16
- python_version: Optional[str] = None,
17
- logger_name: str,
18
- ) -> Path:
19
- """
20
- Set a virtual environment at `path/venv`
21
-
22
- Args:
23
- path : Path
24
- path to the venv actual directory (not its parent).
25
- python_version : default=None
26
- Python version the virtual environment will be based upon
27
-
28
- Returns:
29
- python_bin : Path
30
- path to python interpreter
31
- """
32
- logger = get_logger(logger_name)
33
- logger.debug(f"[_init_venv_v2] {venv_path=}")
34
- interpreter = get_python_interpreter_v2(python_version=python_version)
35
- logger.debug(f"[_init_venv_v2] {interpreter=}")
36
- await execute_command(
37
- command=f"{interpreter} -m venv {venv_path}",
38
- logger_name=logger_name,
39
- )
40
- python_bin = venv_path / "bin/python"
41
- logger.debug(f"[_init_venv_v2] {python_bin=}")
42
- return python_bin
43
-
44
-
45
- async def _pip_install(
46
- task_group: TaskGroupV2,
47
- logger_name: str,
48
- ) -> Path:
49
- """
50
- Install package in venv
51
-
52
- Args:
53
- venv_path:
54
- task_pkg:
55
- logger_name:
56
-
57
- Returns:
58
- The location of the package.
59
- """
60
- settings = Inject(get_settings)
61
-
62
- logger = get_logger(logger_name)
63
-
64
- python_bin = Path(task_group.venv_path) / "bin/python"
65
- pip_install_str = task_group.pip_install_string
66
- logger.info(f"{pip_install_str=}")
67
-
68
- await execute_command(
69
- cwd=Path(task_group.venv_path),
70
- command=(
71
- f"{python_bin} -m pip install --upgrade "
72
- f"'pip<={settings.FRACTAL_MAX_PIP_VERSION}'"
73
- ),
74
- logger_name=logger_name,
75
- )
76
- await execute_command(
77
- cwd=Path(task_group.venv_path),
78
- command=f"{python_bin} -m pip install setuptools",
79
- logger_name=logger_name,
80
- )
81
- await execute_command(
82
- cwd=Path(task_group.venv_path),
83
- command=f"{python_bin} -m pip install {pip_install_str}",
84
- logger_name=logger_name,
85
- )
86
-
87
- if task_group.pinned_package_versions:
88
- for (
89
- pinned_pkg_name,
90
- pinned_pkg_version,
91
- ) in task_group.pinned_package_versions.items():
92
- logger.debug(
93
- "Specific version required: "
94
- f"{pinned_pkg_name}=={pinned_pkg_version}"
95
- )
96
- logger.debug(
97
- "Preliminary check: verify that "
98
- f"{pinned_pkg_name} is already installed"
99
- )
100
- stdout_show = await execute_command(
101
- cwd=Path(task_group.venv_path),
102
- command=f"{python_bin} -m pip show {pinned_pkg_name}",
103
- logger_name=logger_name,
104
- )
105
- current_version = next(
106
- line.split()[-1]
107
- for line in stdout_show.split("\n")
108
- if line.startswith("Version:")
109
- )
110
- if current_version != pinned_pkg_version:
111
- logger.debug(
112
- f"Currently installed version of {pinned_pkg_name} "
113
- f"({current_version}) differs from pinned version "
114
- f"({pinned_pkg_version}); "
115
- f"install version {pinned_pkg_version}."
116
- )
117
- await execute_command(
118
- cwd=Path(task_group.venv_path),
119
- command=(
120
- f"{python_bin} -m pip install "
121
- f"{pinned_pkg_name}=={pinned_pkg_version}"
122
- ),
123
- logger_name=logger_name,
124
- )
125
- else:
126
- logger.debug(
127
- f"Currently installed version of {pinned_pkg_name} "
128
- f"({current_version}) already matches the pinned version."
129
- )
130
-
131
- # Extract package installation path from `pip show`
132
- stdout_show = await execute_command(
133
- cwd=Path(task_group.venv_path),
134
- command=f"{python_bin} -m pip show {task_group.pkg_name}",
135
- logger_name=logger_name,
136
- )
137
-
138
- location = Path(
139
- next(
140
- line.split()[-1]
141
- for line in stdout_show.split("\n")
142
- if line.startswith("Location:")
143
- )
144
- )
145
-
146
- # NOTE
147
- # https://packaging.python.org/en/latest/specifications/recording-installed-packages/
148
- # This directory is named as {name}-{version}.dist-info, with name and
149
- # version fields corresponding to Core metadata specifications. Both
150
- # fields must be normalized (see the name normalization specification and
151
- # the version normalization specification), and replace dash (-)
152
- # characters with underscore (_) characters, so the .dist-info directory
153
- # always has exactly one dash (-) character in its stem, separating the
154
- # name and version fields.
155
- package_root = location / (task_group.pkg_name.replace("-", "_"))
156
- logger.debug(f"[_pip install] {location=}")
157
- logger.debug(f"[_pip install] {task_group.pkg_name=}")
158
- logger.debug(f"[_pip install] {package_root=}")
159
-
160
- # Run `pip freeze --all` and store its output
161
- stdout_freeze = await execute_command(
162
- cwd=Path(task_group.venv_path),
163
- command=f"{python_bin} -m pip freeze --all",
164
- logger_name=logger_name,
165
- )
166
- with (Path(task_group.path) / COLLECTION_FREEZE_FILENAME).open("w") as f:
167
- f.write(stdout_freeze)
168
-
169
- return package_root
170
-
171
-
172
- async def _create_venv_install_package_pip(
173
- *,
174
- task_group: TaskGroupV2,
175
- logger_name: str,
176
- ) -> tuple[Path, Path]:
177
- """
178
- Create venv and install package
179
-
180
- Args:
181
- path: the directory in which to create the environment
182
- task_pkg: object containing the different metadata required to install
183
- the package
184
-
185
- Returns:
186
- python_bin: path to venv's python interpreter
187
- package_root: the location of the package manifest
188
- """
189
- python_bin = await _init_venv_v2(
190
- venv_path=Path(task_group.venv_path),
191
- python_version=task_group.python_version,
192
- logger_name=logger_name,
193
- )
194
- package_root = await _pip_install(
195
- task_group=task_group,
196
- logger_name=logger_name,
197
- )
198
- return python_bin, package_root