fractal-server 2.7.0a3__py3-none-any.whl → 2.7.0a5__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 (53) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/__main__.py +3 -9
  3. fractal_server/app/models/v2/collection_state.py +1 -0
  4. fractal_server/app/models/v2/task.py +27 -3
  5. fractal_server/app/routes/admin/v2/task.py +4 -17
  6. fractal_server/app/routes/admin/v2/task_group.py +21 -0
  7. fractal_server/app/routes/api/v1/task_collection.py +4 -4
  8. fractal_server/app/routes/api/v2/_aux_functions.py +1 -7
  9. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +75 -2
  10. fractal_server/app/routes/api/v2/task.py +16 -42
  11. fractal_server/app/routes/api/v2/task_collection.py +175 -204
  12. fractal_server/app/routes/api/v2/task_collection_custom.py +31 -58
  13. fractal_server/app/routes/api/v2/task_group.py +29 -1
  14. fractal_server/app/routes/api/v2/workflow.py +11 -46
  15. fractal_server/app/routes/api/v2/workflowtask.py +0 -1
  16. fractal_server/app/routes/auth/_aux_auth.py +15 -12
  17. fractal_server/app/routes/auth/group.py +46 -23
  18. fractal_server/app/runner/v2/task_interface.py +4 -9
  19. fractal_server/app/schemas/v2/dataset.py +2 -7
  20. fractal_server/app/schemas/v2/dumps.py +1 -2
  21. fractal_server/app/schemas/v2/job.py +1 -1
  22. fractal_server/app/schemas/v2/project.py +1 -1
  23. fractal_server/app/schemas/v2/task.py +5 -10
  24. fractal_server/app/schemas/v2/task_collection.py +8 -6
  25. fractal_server/app/schemas/v2/task_group.py +31 -3
  26. fractal_server/app/schemas/v2/workflow.py +2 -2
  27. fractal_server/app/schemas/v2/workflowtask.py +2 -5
  28. fractal_server/data_migrations/2_7_0.py +1 -11
  29. fractal_server/images/models.py +2 -4
  30. fractal_server/main.py +1 -1
  31. fractal_server/migrations/versions/034a469ec2eb_task_groups.py +184 -0
  32. fractal_server/string_tools.py +6 -2
  33. fractal_server/tasks/utils.py +19 -5
  34. fractal_server/tasks/v1/_TaskCollectPip.py +1 -1
  35. fractal_server/tasks/v1/background_operations.py +5 -5
  36. fractal_server/tasks/v1/get_collection_data.py +2 -2
  37. fractal_server/tasks/v2/_venv_pip.py +62 -70
  38. fractal_server/tasks/v2/background_operations.py +170 -51
  39. fractal_server/tasks/v2/background_operations_ssh.py +35 -77
  40. fractal_server/tasks/v2/database_operations.py +7 -17
  41. fractal_server/tasks/v2/endpoint_operations.py +91 -145
  42. fractal_server/tasks/v2/templates/_1_create_venv.sh +9 -5
  43. fractal_server/tasks/v2/utils.py +5 -0
  44. fractal_server/utils.py +3 -2
  45. {fractal_server-2.7.0a3.dist-info → fractal_server-2.7.0a5.dist-info}/METADATA +1 -1
  46. {fractal_server-2.7.0a3.dist-info → fractal_server-2.7.0a5.dist-info}/RECORD +49 -52
  47. fractal_server/migrations/versions/742b74e1cc6e_revamp_taskv2_and_taskgroupv2.py +0 -101
  48. fractal_server/migrations/versions/7cf1baae8fb4_task_group_v2.py +0 -66
  49. fractal_server/migrations/versions/df7cc3501bf7_linkusergroup_timestamp_created.py +0 -42
  50. fractal_server/tasks/v2/_TaskCollectPip.py +0 -132
  51. {fractal_server-2.7.0a3.dist-info → fractal_server-2.7.0a5.dist-info}/LICENSE +0 -0
  52. {fractal_server-2.7.0a3.dist-info → fractal_server-2.7.0a5.dist-info}/WHEEL +0 -0
  53. {fractal_server-2.7.0a3.dist-info → fractal_server-2.7.0a5.dist-info}/entry_points.txt +0 -0
@@ -3,6 +3,7 @@ from typing import Optional
3
3
  from typing import Union
4
4
 
5
5
  from pydantic import BaseModel
6
+ from pydantic import Extra
6
7
  from pydantic import Field
7
8
  from pydantic import validator
8
9
 
@@ -109,13 +110,10 @@ class SingleImageUpdate(BaseModel):
109
110
  _types = validator("types", allow_reuse=True)(valdictkeys("types"))
110
111
 
111
112
 
112
- class Filters(BaseModel):
113
+ class Filters(BaseModel, extra=Extra.forbid):
113
114
  attributes: dict[str, Any] = Field(default_factory=dict)
114
115
  types: dict[str, bool] = Field(default_factory=dict)
115
116
 
116
- class Config:
117
- extra = "forbid"
118
-
119
117
  # Validators
120
118
  _attributes = validator("attributes", allow_reuse=True)(
121
119
  valdictkeys("attributes")
fractal_server/main.py CHANGED
@@ -20,7 +20,7 @@ from contextlib import asynccontextmanager
20
20
 
21
21
  from fastapi import FastAPI
22
22
 
23
- from .app.routes.aux._runner import _backend_supports_shutdown # FIXME: change
23
+ from .app.routes.aux._runner import _backend_supports_shutdown
24
24
  from .app.runner.shutdown import cleanup_after_shutdown
25
25
  from .config import get_settings
26
26
  from .logger import config_uvicorn_loggers
@@ -0,0 +1,184 @@
1
+ """task groups
2
+
3
+ Revision ID: 034a469ec2eb
4
+ Revises: da2cb2ac4255
5
+ Create Date: 2024-10-10 16:14:13.976231
6
+
7
+ """
8
+ from datetime import datetime
9
+ from datetime import timezone
10
+
11
+ import sqlalchemy as sa
12
+ import sqlmodel
13
+ from alembic import op
14
+
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision = "034a469ec2eb"
18
+ down_revision = "da2cb2ac4255"
19
+ branch_labels = None
20
+ depends_on = None
21
+
22
+
23
+ def upgrade() -> None:
24
+ op.create_table(
25
+ "taskgroupv2",
26
+ sa.Column("id", sa.Integer(), nullable=False),
27
+ sa.Column("user_id", sa.Integer(), nullable=False),
28
+ sa.Column("user_group_id", sa.Integer(), nullable=True),
29
+ sa.Column(
30
+ "origin", sqlmodel.sql.sqltypes.AutoString(), nullable=False
31
+ ),
32
+ sa.Column(
33
+ "pkg_name", sqlmodel.sql.sqltypes.AutoString(), nullable=False
34
+ ),
35
+ sa.Column(
36
+ "version", sqlmodel.sql.sqltypes.AutoString(), nullable=True
37
+ ),
38
+ sa.Column(
39
+ "python_version", sqlmodel.sql.sqltypes.AutoString(), nullable=True
40
+ ),
41
+ sa.Column("path", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
42
+ sa.Column(
43
+ "venv_path", sqlmodel.sql.sqltypes.AutoString(), nullable=True
44
+ ),
45
+ sa.Column(
46
+ "wheel_path", sqlmodel.sql.sqltypes.AutoString(), nullable=True
47
+ ),
48
+ sa.Column(
49
+ "pip_extras", sqlmodel.sql.sqltypes.AutoString(), nullable=True
50
+ ),
51
+ sa.Column(
52
+ "pinned_package_versions",
53
+ sa.JSON(),
54
+ server_default="{}",
55
+ nullable=True,
56
+ ),
57
+ sa.Column("active", sa.Boolean(), nullable=False),
58
+ sa.Column(
59
+ "timestamp_created", sa.DateTime(timezone=True), nullable=False
60
+ ),
61
+ sa.ForeignKeyConstraint(
62
+ ["user_group_id"],
63
+ ["usergroup.id"],
64
+ name=op.f("fk_taskgroupv2_user_group_id_usergroup"),
65
+ ),
66
+ sa.ForeignKeyConstraint(
67
+ ["user_id"],
68
+ ["user_oauth.id"],
69
+ name=op.f("fk_taskgroupv2_user_id_user_oauth"),
70
+ ),
71
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_taskgroupv2")),
72
+ )
73
+ with op.batch_alter_table("collectionstatev2", schema=None) as batch_op:
74
+ batch_op.add_column(
75
+ sa.Column("taskgroupv2_id", sa.Integer(), nullable=True)
76
+ )
77
+ batch_op.create_foreign_key(
78
+ batch_op.f("fk_collectionstatev2_taskgroupv2_id_taskgroupv2"),
79
+ "taskgroupv2",
80
+ ["taskgroupv2_id"],
81
+ ["id"],
82
+ )
83
+
84
+ with op.batch_alter_table("linkusergroup", schema=None) as batch_op:
85
+ batch_op.add_column(
86
+ sa.Column(
87
+ "timestamp_created",
88
+ sa.DateTime(timezone=True),
89
+ nullable=False,
90
+ server_default=str(datetime(2000, 1, 1, tzinfo=timezone.utc)),
91
+ )
92
+ )
93
+
94
+ with op.batch_alter_table("taskv2", schema=None) as batch_op:
95
+ batch_op.add_column(
96
+ sa.Column("taskgroupv2_id", sa.Integer(), nullable=True)
97
+ )
98
+ batch_op.add_column(
99
+ sa.Column(
100
+ "category", sqlmodel.sql.sqltypes.AutoString(), nullable=True
101
+ )
102
+ )
103
+ batch_op.add_column(
104
+ sa.Column(
105
+ "modality", sqlmodel.sql.sqltypes.AutoString(), nullable=True
106
+ )
107
+ )
108
+ batch_op.add_column(
109
+ sa.Column(
110
+ "authors", sqlmodel.sql.sqltypes.AutoString(), nullable=True
111
+ )
112
+ )
113
+ batch_op.add_column(
114
+ sa.Column("tags", sa.JSON(), server_default="[]", nullable=False)
115
+ )
116
+ batch_op.alter_column(
117
+ "source", existing_type=sa.VARCHAR(), nullable=True
118
+ )
119
+
120
+ try:
121
+ with op.batch_alter_table("taskv2", schema=None) as batch_op:
122
+ batch_op.drop_constraint("uq_taskv2_source", type_="unique")
123
+ except BaseException as e:
124
+ if op.get_bind().dialect.name != "sqlite":
125
+ raise e
126
+ import sqlite3
127
+ import logging
128
+
129
+ logger = logging.getLogger("alembic.runtime.migration")
130
+ logger.warning(
131
+ f"Using sqlite, with {sqlite3.version=} and "
132
+ f"{sqlite3.sqlite_version=}"
133
+ )
134
+
135
+ logger.warning(
136
+ "Could not drop 'uq_taskv2_source' constraint; this is expected "
137
+ "when the database was created before the naming convention "
138
+ "was added."
139
+ )
140
+ logger.warning(
141
+ "As a workaround, we recreate the constraint before dropping it."
142
+ )
143
+ with op.batch_alter_table("taskv2", schema=None) as batch_op:
144
+ batch_op.create_unique_constraint("uq_taskv2_source", ["source"])
145
+ batch_op.drop_constraint("uq_taskv2_source", type_="unique")
146
+
147
+ with op.batch_alter_table("taskv2", schema=None) as batch_op:
148
+ batch_op.create_foreign_key(
149
+ batch_op.f("fk_taskv2_taskgroupv2_id_taskgroupv2"),
150
+ "taskgroupv2",
151
+ ["taskgroupv2_id"],
152
+ ["id"],
153
+ )
154
+
155
+
156
+ def downgrade() -> None:
157
+ # ### commands auto generated by Alembic - please adjust! ###
158
+ with op.batch_alter_table("taskv2", schema=None) as batch_op:
159
+ batch_op.drop_constraint(
160
+ batch_op.f("fk_taskv2_taskgroupv2_id_taskgroupv2"),
161
+ type_="foreignkey",
162
+ )
163
+ batch_op.create_unique_constraint("uq_taskv2_source", ["source"])
164
+ batch_op.alter_column(
165
+ "source", existing_type=sa.VARCHAR(), nullable=False
166
+ )
167
+ batch_op.drop_column("tags")
168
+ batch_op.drop_column("authors")
169
+ batch_op.drop_column("modality")
170
+ batch_op.drop_column("category")
171
+ batch_op.drop_column("taskgroupv2_id")
172
+
173
+ with op.batch_alter_table("linkusergroup", schema=None) as batch_op:
174
+ batch_op.drop_column("timestamp_created")
175
+
176
+ with op.batch_alter_table("collectionstatev2", schema=None) as batch_op:
177
+ batch_op.drop_constraint(
178
+ batch_op.f("fk_collectionstatev2_taskgroupv2_id_taskgroupv2"),
179
+ type_="foreignkey",
180
+ )
181
+ batch_op.drop_column("taskgroupv2_id")
182
+
183
+ op.drop_table("taskgroupv2")
184
+ # ### end Alembic commands ###
@@ -33,14 +33,18 @@ def sanitize_string(value: str) -> str:
33
33
  return new_value
34
34
 
35
35
 
36
- def slugify_task_name_for_source(task_name: str) -> str:
36
+ def slugify_task_name_for_source_v1(task_name: str) -> str:
37
37
  """
38
38
  NOTE: this function is used upon creation of tasks' sources, therefore
39
39
  for the moment we cannot replace it with its more comprehensive version
40
40
  from `fractal_server.string_tools.sanitize_string`, nor we can remove it.
41
- As 2.3.1, we are renaming it to `slugify_task_name_for_source`, to make
41
+
42
+ As of 2.3.1, we are renaming it to `slugify_task_name_for_source`, to make
42
43
  it clear that it should not be used for other purposes.
43
44
 
45
+ As of 2.7.0, we are renaming it to `slugify_task_name_for_source_v1`, to
46
+ make it clear that it is not used for v2.
47
+
44
48
  Args:
45
49
  task_name:
46
50
 
@@ -9,9 +9,11 @@ COLLECTION_LOG_FILENAME = "collection.log"
9
9
  COLLECTION_FREEZE_FILENAME = "collection_freeze.txt"
10
10
 
11
11
 
12
- def get_absolute_venv_path(venv_path: Path) -> Path:
12
+ def get_absolute_venv_path_v1(venv_path: Path) -> Path:
13
13
  """
14
14
  If a path is not absolute, make it a relative path of FRACTAL_TASKS_DIR.
15
+
16
+ As of v2.7.0, we rename this to v1 since it is only to be used in v1.
15
17
  """
16
18
  if venv_path.is_absolute():
17
19
  package_path = venv_path
@@ -33,20 +35,32 @@ def get_freeze_path(base: Path) -> Path:
33
35
  return base / COLLECTION_FREEZE_FILENAME
34
36
 
35
37
 
36
- def get_collection_log(venv_path: Path) -> str:
37
- package_path = get_absolute_venv_path(venv_path)
38
+ def get_collection_log_v1(path: Path) -> str:
39
+ package_path = get_absolute_venv_path_v1(path)
38
40
  log_path = get_log_path(package_path)
39
41
  log = log_path.open().read()
40
42
  return log
41
43
 
42
44
 
43
- def get_collection_freeze(venv_path: Path) -> str:
44
- package_path = get_absolute_venv_path(venv_path)
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
+ def get_collection_freeze_v1(venv_path: Path) -> str:
52
+ package_path = get_absolute_venv_path_v1(venv_path)
45
53
  freeze_path = get_freeze_path(package_path)
46
54
  freeze = freeze_path.open().read()
47
55
  return freeze
48
56
 
49
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
+
50
64
  def _normalize_package_name(name: str) -> str:
51
65
  """
52
66
  Implement PyPa specifications for package-name normalization
@@ -77,7 +77,7 @@ class _TaskCollectPip(TaskCollectPipV1):
77
77
  if self.python_version:
78
78
  python_version = f"py{self.python_version}"
79
79
  else:
80
- python_version = "" # FIXME: can we allow this?
80
+ python_version = ""
81
81
 
82
82
  source = ":".join(
83
83
  (
@@ -6,9 +6,9 @@ import json
6
6
  from pathlib import Path
7
7
  from shutil import rmtree as shell_rmtree
8
8
 
9
- from ...string_tools import slugify_task_name_for_source
9
+ from ...string_tools import slugify_task_name_for_source_v1
10
10
  from ..utils import _normalize_package_name
11
- from ..utils import get_collection_log
11
+ from ..utils import get_collection_log_v1
12
12
  from ..utils import get_collection_path
13
13
  from ..utils import get_log_path
14
14
  from ._TaskCollectPip import _TaskCollectPip
@@ -215,7 +215,7 @@ async def create_package_environment_pip(
215
215
  # Fill in attributes for TaskCreate
216
216
  task_executable = package_root / t.executable
217
217
  cmd = f"{python_bin.as_posix()} {task_executable.as_posix()}"
218
- task_name_slug = slugify_task_name_for_source(t.name)
218
+ task_name_slug = slugify_task_name_for_source_v1(t.name)
219
219
  task_source = f"{task_pkg.package_source}:{task_name_slug}"
220
220
  if not task_executable.exists():
221
221
  raise FileNotFoundError(
@@ -321,7 +321,7 @@ async def background_collect_pip(
321
321
 
322
322
  # Update DB
323
323
  data.status = "OK"
324
- data.log = get_collection_log(venv_path)
324
+ data.log = get_collection_log_v1(venv_path)
325
325
  state.data = data.sanitised_dict()
326
326
  db.add(state)
327
327
  db.merge(state)
@@ -342,7 +342,7 @@ async def background_collect_pip(
342
342
  # Update db
343
343
  data.status = "fail"
344
344
  data.info = f"Original error: {e}"
345
- data.log = get_collection_log(venv_path)
345
+ data.log = get_collection_log_v1(venv_path)
346
346
  state.data = data.sanitised_dict()
347
347
  db.merge(state)
348
348
  db.commit()
@@ -2,12 +2,12 @@ import json
2
2
  from pathlib import Path
3
3
 
4
4
  from fractal_server.app.schemas.v1 import TaskCollectStatusV1
5
- from fractal_server.tasks.utils import get_absolute_venv_path
5
+ from fractal_server.tasks.utils import get_absolute_venv_path_v1
6
6
  from fractal_server.tasks.utils import get_collection_path
7
7
 
8
8
 
9
9
  def get_collection_data(venv_path: Path) -> TaskCollectStatusV1:
10
- package_path = get_absolute_venv_path(venv_path)
10
+ package_path = get_absolute_venv_path_v1(venv_path)
11
11
  collection_path = get_collection_path(package_path)
12
12
  with collection_path.open() as f:
13
13
  data = json.load(f)
@@ -2,17 +2,48 @@ from pathlib import Path
2
2
  from typing import Optional
3
3
 
4
4
  from ..utils import COLLECTION_FREEZE_FILENAME
5
+ from fractal_server.app.models.v2 import TaskGroupV2
5
6
  from fractal_server.config import get_settings
6
7
  from fractal_server.logger import get_logger
7
8
  from fractal_server.syringe import Inject
8
- from fractal_server.tasks.v2._TaskCollectPip import _TaskCollectPip
9
9
  from fractal_server.tasks.v2.utils import get_python_interpreter_v2
10
10
  from fractal_server.utils import execute_command
11
11
 
12
12
 
13
- async def _pip_install(
13
+ async def _init_venv_v2(
14
+ *,
14
15
  venv_path: Path,
15
- task_pkg: _TaskCollectPip,
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,
16
47
  logger_name: str,
17
48
  ) -> Path:
18
49
  """
@@ -30,48 +61,40 @@ async def _pip_install(
30
61
 
31
62
  logger = get_logger(logger_name)
32
63
 
33
- pip = venv_path / "venv/bin/pip"
34
-
35
- extras = f"[{task_pkg.package_extras}]" if task_pkg.package_extras else ""
36
-
37
- if task_pkg.is_local_package:
38
- pip_install_str = f"{task_pkg.package_path.as_posix()}{extras}"
39
- else:
40
- version_string = (
41
- f"=={task_pkg.package_version}" if task_pkg.package_version else ""
42
- )
43
- pip_install_str = f"{task_pkg.package_name}{extras}{version_string}"
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=}")
44
67
 
45
68
  await execute_command(
46
- cwd=venv_path,
69
+ cwd=Path(task_group.venv_path),
47
70
  command=(
48
- f"{pip} install --upgrade "
71
+ f"{python_bin} -m pip install --upgrade "
49
72
  f"'pip<={settings.FRACTAL_MAX_PIP_VERSION}'"
50
73
  ),
51
74
  logger_name=logger_name,
52
75
  )
53
76
  await execute_command(
54
- cwd=venv_path,
55
- command=f"{pip} install {pip_install_str}",
77
+ cwd=Path(task_group.venv_path),
78
+ command=f"{python_bin} -m pip install {pip_install_str}",
56
79
  logger_name=logger_name,
57
80
  )
58
- if task_pkg.pinned_package_versions:
81
+
82
+ if task_group.pinned_package_versions:
59
83
  for (
60
84
  pinned_pkg_name,
61
85
  pinned_pkg_version,
62
- ) in task_pkg.pinned_package_versions.items():
63
-
86
+ ) in task_group.pinned_package_versions.items():
64
87
  logger.debug(
65
88
  "Specific version required: "
66
89
  f"{pinned_pkg_name}=={pinned_pkg_version}"
67
90
  )
68
91
  logger.debug(
69
92
  "Preliminary check: verify that "
70
- f"{pinned_pkg_version} is already installed"
93
+ f"{pinned_pkg_name} is already installed"
71
94
  )
72
95
  stdout_show = await execute_command(
73
- cwd=venv_path,
74
- command=f"{pip} show {pinned_pkg_name}",
96
+ cwd=Path(task_group.venv_path),
97
+ command=f"{python_bin} -m pip show {pinned_pkg_name}",
75
98
  logger_name=logger_name,
76
99
  )
77
100
  current_version = next(
@@ -87,9 +110,9 @@ async def _pip_install(
87
110
  f"install version {pinned_pkg_version}."
88
111
  )
89
112
  await execute_command(
90
- cwd=venv_path,
113
+ cwd=Path(task_group.venv_path),
91
114
  command=(
92
- f"{pip} install "
115
+ f"{python_bin} -m pip install "
93
116
  f"{pinned_pkg_name}=={pinned_pkg_version}"
94
117
  ),
95
118
  logger_name=logger_name,
@@ -102,8 +125,8 @@ async def _pip_install(
102
125
 
103
126
  # Extract package installation path from `pip show`
104
127
  stdout_show = await execute_command(
105
- cwd=venv_path,
106
- command=f"{pip} show {task_pkg.package_name}",
128
+ cwd=Path(task_group.venv_path),
129
+ command=f"{python_bin} -m pip show {task_group.pkg_name}",
107
130
  logger_name=logger_name,
108
131
  )
109
132
 
@@ -124,58 +147,26 @@ async def _pip_install(
124
147
  # characters with underscore (_) characters, so the .dist-info directory
125
148
  # always has exactly one dash (-) character in its stem, separating the
126
149
  # name and version fields.
127
- package_root = location / (task_pkg.package_name.replace("-", "_"))
150
+ package_root = location / (task_group.pkg_name.replace("-", "_"))
128
151
  logger.debug(f"[_pip install] {location=}")
129
- logger.debug(f"[_pip install] {task_pkg.package_name=}")
152
+ logger.debug(f"[_pip install] {task_group.pkg_name=}")
130
153
  logger.debug(f"[_pip install] {package_root=}")
131
154
 
132
155
  # Run `pip freeze --all` and store its output
133
156
  stdout_freeze = await execute_command(
134
- cwd=venv_path, command=f"{pip} freeze --all", logger_name=logger_name
157
+ cwd=Path(task_group.venv_path),
158
+ command=f"{python_bin} -m pip freeze --all",
159
+ logger_name=logger_name,
135
160
  )
136
- with (venv_path / COLLECTION_FREEZE_FILENAME).open("w") as f:
161
+ with (Path(task_group.path) / COLLECTION_FREEZE_FILENAME).open("w") as f:
137
162
  f.write(stdout_freeze)
138
163
 
139
164
  return package_root
140
165
 
141
166
 
142
- async def _init_venv_v2(
143
- *,
144
- path: Path,
145
- python_version: Optional[str] = None,
146
- logger_name: str,
147
- ) -> Path:
148
- """
149
- Set a virtual environment at `path/venv`
150
-
151
- Args:
152
- path : Path
153
- path to directory in which to set up the virtual environment
154
- python_version : default=None
155
- Python version the virtual environment will be based upon
156
-
157
- Returns:
158
- python_bin : Path
159
- path to python interpreter
160
- """
161
- logger = get_logger(logger_name)
162
- logger.debug(f"[_init_venv] {path=}")
163
- interpreter = get_python_interpreter_v2(python_version=python_version)
164
- logger.debug(f"[_init_venv] {interpreter=}")
165
- await execute_command(
166
- cwd=path,
167
- command=f"{interpreter} -m venv venv",
168
- logger_name=logger_name,
169
- )
170
- python_bin = path / "venv/bin/python"
171
- logger.debug(f"[_init_venv] {python_bin=}")
172
- return python_bin
173
-
174
-
175
167
  async def _create_venv_install_package_pip(
176
168
  *,
177
- task_pkg: _TaskCollectPip,
178
- path: Path,
169
+ task_group: TaskGroupV2,
179
170
  logger_name: str,
180
171
  ) -> tuple[Path, Path]:
181
172
  """
@@ -191,11 +182,12 @@ async def _create_venv_install_package_pip(
191
182
  package_root: the location of the package manifest
192
183
  """
193
184
  python_bin = await _init_venv_v2(
194
- path=path,
195
- python_version=task_pkg.python_version,
185
+ venv_path=Path(task_group.venv_path),
186
+ python_version=task_group.python_version,
196
187
  logger_name=logger_name,
197
188
  )
198
189
  package_root = await _pip_install(
199
- venv_path=path, task_pkg=task_pkg, logger_name=logger_name
190
+ task_group=task_group,
191
+ logger_name=logger_name,
200
192
  )
201
193
  return python_bin, package_root