Skip to content

Commit b71b272

Browse files
authored
Merge pull request #3266 from vkarak/feat/add-custom-session-metadata
[feat] Support session annotations with a new `--session-extras` option
2 parents cadbd83 + 38c972c commit b71b272

File tree

5 files changed

+87
-15
lines changed

5 files changed

+87
-15
lines changed

docs/manpage.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,15 @@ Miscellaneous options
11291129
.. versionadded:: 3.9.3
11301130

11311131

1132+
.. option:: --session-extras KV_DATA
1133+
1134+
Annotate the current session with custom key/value metadata.
1135+
1136+
The key/value data is specified as a comma-separated list of `key=value` pairs.
1137+
When listing stored sessions with the :option:`--list-stored-sessions` option, any associated custom metadata will be presented by default.
1138+
1139+
.. versionadded:: 4.7
1140+
11321141
.. option:: --system=NAME
11331142

11341143
Load the configuration for system ``NAME``.

reframe/frontend/cli.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,10 @@ def main():
616616
help=('Print a report for performance tests '
617617
'(default: "now:now/last:+job_nodelist/+result")')
618618
)
619+
reporting_options.add_argument(
620+
'--session-extras', action='store', metavar='KV_DATA',
621+
help='Annotate session with custom key/value data'
622+
)
619623

620624
# Miscellaneous options
621625
misc_options.add_argument(
@@ -1590,6 +1594,15 @@ def module_unuse(*paths):
15901594
if options.restore_session is not None:
15911595
report.update_restored_cases(restored_cases, restored_session)
15921596

1597+
if options.session_extras:
1598+
# Update report's extras
1599+
extras = {}
1600+
for arg in options.session_extras.split(','):
1601+
k, v = arg.split('=', maxsplit=1)
1602+
extras[k] = v
1603+
1604+
report.update_extras(extras)
1605+
15931606
# Print a retry report if we did any retries
15941607
if options.max_retries and runner.stats.failed(run=0):
15951608
printer.retry_report(report)

reframe/frontend/printer.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,14 @@ def table(self, data, **kwargs):
283283
colidx = [i for i, col in enumerate(data[0])
284284
if col not in hide_columns]
285285

286-
tab_data = [[rec[col] for col in colidx] for rec in data]
286+
def _access(seq, i, default=None):
287+
# Safe access of i-th element of a sequence
288+
try:
289+
return seq[i]
290+
except IndexError:
291+
return default
292+
293+
tab_data = [[_access(rec, col) for col in colidx] for rec in data]
287294
else:
288295
tab_data = data
289296

reframe/frontend/reporting/__init__.py

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from reframe.core.logging import getlogger, _format_time_rfc3339, time_function
2626
from reframe.core.runtime import runtime
2727
from reframe.core.warnings import suppress_deprecations
28-
from reframe.utility import nodelist_abbrev
28+
from reframe.utility import nodelist_abbrev, OrderedSet
2929
from .storage import StorageBackend
3030
from .utility import Aggregator, parse_cmp_spec, parse_time_period, is_uuid
3131

@@ -269,6 +269,14 @@ def update_timestamps(self, ts_start, ts_end):
269269
'time_elapsed': ts_end - ts_start
270270
})
271271

272+
def update_extras(self, extras):
273+
'''Attach user-specific metadata to the session'''
274+
275+
# We prepend a special character to the user extras in order to avoid
276+
# possible conflicts with existing keys
277+
for k, v in extras.items():
278+
self.__report['session_info'][f'${k}'] = v
279+
272280
def update_run_stats(self, stats):
273281
session_uuid = self.__report['session_info']['uuid']
274282
for runidx, tasks in stats.runs():
@@ -645,17 +653,38 @@ def session_data(time_period):
645653
'''Retrieve all sessions'''
646654

647655
data = [['UUID', 'Start time', 'End time', 'Num runs', 'Num cases']]
656+
extra_cols = OrderedSet()
648657
for sess_data in StorageBackend.default().fetch_sessions_time_period(
649658
*parse_time_period(time_period) if time_period else (None, None)
650659
):
651660
session_info = sess_data['session_info']
652-
data.append(
653-
[session_info['uuid'],
654-
session_info['time_start'],
655-
session_info['time_end'],
656-
len(sess_data['runs']),
657-
len(sess_data['runs'][0]['testcases'])]
658-
)
661+
record = [session_info['uuid'],
662+
session_info['time_start'],
663+
session_info['time_end'],
664+
len(sess_data['runs']),
665+
len(sess_data['runs'][0]['testcases'])]
666+
667+
# Expand output with any user metadata
668+
for k in session_info:
669+
if k.startswith('$'):
670+
extra_cols.add(k[1:])
671+
672+
# Add any extras recorded so far
673+
for key in extra_cols:
674+
record.append(session_info.get(f'${key}', ''))
675+
676+
data.append(record)
677+
678+
# Do a final grooming pass of the data to expand short records
679+
if extra_cols:
680+
data[0] += extra_cols
681+
682+
for rec in data:
683+
diff = len(extra_cols) - len(rec)
684+
if diff == 0:
685+
break
686+
687+
rec += ['n/a' for _ in range(diff)]
659688

660689
return data
661690

unittests/test_cli.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,13 +1262,14 @@ def table_format(request):
12621262
return request.param
12631263

12641264

1265-
def test_storage_options(run_reframe, tmp_path, table_format):
1266-
def assert_no_crash(returncode, stdout, stderr, exitcode=0):
1267-
assert returncode == exitcode
1268-
assert 'Traceback' not in stdout
1269-
assert 'Traceback' not in stderr
1270-
return returncode, stdout, stderr
1265+
def assert_no_crash(returncode, stdout, stderr, exitcode=0):
1266+
assert returncode == exitcode
1267+
assert 'Traceback' not in stdout
1268+
assert 'Traceback' not in stderr
1269+
return returncode, stdout, stderr
1270+
12711271

1272+
def test_storage_options(run_reframe, tmp_path, table_format):
12721273
run_reframe2 = functools.partial(
12731274
run_reframe,
12741275
checkpath=['unittests/resources/checks/frontend_checks.py'],
@@ -1328,6 +1329,19 @@ def assert_no_crash(returncode, stdout, stderr, exitcode=0):
13281329
assert_no_crash(*run_reframe2(action=f'--delete-stored-session={uuid}'))
13291330

13301331

1332+
def test_session_annotations(run_reframe):
1333+
assert_no_crash(*run_reframe(
1334+
checkpath=['unittests/resources/checks/frontend_checks.py'],
1335+
action='-r',
1336+
more_options=['--session-extras', 'key1=val1,key2=val2',
1337+
'-n', '^PerformanceFailureCheck']
1338+
), exitcode=1)
1339+
1340+
stdout = assert_no_crash(*run_reframe(action='--list-stored-sessions'))[1]
1341+
for text in ['key1', 'key2', 'val1', 'val2']:
1342+
assert text in stdout
1343+
1344+
13311345
def test_performance_compare(run_reframe, table_format):
13321346
def assert_no_crash(returncode, stdout, stderr, exitcode=0):
13331347
assert returncode == exitcode

0 commit comments

Comments
 (0)