Skip to content

Commit 18063bc

Browse files
tui: add estimatedFinishTime field
* Add new field. * Abstract back-compat logic to make it easier to add and maintain support for older scheduler versions.
1 parent f551099 commit 18063bc

File tree

5 files changed

+145
-30
lines changed

5 files changed

+145
-30
lines changed

cylc/flow/tui/data.py

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
from functools import partial
1818
from subprocess import Popen, PIPE
1919

20+
from packaging.specifiers import SpecifierSet
21+
2022
from cylc.flow.exceptions import (
2123
ClientError,
2224
ClientTimeout,
@@ -31,7 +33,7 @@
3133

3234
# the GraphQL query which Tui runs against each of the workflows
3335
# is is subscribed to
34-
QUERY = '''
36+
_QUERY = '''
3537
query cli($taskStates: [String]){
3638
workflows {
3739
id
@@ -63,6 +65,7 @@
6365
jobRunnerName
6466
jobId
6567
startedTime
68+
estimatedFinishTime
6669
finishedTime
6770
}
6871
task {
@@ -105,24 +108,32 @@
105108
}
106109
'''
107110

108-
# BACK COMPAT: isRetry, isWallclock, isXtriggered
109-
# url:
110-
# https://github.com/cylc/cylc-flow/pull/6671
111-
# from:
112-
# Cylc 8.4.0
113-
# to:
114-
# Cylc 8.5.0
115-
# remove at:
116-
# Cylc 8.7.0
117-
# * set min Cylc version requirement in the updater scan pipeline
118-
# * replace `data.get('isRetry', False)` with 1data['isRetry']`, etc
119-
COMPAT_QUERY = (
120-
QUERY
121-
.replace('isRetry', '')
122-
.replace('isWallclock', '')
123-
.replace('isXtriggered', '')
111+
112+
# graphql queries for every version of Cylc that Tui supports:
113+
_COMPAT_QUERIES = (
114+
(
115+
# regular query for current and future scheduler versions
116+
SpecifierSet('>=8.6.dev'), _QUERY
117+
),
118+
(
119+
# BACK COMPAT
120+
# estimatedFinishTime field added at 8.6.0
121+
SpecifierSet('>=8.5.0, <8.6'),
122+
_QUERY.replace('estimatedFinishTime', '')
123+
),
124+
(
125+
# BACK COMPAT
126+
# isRetry, isWallclock and isXtriggered fields added at 8.5.0
127+
SpecifierSet('>=8, <8.5'),
128+
_QUERY
129+
.replace('isRetry', '')
130+
.replace('isWallclock', '')
131+
.replace('isXtriggered', '')
132+
.replace('estimatedFinishTime', ''),
133+
),
124134
)
125135

136+
126137
# the list of mutations we can call on a running scheduler
127138
MUTATIONS = {
128139
'workflow': [
@@ -160,6 +171,32 @@
160171
}
161172

162173

174+
class VersionIncompat(Exception):
175+
...
176+
177+
178+
def get_query(scheduler_version: str) -> str:
179+
"""Return a GraphQL query compatibile with the provided scheduler version.
180+
181+
Args:
182+
scheduler_version: The version of the scheduler we are connecting to.
183+
184+
Returns:
185+
The GraphQL query string.
186+
187+
Raises:
188+
VersionIncompat:
189+
If the scheduler version is not supported.
190+
191+
"""
192+
for query_version_range, query in _COMPAT_QUERIES:
193+
if scheduler_version in query_version_range:
194+
return query
195+
raise VersionIncompat(
196+
f'Scheduler version {scheduler_version} is not supported'
197+
)
198+
199+
163200
def cli_cmd(*cmd, ret=False):
164201
"""Issue a CLI command.
165202

cylc/flow/tui/updater.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
from multiprocessing import Queue
2929
from time import time
3030

31-
from packaging.version import parse as parse_version
3231
from zmq.error import ZMQError
3332

3433
from cylc.flow.exceptions import (
@@ -49,7 +48,8 @@
4948
TASK_STATUSES_ORDERED,
5049
)
5150
from cylc.flow.tui.data import (
52-
QUERY, COMPAT_QUERY
51+
VersionIncompat,
52+
get_query,
5353
)
5454
from cylc.flow.tui.util import (
5555
NaturalSort,
@@ -271,15 +271,9 @@ async def _update_workflow(self, w_id, client, data):
271271
return
272272

273273
try:
274-
if (
275-
parse_version(client.scheduler_version)
276-
< parse_version('8.5.0.dev')
277-
):
278-
# BACK COMPAT: isRetry, isWallclock, isXtriggered
279-
# (see comment in cylc.flow.tui.data)
280-
query = COMPAT_QUERY
281-
else:
282-
query = QUERY
274+
# get a graphql query compatible with this workflow
275+
query = get_query(client.scheduler_version)
276+
283277
# fetch the data from the workflow
284278
workflow_update = await client.async_request(
285279
'graphql',
@@ -318,6 +312,12 @@ async def _update_workflow(self, w_id, client, data):
318312
'Timeout communicating with workflow.'
319313
' Use "--comms-timeout" to increase the timeout',
320314
)
315+
except VersionIncompat as exc:
316+
set_message(
317+
data,
318+
w_id,
319+
str(exc),
320+
)
321321
except (CylcError, ZMQError) as exc:
322322
# something went wrong :(
323323
# remove the client on any error, we'll reconnect next time
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<pre><span style="color:#ff0000;background:#e5e5e5;font-weight:bold">C</span><span style="color:#ffff00;background:#e5e5e5;font-weight:bold">y</span><span style="color:#00ff00;background:#e5e5e5;font-weight:bold">l</span><span style="color:#5c5cff;background:#e5e5e5;font-weight:bold">c</span><span style="color:#000000;background:#e5e5e5;font-weight:bold"> Tui </span><span style="color:#7f7f7f;background:#e5e5e5">1.2.3</span><span style="color:#7f7f7f;background:#e5e5e5"> workflows filtered (</span><span style="color:#7f7f7f;background:#e5e5e5;font-weight:bold">W</span><span style="color:#7f7f7f;background:#e5e5e5"> - edit, </span><span style="color:#7f7f7f;background:#e5e5e5;font-weight:bold">E</span><span style="color:#7f7f7f;background:#e5e5e5"> - reset)</span><span style="color:#000000;background:#e5e5e5"> </span>
2+
<span style="color:#000000;background:#e5e5e5"> </span>
3+
<span style="color:#000000;background:#e5e5e5">-</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">~cylc </span>
4+
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#e5e5e5;background:#000000">-</span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5;font-weight:bold">one</span><span style="color:#000000;background:#e5e5e5"> - </span><span style="color:#cdcd00;background:#e5e5e5">paused</span><span style="color:#000000;background:#e5e5e5"> </span>
5+
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">Error - Scheduler version 6.11.4 is not supported </span>
6+
<span style="color:#000000;background:#e5e5e5"> </span>
7+
<span style="color:#000000;background:#e5e5e5"> </span>
8+
<span style="color:#000000;background:#e5e5e5"> </span>
9+
<span style="color:#000000;background:#e5e5e5"> </span>
10+
<span style="color:#000000;background:#e5e5e5"> </span>
11+
<span style="color:#000000;background:#e5e5e5"> </span>
12+
<span style="color:#000000;background:#e5e5e5"> </span>
13+
<span style="color:#000000;background:#e5e5e5"> </span>
14+
<span style="color:#000000;background:#e5e5e5"> </span>
15+
<span style="color:#000000;background:#e5e5e5"> </span>
16+
<span style="color:#000000;background:#e5e5e5"> </span>
17+
<span style="color:#000000;background:#e5e5e5"> </span>
18+
<span style="color:#000000;background:#e5e5e5"> </span>
19+
<span style="color:#ffffff;background:#0000ee">quit: </span><span style="color:#00ffff;background:#0000ee">q</span><span style="color:#ffffff;background:#0000ee"> help: </span><span style="color:#00ffff;background:#0000ee">h</span><span style="color:#ffffff;background:#0000ee"> context: </span><span style="color:#00ffff;background:#0000ee">enter</span><span style="color:#ffffff;background:#0000ee"> tree: </span><span style="color:#00ffff;background:#0000ee">- ← + → </span><span style="color:#ffffff;background:#0000ee"> navigation: </span><span style="color:#00ffff;background:#0000ee">↑ ↓ ↥ ↧ Home End </span><span style="color:#ffffff;background:#0000ee"> </span>
20+
<span style="color:#ffffff;background:#0000ee">filter tasks: </span><span style="color:#00ffff;background:#0000ee">T f s r R </span><span style="color:#ffffff;background:#0000ee"> filter workflows: </span><span style="color:#00ffff;background:#0000ee">W E p </span><span style="color:#ffffff;background:#0000ee"> </span>
21+
</pre>

tests/integration/tui/test_app.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,21 @@
2020
import pytest
2121
import urwid
2222

23+
from cylc.flow import __version__
2324
from cylc.flow.cycling.integer import IntegerPoint
2425
from cylc.flow.id import TaskTokens
2526
from cylc.flow.task_outputs import TASK_OUTPUT_SUCCEEDED
2627
from cylc.flow.task_state import (
2728
TASK_STATUS_EXPIRED,
2829
TASK_STATUS_FAILED,
2930
TASK_STATUS_RUNNING,
30-
TASK_STATUS_SUBMITTED,
3131
TASK_STATUS_SUBMIT_FAILED,
32+
TASK_STATUS_SUBMITTED,
3233
TASK_STATUS_SUCCEEDED,
3334
TASK_STATUS_WAITING,
3435
)
3536
from cylc.flow.tui.util import MODIFIER_ATTR_MAPPING
37+
from cylc.flow.workflow_files import get_contact_file_path
3638
from cylc.flow.workflow_status import StopMode
3739

3840

@@ -630,3 +632,37 @@ async def test_states(flow, scheduler, start, rakiura):
630632
'the task should show as waiting+runahead in the context menu,'
631633
' the task should be marked as flows=None'
632634
)
635+
636+
637+
async def test_incompat_scheduler_version(
638+
one_conf,
639+
flow,
640+
scheduler,
641+
start,
642+
rakiura
643+
):
644+
"""It should handle workflows it cannot subscribe to."""
645+
schd = scheduler(flow(one_conf, name='one'))
646+
647+
async with start(schd):
648+
# make it look like the scheduler is reallllyyyy old
649+
contact = get_contact_file_path(schd.workflow)
650+
with open(contact, 'r') as contact_file:
651+
contact_lines = contact_file.read().replace(__version__, '6.11.4')
652+
with open(contact, 'w') as contact_file:
653+
contact_file.write(''.join(contact_lines))
654+
655+
with rakiura(size='80,20') as rk:
656+
await schd.update_data_structure()
657+
# wait for the workflow to appear (collapsed)
658+
rk.wait_until_loaded('#spring')
659+
660+
# expand the workflow (subscribes to updates from it)
661+
rk.force_update()
662+
rk.user_input('down', 'right')
663+
664+
# it should be marked as incompatible
665+
rk.compare_screenshot(
666+
'on-load',
667+
'the workflow should be marked as incompatible',
668+
)

tests/unit/tui/test_data.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@
1515
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

1717

18+
import pytest
19+
20+
from cylc.flow import __version__
1821
import cylc.flow.tui.data
19-
from cylc.flow.tui.data import generate_mutation
22+
from cylc.flow.tui.data import (
23+
_QUERY,
24+
VersionIncompat,
25+
generate_mutation,
26+
get_query,
27+
)
2028

2129

2230
def test_generate_mutation(monkeypatch):
@@ -36,3 +44,16 @@ def test_generate_mutation(monkeypatch):
3644
}
3745
}
3846
'''
47+
48+
49+
def test_query_compat():
50+
"""It should return a query or raise an exception."""
51+
# old version - unsupported
52+
with pytest.raises(VersionIncompat, match='6.11.4'):
53+
get_query('6.11.4')
54+
55+
# current version - supported
56+
assert get_query(__version__) == _QUERY
57+
58+
# future version - supported
59+
assert get_query('9.0.0') == _QUERY

0 commit comments

Comments
 (0)