Skip to content

Commit 90cba23

Browse files
Merge pull request #5769 from oliver-sanders/id_key
undefined
2 parents 97e5e3d + 615013b commit 90cba23

17 files changed

+452
-221
lines changed

changes.d/5769.feat.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Include task messages and workflow port as appropriate in emails configured by "mail events".

cylc/flow/id.py

Lines changed: 34 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class Tokens(dict):
7474
<id: w//c>
7575
>>> Tokens(workflow='w', cycle='c')['job']
7676
77-
# Make a copy (note Tokens are mutable):
77+
# Make a copy (note Tokens are immutable):
7878
>>> tokens.duplicate()
7979
<id: ~u/w//c/t/01>
8080
>>> tokens.duplicate(job='02') # make changes at the same time
@@ -118,9 +118,10 @@ def __init__(
118118
dict.__init__(self, **kwargs)
119119

120120
def __setitem__(self, key, value):
121-
if key not in self._KEYS:
122-
raise ValueError(f'Invalid token: {key}')
123-
dict.__setitem__(self, key, value)
121+
raise Exception('Tokens objects are not mutable')
122+
123+
def update(self, other):
124+
raise Exception('Tokens objects are not mutable')
124125

125126
def __getitem__(self, key):
126127
try:
@@ -151,6 +152,9 @@ def __repr__(self):
151152
id_ = self.id
152153
return f'<id: {id_}>'
153154

155+
def __hash__(self):
156+
return hash(tuple(self.values()))
157+
154158
def __eq__(self, other):
155159
if not isinstance(other, self.__class__):
156160
return False
@@ -159,6 +163,12 @@ def __eq__(self, other):
159163
for key in self._KEYS
160164
)
161165

166+
def __lt__(self, other):
167+
return self.id < other.id
168+
169+
def __gt__(self, other):
170+
return self.id > other.id
171+
162172
def __ne__(self, other):
163173
if not isinstance(other, self.__class__):
164174
return True
@@ -336,62 +346,20 @@ def is_null(self) -> bool:
336346
>>> tokens = Tokens()
337347
>>> tokens.is_null
338348
True
339-
>>> tokens['job_sel'] = 'x'
340-
>>> tokens.is_null
349+
>>> tokens.duplicate(job_sel='x').is_null
341350
True
342-
>>> tokens['job'] = '01'
343-
>>> tokens.is_null
351+
>>> tokens.duplicate(job='01').is_null
344352
False
345353
346354
"""
347355
return not any(
348356
self[key] for key in self._REGULAR_KEYS
349357
)
350358

351-
def update_tokens(
352-
self,
353-
tokens: 'Optional[Tokens]' = None,
354-
**kwargs
355-
) -> None:
356-
"""Update the tokens dictionary.
357-
358-
Similar to dict.update but with an optional Tokens argument.
359-
360-
Examples:
361-
>>> tokens = Tokens('x')
362-
>>> tokens.update_tokens(workflow='y')
363-
>>> tokens
364-
<id: y>
365-
>>> tokens.update_tokens(Tokens('z'))
366-
>>> tokens
367-
<id: z>
368-
>>> tokens.update_tokens(Tokens('a'), cycle='b')
369-
>>> tokens
370-
<id: a//b>
371-
372-
"""
373-
if tokens:
374-
for key, value in tokens.items():
375-
self[key] = value
376-
for key, value in kwargs.items():
377-
self[key] = value
378-
379-
def update(self, other):
380-
"""dict.update.
381-
382-
Example:
383-
>>> tokens = Tokens(workflow='w')
384-
>>> tokens.update({'cycle': 'c'})
385-
>>> tokens.id
386-
'w//c'
387-
388-
"""
389-
return self.update_tokens(**other)
390-
391359
def duplicate(
392360
self,
393-
tokens: 'Optional[Tokens]' = None,
394-
**kwargs
361+
*tokens_list,
362+
**kwargs,
395363
) -> 'Tokens':
396364
"""Duplicate a tokens object.
397365
@@ -408,17 +376,28 @@ def duplicate(
408376
>>> id(tokens1) == id(tokens2)
409377
False
410378
411-
Make a copy and modify it:
379+
Make a copy with a modification:
412380
>>> tokens1.duplicate(cycle='1').id
413381
'~u/w//1'
414382
415-
Original not changed
383+
The Original is not changed:
416384
>>> tokens1.id
417385
'~u/w'
386+
387+
Arguments override in definition order:
388+
>>> Tokens.duplicate(
389+
... tokens1,
390+
... Tokens(cycle='c', task='a', job='01'),
391+
... task='b'
392+
... ).id
393+
'~u/w//c/b/01'
394+
418395
"""
419-
ret = Tokens(self)
420-
ret.update_tokens(tokens, **kwargs)
421-
return ret
396+
_kwargs = {}
397+
for tokens in (self, *tokens_list):
398+
_kwargs.update(tokens)
399+
_kwargs.update(kwargs)
400+
return Tokens(**_kwargs)
422401

423402

424403
# //cycle[:sel][/task[:sel][/job[:sel]]]

cylc/flow/id_cli.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ async def parse_ids_async(
294294

295295
# infer the run number if not specified the ID (and if possible)
296296
if infer_latest_runs:
297-
_infer_latest_runs(*tokens_list, src_path=src_path)
297+
_infer_latest_runs(tokens_list, src_path=src_path)
298298

299299
_validate_number(
300300
*tokens_list,
@@ -409,12 +409,14 @@ def _validate_workflow_ids(*tokens_list, src_path):
409409
detect_both_flow_and_suite(src_path)
410410

411411

412-
def _infer_latest_runs(*tokens_list, src_path):
412+
def _infer_latest_runs(tokens_list, src_path):
413413
for ind, tokens in enumerate(tokens_list):
414414
if ind == 0 and src_path:
415415
# source workflow passed in as a path
416416
continue
417-
tokens['workflow'] = infer_latest_run_from_id(tokens['workflow'])
417+
tokens_list[ind] = tokens.duplicate(
418+
workflow=infer_latest_run_from_id(tokens['workflow'])
419+
)
418420
pass
419421

420422

cylc/flow/scripts/completion_server.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,6 @@ async def list_in_workflow(tokens: Tokens, infer_run=True) -> t.List[str]:
390390
# list possible IDs
391391
cli_detokenise(
392392
tokens.duplicate(
393-
tokens=None,
394393
# use the workflow ID provided on the CLI to allow
395394
# run name inference
396395
workflow=input_workflow,

0 commit comments

Comments
 (0)