Skip to content

Commit 55872e4

Browse files
committed
Merge 'upstream/master' into 8.3.x-sync
2 parents dd170d4 + e28e0c0 commit 55872e4

File tree

10 files changed

+87
-17
lines changed

10 files changed

+87
-17
lines changed

changes.d/6137.feat.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
New Cylc lint rule: S014: Don't use job runner specific execution time limit directives, use execution time limit.

cylc/flow/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@
1515
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
"""Set up the cylc environment."""
1717

18-
import os
1918
import logging
20-
19+
import os
2120

2221
CYLC_LOG = 'cylc'
2322

@@ -53,7 +52,7 @@ def environ_init():
5352

5453
environ_init()
5554

56-
__version__ = '8.3.3.dev'
55+
__version__ = '8.4.0.dev'
5756

5857

5958
def iter_entry_points(entry_point_name):

cylc/flow/job_runner_handlers/loadleveler.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class LoadlevelerHandler():
8383
re.compile("^llsubmit: Processed command file through Submit Filter:")]
8484
SUBMIT_CMD_TMPL = "llsubmit '%(job)s'"
8585
VACATION_SIGNAL = "USR1"
86+
TIME_LIMIT_DIRECTIVE = "wall_clock_limit"
8687

8788
def format_directives(self, job_conf):
8889
"""Format the job directives for a job file."""
@@ -96,8 +97,8 @@ def format_directives(self, job_conf):
9697
directives["output"] = job_file_path + ".out"
9798
directives["error"] = job_file_path + ".err"
9899
if (job_conf["execution_time_limit"] and
99-
directives.get("wall_clock_limit") is None):
100-
directives["wall_clock_limit"] = "%d,%d" % (
100+
directives.get(self.TIME_LIMIT_DIRECTIVE) is None):
101+
directives[self.TIME_LIMIT_DIRECTIVE] = "%d,%d" % (
101102
job_conf["execution_time_limit"] + 60,
102103
job_conf["execution_time_limit"])
103104
for key, value in list(job_conf["directives"].items()):

cylc/flow/job_runner_handlers/lsf.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class LSFHandler():
7070
POLL_CMD = "bjobs"
7171
REC_ID_FROM_SUBMIT_OUT = re.compile(r"^Job <(?P<id>\d+)>")
7272
SUBMIT_CMD_TMPL = "bsub"
73+
TIME_LIMIT_DIRECTIVE = "-W"
7374

7475
@classmethod
7576
def format_directives(cls, job_conf):
@@ -82,8 +83,11 @@ def format_directives(cls, job_conf):
8283
)
8384
directives["-o"] = job_file_path + ".out"
8485
directives["-e"] = job_file_path + ".err"
85-
if job_conf["execution_time_limit"] and directives.get("-W") is None:
86-
directives["-W"] = str(math.ceil(
86+
if (
87+
job_conf["execution_time_limit"]
88+
and directives.get(cls.TIME_LIMIT_DIRECTIVE) is None
89+
):
90+
directives[cls.TIME_LIMIT_DIRECTIVE] = str(math.ceil(
8791
job_conf["execution_time_limit"] / 60))
8892
for key, value in list(job_conf["directives"].items()):
8993
directives[key] = value

cylc/flow/job_runner_handlers/moab.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class MoabHandler:
7878
POLL_CMD = "checkjob"
7979
REC_ID_FROM_SUBMIT_OUT = re.compile(r"""\A\s*(?P<id>\S+)\s*\Z""")
8080
SUBMIT_CMD_TMPL = "msub '%(job)s'"
81+
TIME_LIMIT_DIRECTIVE = "-l walltime"
8182

8283
def format_directives(self, job_conf):
8384
"""Format the job directives for a job file."""
@@ -91,8 +92,9 @@ def format_directives(self, job_conf):
9192
directives["-o"] = job_file_path + ".out"
9293
directives["-e"] = job_file_path + ".err"
9394
if (job_conf["execution_time_limit"] and
94-
directives.get("-l walltime") is None):
95-
directives["-l walltime"] = "%d" % job_conf["execution_time_limit"]
95+
directives.get(self.TIME_LIMIT_DIRECTIVE) is None):
96+
directives[self.TIME_LIMIT_DIRECTIVE] = "%d" % job_conf[
97+
"execution_time_limit"]
9698
# restartable?
9799
directives.update(job_conf["directives"])
98100
lines = []

cylc/flow/job_runner_handlers/pbs.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class PBSHandler:
8484
POLL_CANT_CONNECT_ERR = "Connection refused"
8585
REC_ID_FROM_SUBMIT_OUT = re.compile(r"^\s*(?P<id>\d+)", re.M)
8686
SUBMIT_CMD_TMPL = "qsub '%(job)s'"
87+
TIME_LIMIT_DIRECTIVE = "-l walltime"
8788

8889
def format_directives(self, job_conf):
8990
"""Format the job directives for a job file."""
@@ -105,9 +106,12 @@ def format_directives(self, job_conf):
105106

106107
directives["-o"] = job_file_path + ".out"
107108
directives["-e"] = job_file_path + ".err"
108-
if (job_conf["execution_time_limit"] and
109-
directives.get("-l walltime") is None):
110-
directives["-l walltime"] = "%d" % job_conf["execution_time_limit"]
109+
if (
110+
job_conf["execution_time_limit"]
111+
and directives.get(self.TIME_LIMIT_DIRECTIVE) is None
112+
):
113+
directives[self.TIME_LIMIT_DIRECTIVE] = "%d" % job_conf[
114+
"execution_time_limit"]
111115
for key, value in list(job_conf["directives"].items()):
112116
directives[key] = value
113117
lines = []

cylc/flow/job_runner_handlers/sge.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
-cwd =
3838
-q = foo
3939
-l h_data = 1024M
40-
-l h_rt = 24:00:00
4140
4241
These are written to the top of the job script like this:
4342
@@ -76,6 +75,7 @@ class SGEHandler:
7675
POLL_CMD = "qstat"
7776
REC_ID_FROM_SUBMIT_OUT = re.compile(r"\D+(?P<id>\d+)\D+")
7877
SUBMIT_CMD_TMPL = "qsub '%(job)s'"
78+
TIME_LIMIT_DIRECTIVE = "-l h_rt"
7979

8080
def format_directives(self, job_conf):
8181
"""Format the job directives for a job file."""
@@ -88,8 +88,8 @@ def format_directives(self, job_conf):
8888
directives['-o'] = job_file_path + ".out"
8989
directives['-e'] = job_file_path + ".err"
9090
if (job_conf["execution_time_limit"] and
91-
directives.get("-l h_rt") is None):
92-
directives["-l h_rt"] = "%d:%02d:%02d" % (
91+
directives.get(self.TIME_LIMIT_DIRECTIVE) is None):
92+
directives[self.TIME_LIMIT_DIRECTIVE] = "%d:%02d:%02d" % (
9393
job_conf["execution_time_limit"] / 3600,
9494
(job_conf["execution_time_limit"] / 60) % 60,
9595
job_conf["execution_time_limit"] % 60)

cylc/flow/job_runner_handlers/slurm.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ class SLURMHandler():
135135
# Separator between het job directive sections
136136
SEP_HETJOB = "#SBATCH hetjob"
137137

138+
TIME_LIMIT_DIRECTIVE = "--time"
139+
138140
@classmethod
139141
def filter_poll_many_output(cls, out):
140142
"""Return list of job IDs extracted from job poll stdout.
@@ -161,8 +163,8 @@ def format_directives(cls, job_conf):
161163
directives['--output'] = job_file_path.replace('%', '%%') + ".out"
162164
directives['--error'] = job_file_path.replace('%', '%%') + ".err"
163165
if (job_conf["execution_time_limit"] and
164-
directives.get("--time") is None):
165-
directives["--time"] = "%d:%02d" % (
166+
directives.get(cls.TIME_LIMIT_DIRECTIVE) is None):
167+
directives[cls.TIME_LIMIT_DIRECTIVE] = "%d:%02d" % (
166168
job_conf["execution_time_limit"] / 60,
167169
job_conf["execution_time_limit"] % 60)
168170
for key, value in list(job_conf['directives'].items()):

cylc/flow/scripts/lint.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
max-line-length = 130 # Max line length for linting
5151
"""
5252
import functools
53+
import pkgutil
5354
from pathlib import Path
5455
import re
5556
import sys
@@ -78,6 +79,8 @@
7879
from cylc.flow import LOG
7980
from cylc.flow.exceptions import CylcError
8081
import cylc.flow.flags
82+
from cylc.flow import job_runner_handlers
83+
from cylc.flow.job_runner_mgr import JobRunnerManager
8184
from cylc.flow.loggingutil import set_timestamps
8285
from cylc.flow.option_parsers import (
8386
CylcOptionParser as COP,
@@ -162,6 +165,34 @@
162165
}
163166

164167

168+
def get_wallclock_directives():
169+
"""Get a set of directives equivalent to execution time limit"""
170+
job_runner_manager = JobRunnerManager()
171+
directives = {}
172+
for module in pkgutil.iter_modules(job_runner_handlers.__path__):
173+
directive = getattr(
174+
job_runner_manager._get_sys(module.name),
175+
'TIME_LIMIT_DIRECTIVE',
176+
None
177+
)
178+
if directive:
179+
directives[module.name] = directive
180+
return directives
181+
182+
183+
WALLCLOCK_DIRECTIVES = get_wallclock_directives()
184+
185+
186+
def check_wallclock_directives(line: str) -> Union[Dict[str, str], bool]:
187+
"""Check for job runner specific directives
188+
equivelent to exection time limit.
189+
"""
190+
for directive in set(WALLCLOCK_DIRECTIVES.values()):
191+
if line.strip().startswith(directive):
192+
return {'directive': line.strip()}
193+
return False
194+
195+
165196
def check_jinja2_no_shebang(
166197
line: str,
167198
file: Path,
@@ -533,6 +564,30 @@ def list_wrapper(line: str, check: Callable) -> Optional[Dict[str, str]]:
533564
'S013': {
534565
'short': 'Items should be indented in 4 space blocks.',
535566
FUNCTION: check_indentation
567+
},
568+
'S014': {
569+
'short': (
570+
'Use ``[runtime][TASK]execution time limit``'
571+
' rather than job runner directive: ``{directive}``.'
572+
),
573+
'rst': (
574+
'Using ``[runtime][TASK]execution time limit`` is'
575+
' recommended in preference to using job runner'
576+
' directives because it allows Cylc to retain awareness'
577+
' of whether the job should have finished, even if contact'
578+
' with the target job runner\'s platform has been lost.'
579+
' \n\nThe following directives are considered equivelent to'
580+
' execution time limit:\n * '
581+
)
582+
+ '\n * '.join((
583+
f'``{directive}`` ({job_runner})'
584+
for job_runner, directive in WALLCLOCK_DIRECTIVES.items()
585+
)) + (
586+
'\n\n.. note:: Using ``execution time limit`` which'
587+
' is automatically translated to the job runner\'s timeout'
588+
' directive can make your workflow more portable.'
589+
),
590+
FUNCTION: check_wallclock_directives,
536591
}
537592
}
538593
# Subset of deprecations which are tricky (impossible?) to scrape from the

tests/unit/scripts/test_lint.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@
178178
something\t
179179
[[bar]]
180180
platform = $(some-script foo)
181+
[[[directives]]]
182+
-l walltime = 666
181183
[[baz]]
182184
platform = `no backticks`
183185
""" + (

0 commit comments

Comments
 (0)