Skip to content

Commit 9401a1a

Browse files
committed
Merge 'upstream/8.6.x' into paused-trigger-platform
2 parents dfcdcab + 68c5749 commit 9401a1a

File tree

18 files changed

+354
-44
lines changed

18 files changed

+354
-44
lines changed

changes.d/6261.feat.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow a workflow to be restarted by `cylc vr` even if there are no changes to reinstall.

changes.d/7007.fix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix bug where cylc play --pause would resume a paused workflow.

changes.d/7041.fix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Removed spurious "tasks not killable" warnings for `cylc stop --kill`.

changes.d/7054.fix.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix an issue where job submission could fail with the message
2+
`No such file or directory` when tasks were triggered while the workflow was
3+
paused.

cylc/flow/cfgspec/workflow.py

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ def get_script_common_text(this: str, example: Optional[str] = None):
488488
default = None
489489
elif item.endswith("handlers"):
490490
desc = desc + '\n\n' + dedent(f'''
491-
Examples:
491+
.. rubric:: Examples:
492492
493493
.. code-block:: cylc
494494
@@ -548,7 +548,7 @@ def get_script_common_text(this: str, example: Optional[str] = None):
548548
Conf('<parameter>', VDR.V_PARAMETER_LIST, desc='''
549549
A custom parameter to use in a workflow.
550550
551-
Examples:
551+
.. rubric:: Examples:
552552
553553
- ``run = control, test1, test2``
554554
- ``mem = 1..5`` (equivalent to ``1, 2, 3, 4, 5``).
@@ -623,6 +623,24 @@ def get_script_common_text(this: str, example: Optional[str] = None):
623623
624624
This item can be overridden on the command line using
625625
``cylc play --initial-cycle-point`` or ``--icp``.
626+
627+
.. rubric:: Examples:
628+
629+
``2000-01-01T00:00Z``
630+
The 1st of January at midnight.
631+
``2000``
632+
Shorthand for ``2000-01-01T00:00``.
633+
``now``
634+
The exact time when the workflow is started. By default this is
635+
to the minute, e.g: ``2001-02-03T12:34``.
636+
``previous(T-00)``
637+
The hour when the workflow was started. I.e, The time when the
638+
workflow is started with the minute cut off.
639+
``previous(T00)``
640+
The day when the workflow was started. I.e, the time when the
641+
workflow was started with the hour and minute cut off.
642+
``next(T00)``
643+
The day after the workflow was started (at midnight).
626644
''')
627645
# NOTE: final cycle point is not a V_CYCLE_POINT to allow expressions
628646
# such as '+P1Y' (relative to initial cycle point)
@@ -635,11 +653,16 @@ def get_script_common_text(this: str, example: Optional[str] = None):
635653
This item can be overridden on the command line using
636654
``cylc play --final-cycle-point`` or ``--fcp``.
637655
638-
Examples:
656+
.. rubric:: Examples:
639657
640-
- ``2000`` - Shorthand for ``2000-01-01T00:00``.
641-
- ``+P1D`` - The initial cycle point plus one day.
642-
- ``2000 +P1D +P1Y`` - The year ``2000`` plus one day and one year.
658+
``2000-01-01T00:00Z``
659+
The 1st of January at midnight.
660+
``2000``
661+
Shorthand for ``2000-01-01T00:00``.
662+
``+P1D``
663+
The initial cycle point plus one day.
664+
``2000 +P1D +P1Y``
665+
The year ``2000`` plus one day and one year.
643666
''')
644667
Conf('initial cycle point constraints', VDR.V_STRING_LIST,
645668
desc=CYCLE_POINT_CONSTRAINTS.format('initial') + dedent('''
@@ -676,7 +699,7 @@ def get_script_common_text(this: str, example: Optional[str] = None):
676699
choosing not to run that part of the graph. You can play
677700
the workflow and continue.
678701
679-
Examples:
702+
.. rubric:: Examples:
680703
681704
- ``2000`` - Shorthand for ``2000-01-01T00:00``.
682705
- ``+P1D`` - The initial cycle point plus one day.
@@ -717,7 +740,7 @@ def get_script_common_text(this: str, example: Optional[str] = None):
717740
value instead, in which case the number of cycles
718741
within the interval depends on the cycling intervals present.
719742
720-
Examples:
743+
.. rubric:: Examples:
721744
722745
``P0``
723746
Only one cycle can be active at a time.
@@ -831,7 +854,7 @@ def get_script_common_text(this: str, example: Optional[str] = None):
831854
832855
:ref:`ClockExpireTasks`.
833856
834-
Examples:
857+
.. rubric:: Examples:
835858
836859
``foo(PT1H)`` - expire task ``foo`` if the current wall clock
837860
time has reached 1 hour after the task's cycle point.
@@ -1035,7 +1058,7 @@ def get_script_common_text(this: str, example: Optional[str] = None):
10351058
10361059
See :ref:`task namespace rules. <namespace-names>`
10371060
1038-
Examples of legal values:
1061+
..rubric:: Examples:
10391062
10401063
- ``[foo]``
10411064
- ``[foo, bar, baz]``

cylc/flow/config.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,27 +1036,27 @@ def _check_circular_helper(x2ys, y2xs):
10361036
del y2xs[y01]
10371037
del x2ys[sx01]
10381038

1039-
def _check_sequence_bounds(self):
1040-
"""Check bounds of sequences against the start point."""
1039+
def _check_sequence_bounds(self) -> None:
1040+
"""Check bounds of sequences against the initial cycle point."""
10411041
out_of_bounds = [
10421042
str(seq)
10431043
for seq in self.sequences
1044-
if seq.get_first_point(self.start_point) is None
1044+
if seq.get_first_point(self.initial_point) is None
10451045
]
10461046
if out_of_bounds:
10471047
if len(out_of_bounds) > 1:
10481048
# avoid spamming users with multiple warnings
10491049
out_of_bounds_str = '\n'.join(
1050-
wrap(', '.join(out_of_bounds), 70))
1050+
wrap(', '.join(out_of_bounds), 70)
1051+
)
10511052
msg = (
1052-
"multiple sequences out of bounds for"
1053-
" initial cycle point "
1054-
f"{self.start_point}:\n{out_of_bounds_str}"
1053+
"multiple sequences out of bounds for initial cycle point "
1054+
f"{self.initial_point}:\n{out_of_bounds_str}"
10551055
)
10561056
else:
10571057
msg = (
10581058
f"{out_of_bounds[0]}: sequence out of bounds for "
1059-
f"initial cycle point {self.start_point}"
1059+
f"initial cycle point {self.initial_point}"
10601060
)
10611061
LOG.warning(msg)
10621062

cylc/flow/scheduler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,8 +1505,8 @@ async def workflow_shutdown(self):
15051505
self.time_next_kill is not None
15061506
and time() > self.time_next_kill
15071507
):
1508-
await commands.run_cmd(commands.poll_tasks(self, ['*/*']))
1509-
await commands.run_cmd(commands.kill_tasks(self, ['*/*']))
1508+
self.task_job_mgr.poll_task_jobs(self.pool.get_tasks())
1509+
self.kill_tasks(self.pool.get_tasks(), warn=False)
15101510
self.time_next_kill = time() + self.INTERVAL_STOP_KILL
15111511

15121512
# Is the workflow set to auto stop [+restart] now ...

cylc/flow/scheduler_cli.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,11 @@ async def _resume(workflow_id, options):
495495
LOG.critical('Cannot tell if the workflow is running')
496496
sys.exit(1)
497497
else:
498+
# It's running but the user has specified --pause:
499+
if options.paused_start:
500+
LOG.warning('Workflow already running: Remove --pause to resume.')
501+
sys.exit(0)
502+
498503
# It's running: resume it and exit.
499504
print("Resuming already-running workflow")
500505
mutation_kwargs = {

cylc/flow/scripts/validate_reinstall.py

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@
1818

1919
"""cylc validate-reinstall [OPTIONS] ARGS
2020
21-
Validate, reinstall and apply changes to a workflow.
21+
Validate, reinstall, and reload or restart a workflow.
2222
23-
Validate and reinstall a workflow then either:
23+
This command updates a workflow to reflect any new changes made in the workflow
24+
source directory since it was installed.
2425
25-
* "Reload" the workflow (if it is running),
26-
* or "play" it (if it is stopped).
26+
The workflow will be reinstalled, then either:
27+
* Reloaded (if the workflow is running),
28+
* or restarted (if it is stopped).
29+
30+
Cylc will show you the list of files changed and prompt for confirmation before
31+
reinstalling. Use the '--yes' option to bypass this.
2732
2833
This command is equivalent to:
2934
$ cylc validate myworkflow --against-source
@@ -77,10 +82,8 @@
7782
RELOAD_OPTIONS,
7883
run as cylc_reload
7984
)
80-
from cylc.flow.terminal import cli_function
81-
from cylc.flow.workflow_files import (
82-
get_workflow_run_dir,
83-
)
85+
from cylc.flow.terminal import cli_function, is_terminal
86+
from cylc.flow.workflow_files import get_workflow_run_dir
8487

8588

8689
CYLC_ROSE_OPTIONS = COP.get_cylc_rose_options()
@@ -94,6 +97,8 @@
9497
modify={'cylc-rose': 'validate, install'}
9598
)
9699

100+
_input = input # to enable testing
101+
97102

98103
def get_option_parser() -> COP:
99104
parser = COP(
@@ -191,32 +196,56 @@ async def vr_cli(
191196
# against source option:
192197
options.against_source = Path(get_workflow_run_dir(workflow_id))
193198

194-
# Run cylc validate
199+
# Run "cylc validate"
195200
log_subcommand('validate --against-source', workflow_id)
196201
await cylc_validate(parser, options, workflow_id)
197202

198203
# Unset options that do not apply after validation:
199204
delattr(options, 'against_source')
200205
delattr(options, 'is_validate')
201206

207+
# Run "cylc reinstall"
202208
log_subcommand('reinstall', workflow_id)
203209
reinstall_ok = await cylc_reinstall(
204-
options, workflow_id,
210+
options,
211+
workflow_id,
205212
[],
206213
print_reload_tip=False
207214
)
208215

209216
if not reinstall_ok:
210-
# No changes, OR user said No to the reinstall prompt: abort.
211-
return False
212-
213-
# Run reload if workflow is running or paused:
217+
# No changes OR user said No to the reinstall prompt.
218+
219+
# If not a terminal and not --yes do nothing.
220+
if not is_terminal() and not options.skip_interactive:
221+
return False
222+
223+
# Else if not --yes, prompt user to continue or not.
224+
if not options.skip_interactive:
225+
usr = None
226+
if not workflow_running:
227+
# No changes to install, and the workflow is not running.
228+
# Can still restart with no changes.
229+
action = "Restart"
230+
else:
231+
# No changes to install, and the workflow is running.
232+
# Reload is probably pointless, but not necessarily (the user
233+
# could modify the run dir and do 'vr' to restart or reload).
234+
action = "Reload"
235+
while usr not in ['y', 'n']:
236+
usr = _input(
237+
cparse(f'<bold>{action} anyway?</bold> [y/n]: ')
238+
).lower()
239+
if usr == 'n':
240+
return False
241+
242+
# Run "cylc reload" (if workflow is running or paused)
214243
if workflow_running:
215244
log_subcommand('reload', workflow_id)
216245
await cylc_reload(options, workflow_id)
217246
return True
218247

219-
# run play anyway, to play a stopped workflow:
248+
# Run "cylc play" (if workflow is stopped)
220249
else:
221250
set_timestamps(LOG, options.log_timestamp)
222251
cleanup_sysargv(

cylc/flow/task_job_mgr.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,9 @@ def kill_prep_task(self, itask: 'TaskProxy') -> None:
222222
self._set_retry_timers(itask)
223223
self._prep_submit_task_job_error(itask, '(killed in job prep)', '')
224224

225-
def poll_task_jobs(self, itasks, msg=None):
225+
def poll_task_jobs(
226+
self, itasks: 'Iterable[TaskProxy]', msg: str | None = None
227+
):
226228
"""Poll jobs of specified tasks.
227229
228230
This method uses _poll_task_jobs_callback() and

0 commit comments

Comments
 (0)