Skip to content

Commit 75f89b3

Browse files
committed
Define simple models for job messages.
1 parent 337ac8d commit 75f89b3

File tree

7 files changed

+185
-70
lines changed

7 files changed

+185
-70
lines changed

client/src/api/schema/schema.ts

+55-1
Original file line numberDiff line numberDiff line change
@@ -8917,6 +8917,22 @@ export interface components {
89178917
*/
89188918
src: components["schemas"]["DataItemSourceType"];
89198919
};
8920+
/** ExitCodeJobMessage */
8921+
ExitCodeJobMessage: {
8922+
/** Code Desc */
8923+
code_desc: string | null;
8924+
/** Desc */
8925+
desc: string | null;
8926+
/** Error Level */
8927+
error_level: number;
8928+
/** Exit Code */
8929+
exit_code: number;
8930+
/**
8931+
* Type
8932+
* @constant
8933+
*/
8934+
type: "exit_code";
8935+
};
89208936
/** ExportHistoryArchivePayload */
89218937
ExportHistoryArchivePayload: {
89228938
/**
@@ -14224,6 +14240,20 @@ export interface components {
1422414240
*/
1422514241
source: components["schemas"]["DatasetSourceType"];
1422614242
};
14243+
/** MaxDiscoveredFilesJobMessage */
14244+
MaxDiscoveredFilesJobMessage: {
14245+
/** Code Desc */
14246+
code_desc: string | null;
14247+
/** Desc */
14248+
desc: string | null;
14249+
/** Error Level */
14250+
error_level: number;
14251+
/**
14252+
* Type
14253+
* @constant
14254+
*/
14255+
type: "max_discovered_files";
14256+
};
1422714257
/** MessageExceptionModel */
1422814258
MessageExceptionModel: {
1422914259
/** Err Code */
@@ -15537,6 +15567,24 @@ export interface components {
1553715567
/** Workflow */
1553815568
workflow: string;
1553915569
};
15570+
/** RegexJobMessage */
15571+
RegexJobMessage: {
15572+
/** Code Desc */
15573+
code_desc: string | null;
15574+
/** Desc */
15575+
desc: string | null;
15576+
/** Error Level */
15577+
error_level: number;
15578+
/** Match */
15579+
match: string | null;
15580+
/** Stream */
15581+
stream: string | null;
15582+
/**
15583+
* Type
15584+
* @constant
15585+
*/
15586+
type: "regex";
15587+
};
1554015588
/** ReloadFeedback */
1554115589
ReloadFeedback: {
1554215590
/** Failed */
@@ -16218,7 +16266,13 @@ export interface components {
1621816266
* Job Messages
1621916267
* @description List with additional information and possible reasons for a failed job.
1622016268
*/
16221-
job_messages?: unknown[] | null;
16269+
job_messages?:
16270+
| (
16271+
| components["schemas"]["ExitCodeJobMessage"]
16272+
| components["schemas"]["RegexJobMessage"]
16273+
| components["schemas"]["MaxDiscoveredFilesJobMessage"]
16274+
)[]
16275+
| null;
1622216276
/**
1622316277
* Job Metrics
1622416278
* @description Collections of metrics provided by `JobInstrumenter` plugins on a particular job. Only administrators can see these metrics.

client/src/components/DatasetInformation/DatasetError.test.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { createPinia } from "pinia";
55
import { getLocalVue } from "tests/jest/helpers";
66

77
import { HttpResponse, useServerMock } from "@/api/client/__mocks__";
8+
import { type components } from "@/api/schema";
89
import { useUserStore } from "@/stores/userStore";
910

1011
import DatasetError from "./DatasetError.vue";
@@ -15,8 +16,26 @@ const DATASET_ID = "dataset_id";
1516

1617
const { server, http } = useServerMock();
1718

19+
type RegexJobMessage = components["schemas"]["RegexJobMessage"];
20+
1821
async function montDatasetError(has_duplicate_inputs = true, has_empty_inputs = true, user_email = "") {
1922
const pinia = createPinia();
23+
const error1: RegexJobMessage = {
24+
desc: "message_1",
25+
code_desc: null,
26+
stream: null,
27+
match: null,
28+
type: "regex",
29+
error_level: 1,
30+
};
31+
const error2: RegexJobMessage = {
32+
desc: "message_2",
33+
code_desc: null,
34+
stream: null,
35+
match: null,
36+
type: "regex",
37+
error_level: 1,
38+
};
2039

2140
server.use(
2241
http.get("/api/datasets/{dataset_id}", ({ response }) => {
@@ -35,7 +54,7 @@ async function montDatasetError(has_duplicate_inputs = true, has_empty_inputs =
3554
tool_id: "tool_id",
3655
tool_stderr: "tool_stderr",
3756
job_stderr: "job_stderr",
38-
job_messages: [{ desc: "message_1" }, { desc: "message_2" }],
57+
job_messages: [error1, error2],
3958
user_email,
4059
create_time: "2021-01-01T00:00:00",
4160
update_time: "2021-01-01T00:00:00",

lib/galaxy/metadata/set_metadata.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
from functools import partial
2121
from pathlib import Path
2222
from typing import (
23-
Any,
24-
Dict,
2523
List,
2624
Optional,
2725
)
@@ -63,6 +61,7 @@
6361
ObjectStore,
6462
)
6563
from galaxy.tool_util.output_checker import (
64+
AnyJobMessage,
6665
check_output,
6766
DETECTED_JOB_STATE,
6867
)
@@ -224,7 +223,7 @@ def set_meta(new_dataset_instance, file_dict):
224223

225224
export_store = None
226225
final_job_state = Job.states.OK
227-
job_messages: List[Dict[str, Any]] = []
226+
job_messages: List[AnyJobMessage] = []
228227
if extended_metadata_collection:
229228
tool_dict = metadata_params["tool"]
230229
stdio_exit_code_dicts, stdio_regex_dicts = tool_dict["stdio_exit_codes"], tool_dict["stdio_regexes"]

lib/galaxy/model/__init__.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@
190190
from galaxy.security import get_permitted_actions
191191
from galaxy.security.idencoding import IdEncodingHelper
192192
from galaxy.security.validate_user_input import validate_password_str
193+
from galaxy.tool_util.output_checker import AnyJobMessage
193194
from galaxy.util import (
194195
directory_hash_id,
195196
enum_values,
@@ -562,6 +563,7 @@ def cached_id(galaxy_model_object):
562563

563564

564565
class JobLike:
566+
job_messages: Mapped[Optional[List[AnyJobMessage]]]
565567
MAX_NUMERIC = 10 ** (JOB_METRIC_PRECISION - JOB_METRIC_SCALE) - 1
566568

567569
def _init_metrics(self):
@@ -596,7 +598,14 @@ def metrics(self):
596598
# TODO: Make iterable, concatenate with chain
597599
return self.text_metrics + self.numeric_metrics
598600

599-
def set_streams(self, tool_stdout, tool_stderr, job_stdout=None, job_stderr=None, job_messages=None):
601+
def set_streams(
602+
self,
603+
tool_stdout,
604+
tool_stderr,
605+
job_stdout=None,
606+
job_stderr=None,
607+
job_messages: Optional[List[AnyJobMessage]] = None,
608+
):
600609
def shrink_and_unicodify(what, stream):
601610
if stream and len(stream) > galaxy.util.DATABASE_MAX_STRING_SIZE:
602611
log.info(
@@ -621,7 +630,7 @@ def shrink_and_unicodify(what, stream):
621630
self.job_stderr = None
622631

623632
if job_messages is not None:
624-
self.job_messages = job_messages
633+
self.job_messages = cast(Optional[List[AnyJobMessage]], job_messages)
625634

626635
def log_str(self):
627636
extra = ""
@@ -1485,7 +1494,7 @@ class Job(Base, JobLike, UsesCreateAndUpdateTime, Dictifiable, Serializable):
14851494
copied_from_job_id: Mapped[Optional[int]]
14861495
command_line: Mapped[Optional[str]] = mapped_column(TEXT)
14871496
dependencies: Mapped[Optional[bytes]] = mapped_column(MutableJSONType)
1488-
job_messages: Mapped[Optional[bytes]] = mapped_column(MutableJSONType)
1497+
job_messages: Mapped[Optional[List[AnyJobMessage]]] = mapped_column(MutableJSONType)
14891498
param_filename: Mapped[Optional[str]] = mapped_column(String(1024))
14901499
runner_name: Mapped[Optional[str]] = mapped_column(String(255))
14911500
job_stdout: Mapped[Optional[str]] = mapped_column(TEXT)
@@ -2260,7 +2269,7 @@ class Task(Base, JobLike, RepresentById):
22602269
tool_stdout: Mapped[Optional[str]] = mapped_column(TEXT)
22612270
tool_stderr: Mapped[Optional[str]] = mapped_column(TEXT)
22622271
exit_code: Mapped[Optional[int]]
2263-
job_messages: Mapped[Optional[bytes]] = mapped_column(MutableJSONType)
2272+
job_messages: Mapped[Optional[List[AnyJobMessage]]] = mapped_column(MutableJSONType)
22642273
info: Mapped[Optional[str]] = mapped_column(TrimmedString(255))
22652274
traceback: Mapped[Optional[str]] = mapped_column(TEXT)
22662275
job_id: Mapped[int] = mapped_column(ForeignKey("job.id"), index=True)

lib/galaxy/schema/jobs.py

-52
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
DataItemSourceType,
2424
EncodedDataItemSourceId,
2525
EncodedJobParameterHistoryItem,
26-
JobMetricCollection,
2726
JobState,
2827
JobSummary,
2928
Model,
@@ -248,54 +247,3 @@ class JobDisplayParametersSummary(Model):
248247
title="Outputs",
249248
description="Dictionary mapping all the tool outputs (by name) with the corresponding dataset information in a nested format.",
250249
)
251-
252-
253-
class ShowFullJobResponse(EncodedJobDetails):
254-
tool_stdout: Optional[str] = Field(
255-
default=None,
256-
title="Tool Standard Output",
257-
description="The captured standard output of the tool executed by the job.",
258-
)
259-
tool_stderr: Optional[str] = Field(
260-
default=None,
261-
title="Tool Standard Error",
262-
description="The captured standard error of the tool executed by the job.",
263-
)
264-
job_stdout: Optional[str] = Field(
265-
default=None,
266-
title="Job Standard Output",
267-
description="The captured standard output of the job execution.",
268-
)
269-
job_stderr: Optional[str] = Field(
270-
default=None,
271-
title="Job Standard Error",
272-
description="The captured standard error of the job execution.",
273-
)
274-
stdout: Optional[str] = Field( # Redundant? it seems to be (tool_stdout + "\n" + job_stdout)
275-
default=None,
276-
title="Standard Output",
277-
description="Combined tool and job standard output streams.",
278-
)
279-
stderr: Optional[str] = Field( # Redundant? it seems to be (tool_stderr + "\n" + job_stderr)
280-
default=None,
281-
title="Standard Error",
282-
description="Combined tool and job standard error streams.",
283-
)
284-
job_messages: Optional[List[Any]] = Field(
285-
default=None,
286-
title="Job Messages",
287-
description="List with additional information and possible reasons for a failed job.",
288-
)
289-
dependencies: Optional[List[Any]] = Field(
290-
default=None,
291-
title="Job dependencies",
292-
description="The dependencies of the job.",
293-
)
294-
job_metrics: Optional[JobMetricCollection] = Field(
295-
default=None,
296-
title="Job Metrics",
297-
description=(
298-
"Collections of metrics provided by `JobInstrumenter` plugins on a particular job. "
299-
"Only administrators can see these metrics."
300-
),
301-
)

lib/galaxy/tool_util/output_checker.py

+40-8
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@
22
from enum import Enum
33
from logging import getLogger
44
from typing import (
5-
Any,
6-
Dict,
75
List,
6+
Optional,
87
Tuple,
98
TYPE_CHECKING,
9+
Union,
10+
)
11+
12+
from typing_extensions import (
13+
TypedDict,
14+
Literal,
1015
)
1116

1217
from galaxy.tool_util.parser.stdio import StdioErrorLevel
@@ -29,8 +34,35 @@ class DETECTED_JOB_STATE(str, Enum):
2934
ERROR_PEEK_SIZE = 2000
3035

3136

37+
JobMessageTypeLiteral = Literal["regex", "exit_code", "max_discovered_files"]
38+
39+
40+
class JobMessage(TypedDict):
41+
desc: Optional[str]
42+
code_desc: Optional[str]
43+
error_level: float # Literal[0, 1, 1.1, 2, 3, 4] - mypy doesn't like literal floats.
44+
45+
46+
class RegexJobMessage(JobMessage):
47+
type: Literal["regex"]
48+
stream: Optional[str]
49+
match: Optional[str]
50+
51+
52+
class ExitCodeJobMessage(JobMessage):
53+
type: Literal["exit_code"]
54+
exit_code: int
55+
56+
57+
class MaxDiscoveredFilesJobMessage(JobMessage):
58+
type: Literal["max_discovered_files"]
59+
60+
61+
AnyJobMessage = Union[ExitCodeJobMessage, RegexJobMessage, MaxDiscoveredFilesJobMessage]
62+
63+
3264
def check_output_regex(
33-
regex: "ToolStdioRegex", stream: str, stream_name: str, job_messages: List[Dict[str, Any]], max_error_level: int
65+
regex: "ToolStdioRegex", stream: str, stream_name: str, job_messages: List[AnyJobMessage], max_error_level: int
3466
) -> int:
3567
"""
3668
check a single regex against a stream
@@ -55,10 +87,10 @@ def check_output(
5587
stdout: str,
5688
stderr: str,
5789
tool_exit_code: int,
58-
) -> Tuple[str, str, str, List[Dict[str, Any]]]:
90+
) -> Tuple[str, str, str, List[AnyJobMessage]]:
5991
"""
6092
Check the output of a tool - given the stdout, stderr, and the tool's
61-
exit code, return DETECTED_JOB_STATE.OK if the tool exited succesfully or
93+
exit code, return DETECTED_JOB_STATE.OK if the tool exited successfully or
6294
error type otherwise. No exceptions should be thrown. If this code encounters
6395
an exception, it returns OK so that the workflow can continue;
6496
otherwise, a bug in this code could halt workflow progress.
@@ -77,7 +109,7 @@ def check_output(
77109
# messages are added it the order of detection
78110

79111
# If job is failed, track why.
80-
job_messages = []
112+
job_messages: List[AnyJobMessage] = []
81113

82114
try:
83115
# Check exit codes and match regular expressions against stdout and
@@ -103,7 +135,7 @@ def check_output(
103135
if None is code_desc:
104136
code_desc = ""
105137
desc = f"{StdioErrorLevel.desc(stdio_exit_code.error_level)}: Exit code {tool_exit_code} ({code_desc})"
106-
reason = {
138+
reason: ExitCodeJobMessage = {
107139
"type": "exit_code",
108140
"desc": desc,
109141
"exit_code": tool_exit_code,
@@ -168,7 +200,7 @@ def check_output(
168200
return state, stdout, stderr, job_messages
169201

170202

171-
def __regex_err_msg(match: re.Match, stream: str, regex: "ToolStdioRegex"):
203+
def __regex_err_msg(match: re.Match, stream: str, regex: "ToolStdioRegex") -> RegexJobMessage:
172204
"""
173205
Return a message about the match on tool output using the given
174206
ToolStdioRegex regex object. The regex_match is a MatchObject

0 commit comments

Comments
 (0)