fractal-server 2.7.0a11__py3-none-any.whl → 2.8.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/user_settings.py +1 -0
- fractal_server/app/models/v2/task.py +16 -2
- fractal_server/app/routes/admin/v2/task_group.py +7 -0
- fractal_server/app/routes/api/v2/dataset.py +39 -6
- fractal_server/app/routes/api/v2/task.py +4 -6
- fractal_server/app/routes/api/v2/task_collection.py +17 -44
- fractal_server/app/routes/api/v2/task_collection_custom.py +5 -4
- fractal_server/app/schemas/user_settings.py +18 -0
- fractal_server/app/schemas/v2/__init__.py +1 -0
- fractal_server/app/schemas/v2/dataset.py +5 -3
- fractal_server/app/schemas/v2/task_collection.py +20 -4
- fractal_server/app/schemas/v2/task_group.py +8 -1
- fractal_server/app/security/__init__.py +8 -1
- fractal_server/config.py +8 -28
- fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +39 -0
- fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +42 -0
- fractal_server/tasks/utils.py +0 -31
- fractal_server/tasks/v1/background_operations.py +11 -11
- fractal_server/tasks/v1/endpoint_operations.py +5 -5
- fractal_server/tasks/v1/utils.py +2 -2
- fractal_server/tasks/v2/collection_local.py +357 -0
- fractal_server/tasks/v2/{background_operations_ssh.py → collection_ssh.py} +108 -102
- fractal_server/tasks/v2/templates/_1_create_venv.sh +0 -8
- fractal_server/tasks/v2/templates/{_2_upgrade_pip.sh → _2_preliminary_pip_operations.sh} +2 -1
- fractal_server/tasks/v2/templates/_3_pip_install.sh +22 -1
- fractal_server/tasks/v2/templates/_5_pip_show.sh +5 -5
- fractal_server/tasks/v2/utils_background.py +209 -0
- fractal_server/tasks/v2/utils_package_names.py +77 -0
- fractal_server/tasks/v2/{utils.py → utils_python_interpreter.py} +0 -26
- fractal_server/tasks/v2/utils_templates.py +59 -0
- fractal_server/utils.py +48 -3
- {fractal_server-2.7.0a11.dist-info → fractal_server-2.8.0.dist-info}/METADATA +14 -17
- {fractal_server-2.7.0a11.dist-info → fractal_server-2.8.0.dist-info}/RECORD +38 -35
- fractal_server/data_migrations/2_7_0.py +0 -323
- fractal_server/tasks/v2/_venv_pip.py +0 -193
- fractal_server/tasks/v2/background_operations.py +0 -456
- /fractal_server/{tasks/v2/endpoint_operations.py → app/routes/api/v2/_aux_functions_task_collection.py} +0 -0
- {fractal_server-2.7.0a11.dist-info → fractal_server-2.8.0.dist-info}/LICENSE +0 -0
- {fractal_server-2.7.0a11.dist-info → fractal_server-2.8.0.dist-info}/WHEEL +0 -0
- {fractal_server-2.7.0a11.dist-info → fractal_server-2.8.0.dist-info}/entry_points.txt +0 -0
fractal_server/tasks/utils.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import re
|
2
1
|
from pathlib import Path
|
3
2
|
|
4
3
|
from fractal_server.config import get_settings
|
@@ -42,38 +41,8 @@ def get_collection_log_v1(path: Path) -> str:
|
|
42
41
|
return log
|
43
42
|
|
44
43
|
|
45
|
-
def get_collection_log_v2(path: Path) -> str:
|
46
|
-
log_path = get_log_path(path)
|
47
|
-
log = log_path.open().read()
|
48
|
-
return log
|
49
|
-
|
50
|
-
|
51
44
|
def get_collection_freeze_v1(venv_path: Path) -> str:
|
52
45
|
package_path = get_absolute_venv_path_v1(venv_path)
|
53
46
|
freeze_path = get_freeze_path(package_path)
|
54
47
|
freeze = freeze_path.open().read()
|
55
48
|
return freeze
|
56
|
-
|
57
|
-
|
58
|
-
def get_collection_freeze_v2(path: Path) -> str:
|
59
|
-
freeze_path = get_freeze_path(path)
|
60
|
-
freeze = freeze_path.open().read()
|
61
|
-
return freeze
|
62
|
-
|
63
|
-
|
64
|
-
def _normalize_package_name(name: str) -> str:
|
65
|
-
"""
|
66
|
-
Implement PyPa specifications for package-name normalization
|
67
|
-
|
68
|
-
The name should be lowercased with all runs of the characters `.`, `-`,
|
69
|
-
or `_` replaced with a single `-` character. This can be implemented in
|
70
|
-
Python with the re module.
|
71
|
-
(https://packaging.python.org/en/latest/specifications/name-normalization)
|
72
|
-
|
73
|
-
Args:
|
74
|
-
name: The non-normalized package name.
|
75
|
-
|
76
|
-
Returns:
|
77
|
-
The normalized package name.
|
78
|
-
"""
|
79
|
-
return re.sub(r"[-_.]+", "-", name).lower()
|
@@ -7,10 +7,10 @@ from pathlib import Path
|
|
7
7
|
from shutil import rmtree as shell_rmtree
|
8
8
|
|
9
9
|
from ...string_tools import slugify_task_name_for_source_v1
|
10
|
-
from ..utils import _normalize_package_name
|
11
10
|
from ..utils import get_collection_log_v1
|
12
11
|
from ..utils import get_collection_path
|
13
12
|
from ..utils import get_log_path
|
13
|
+
from ..v2.utils_package_names import normalize_package_name
|
14
14
|
from ._TaskCollectPip import _TaskCollectPip
|
15
15
|
from .utils import _init_venv_v1
|
16
16
|
from fractal_server.app.db import DBSyncSession
|
@@ -23,7 +23,7 @@ from fractal_server.app.schemas.v1 import TaskReadV1
|
|
23
23
|
from fractal_server.logger import close_logger
|
24
24
|
from fractal_server.logger import get_logger
|
25
25
|
from fractal_server.logger import set_logger
|
26
|
-
from fractal_server.utils import
|
26
|
+
from fractal_server.utils import execute_command_async
|
27
27
|
|
28
28
|
|
29
29
|
async def _pip_install(
|
@@ -60,12 +60,12 @@ async def _pip_install(
|
|
60
60
|
cmd_install = f"{pip} install {pip_install_str}"
|
61
61
|
cmd_inspect = f"{pip} show {task_pkg.package}"
|
62
62
|
|
63
|
-
await
|
63
|
+
await execute_command_async(
|
64
64
|
cwd=venv_path,
|
65
65
|
command=f"{pip} install --upgrade pip",
|
66
66
|
logger_name=logger_name,
|
67
67
|
)
|
68
|
-
await
|
68
|
+
await execute_command_async(
|
69
69
|
cwd=venv_path, command=cmd_install, logger_name=logger_name
|
70
70
|
)
|
71
71
|
if task_pkg.pinned_package_versions:
|
@@ -82,7 +82,7 @@ async def _pip_install(
|
|
82
82
|
"Preliminary check: verify that "
|
83
83
|
f"{pinned_pkg_version} is already installed"
|
84
84
|
)
|
85
|
-
stdout_inspect = await
|
85
|
+
stdout_inspect = await execute_command_async(
|
86
86
|
cwd=venv_path,
|
87
87
|
command=f"{pip} show {pinned_pkg_name}",
|
88
88
|
logger_name=logger_name,
|
@@ -99,7 +99,7 @@ async def _pip_install(
|
|
99
99
|
f"({pinned_pkg_version}); "
|
100
100
|
f"install version {pinned_pkg_version}."
|
101
101
|
)
|
102
|
-
await
|
102
|
+
await execute_command_async(
|
103
103
|
cwd=venv_path,
|
104
104
|
command=(
|
105
105
|
f"{pip} install "
|
@@ -114,7 +114,7 @@ async def _pip_install(
|
|
114
114
|
)
|
115
115
|
|
116
116
|
# Extract package installation path from `pip show`
|
117
|
-
stdout_inspect = await
|
117
|
+
stdout_inspect = await execute_command_async(
|
118
118
|
cwd=venv_path, command=cmd_inspect, logger_name=logger_name
|
119
119
|
)
|
120
120
|
|
@@ -165,8 +165,8 @@ async def _create_venv_install_package(
|
|
165
165
|
"""
|
166
166
|
|
167
167
|
# Normalize package name
|
168
|
-
task_pkg.package_name =
|
169
|
-
task_pkg.package =
|
168
|
+
task_pkg.package_name = normalize_package_name(task_pkg.package_name)
|
169
|
+
task_pkg.package = normalize_package_name(task_pkg.package)
|
170
170
|
|
171
171
|
python_bin = await _init_venv_v1(
|
172
172
|
path=path,
|
@@ -192,8 +192,8 @@ async def create_package_environment_pip(
|
|
192
192
|
logger = get_logger(logger_name)
|
193
193
|
|
194
194
|
# Normalize package name
|
195
|
-
task_pkg.package_name =
|
196
|
-
task_pkg.package =
|
195
|
+
task_pkg.package_name = normalize_package_name(task_pkg.package_name)
|
196
|
+
task_pkg.package = normalize_package_name(task_pkg.package)
|
197
197
|
|
198
198
|
# Only proceed if package, version and manifest attributes are set
|
199
199
|
task_pkg.check()
|
@@ -4,14 +4,14 @@ from typing import Optional
|
|
4
4
|
from typing import Union
|
5
5
|
from zipfile import ZipFile
|
6
6
|
|
7
|
-
from ..
|
7
|
+
from ..v2.utils_package_names import normalize_package_name
|
8
8
|
from ._TaskCollectPip import _TaskCollectPip as _TaskCollectPipV1
|
9
9
|
from .utils import get_python_interpreter_v1
|
10
10
|
from fractal_server.app.schemas.v1 import ManifestV1
|
11
11
|
from fractal_server.config import get_settings
|
12
12
|
from fractal_server.logger import get_logger
|
13
13
|
from fractal_server.syringe import Inject
|
14
|
-
from fractal_server.utils import
|
14
|
+
from fractal_server.utils import execute_command_async
|
15
15
|
|
16
16
|
|
17
17
|
FRACTAL_PUBLIC_TASK_SUBDIR = ".fractal"
|
@@ -32,7 +32,7 @@ async def download_package(
|
|
32
32
|
)
|
33
33
|
package_and_version = f"{task_pkg.package}{version}"
|
34
34
|
cmd = f"{pip} download --no-deps {package_and_version} -d {dest}"
|
35
|
-
stdout = await
|
35
|
+
stdout = await execute_command_async(command=cmd, cwd=Path("."))
|
36
36
|
pkg_file = next(
|
37
37
|
line.split()[-1] for line in stdout.split("\n") if "Saved" in line
|
38
38
|
)
|
@@ -123,7 +123,7 @@ def inspect_package(path: Path, logger_name: Optional[str] = None) -> dict:
|
|
123
123
|
logger.debug("Package name and version read correctly.")
|
124
124
|
|
125
125
|
# Normalize package name:
|
126
|
-
pkg_name =
|
126
|
+
pkg_name = normalize_package_name(pkg_name)
|
127
127
|
|
128
128
|
info = dict(
|
129
129
|
pkg_name=pkg_name,
|
@@ -148,7 +148,7 @@ def create_package_dir_pip(
|
|
148
148
|
f"Cannot create venv folder for package `{task_pkg.package}` "
|
149
149
|
"with `version=None`."
|
150
150
|
)
|
151
|
-
normalized_package =
|
151
|
+
normalized_package = normalize_package_name(task_pkg.package)
|
152
152
|
package_dir = f"{normalized_package}{task_pkg.package_version}"
|
153
153
|
venv_path = settings.FRACTAL_TASKS_DIR / user / package_dir
|
154
154
|
if create:
|
fractal_server/tasks/v1/utils.py
CHANGED
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
2
2
|
from typing import Optional
|
3
3
|
|
4
4
|
from fractal_server.logger import get_logger
|
5
|
-
from fractal_server.utils import
|
5
|
+
from fractal_server.utils import execute_command_async
|
6
6
|
|
7
7
|
|
8
8
|
def get_python_interpreter_v1(version: Optional[str] = None) -> str:
|
@@ -57,7 +57,7 @@ async def _init_venv_v1(
|
|
57
57
|
logger.debug(f"[_init_venv] {path=}")
|
58
58
|
interpreter = get_python_interpreter_v1(version=python_version)
|
59
59
|
logger.debug(f"[_init_venv] {interpreter=}")
|
60
|
-
await
|
60
|
+
await execute_command_async(
|
61
61
|
cwd=path,
|
62
62
|
command=f"{interpreter} -m venv venv",
|
63
63
|
logger_name=logger_name,
|
@@ -0,0 +1,357 @@
|
|
1
|
+
import json
|
2
|
+
import shutil
|
3
|
+
from pathlib import Path
|
4
|
+
from tempfile import TemporaryDirectory
|
5
|
+
|
6
|
+
from sqlalchemy.orm.attributes import flag_modified
|
7
|
+
|
8
|
+
from .database_operations import create_db_tasks_and_update_task_group
|
9
|
+
from fractal_server.app.db import get_sync_db
|
10
|
+
from fractal_server.app.models.v2 import CollectionStateV2
|
11
|
+
from fractal_server.app.models.v2 import TaskGroupV2
|
12
|
+
from fractal_server.app.schemas.v2 import CollectionStatusV2
|
13
|
+
from fractal_server.app.schemas.v2 import TaskReadV2
|
14
|
+
from fractal_server.app.schemas.v2.manifest import ManifestV2
|
15
|
+
from fractal_server.config import get_settings
|
16
|
+
from fractal_server.logger import get_logger
|
17
|
+
from fractal_server.logger import set_logger
|
18
|
+
from fractal_server.syringe import Inject
|
19
|
+
from fractal_server.tasks.utils import get_log_path
|
20
|
+
from fractal_server.tasks.v2.utils_background import _handle_failure
|
21
|
+
from fractal_server.tasks.v2.utils_background import _prepare_tasks_metadata
|
22
|
+
from fractal_server.tasks.v2.utils_background import _refresh_logs
|
23
|
+
from fractal_server.tasks.v2.utils_background import (
|
24
|
+
_set_collection_state_data_status,
|
25
|
+
)
|
26
|
+
from fractal_server.tasks.v2.utils_background import check_task_files_exist
|
27
|
+
from fractal_server.tasks.v2.utils_package_names import compare_package_names
|
28
|
+
from fractal_server.tasks.v2.utils_python_interpreter import (
|
29
|
+
get_python_interpreter_v2,
|
30
|
+
)
|
31
|
+
from fractal_server.tasks.v2.utils_templates import customize_template
|
32
|
+
from fractal_server.tasks.v2.utils_templates import parse_script_5_stdout
|
33
|
+
from fractal_server.utils import execute_command_sync
|
34
|
+
|
35
|
+
|
36
|
+
def _customize_and_run_template(
|
37
|
+
script_filename: str,
|
38
|
+
replacements: list[tuple[str, str]],
|
39
|
+
script_dir: str,
|
40
|
+
logger_name: str,
|
41
|
+
) -> str:
|
42
|
+
"""
|
43
|
+
Customize one of the template bash scripts.
|
44
|
+
|
45
|
+
Args:
|
46
|
+
script_filename:
|
47
|
+
replacements:
|
48
|
+
script_dir:
|
49
|
+
logger_name:
|
50
|
+
"""
|
51
|
+
logger = get_logger(logger_name)
|
52
|
+
logger.debug(f"_customize_and_run_template {script_filename} - START")
|
53
|
+
|
54
|
+
script_path_local = Path(script_dir) / script_filename
|
55
|
+
# Read template
|
56
|
+
customize_template(
|
57
|
+
template_name=script_filename,
|
58
|
+
replacements=replacements,
|
59
|
+
script_path=script_path_local,
|
60
|
+
)
|
61
|
+
|
62
|
+
cmd = f"bash {script_path_local}"
|
63
|
+
logger.debug(f"Now run '{cmd}' ")
|
64
|
+
|
65
|
+
stdout = execute_command_sync(command=cmd)
|
66
|
+
|
67
|
+
logger.debug(f"Standard output of '{cmd}':\n{stdout}")
|
68
|
+
logger.debug(f"_customize_and_run_template {script_filename} - END")
|
69
|
+
|
70
|
+
return stdout
|
71
|
+
|
72
|
+
|
73
|
+
def collect_package_local(
|
74
|
+
*,
|
75
|
+
state_id: int,
|
76
|
+
task_group: TaskGroupV2,
|
77
|
+
) -> None:
|
78
|
+
"""
|
79
|
+
Collect a task package.
|
80
|
+
|
81
|
+
This function is run as a background task, therefore exceptions must be
|
82
|
+
handled.
|
83
|
+
|
84
|
+
NOTE: by making this function sync, it will run within a thread - due to
|
85
|
+
starlette/fastapi handling of background tasks (see
|
86
|
+
https://github.com/encode/starlette/blob/master/starlette/background.py).
|
87
|
+
|
88
|
+
|
89
|
+
Arguments:
|
90
|
+
state_id:
|
91
|
+
task_group:
|
92
|
+
"""
|
93
|
+
|
94
|
+
# Create the task_group path
|
95
|
+
with TemporaryDirectory() as tmpdir:
|
96
|
+
|
97
|
+
# Setup logger in tmpdir
|
98
|
+
LOGGER_NAME = "task_collection_local"
|
99
|
+
log_file_path = get_log_path(Path(tmpdir))
|
100
|
+
logger = set_logger(
|
101
|
+
logger_name=LOGGER_NAME,
|
102
|
+
log_file_path=log_file_path,
|
103
|
+
)
|
104
|
+
|
105
|
+
# Log some info
|
106
|
+
logger.debug("START")
|
107
|
+
for key, value in task_group.model_dump().items():
|
108
|
+
logger.debug(f"task_group.{key}: {value}")
|
109
|
+
|
110
|
+
# Open a DB session
|
111
|
+
with next(get_sync_db()) as db:
|
112
|
+
|
113
|
+
# Check that the task_group path does not exist
|
114
|
+
if Path(task_group.path).exists():
|
115
|
+
error_msg = f"{task_group.path} already exists."
|
116
|
+
logger.error(error_msg)
|
117
|
+
_handle_failure(
|
118
|
+
state_id=state_id,
|
119
|
+
logger_name=LOGGER_NAME,
|
120
|
+
log_file_path=log_file_path,
|
121
|
+
exception=FileExistsError(error_msg),
|
122
|
+
db=db,
|
123
|
+
task_group_id=task_group.id,
|
124
|
+
)
|
125
|
+
return
|
126
|
+
|
127
|
+
try:
|
128
|
+
# Prepare replacements for task-collection scripts
|
129
|
+
python_bin = get_python_interpreter_v2(
|
130
|
+
python_version=task_group.python_version
|
131
|
+
)
|
132
|
+
install_string = task_group.pip_install_string
|
133
|
+
settings = Inject(get_settings)
|
134
|
+
replacements = [
|
135
|
+
("__PACKAGE_NAME__", task_group.pkg_name),
|
136
|
+
("__TASK_GROUP_DIR__", task_group.path),
|
137
|
+
("__PACKAGE_ENV_DIR__", task_group.venv_path),
|
138
|
+
("__PYTHON__", python_bin),
|
139
|
+
("__INSTALL_STRING__", install_string),
|
140
|
+
(
|
141
|
+
"__FRACTAL_MAX_PIP_VERSION__",
|
142
|
+
settings.FRACTAL_MAX_PIP_VERSION,
|
143
|
+
),
|
144
|
+
(
|
145
|
+
"__PINNED_PACKAGE_LIST__",
|
146
|
+
task_group.pinned_package_versions_string,
|
147
|
+
),
|
148
|
+
]
|
149
|
+
|
150
|
+
common_args = dict(
|
151
|
+
replacements=replacements,
|
152
|
+
script_dir=task_group.path,
|
153
|
+
logger_name=LOGGER_NAME,
|
154
|
+
)
|
155
|
+
|
156
|
+
logger.debug("installing - START")
|
157
|
+
_set_collection_state_data_status(
|
158
|
+
state_id=state_id,
|
159
|
+
new_status=CollectionStatusV2.INSTALLING,
|
160
|
+
logger_name=LOGGER_NAME,
|
161
|
+
db=db,
|
162
|
+
)
|
163
|
+
|
164
|
+
# Create main path for task group
|
165
|
+
Path(task_group.path).mkdir(parents=True)
|
166
|
+
logger.debug(f"Created {task_group.path}")
|
167
|
+
|
168
|
+
# Create venv
|
169
|
+
logger.debug(
|
170
|
+
(f"START - Create python venv {task_group.venv_path}")
|
171
|
+
)
|
172
|
+
cmd = (
|
173
|
+
f"python{task_group.python_version} -m venv "
|
174
|
+
f"{task_group.venv_path} --copies"
|
175
|
+
)
|
176
|
+
stdout = execute_command_sync(command=cmd)
|
177
|
+
logger.debug(
|
178
|
+
(f"END - Create python venv folder {task_group.venv_path}")
|
179
|
+
)
|
180
|
+
_refresh_logs(
|
181
|
+
state_id=state_id,
|
182
|
+
log_file_path=log_file_path,
|
183
|
+
db=db,
|
184
|
+
)
|
185
|
+
# Close db connections before long pip related operations
|
186
|
+
# Warning this expunge all ORM objects.
|
187
|
+
# https://docs.sqlalchemy.org/en/20/orm/session_api.html#sqlalchemy.orm.Session.close
|
188
|
+
db.close()
|
189
|
+
|
190
|
+
stdout = _customize_and_run_template(
|
191
|
+
script_filename="_2_preliminary_pip_operations.sh",
|
192
|
+
**common_args,
|
193
|
+
)
|
194
|
+
_refresh_logs(
|
195
|
+
state_id=state_id,
|
196
|
+
log_file_path=log_file_path,
|
197
|
+
db=db,
|
198
|
+
)
|
199
|
+
stdout = _customize_and_run_template(
|
200
|
+
script_filename="_3_pip_install.sh",
|
201
|
+
**common_args,
|
202
|
+
)
|
203
|
+
_refresh_logs(
|
204
|
+
state_id=state_id,
|
205
|
+
log_file_path=log_file_path,
|
206
|
+
db=db,
|
207
|
+
)
|
208
|
+
stdout_pip_freeze = _customize_and_run_template(
|
209
|
+
script_filename="_4_pip_freeze.sh",
|
210
|
+
**common_args,
|
211
|
+
)
|
212
|
+
logger.debug("installing - END")
|
213
|
+
_refresh_logs(
|
214
|
+
state_id=state_id,
|
215
|
+
log_file_path=log_file_path,
|
216
|
+
db=db,
|
217
|
+
)
|
218
|
+
|
219
|
+
logger.debug("collecting - START")
|
220
|
+
_set_collection_state_data_status(
|
221
|
+
state_id=state_id,
|
222
|
+
new_status=CollectionStatusV2.COLLECTING,
|
223
|
+
logger_name=LOGGER_NAME,
|
224
|
+
db=db,
|
225
|
+
)
|
226
|
+
_refresh_logs(
|
227
|
+
state_id=state_id,
|
228
|
+
log_file_path=log_file_path,
|
229
|
+
db=db,
|
230
|
+
)
|
231
|
+
|
232
|
+
stdout = _customize_and_run_template(
|
233
|
+
script_filename="_5_pip_show.sh",
|
234
|
+
**common_args,
|
235
|
+
)
|
236
|
+
_refresh_logs(
|
237
|
+
state_id=state_id,
|
238
|
+
log_file_path=log_file_path,
|
239
|
+
db=db,
|
240
|
+
)
|
241
|
+
|
242
|
+
pkg_attrs = parse_script_5_stdout(stdout)
|
243
|
+
for key, value in pkg_attrs.items():
|
244
|
+
logger.debug(
|
245
|
+
f"collecting - parsed from pip-show: {key}={value}"
|
246
|
+
)
|
247
|
+
# Check package_name match between pip show and task-group
|
248
|
+
package_name_pip_show = pkg_attrs.get("package_name")
|
249
|
+
package_name_task_group = task_group.pkg_name
|
250
|
+
compare_package_names(
|
251
|
+
pkg_name_pip_show=package_name_pip_show,
|
252
|
+
pkg_name_task_group=package_name_task_group,
|
253
|
+
logger_name=LOGGER_NAME,
|
254
|
+
)
|
255
|
+
# Extract/drop parsed attributes
|
256
|
+
package_name = package_name_task_group
|
257
|
+
python_bin = pkg_attrs.pop("python_bin")
|
258
|
+
package_root_parent = pkg_attrs.pop("package_root_parent")
|
259
|
+
|
260
|
+
# TODO : Use more robust logic to determine `package_root`.
|
261
|
+
# Examples: use `importlib.util.find_spec`, or parse the output
|
262
|
+
# of `pip show --files {package_name}`.
|
263
|
+
package_name_underscore = package_name.replace("-", "_")
|
264
|
+
package_root = (
|
265
|
+
Path(package_root_parent) / package_name_underscore
|
266
|
+
).as_posix()
|
267
|
+
|
268
|
+
# Read and validate manifest file
|
269
|
+
manifest_path = pkg_attrs.pop("manifest_path")
|
270
|
+
logger.info(f"collecting - now loading {manifest_path=}")
|
271
|
+
with open(manifest_path) as json_data:
|
272
|
+
pkg_manifest_dict = json.load(json_data)
|
273
|
+
logger.info(f"collecting - loaded {manifest_path=}")
|
274
|
+
logger.info("collecting - now validating manifest content")
|
275
|
+
pkg_manifest = ManifestV2(**pkg_manifest_dict)
|
276
|
+
logger.info("collecting - validated manifest content")
|
277
|
+
_refresh_logs(
|
278
|
+
state_id=state_id,
|
279
|
+
log_file_path=log_file_path,
|
280
|
+
db=db,
|
281
|
+
)
|
282
|
+
|
283
|
+
logger.info("collecting - _prepare_tasks_metadata - start")
|
284
|
+
task_list = _prepare_tasks_metadata(
|
285
|
+
package_manifest=pkg_manifest,
|
286
|
+
package_version=task_group.version,
|
287
|
+
package_root=Path(package_root),
|
288
|
+
python_bin=Path(python_bin),
|
289
|
+
)
|
290
|
+
check_task_files_exist(task_list=task_list)
|
291
|
+
logger.info("collecting - _prepare_tasks_metadata - end")
|
292
|
+
_refresh_logs(
|
293
|
+
state_id=state_id,
|
294
|
+
log_file_path=log_file_path,
|
295
|
+
db=db,
|
296
|
+
)
|
297
|
+
|
298
|
+
logger.info(
|
299
|
+
"collecting - create_db_tasks_and_update_task_group - "
|
300
|
+
"start"
|
301
|
+
)
|
302
|
+
task_group = create_db_tasks_and_update_task_group(
|
303
|
+
task_list=task_list,
|
304
|
+
task_group_id=task_group.id,
|
305
|
+
db=db,
|
306
|
+
)
|
307
|
+
logger.info(
|
308
|
+
"collecting - create_db_tasks_and_update_task_group - end"
|
309
|
+
)
|
310
|
+
|
311
|
+
logger.debug("collecting - END")
|
312
|
+
|
313
|
+
# Finalize (write metadata to DB)
|
314
|
+
logger.debug("finalising - START")
|
315
|
+
|
316
|
+
_refresh_logs(
|
317
|
+
state_id=state_id,
|
318
|
+
log_file_path=log_file_path,
|
319
|
+
db=db,
|
320
|
+
)
|
321
|
+
collection_state = db.get(CollectionStateV2, state_id)
|
322
|
+
collection_state.data["freeze"] = stdout_pip_freeze
|
323
|
+
collection_state.data["status"] = CollectionStatusV2.OK
|
324
|
+
# FIXME: The `task_list` key is likely not used by any client,
|
325
|
+
# we should consider dropping it
|
326
|
+
task_read_list = [
|
327
|
+
TaskReadV2(**task.model_dump()).dict()
|
328
|
+
for task in task_group.task_list
|
329
|
+
]
|
330
|
+
collection_state.data["task_list"] = task_read_list
|
331
|
+
flag_modified(collection_state, "data")
|
332
|
+
db.commit()
|
333
|
+
logger.debug("finalising - END")
|
334
|
+
logger.debug("END")
|
335
|
+
|
336
|
+
except Exception as collection_e:
|
337
|
+
# Delete corrupted package dir
|
338
|
+
try:
|
339
|
+
logger.info(f"Now delete folder {task_group.path}")
|
340
|
+
|
341
|
+
shutil.rmtree(task_group.path)
|
342
|
+
logger.info(f"Deleted folder {task_group.path}")
|
343
|
+
except Exception as rm_e:
|
344
|
+
logger.error(
|
345
|
+
"Removing folder failed.\n"
|
346
|
+
f"Original error:\n{str(rm_e)}"
|
347
|
+
)
|
348
|
+
|
349
|
+
_handle_failure(
|
350
|
+
state_id=state_id,
|
351
|
+
logger_name=LOGGER_NAME,
|
352
|
+
log_file_path=log_file_path,
|
353
|
+
exception=collection_e,
|
354
|
+
db=db,
|
355
|
+
task_group_id=task_group.id,
|
356
|
+
)
|
357
|
+
return
|