virtool-workflow 7.0.0__tar.gz → 7.1.1__tar.gz
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.
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/PKG-INFO +3 -5
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/pyproject.toml +9 -3
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/api/acquire.py +6 -2
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/api/utils.py +8 -8
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/data/indexes.py +12 -9
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/data/samples.py +15 -7
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/data/subtractions.py +13 -6
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/decorators.py +7 -5
- virtool_workflow-7.1.1/virtool_workflow/errors.py +62 -0
- virtool_workflow-7.1.1/virtool_workflow/files.py +40 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/hooks.py +2 -3
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/pytest_plugin/data.py +0 -1
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/run_subprocess.py +3 -2
- virtool_workflow-7.0.0/virtool_workflow/errors.py +0 -61
- virtool_workflow-7.0.0/virtool_workflow/files.py +0 -24
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/LICENSE +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/README.md +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/__init__.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/analysis/__init__.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/analysis/fastqc.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/analysis/skewer.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/analysis/trimming.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/analysis/utils.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/api/__init__.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/api/client.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/cli.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/data/__init__.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/data/analyses.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/data/hmms.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/data/jobs.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/data/ml.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/data/uploads.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/pytest_plugin/__init__.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/pytest_plugin/utils.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/__init__.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/config.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/discover.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/events.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/hook.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/path.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/ping.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/redis.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/run.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/sentry.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/utils.py +0 -0
- {virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/workflow.py +0 -0
@@ -1,16 +1,14 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: virtool-workflow
|
3
|
-
Version: 7.
|
3
|
+
Version: 7.1.1
|
4
4
|
Summary: A framework for developing bioinformatics workflows for Virtool.
|
5
5
|
Home-page: https://github.com/virtool/virtool-workflow
|
6
6
|
License: MIT
|
7
7
|
Author: Ian Boyes
|
8
8
|
Maintainer: Ian Boyes
|
9
|
-
Requires-Python: >=3.
|
9
|
+
Requires-Python: >=3.12,<3.13
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
14
12
|
Classifier: Programming Language :: Python :: 3.12
|
15
13
|
Requires-Dist: aiofiles (>=0.7.0,<0.8.0)
|
16
14
|
Requires-Dist: aiohttp (>=3.8.1,<4.0.0)
|
@@ -20,7 +18,7 @@ Requires-Dist: orjson (>=3.9.9,<4.0.0)
|
|
20
18
|
Requires-Dist: pydantic-factories (>=1.17.3,<2.0.0)
|
21
19
|
Requires-Dist: pyfixtures (>=1.0.0,<2.0.0)
|
22
20
|
Requires-Dist: sentry-sdk (>=2.3.1,<3.0.0)
|
23
|
-
Requires-Dist: virtool-core (>=
|
21
|
+
Requires-Dist: virtool-core (>=14.0.0,<15.0.0)
|
24
22
|
Project-URL: Repository, https://github.com/virtool/virtool-workflow
|
25
23
|
Description-Content-Type: text/markdown
|
26
24
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "virtool-workflow"
|
3
|
-
version = "7.
|
3
|
+
version = "7.1.1"
|
4
4
|
description = "A framework for developing bioinformatics workflows for Virtool."
|
5
5
|
authors = [
|
6
6
|
"Ian Boyes",
|
@@ -29,7 +29,7 @@ packages = [
|
|
29
29
|
]
|
30
30
|
|
31
31
|
[tool.poetry.dependencies]
|
32
|
-
python = "
|
32
|
+
python = "~3.12"
|
33
33
|
aiofiles = "^0.7.0"
|
34
34
|
aiohttp = "^3.8.1"
|
35
35
|
biopython = "^1.81"
|
@@ -38,7 +38,7 @@ orjson = "^3.9.9"
|
|
38
38
|
pydantic-factories = "^1.17.3"
|
39
39
|
pyfixtures = "^1.0.0"
|
40
40
|
sentry-sdk = "^2.3.1"
|
41
|
-
virtool-core = "^
|
41
|
+
virtool-core = "^14.0.0"
|
42
42
|
|
43
43
|
[tool.poetry.scripts]
|
44
44
|
run-workflow = "virtool_workflow.cli:cli_main"
|
@@ -71,8 +71,14 @@ exclude = [
|
|
71
71
|
".ruff_cache",
|
72
72
|
"__pypackages__",
|
73
73
|
]
|
74
|
+
target-version = "py312"
|
74
75
|
|
75
76
|
[tool.ruff.lint]
|
77
|
+
ignore = [
|
78
|
+
"ANN101",
|
79
|
+
"D203",
|
80
|
+
"D213"
|
81
|
+
]
|
76
82
|
select = ["ALL"]
|
77
83
|
|
78
84
|
[build-system]
|
@@ -4,7 +4,11 @@ from aiohttp import ClientConnectionError, ClientSession, TCPConnector
|
|
4
4
|
from structlog import get_logger
|
5
5
|
from virtool_core.models.job import JobAcquired
|
6
6
|
|
7
|
-
from virtool_workflow.errors import
|
7
|
+
from virtool_workflow.errors import (
|
8
|
+
JobAlreadyAcquiredError,
|
9
|
+
JobsAPIError,
|
10
|
+
JobsAPIServerError,
|
11
|
+
)
|
8
12
|
|
9
13
|
logger = get_logger("api")
|
10
14
|
|
@@ -39,7 +43,7 @@ async def acquire_job_by_id(
|
|
39
43
|
|
40
44
|
if resp.status == 400:
|
41
45
|
if "already acquired" in await resp.text():
|
42
|
-
raise
|
46
|
+
raise JobAlreadyAcquiredError(await resp.json())
|
43
47
|
|
44
48
|
logger.critical(
|
45
49
|
"unexpected api error during job acquisition",
|
@@ -10,10 +10,10 @@ from aiohttp import (
|
|
10
10
|
from structlog import get_logger
|
11
11
|
|
12
12
|
from virtool_workflow.errors import (
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
JobsAPIBadRequestError,
|
14
|
+
JobsAPIConflictError,
|
15
|
+
JobsAPIForbiddenError,
|
16
|
+
JobsAPINotFoundError,
|
17
17
|
JobsAPIServerError,
|
18
18
|
)
|
19
19
|
|
@@ -80,10 +80,10 @@ async def raise_exception_by_status_code(resp: ClientResponse):
|
|
80
80
|
:raise JobsAPIServerError: the response status code is 500
|
81
81
|
"""
|
82
82
|
status_exception_map = {
|
83
|
-
400:
|
84
|
-
403:
|
85
|
-
404:
|
86
|
-
409:
|
83
|
+
400: JobsAPIBadRequestError,
|
84
|
+
403: JobsAPIForbiddenError,
|
85
|
+
404: JobsAPINotFoundError,
|
86
|
+
409: JobsAPIConflictError,
|
87
87
|
500: JobsAPIServerError,
|
88
88
|
}
|
89
89
|
|
@@ -13,7 +13,7 @@ from virtool_core.models.reference import ReferenceNested
|
|
13
13
|
from virtool_core.utils import decompress_file
|
14
14
|
|
15
15
|
from virtool_workflow.api.client import APIClient
|
16
|
-
from virtool_workflow.errors import
|
16
|
+
from virtool_workflow.errors import MissingJobArgumentError
|
17
17
|
from virtool_workflow.files import VirtoolFileFormat
|
18
18
|
|
19
19
|
logger = get_logger("api")
|
@@ -43,9 +43,7 @@ class WFIndex:
|
|
43
43
|
|
44
44
|
@property
|
45
45
|
def bowtie_path(self) -> Path:
|
46
|
-
"""The path to the Bowtie2 index prefix for the Virtool index.
|
47
|
-
|
48
|
-
"""
|
46
|
+
"""The path to the Bowtie2 index prefix for the Virtool index."""
|
49
47
|
return self.path / "reference"
|
50
48
|
|
51
49
|
@property
|
@@ -151,7 +149,10 @@ class WFNewIndex:
|
|
151
149
|
await self._api.patch_json(f"/indexes/{self.id}", {})
|
152
150
|
|
153
151
|
async def upload(
|
154
|
-
self,
|
152
|
+
self,
|
153
|
+
path: Path,
|
154
|
+
fmt: VirtoolFileFormat = "fasta",
|
155
|
+
name: str | None = None,
|
155
156
|
):
|
156
157
|
"""Upload a file to associate with the index being built.
|
157
158
|
|
@@ -272,14 +273,16 @@ async def index(
|
|
272
273
|
|
273
274
|
@fixture
|
274
275
|
async def new_index(
|
275
|
-
_api: APIClient,
|
276
|
+
_api: APIClient,
|
277
|
+
job: Job,
|
278
|
+
proc: int,
|
279
|
+
work_path: Path,
|
276
280
|
) -> WFNewIndex:
|
277
|
-
"""The :class:`.WFNewIndex` for an index being created by the current job.
|
278
|
-
"""
|
281
|
+
"""The :class:`.WFNewIndex` for an index being created by the current job."""
|
279
282
|
try:
|
280
283
|
id_ = job.args["index_id"]
|
281
284
|
except KeyError:
|
282
|
-
raise
|
285
|
+
raise MissingJobArgumentError("Missing jobs args key 'index_id'")
|
283
286
|
|
284
287
|
log = logger.bind(resource="new_index", id=id_, job_id=job.id)
|
285
288
|
log.info("loading index")
|
@@ -12,7 +12,7 @@ from virtool_core.models.samples import Quality, Sample
|
|
12
12
|
from virtool_workflow.analysis.utils import ReadPaths
|
13
13
|
from virtool_workflow.api.client import APIClient
|
14
14
|
from virtool_workflow.data.uploads import WFUploads
|
15
|
-
from virtool_workflow.errors import
|
15
|
+
from virtool_workflow.errors import JobsAPINotFoundError
|
16
16
|
from virtool_workflow.files import VirtoolFileFormat
|
17
17
|
|
18
18
|
logger = get_logger("api")
|
@@ -83,7 +83,10 @@ class WFNewSample:
|
|
83
83
|
|
84
84
|
@fixture
|
85
85
|
async def sample(
|
86
|
-
_api: APIClient,
|
86
|
+
_api: APIClient,
|
87
|
+
job: Job,
|
88
|
+
uploads: WFUploads,
|
89
|
+
work_path: Path,
|
87
90
|
) -> WFSample:
|
88
91
|
"""The sample associated with the current job."""
|
89
92
|
id_ = job.args["sample_id"]
|
@@ -92,8 +95,8 @@ async def sample(
|
|
92
95
|
|
93
96
|
try:
|
94
97
|
sample_json = await _api.get_json(base_url_path)
|
95
|
-
except
|
96
|
-
raise
|
98
|
+
except JobsAPINotFoundError:
|
99
|
+
raise JobsAPINotFoundError("Sample not found")
|
97
100
|
|
98
101
|
sample = Sample(**sample_json)
|
99
102
|
|
@@ -101,7 +104,8 @@ async def sample(
|
|
101
104
|
await asyncio.to_thread(reads_path.mkdir, exist_ok=True, parents=True)
|
102
105
|
|
103
106
|
await _api.get_file(
|
104
|
-
f"{base_url_path}/reads/reads_1.fq.gz",
|
107
|
+
f"{base_url_path}/reads/reads_1.fq.gz",
|
108
|
+
reads_path / "reads_1.fq.gz",
|
105
109
|
)
|
106
110
|
|
107
111
|
if sample.paired:
|
@@ -110,7 +114,8 @@ async def sample(
|
|
110
114
|
reads_path / "reads_2.fq.gz",
|
111
115
|
)
|
112
116
|
await _api.get_file(
|
113
|
-
f"{base_url_path}/reads/reads_2.fq.gz",
|
117
|
+
f"{base_url_path}/reads/reads_2.fq.gz",
|
118
|
+
reads_path / "reads_2.fq.gz",
|
114
119
|
)
|
115
120
|
else:
|
116
121
|
read_paths = (reads_path / "reads_1.fq.gz",)
|
@@ -127,7 +132,10 @@ async def sample(
|
|
127
132
|
|
128
133
|
@fixture
|
129
134
|
async def new_sample(
|
130
|
-
_api: APIClient,
|
135
|
+
_api: APIClient,
|
136
|
+
job: Job,
|
137
|
+
uploads: WFUploads,
|
138
|
+
work_path: Path,
|
131
139
|
) -> WFNewSample:
|
132
140
|
"""The sample associated with the current job."""
|
133
141
|
id_ = job.args["sample_id"]
|
@@ -15,7 +15,7 @@ from virtool_core.models.subtraction import (
|
|
15
15
|
from virtool_workflow.api.client import APIClient
|
16
16
|
from virtool_workflow.data.analyses import WFAnalysis
|
17
17
|
from virtool_workflow.data.uploads import WFUploads
|
18
|
-
from virtool_workflow.errors import
|
18
|
+
from virtool_workflow.errors import MissingJobArgumentError
|
19
19
|
|
20
20
|
logger = get_logger("api")
|
21
21
|
|
@@ -118,7 +118,9 @@ class WFNewSubtraction:
|
|
118
118
|
|
119
119
|
@fixture
|
120
120
|
async def subtractions(
|
121
|
-
_api: APIClient,
|
121
|
+
_api: APIClient,
|
122
|
+
analysis: WFAnalysis,
|
123
|
+
work_path: Path,
|
122
124
|
) -> list[WFSubtraction]:
|
123
125
|
"""The subtractions to be used for the current analysis job."""
|
124
126
|
subtraction_work_path = work_path / "subtractions"
|
@@ -158,7 +160,10 @@ async def subtractions(
|
|
158
160
|
|
159
161
|
@fixture
|
160
162
|
async def new_subtraction(
|
161
|
-
_api: APIClient,
|
163
|
+
_api: APIClient,
|
164
|
+
job: Job,
|
165
|
+
uploads: WFUploads,
|
166
|
+
work_path: Path,
|
162
167
|
) -> WFNewSubtraction:
|
163
168
|
"""A new subtraction that will be created during the current job.
|
164
169
|
|
@@ -167,12 +172,12 @@ async def new_subtraction(
|
|
167
172
|
try:
|
168
173
|
id_ = job.args["subtraction_id"]
|
169
174
|
except KeyError:
|
170
|
-
raise
|
175
|
+
raise MissingJobArgumentError("subtraction_id")
|
171
176
|
|
172
177
|
try:
|
173
178
|
upload_id = job.args["files"][0]["id"]
|
174
179
|
except KeyError:
|
175
|
-
raise
|
180
|
+
raise MissingJobArgumentError("files")
|
176
181
|
|
177
182
|
subtraction_json = await _api.get_json(f"/subtractions/{id_}")
|
178
183
|
subtraction_ = Subtraction(**subtraction_json)
|
@@ -221,7 +226,9 @@ async def new_subtraction(
|
|
221
226
|
log.info("Uploading subtraction file")
|
222
227
|
|
223
228
|
await _api.put_file(
|
224
|
-
f"/subtractions/{subtraction_.id}/files/{filename}",
|
229
|
+
f"/subtractions/{subtraction_.id}/files/{filename}",
|
230
|
+
path,
|
231
|
+
"unknown",
|
225
232
|
)
|
226
233
|
|
227
234
|
log.info("Finished uploading subtraction file")
|
@@ -1,11 +1,13 @@
|
|
1
1
|
"""Create Workflows by decorating module scope functions."""
|
2
|
+
|
3
|
+
from collections.abc import Callable
|
2
4
|
from types import ModuleType
|
3
|
-
from typing import Callable
|
4
5
|
|
6
|
+
from virtool_workflow.errors import WorkflowStepsError
|
5
7
|
from virtool_workflow.workflow import Workflow
|
6
8
|
|
7
9
|
|
8
|
-
def step(func: Callable = None, *, name: str | None = None) -> Callable:
|
10
|
+
def step(func: Callable | None = None, *, name: str | None = None) -> Callable:
|
9
11
|
"""Mark a function as a workflow step function.
|
10
12
|
|
11
13
|
:param func: the workflow step function
|
@@ -16,7 +18,7 @@ def step(func: Callable = None, *, name: str | None = None) -> Callable:
|
|
16
18
|
return lambda _f: step(_f, name=name)
|
17
19
|
|
18
20
|
func.__workflow_marker__ = "step"
|
19
|
-
func.__workflow_step_props__ =
|
21
|
+
func.__workflow_step_props__ = {"name": name}
|
20
22
|
|
21
23
|
return func
|
22
24
|
|
@@ -39,7 +41,7 @@ def collect(module: ModuleType) -> Workflow:
|
|
39
41
|
if marked.__workflow_marker__ == "step":
|
40
42
|
workflow.step(marked, **marked.__workflow_step_props__)
|
41
43
|
|
42
|
-
if
|
43
|
-
raise
|
44
|
+
if not workflow.steps:
|
45
|
+
raise WorkflowStepsError(str(module))
|
44
46
|
|
45
47
|
return workflow
|
@@ -0,0 +1,62 @@
|
|
1
|
+
"""Custom exceptions for ``virtool_workflow``."""
|
2
|
+
|
3
|
+
from subprocess import SubprocessError
|
4
|
+
|
5
|
+
|
6
|
+
class JobAlreadyAcquiredError(Exception):
|
7
|
+
"""Raised when an attempt is made to reacquire a job."""
|
8
|
+
|
9
|
+
def __init__(self, job_id: str) -> None:
|
10
|
+
"""Initialize the exception with a message containing the job ID."""
|
11
|
+
super().__init__(
|
12
|
+
f"Job {job_id} is has already been acquired.",
|
13
|
+
)
|
14
|
+
|
15
|
+
|
16
|
+
class JobsAPIError(Exception):
|
17
|
+
"""A base exception for errors due to HTTP errors from the jobs API."""
|
18
|
+
|
19
|
+
|
20
|
+
class JobsAPIBadRequestError(JobsAPIError):
|
21
|
+
"""A ``400 Bad Request`` response was received from the jobs API."""
|
22
|
+
|
23
|
+
status = 400
|
24
|
+
|
25
|
+
|
26
|
+
class JobsAPIForbiddenError(JobsAPIError):
|
27
|
+
"""A ``403 Forbidden`` response was received from the jobs API."""
|
28
|
+
|
29
|
+
status = 403
|
30
|
+
|
31
|
+
|
32
|
+
class JobsAPINotFoundError(JobsAPIError):
|
33
|
+
"""A ``404 Not Found`` response was received from the jobs API."""
|
34
|
+
|
35
|
+
status = 404
|
36
|
+
|
37
|
+
|
38
|
+
class JobsAPIConflictError(JobsAPIError):
|
39
|
+
"""A ``409 Conflict`` response was received from the jobs API."""
|
40
|
+
|
41
|
+
status = 409
|
42
|
+
|
43
|
+
|
44
|
+
class JobsAPIServerError(JobsAPIError):
|
45
|
+
"""A ``500 Internal Server Error`` response was received from the jobs API."""
|
46
|
+
|
47
|
+
status = 500
|
48
|
+
|
49
|
+
|
50
|
+
class MissingJobArgumentError(ValueError):
|
51
|
+
"""The `job.args` dict is missing a required key for some funcionality."""
|
52
|
+
|
53
|
+
|
54
|
+
class WorkflowStepsError(Exception):
|
55
|
+
"""Raised when no workflow steps are found in a module."""
|
56
|
+
|
57
|
+
def __init__(self, module: str) -> None:
|
58
|
+
super().__init__(f"No workflow steps could be found in {module}")
|
59
|
+
|
60
|
+
|
61
|
+
class SubprocessFailedError(SubprocessError):
|
62
|
+
"""Subprocess exited with non-zero status during a workflow."""
|
@@ -0,0 +1,40 @@
|
|
1
|
+
"""Dataclasses for describing files uploaded to the Virtool server."""
|
2
|
+
|
3
|
+
import datetime
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from typing import Literal
|
6
|
+
|
7
|
+
VirtoolFileFormat = Literal[
|
8
|
+
"sam",
|
9
|
+
"bam",
|
10
|
+
"fasta",
|
11
|
+
"fastq",
|
12
|
+
"csv",
|
13
|
+
"tsv",
|
14
|
+
"json",
|
15
|
+
"unknown",
|
16
|
+
]
|
17
|
+
"""A literal type hint for the format of a :class:`.VirtoolFile`."""
|
18
|
+
|
19
|
+
|
20
|
+
@dataclass
|
21
|
+
class VirtoolFile:
|
22
|
+
"""A description of a file uploaded to the Virtool server."""
|
23
|
+
|
24
|
+
id: int
|
25
|
+
"""The unique ID for the file."""
|
26
|
+
|
27
|
+
name: str
|
28
|
+
"""The name of the file."""
|
29
|
+
|
30
|
+
size: int
|
31
|
+
"""The size of the file in bytes."""
|
32
|
+
|
33
|
+
format: VirtoolFileFormat
|
34
|
+
"""The format of the file."""
|
35
|
+
|
36
|
+
name_on_disk: str | None = None
|
37
|
+
"""The actual name of the file on disk."""
|
38
|
+
|
39
|
+
uploaded_at: datetime.datetime | None = None
|
40
|
+
"""When the file was uploaded."""
|
@@ -1,5 +1,4 @@
|
|
1
|
-
"""Hooks
|
2
|
-
"""
|
1
|
+
"""Hooks do things when events happen during the workflow lifecycle."""
|
3
2
|
|
4
3
|
from virtool_workflow.runtime.hook import Hook
|
5
4
|
|
@@ -124,7 +123,7 @@ __all__ = [
|
|
124
123
|
]
|
125
124
|
|
126
125
|
|
127
|
-
def cleanup_builtin_status_hooks():
|
126
|
+
def cleanup_builtin_status_hooks() -> None:
|
128
127
|
"""Clear callbacks for built-in status hooks.
|
129
128
|
|
130
129
|
This prevents carryover of hooks between tests. Carryover won't be encountered in
|
{virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/runtime/run_subprocess.py
RENAMED
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Code for running and managing subprocesses."""
|
2
|
+
|
2
3
|
import asyncio
|
3
4
|
from asyncio.subprocess import Process
|
4
5
|
from pathlib import Path
|
@@ -8,7 +9,7 @@ from pyfixtures import fixture
|
|
8
9
|
from structlog import get_logger
|
9
10
|
from virtool_core.utils import timestamp
|
10
11
|
|
11
|
-
from virtool_workflow.errors import
|
12
|
+
from virtool_workflow.errors import SubprocessFailedError
|
12
13
|
|
13
14
|
logger = get_logger("subprocess")
|
14
15
|
|
@@ -146,7 +147,7 @@ async def _run_subprocess(
|
|
146
147
|
# Exit code 15 indicates that the process was terminated. This is expected
|
147
148
|
# when the workflow fails for some other reason, hence not an exception
|
148
149
|
if process.returncode not in [0, 15, -15]:
|
149
|
-
raise
|
150
|
+
raise SubprocessFailedError(
|
150
151
|
f"{command[0]} failed with exit code {process.returncode}\n"
|
151
152
|
f"arguments: {command}\n",
|
152
153
|
)
|
@@ -1,61 +0,0 @@
|
|
1
|
-
from subprocess import SubprocessError
|
2
|
-
|
3
|
-
|
4
|
-
class IllegalJobArguments(ValueError):
|
5
|
-
"""The `job.args` dict is in an illegal state."""
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
class InsufficientJobRights(Exception):
|
10
|
-
...
|
11
|
-
|
12
|
-
|
13
|
-
class JobAlreadyAcquired(Exception):
|
14
|
-
def __init__(self, job_id: str):
|
15
|
-
super(JobAlreadyAcquired, self).__init__(
|
16
|
-
f"Job {job_id} is has already been acquired.",
|
17
|
-
)
|
18
|
-
|
19
|
-
|
20
|
-
class JobAlreadyFinalized(Exception):
|
21
|
-
...
|
22
|
-
|
23
|
-
|
24
|
-
class JobsAPIError(Exception):
|
25
|
-
"""A base exception for errors due to HTTP errors from the jobs API."""
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
class JobsAPIBadRequest(JobsAPIError):
|
30
|
-
"""A ``400 Bad Request`` response from the jobs API."""
|
31
|
-
|
32
|
-
status = 400
|
33
|
-
|
34
|
-
|
35
|
-
class JobsAPIForbidden(JobsAPIError):
|
36
|
-
status = 403
|
37
|
-
|
38
|
-
|
39
|
-
class JobsAPINotFound(JobsAPIError):
|
40
|
-
status = 404
|
41
|
-
|
42
|
-
|
43
|
-
class JobsAPIConflict(JobsAPIError):
|
44
|
-
status = 409
|
45
|
-
|
46
|
-
|
47
|
-
class JobsAPIServerError(JobsAPIError):
|
48
|
-
status = 500
|
49
|
-
|
50
|
-
|
51
|
-
class MissingJobArgument(ValueError):
|
52
|
-
"""The `job.args` dict is missing a required key for some funcionality."""
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
class NotFound(KeyError):
|
57
|
-
...
|
58
|
-
|
59
|
-
|
60
|
-
class SubprocessFailed(SubprocessError):
|
61
|
-
"""Subprocess exited with non-zero status during a workflow."""
|
@@ -1,24 +0,0 @@
|
|
1
|
-
from dataclasses import dataclass
|
2
|
-
from datetime import datetime
|
3
|
-
from typing import Literal
|
4
|
-
|
5
|
-
VirtoolFileFormat = Literal[
|
6
|
-
"sam",
|
7
|
-
"bam",
|
8
|
-
"fasta",
|
9
|
-
"fastq",
|
10
|
-
"csv",
|
11
|
-
"tsv",
|
12
|
-
"json",
|
13
|
-
"unknown",
|
14
|
-
]
|
15
|
-
|
16
|
-
|
17
|
-
@dataclass
|
18
|
-
class VirtoolFile:
|
19
|
-
id: int
|
20
|
-
name: str
|
21
|
-
size: int
|
22
|
-
format: VirtoolFileFormat
|
23
|
-
name_on_disk: str = None
|
24
|
-
uploaded_at: datetime = None
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{virtool_workflow-7.0.0 → virtool_workflow-7.1.1}/virtool_workflow/pytest_plugin/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|