Skip to content

Commit 0e9496f

Browse files
author
Vasileios Karakasis
authored
Merge branch 'master' into doc/copyright-dates
2 parents 18a34db + 1b57bc5 commit 0e9496f

File tree

13 files changed

+105
-55
lines changed

13 files changed

+105
-55
lines changed

docs/advanced.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ The important bit here is the following line that sets the time limit for the te
227227
:lines: 17
228228
:dedent: 8
229229

230-
The :attr:`time_limit <reframe.core.pipeline.RegressionTest.time_limit>` attribute is a three-tuple in the form ``(HOURS, MINUTES, SECONDS)``.
230+
The :attr:`time_limit <reframe.core.pipeline.RegressionTest.time_limit>` attribute is a string in the form ``<DAYS>d<HOURS>h<MINUTES>m<SECONDS>s)``.
231231
Time limits are implemented for all the scheduler backends.
232232

233233
The sanity condition for this test verifies that associated job has been canceled due to the time limit (note that this message is SLURM-specific).

docs/tutorial.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ For schedulers that do not provide the same functionality, some of the variables
527527
================================================ ===========================================
528528
:class:`RegressionTest` attribute Corresponding SLURM option
529529
================================================ ===========================================
530-
``time_limit = (0, 10, 30)`` ``--time=00:10:30``
530+
``time_limit = '10m30s`` ``--time=00:10:30``
531531
``use_multithreading = True`` ``--hint=multithread``
532532
``use_multithreading = False`` ``--hint=nomultithread``
533533
``exclusive_access = True`` ``--exclusive``

reframe/core/fields.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
#
99

1010
import copy
11+
import datetime
1112
import os
13+
import re
1214

1315
import reframe.utility.typecheck as types
1416
from reframe.core.exceptions import user_deprecation_warning
@@ -105,23 +107,32 @@ def __set__(self, obj, value):
105107

106108

107109
class TimerField(TypedField):
108-
'''Stores a timer in the form of a tuple ``(hh, mm, ss)``'''
110+
'''Stores a timer in the form of a :class:`datetime.timedelta` object'''
109111

110112
def __init__(self, fieldname, *other_types):
111-
super().__init__(fieldname, types.Tuple[int, int, int], *other_types)
113+
super().__init__(fieldname, datetime.timedelta, str,
114+
types.Tuple[int, int, int], *other_types)
112115

113116
def __set__(self, obj, value):
114117
self._check_type(value)
115-
if value is not None:
116-
# Check also the values for minutes and seconds
118+
if isinstance(value, tuple):
119+
user_deprecation_warning(
120+
"setting a timer field from a tuple is deprecated: "
121+
"please use a string '<days>d<hours>h<minutes>m<seconds>s'")
117122
h, m, s = value
118-
if h < 0 or m < 0 or s < 0:
119-
raise ValueError('timer field must have '
120-
'non-negative values')
121-
122-
if m > 59 or s > 59:
123-
raise ValueError('minutes and seconds in a timer '
124-
'field must not exceed 59')
123+
value = datetime.timedelta(hours=h, minutes=m, seconds=s)
124+
125+
if isinstance(value, str):
126+
time_match = re.match(r'^((?P<days>\d+)d)?'
127+
r'((?P<hours>\d+)h)?'
128+
r'((?P<minutes>\d+)m)?'
129+
r'((?P<seconds>\d+)s)?$',
130+
value)
131+
if not time_match:
132+
raise ValueError('invalid format for timer field')
133+
134+
value = datetime.timedelta(
135+
**{k: int(v) for k, v in time_match.groupdict().items() if v})
125136

126137
# Call Field's __set__() method, type checking is already performed
127138
Field.__set__(self, obj, value)

reframe/core/launchers/mpi.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from reframe.core.launchers import JobLauncher
77
from reframe.core.launchers.registry import register_launcher
8+
from reframe.utility import seconds_to_hms
89

910

1011
@register_launcher('srun')
@@ -57,7 +58,8 @@ def command(self, job):
5758
ret += ['--job-name=%s' % job.name]
5859

5960
if job.time_limit:
60-
ret += ['--time=%d:%d:%d' % job.time_limit]
61+
h, m, s = seconds_to_hms(job.time_limit.total_seconds())
62+
ret += ['--time=%d:%d:%d' % (h, m, s)]
6163

6264
if job.stdout:
6365
ret += ['--output=%s' % job.stdout]

reframe/core/pipeline.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -545,20 +545,28 @@ class RegressionTest(metaclass=RegressionTestMeta):
545545

546546
#: Time limit for this test.
547547
#:
548-
#: Time limit is specified as a three-tuple in the form ``(hh, mm, ss)``,
549-
#: with ``hh >= 0``, ``0 <= mm <= 59`` and ``0 <= ss <= 59``.
548+
#: Time limit is specified as a string in the form
549+
#: ``<days>d<hours>h<minutes>m<seconds>s``.
550550
#: If set to :class:`None`, no time limit will be set.
551551
#: The default time limit of the system partition's scheduler will be used.
552552
#:
553+
#: The value is internaly kept as a :class:`datetime.timedelta` object.
554+
#: For example '2h30m' is represented as
555+
#: `datetime.timedelta(hours=2, minutes=30)`
553556
#:
554-
#: :type: :class:`tuple[int]`
555-
#: :default: ``(0, 10, 0)``
557+
#: :type: :class:`str` or :class:`datetime.timedelta`
558+
#: :default: ``'10m'``
556559
#:
557560
#: .. note::
558561
#: .. versionchanged:: 2.15
559562
#:
560563
#: This attribute may be set to :class:`None`.
561564
#:
565+
#: .. warning::
566+
#: .. versionchanged:: 3.0
567+
#:
568+
#: The old syntax using a ``(h, m, s)`` tuple is deprecated.
569+
#:
562570
time_limit = fields.TimerField('time_limit', type(None))
563571

564572
#: Extra resources for this test.
@@ -715,7 +723,7 @@ def _rfm_init(self, name=None, prefix=None):
715723
self.variables = {}
716724

717725
# Time limit for the check
718-
self.time_limit = (0, 10, 0)
726+
self.time_limit = '10m'
719727

720728
# Runtime information of the test
721729
self._current_partition = None

reframe/core/schedulers/local.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import stat
1010
import subprocess
1111
import time
12-
from datetime import datetime
12+
from datetime import datetime, timedelta
1313

1414
import reframe.core.schedulers as sched
1515
import reframe.utility.os_ext as os_ext
@@ -125,7 +125,7 @@ def cancel(self, job):
125125

126126
# Set the time limit to the grace period and let wait() do the final
127127
# killing
128-
job.time_limit = (0, 0, self._cancel_grace_period)
128+
job.time_limit = timedelta(seconds=self._cancel_grace_period)
129129
self.wait(job)
130130

131131
def wait(self, job):
@@ -143,8 +143,7 @@ def wait(self, job):
143143

144144
# Convert job's time_limit to seconds
145145
if job.time_limit is not None:
146-
h, m, s = job.time_limit
147-
timeout = h * 3600 + m * 60 + s
146+
timeout = job.time_limit.total_seconds()
148147
else:
149148
timeout = 0
150149

reframe/core/schedulers/pbs.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from reframe.core.exceptions import SpawnedProcessError, JobError
2323
from reframe.core.logging import getlogger
2424
from reframe.core.schedulers.registry import register_scheduler
25+
from reframe.utility import seconds_to_hms
2526

2627

2728
# Time to wait after a job is finished for its standard output/error to be
@@ -79,8 +80,9 @@ def emit_preamble(self, job):
7980
]
8081

8182
if job.time_limit is not None:
83+
h, m, s = seconds_to_hms(job.time_limit.total_seconds())
8284
preamble.append(
83-
self._format_option('-l walltime=%d:%d:%d' % job.time_limit))
85+
self._format_option('-l walltime=%d:%d:%d' % (h, m, s)))
8486

8587
if job.sched_partition:
8688
preamble.append(

reframe/core/schedulers/slurm.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
JobBlockedError, JobError)
2121
from reframe.core.logging import getlogger
2222
from reframe.core.schedulers.registry import register_scheduler
23+
from reframe.utility import seconds_to_hms
2324

2425

2526
def slurm_state_completed(state):
@@ -155,8 +156,9 @@ def emit_preamble(self, job):
155156
self._format_option(job.stderr, errfile_fmt)]
156157

157158
if job.time_limit is not None:
159+
h, m, s = seconds_to_hms(job.time_limit.total_seconds())
158160
preamble.append(
159-
self._format_option('%d:%d:%d' % job.time_limit, '--time={0}')
161+
self._format_option('%d:%d:%d' % (h, m, s), '--time={0}')
160162
)
161163

162164
if job.sched_exclusive_access:

reframe/utility/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
from collections import UserDict
1717

1818

19+
def seconds_to_hms(seconds):
20+
'''Convert time in seconds to a tuple of ``(hour, minutes, seconds)``.'''
21+
22+
m, s = divmod(seconds, 60)
23+
h, m = divmod(m, 60)
24+
return h, m, s
25+
26+
1927
def _get_module_name(filename):
2028
barename, _ = os.path.splitext(filename)
2129
if os.path.basename(filename) == '__init__.py':

tutorial/advanced/advanced_example5.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def __init__(self):
1414
'of a user-defined time limit')
1515
self.valid_systems = ['daint:gpu', 'daint:mc']
1616
self.valid_prog_environs = ['*']
17-
self.time_limit = (0, 1, 0)
17+
self.time_limit = '1m'
1818
self.executable = 'sleep'
1919
self.executable_opts = ['100']
2020
self.sanity_patterns = sn.assert_found(

0 commit comments

Comments
 (0)