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.
- fractal_server/__init__.py +1 -1
- fractal_server/app/models/v1/state.py +1 -2
- fractal_server/app/routes/admin/v1.py +2 -2
- fractal_server/app/routes/admin/v2.py +2 -2
- fractal_server/app/routes/api/v1/job.py +2 -2
- fractal_server/app/routes/api/v1/task_collection.py +4 -4
- fractal_server/app/routes/api/v2/__init__.py +23 -3
- fractal_server/app/routes/api/v2/job.py +2 -2
- fractal_server/app/routes/api/v2/submit.py +6 -0
- fractal_server/app/routes/api/v2/task_collection.py +74 -34
- fractal_server/app/routes/api/v2/task_collection_custom.py +170 -0
- fractal_server/app/routes/api/v2/task_collection_ssh.py +125 -0
- fractal_server/app/routes/aux/_runner.py +10 -2
- fractal_server/app/runner/compress_folder.py +120 -0
- fractal_server/app/runner/executors/slurm/__init__.py +0 -3
- fractal_server/app/runner/executors/slurm/_batching.py +0 -1
- fractal_server/app/runner/executors/slurm/_slurm_config.py +9 -9
- fractal_server/app/runner/executors/slurm/ssh/__init__.py +3 -0
- fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +112 -0
- fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +120 -0
- fractal_server/app/runner/executors/slurm/ssh/executor.py +1488 -0
- fractal_server/app/runner/executors/slurm/sudo/__init__.py +3 -0
- fractal_server/app/runner/executors/slurm/{_check_jobs_status.py → sudo/_check_jobs_status.py} +1 -1
- fractal_server/app/runner/executors/slurm/{_executor_wait_thread.py → sudo/_executor_wait_thread.py} +1 -1
- fractal_server/app/runner/executors/slurm/{_subprocess_run_as_user.py → sudo/_subprocess_run_as_user.py} +1 -1
- fractal_server/app/runner/executors/slurm/{executor.py → sudo/executor.py} +12 -12
- fractal_server/app/runner/extract_archive.py +38 -0
- fractal_server/app/runner/v1/__init__.py +78 -40
- fractal_server/app/runner/v1/_slurm/__init__.py +1 -1
- fractal_server/app/runner/v2/__init__.py +147 -62
- fractal_server/app/runner/v2/_local_experimental/__init__.py +22 -12
- fractal_server/app/runner/v2/_local_experimental/executor.py +12 -8
- fractal_server/app/runner/v2/_slurm/__init__.py +1 -6
- fractal_server/app/runner/v2/_slurm_ssh/__init__.py +125 -0
- fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +83 -0
- fractal_server/app/runner/v2/_slurm_ssh/get_slurm_config.py +182 -0
- fractal_server/app/runner/v2/runner_functions_low_level.py +9 -11
- fractal_server/app/runner/versions.py +30 -0
- fractal_server/app/schemas/v1/__init__.py +1 -0
- fractal_server/app/schemas/{state.py → v1/state.py} +4 -21
- fractal_server/app/schemas/v2/__init__.py +4 -1
- fractal_server/app/schemas/v2/task_collection.py +101 -30
- fractal_server/config.py +184 -3
- fractal_server/main.py +27 -1
- fractal_server/ssh/__init__.py +4 -0
- fractal_server/ssh/_fabric.py +245 -0
- fractal_server/tasks/utils.py +12 -64
- fractal_server/tasks/v1/background_operations.py +2 -2
- fractal_server/tasks/{endpoint_operations.py → v1/endpoint_operations.py} +7 -12
- fractal_server/tasks/v1/utils.py +67 -0
- fractal_server/tasks/v2/_TaskCollectPip.py +61 -32
- fractal_server/tasks/v2/_venv_pip.py +195 -0
- fractal_server/tasks/v2/background_operations.py +257 -295
- fractal_server/tasks/v2/background_operations_ssh.py +317 -0
- fractal_server/tasks/v2/endpoint_operations.py +136 -0
- fractal_server/tasks/v2/templates/_1_create_venv.sh +46 -0
- fractal_server/tasks/v2/templates/_2_upgrade_pip.sh +30 -0
- fractal_server/tasks/v2/templates/_3_pip_install.sh +32 -0
- fractal_server/tasks/v2/templates/_4_pip_freeze.sh +21 -0
- fractal_server/tasks/v2/templates/_5_pip_show.sh +59 -0
- fractal_server/tasks/v2/utils.py +54 -0
- {fractal_server-2.2.0a1.dist-info → fractal_server-2.3.0.dist-info}/METADATA +4 -2
- {fractal_server-2.2.0a1.dist-info → fractal_server-2.3.0.dist-info}/RECORD +66 -42
- fractal_server/tasks/v2/get_collection_data.py +0 -14
- {fractal_server-2.2.0a1.dist-info → fractal_server-2.3.0.dist-info}/LICENSE +0 -0
- {fractal_server-2.2.0a1.dist-info → fractal_server-2.3.0.dist-info}/WHEEL +0 -0
- {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
|