fractal-server 2.2.0a1__py3-none-any.whl → 2.3.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 (67) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/models/v1/state.py +1 -2
  3. fractal_server/app/routes/admin/v1.py +2 -2
  4. fractal_server/app/routes/admin/v2.py +2 -2
  5. fractal_server/app/routes/api/v1/job.py +2 -2
  6. fractal_server/app/routes/api/v1/task_collection.py +4 -4
  7. fractal_server/app/routes/api/v2/__init__.py +23 -3
  8. fractal_server/app/routes/api/v2/job.py +2 -2
  9. fractal_server/app/routes/api/v2/submit.py +6 -0
  10. fractal_server/app/routes/api/v2/task_collection.py +74 -34
  11. fractal_server/app/routes/api/v2/task_collection_custom.py +170 -0
  12. fractal_server/app/routes/api/v2/task_collection_ssh.py +125 -0
  13. fractal_server/app/routes/aux/_runner.py +10 -2
  14. fractal_server/app/runner/compress_folder.py +120 -0
  15. fractal_server/app/runner/executors/slurm/__init__.py +0 -3
  16. fractal_server/app/runner/executors/slurm/_batching.py +0 -1
  17. fractal_server/app/runner/executors/slurm/_slurm_config.py +9 -9
  18. fractal_server/app/runner/executors/slurm/ssh/__init__.py +3 -0
  19. fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +112 -0
  20. fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +120 -0
  21. fractal_server/app/runner/executors/slurm/ssh/executor.py +1488 -0
  22. fractal_server/app/runner/executors/slurm/sudo/__init__.py +3 -0
  23. fractal_server/app/runner/executors/slurm/{_check_jobs_status.py → sudo/_check_jobs_status.py} +1 -1
  24. fractal_server/app/runner/executors/slurm/{_executor_wait_thread.py → sudo/_executor_wait_thread.py} +1 -1
  25. fractal_server/app/runner/executors/slurm/{_subprocess_run_as_user.py → sudo/_subprocess_run_as_user.py} +1 -1
  26. fractal_server/app/runner/executors/slurm/{executor.py → sudo/executor.py} +12 -12
  27. fractal_server/app/runner/extract_archive.py +38 -0
  28. fractal_server/app/runner/v1/__init__.py +78 -40
  29. fractal_server/app/runner/v1/_slurm/__init__.py +1 -1
  30. fractal_server/app/runner/v2/__init__.py +147 -62
  31. fractal_server/app/runner/v2/_local_experimental/__init__.py +22 -12
  32. fractal_server/app/runner/v2/_local_experimental/executor.py +12 -8
  33. fractal_server/app/runner/v2/_slurm/__init__.py +1 -6
  34. fractal_server/app/runner/v2/_slurm_ssh/__init__.py +125 -0
  35. fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +83 -0
  36. fractal_server/app/runner/v2/_slurm_ssh/get_slurm_config.py +182 -0
  37. fractal_server/app/runner/v2/runner_functions_low_level.py +9 -11
  38. fractal_server/app/runner/versions.py +30 -0
  39. fractal_server/app/schemas/v1/__init__.py +1 -0
  40. fractal_server/app/schemas/{state.py → v1/state.py} +4 -21
  41. fractal_server/app/schemas/v2/__init__.py +4 -1
  42. fractal_server/app/schemas/v2/task_collection.py +101 -30
  43. fractal_server/config.py +184 -3
  44. fractal_server/main.py +27 -1
  45. fractal_server/ssh/__init__.py +4 -0
  46. fractal_server/ssh/_fabric.py +245 -0
  47. fractal_server/tasks/utils.py +12 -64
  48. fractal_server/tasks/v1/background_operations.py +2 -2
  49. fractal_server/tasks/{endpoint_operations.py → v1/endpoint_operations.py} +7 -12
  50. fractal_server/tasks/v1/utils.py +67 -0
  51. fractal_server/tasks/v2/_TaskCollectPip.py +61 -32
  52. fractal_server/tasks/v2/_venv_pip.py +195 -0
  53. fractal_server/tasks/v2/background_operations.py +257 -295
  54. fractal_server/tasks/v2/background_operations_ssh.py +317 -0
  55. fractal_server/tasks/v2/endpoint_operations.py +136 -0
  56. fractal_server/tasks/v2/templates/_1_create_venv.sh +46 -0
  57. fractal_server/tasks/v2/templates/_2_upgrade_pip.sh +30 -0
  58. fractal_server/tasks/v2/templates/_3_pip_install.sh +32 -0
  59. fractal_server/tasks/v2/templates/_4_pip_freeze.sh +21 -0
  60. fractal_server/tasks/v2/templates/_5_pip_show.sh +59 -0
  61. fractal_server/tasks/v2/utils.py +54 -0
  62. {fractal_server-2.2.0a1.dist-info → fractal_server-2.3.0.dist-info}/METADATA +4 -2
  63. {fractal_server-2.2.0a1.dist-info → fractal_server-2.3.0.dist-info}/RECORD +66 -42
  64. fractal_server/tasks/v2/get_collection_data.py +0 -14
  65. {fractal_server-2.2.0a1.dist-info → fractal_server-2.3.0.dist-info}/LICENSE +0 -0
  66. {fractal_server-2.2.0a1.dist-info → fractal_server-2.3.0.dist-info}/WHEEL +0 -0
  67. {fractal_server-2.2.0a1.dist-info → fractal_server-2.3.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,195 @@
1
+ from pathlib import Path
2
+ from typing import Optional
3
+
4
+ from ..utils import COLLECTION_FREEZE_FILENAME
5
+ from fractal_server.logger import get_logger
6
+ from fractal_server.tasks.v2._TaskCollectPip import _TaskCollectPip
7
+ from fractal_server.tasks.v2.utils import get_python_interpreter_v2
8
+ from fractal_server.utils import execute_command
9
+
10
+
11
+ async def _pip_install(
12
+ venv_path: Path,
13
+ task_pkg: _TaskCollectPip,
14
+ logger_name: str,
15
+ ) -> Path:
16
+ """
17
+ Install package in venv
18
+
19
+ Args:
20
+ venv_path:
21
+ task_pkg:
22
+ logger_name:
23
+
24
+ Returns:
25
+ The location of the package.
26
+ """
27
+
28
+ logger = get_logger(logger_name)
29
+
30
+ pip = venv_path / "venv/bin/pip"
31
+
32
+ extras = f"[{task_pkg.package_extras}]" if task_pkg.package_extras else ""
33
+
34
+ if task_pkg.is_local_package:
35
+ pip_install_str = f"{task_pkg.package_path.as_posix()}{extras}"
36
+ else:
37
+ version_string = (
38
+ f"=={task_pkg.package_version}" if task_pkg.package_version else ""
39
+ )
40
+ pip_install_str = f"{task_pkg.package_name}{extras}{version_string}"
41
+
42
+ await execute_command(
43
+ cwd=venv_path,
44
+ command=f"{pip} install --upgrade pip",
45
+ logger_name=logger_name,
46
+ )
47
+ await execute_command(
48
+ cwd=venv_path,
49
+ command=f"{pip} install {pip_install_str}",
50
+ logger_name=logger_name,
51
+ )
52
+ if task_pkg.pinned_package_versions:
53
+ for (
54
+ pinned_pkg_name,
55
+ pinned_pkg_version,
56
+ ) in task_pkg.pinned_package_versions.items():
57
+
58
+ logger.debug(
59
+ "Specific version required: "
60
+ f"{pinned_pkg_name}=={pinned_pkg_version}"
61
+ )
62
+ logger.debug(
63
+ "Preliminary check: verify that "
64
+ f"{pinned_pkg_version} is already installed"
65
+ )
66
+ stdout_show = await execute_command(
67
+ cwd=venv_path,
68
+ command=f"{pip} show {pinned_pkg_name}",
69
+ logger_name=logger_name,
70
+ )
71
+ current_version = next(
72
+ line.split()[-1]
73
+ for line in stdout_show.split("\n")
74
+ if line.startswith("Version:")
75
+ )
76
+ if current_version != pinned_pkg_version:
77
+ logger.debug(
78
+ f"Currently installed version of {pinned_pkg_name} "
79
+ f"({current_version}) differs from pinned version "
80
+ f"({pinned_pkg_version}); "
81
+ f"install version {pinned_pkg_version}."
82
+ )
83
+ await execute_command(
84
+ cwd=venv_path,
85
+ command=(
86
+ f"{pip} install "
87
+ f"{pinned_pkg_name}=={pinned_pkg_version}"
88
+ ),
89
+ logger_name=logger_name,
90
+ )
91
+ else:
92
+ logger.debug(
93
+ f"Currently installed version of {pinned_pkg_name} "
94
+ f"({current_version}) already matches the pinned version."
95
+ )
96
+
97
+ # Extract package installation path from `pip show`
98
+ stdout_show = await execute_command(
99
+ cwd=venv_path,
100
+ command=f"{pip} show {task_pkg.package_name}",
101
+ logger_name=logger_name,
102
+ )
103
+
104
+ location = Path(
105
+ next(
106
+ line.split()[-1]
107
+ for line in stdout_show.split("\n")
108
+ if line.startswith("Location:")
109
+ )
110
+ )
111
+
112
+ # NOTE
113
+ # https://packaging.python.org/en/latest/specifications/recording-installed-packages/
114
+ # This directory is named as {name}-{version}.dist-info, with name and
115
+ # version fields corresponding to Core metadata specifications. Both
116
+ # fields must be normalized (see the name normalization specification and
117
+ # the version normalization specification), and replace dash (-)
118
+ # characters with underscore (_) characters, so the .dist-info directory
119
+ # always has exactly one dash (-) character in its stem, separating the
120
+ # name and version fields.
121
+ package_root = location / (task_pkg.package_name.replace("-", "_"))
122
+ logger.debug(f"[_pip install] {location=}")
123
+ logger.debug(f"[_pip install] {task_pkg.package_name=}")
124
+ logger.debug(f"[_pip install] {package_root=}")
125
+
126
+ # Run `pip freeze --all` and store its output
127
+ stdout_freeze = await execute_command(
128
+ cwd=venv_path, command=f"{pip} freeze --all", logger_name=logger_name
129
+ )
130
+ with (venv_path / COLLECTION_FREEZE_FILENAME).open("w") as f:
131
+ f.write(stdout_freeze)
132
+
133
+ return package_root
134
+
135
+
136
+ async def _init_venv_v2(
137
+ *,
138
+ path: Path,
139
+ python_version: Optional[str] = None,
140
+ logger_name: str,
141
+ ) -> Path:
142
+ """
143
+ Set a virtual environment at `path/venv`
144
+
145
+ Args:
146
+ path : Path
147
+ path to directory in which to set up the virtual environment
148
+ python_version : default=None
149
+ Python version the virtual environment will be based upon
150
+
151
+ Returns:
152
+ python_bin : Path
153
+ path to python interpreter
154
+ """
155
+ logger = get_logger(logger_name)
156
+ logger.debug(f"[_init_venv] {path=}")
157
+ interpreter = get_python_interpreter_v2(python_version=python_version)
158
+ logger.debug(f"[_init_venv] {interpreter=}")
159
+ await execute_command(
160
+ cwd=path,
161
+ command=f"{interpreter} -m venv venv",
162
+ logger_name=logger_name,
163
+ )
164
+ python_bin = path / "venv/bin/python"
165
+ logger.debug(f"[_init_venv] {python_bin=}")
166
+ return python_bin
167
+
168
+
169
+ async def _create_venv_install_package_pip(
170
+ *,
171
+ task_pkg: _TaskCollectPip,
172
+ path: Path,
173
+ logger_name: str,
174
+ ) -> tuple[Path, Path]:
175
+ """
176
+ Create venv and install package
177
+
178
+ Args:
179
+ path: the directory in which to create the environment
180
+ task_pkg: object containing the different metadata required to install
181
+ the package
182
+
183
+ Returns:
184
+ python_bin: path to venv's python interpreter
185
+ package_root: the location of the package manifest
186
+ """
187
+ python_bin = await _init_venv_v2(
188
+ path=path,
189
+ python_version=task_pkg.python_version,
190
+ logger_name=logger_name,
191
+ )
192
+ package_root = await _pip_install(
193
+ venv_path=path, task_pkg=task_pkg, logger_name=logger_name
194
+ )
195
+ return python_bin, package_root