Skip to content

Commit a77214b

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 a77214b

File tree

4 files changed

+122
-30
lines changed

4 files changed

+122
-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

tests/integration/tui/test_app.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@
2222

2323
from cylc.flow.cycling.integer import IntegerPoint
2424
from cylc.flow.id import TaskTokens
25+
from cylc.flow.network import get_location
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
)
@@ -630,3 +631,39 @@ async def test_states(flow, scheduler, start, rakiura):
630631
'the task should show as waiting+runahead in the context menu,'
631632
' the task should be marked as flows=None'
632633
)
634+
635+
636+
async def test_incompat_scheduler_version(
637+
one_conf,
638+
flow,
639+
scheduler,
640+
start,
641+
rakiura,
642+
monkeypatch,
643+
):
644+
"""It should handle workflows it cannot subscribe to."""
645+
# make it look like the scheduler is reallllyyyy old
646+
def patched_get_location(*args, **kwargs):
647+
*_, _scheduler_version = get_location(*args, **kwargs)
648+
return *_, '6.11.4'
649+
650+
monkeypatch.setattr(
651+
'cylc.flow.network.client.get_location', patched_get_location
652+
)
653+
654+
schd = scheduler(flow(one_conf, name='one'))
655+
with rakiura(size='80,20') as rk:
656+
async with start(schd):
657+
await schd.update_data_structure()
658+
# wait for the workflow to appear (collapsed)
659+
rk.wait_until_loaded('#spring')
660+
661+
# expand the workflow (subscribes to updates from it)
662+
rk.force_update()
663+
rk.user_input('down', 'right')
664+
665+
# it should be marked as incompatible
666+
rk.compare_screenshot(
667+
'on-load',
668+
'the workflow should be marked as incompatible',
669+
)

tests/unit/tui/test_data.py

Lines changed: 19 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,13 @@ 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

0 commit comments

Comments
 (0)