Skip to content

Commit 65c7877

Browse files
authored
Merge pull request #6649 from cylc/8.4.x-sync
🤖 Merge 8.4.x-sync into master
2 parents e42cf54 + 0a35341 commit 65c7877

File tree

5 files changed

+159
-116
lines changed

5 files changed

+159
-116
lines changed

changes.d/6647.fix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ensure `cylc message` exceptions are printed to `job.err`.

cylc/flow/task_message.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ def write_messages(workflow, job_id, messages, event_time):
102102
_append_job_status_file(workflow, job_id, event_time, messages)
103103

104104

105-
def send_messages(workflow, job_id, messages, event_time):
105+
def send_messages(
106+
workflow: str, job_id: str, messages: List[list], event_time: str
107+
) -> None:
106108
workflow = os.path.normpath(workflow)
107109
try:
108110
pclient = get_client(workflow)
@@ -111,23 +113,24 @@ def send_messages(workflow, job_id, messages, event_time):
111113
# either the workflow is stopped or the contact file is not present
112114
# on the job host (i.e. comms method is polling)
113115
# eitherway don't try messaging
114-
pass
115-
except Exception:
116-
# Backward communication not possible
116+
return
117+
except Exception as exc:
118+
print(f"{type(exc).__name__}: {exc}", file=sys.stderr)
117119
if cylc.flow.flags.verbosity > 1:
118120
import traceback
119121
traceback.print_exc()
120-
else:
121-
mutation_kwargs = {
122-
'request_string': MUTATION,
123-
'variables': {
124-
'wFlows': [workflow],
125-
'taskJob': job_id,
126-
'eventTime': event_time,
127-
'messages': messages,
128-
}
122+
# cylc message shouldn't fail if the client can't initialize.
123+
return
124+
mutation_kwargs = {
125+
'request_string': MUTATION,
126+
'variables': {
127+
'wFlows': [workflow],
128+
'taskJob': job_id,
129+
'eventTime': event_time,
130+
'messages': messages,
129131
}
130-
pclient('graphql', mutation_kwargs)
132+
}
133+
pclient('graphql', mutation_kwargs)
131134

132135

133136
def _append_job_status_file(workflow, job_id, event_time, messages):
@@ -138,7 +141,8 @@ def _append_job_status_file(workflow, job_id, event_time, messages):
138141
try:
139142
job_status_file = open(job_log_name + '.status', 'a') # noqa: SIM115
140143
# TODO: niceify read/write/appending messages to this file
141-
except IOError:
144+
except IOError as exc:
145+
print(f"{type(exc).__name__}: {exc}", file=sys.stderr)
142146
if cylc.flow.flags.verbosity > 1:
143147
import traceback
144148
traceback.print_exc()

tests/functional/cylc-message/00-ssh/flow.cylc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
[runtime]
1010
[[t0]]
1111
script = """
12-
cylc broadcast "${CYLC_WORKFLOW_ID}" '--name=t1' '--set=script="true"'
12+
cylc broadcast "${CYLC_WORKFLOW_ID}" --name=t1 --set=script="true"
1313
"""
1414
platform = {{ environ['CYLC_TEST_PLATFORM'] }}
1515
[[t1]]

tests/unit/parsec/test_fileparse.py

Lines changed: 100 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -46,94 +46,6 @@
4646
)
4747

4848

49-
def get_multiline():
50-
"""Data provider for multiline tests. Returned values are:
51-
52-
file_lines, value, index, maxline, error_message, expected
53-
"""
54-
r = [
55-
([], "'''single line'''", 0, 0, None, ("'''single line'''", 0)),
56-
(
57-
["'''single line"],
58-
"'''single line", # missing closing quote
59-
0,
60-
0,
61-
FileParseError,
62-
{
63-
'reason': 'Multiline string not closed',
64-
'line': "'''single line"
65-
}
66-
),
67-
(
68-
["'''", "single line"],
69-
"'''\n '''single line", # missing closing quote
70-
0,
71-
0,
72-
FileParseError,
73-
{
74-
'reason': 'Invalid line',
75-
'line': "'''"
76-
}
77-
),
78-
(
79-
["", "another value"], # multiline, but we forgot to close quotes
80-
"'''a\n#b",
81-
0,
82-
1,
83-
FileParseError,
84-
{
85-
'reason': "Multiline string not closed"
86-
}
87-
),
88-
(
89-
["", "c'''"],
90-
"'''a\n#b",
91-
0,
92-
1,
93-
None,
94-
("'''a\n#b\nc'''", 1)
95-
),
96-
(
97-
["", "c'''"], # multiline, but we forgot to close quotes
98-
"'''a\n#b",
99-
0,
100-
10000, # no error. The function will stop before on the quotes
101-
None,
102-
("'''a\n#b\nc'''", 1)
103-
),
104-
(
105-
["", "c", "hello", ""], # quotes out of balance
106-
"'''a\n#b",
107-
0,
108-
3,
109-
FileParseError,
110-
{
111-
'reason': "Multiline string not closed"
112-
}
113-
),
114-
(
115-
["", "c", "hello", ""],
116-
"'''a\n#b",
117-
0,
118-
4, # one too many
119-
IndexError,
120-
None
121-
),
122-
(
123-
["", "a'''c", "hello", ""],
124-
"'''a\n#b",
125-
0,
126-
3,
127-
FileParseError,
128-
{
129-
'reason': 'Invalid line',
130-
'line': "a'''c"
131-
}
132-
)
133-
]
134-
return r
135-
136-
13749
def test_file_parse_error():
13850
error = FileParseError(reason="No reason")
13951
assert str(error) == "No reason"
@@ -267,18 +179,106 @@ def test_addict_replace_value_1():
267179
assert cfg['scheduling']['graph']['team']['graph'] == ['ABC', 'test']
268180

269181

270-
def test_multiline():
271-
for flines, value, index, maxline, exc, expected in get_multiline():
272-
if exc is not None:
273-
with pytest.raises(exc) as cm:
274-
multiline(flines, value, index, maxline)
275-
if isinstance(cm.value, FileParseError):
276-
exc = cm.value
277-
for key, attr in expected.items():
278-
assert getattr(exc, key) == attr
279-
else:
280-
r = multiline(flines, value, index, maxline)
281-
assert r == expected
182+
@pytest.mark.parametrize(
183+
'flines, value, index, maxline, exc, expected',
184+
(
185+
param(
186+
[],
187+
"'''single line'''",
188+
0,
189+
0,
190+
None,
191+
("'''single line'''", 0),
192+
id='single-line',
193+
),
194+
param(
195+
["'''single line"],
196+
"'''single line", # missing closing quote
197+
0,
198+
0,
199+
FileParseError,
200+
{
201+
'reason': 'Multiline string not closed',
202+
'line': "'''single line",
203+
},
204+
id='missing-closing-quote',
205+
),
206+
param(
207+
["'''", "single line"],
208+
"'''\n '''single line", # missing closing quote
209+
0,
210+
0,
211+
FileParseError,
212+
{'reason': 'Invalid line', 'line': "'''"},
213+
id='missing-closing-quote2',
214+
),
215+
param(
216+
["", "another value"], # multiline, but we forgot to close quotes
217+
"'''a\n#b",
218+
0,
219+
1,
220+
FileParseError,
221+
{'reason': "Multiline string not closed"},
222+
id='multiline-missing-closing-quote',
223+
),
224+
param(
225+
["", "c'''"],
226+
"'''a\n#b",
227+
0,
228+
1,
229+
None,
230+
("'''a\n#b\nc'''", 1),
231+
id='good-path',
232+
),
233+
param(
234+
["", "c'''"], # multiline, but we forgot to close quotes
235+
"'''a\n#b",
236+
0,
237+
10000, # no error. The function will stop before on the quotes
238+
None,
239+
("'''a\n#b\nc'''", 1),
240+
id='multiline-missing-closing-quote2',
241+
),
242+
param(
243+
["", "c", "hello", ""], # quotes out of balance
244+
"'''a\n#b",
245+
0,
246+
3,
247+
FileParseError,
248+
{'reason': "Multiline string not closed"},
249+
id='unbalanced-quotes',
250+
),
251+
param(
252+
["", "c", "hello", ""],
253+
"'''a\n#b",
254+
0,
255+
4, # one too many
256+
IndexError,
257+
None,
258+
id='one-too-many',
259+
),
260+
param(
261+
["", "a'''c", "hello", ""],
262+
"'''a\n#b",
263+
0,
264+
3,
265+
FileParseError,
266+
{'reason': 'Invalid line', 'line': "a'''c"},
267+
id='invalid-quotes',
268+
),
269+
),
270+
)
271+
def test_multiline(flines, value, index, maxline, exc, expected):
272+
if exc is not None:
273+
with pytest.raises(exc) as cm:
274+
multiline(flines, value, index, maxline)
275+
if isinstance(cm.value, FileParseError):
276+
exc = cm.value
277+
for key, attr in expected.items():
278+
assert getattr(exc, key) == attr
279+
else:
280+
r = multiline(flines, value, index, maxline)
281+
assert r == expected
282282

283283

284284
def test_read_and_proc_no_template_engine():

tests/unit/test_task_message.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# THIS FILE IS PART OF THE CYLC WORKFLOW ENGINE.
2+
# Copyright (C) NIWA & British Crown (Met Office) & Contributors.
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License
15+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
from socket import gaierror
18+
19+
import pytest
20+
21+
from cylc.flow.task_message import send_messages
22+
23+
24+
def test_send_messages_err(
25+
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture
26+
):
27+
"""If an error occurs while initializing the client, it should be printed.
28+
"""
29+
exc_msg = 'Relic malfunction detected'
30+
31+
def mock_get_client(*a, **k):
32+
raise gaierror(-2, exc_msg)
33+
34+
monkeypatch.setattr('cylc.flow.task_message.get_client', mock_get_client)
35+
send_messages(
36+
'arasaka', '1/v/01', [['INFO', 'silverhand']], '2077-01-01T00:00:00Z'
37+
)
38+
assert f"gaierror: [Errno -2] {exc_msg}" in capsys.readouterr().err

0 commit comments

Comments
 (0)