Skip to content

Commit b6db404

Browse files
committed
Remove sh binding from YAML configuration
- Introduce also some additional basic bindings
1 parent 544b96c commit b6db404

File tree

4 files changed

+60
-28
lines changed

4 files changed

+60
-28
lines changed

docs/config_reference.rst

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2316,10 +2316,12 @@ Since the configuration is loaded as a Python module, you can generate parts of
23162316
The YAML configuration on the other hand is more static, although not fully.
23172317
Code generation can still be used with the YAML configuration as it is treated as a Jinja2 template, where ReFrame provides the following bindings:
23182318

2319-
- ``hostname``: The local host's hostname.
23202319
- ``getenv(<envvar>)``: Retrieve an environment variable.
2321-
- ``sh(<command>)``: Retrieve the standard output of a shell command.
2322-
The command must be successful.
2320+
- ``gid``: The real group id of the ReFrame process.
2321+
- ``group``: The group name of the ReFrame process.
2322+
- ``hostname``: The local host's hostname.
2323+
- ``uid``: The real user id of the ReFrame process.
2324+
- ``user``: The user name of the ReFrame process.
23232325

23242326
These are two examples of YAML logging configuration that uses one of those bindings:
23252327

@@ -2339,17 +2341,7 @@ These are two examples of YAML logging configuration that uses one of those bind
23392341
logging:
23402342
- handlers:
23412343
- type: file
2342-
name: reframe-{{ sh("hostname -s") }}.log
2343-
level: debug2
2344-
format: "[%(asctime)s.%(msecs)03d] %(levelname)s: %(check_info)s: %(message)s"
2345-
append: false
2346-
2347-
.. code-block:: yaml
2348-
2349-
logging:
2350-
- handlers:
2351-
- type: file
2352-
name: reframe-{{ getenv("USER") }}.log
2344+
name: reframe-{{ getenv("FOO") }}.log
23532345
level: debug2
23542346
format: "[%(asctime)s.%(msecs)03d] %(levelname)s: %(check_info)s: %(message)s"
23552347
append: false

reframe/core/config.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,21 @@
1010
import importlib
1111
import io
1212
import itertools
13-
import jinja2
1413
import json
1514
import jsonschema
1615
import os
1716
import re
1817
import socket
1918
import yaml
19+
from jinja2.sandbox import SandboxedEnvironment
2020

2121
import reframe
2222
import reframe.core.settings as settings
2323
import reframe.utility as util
2424
import reframe.utility.jsonext as jsonext
2525
import reframe.utility.osext as osext
2626
from reframe.core.environments import normalize_module_list
27-
from reframe.core.exceptions import (ConfigError, ReframeFatalError,
28-
SpawnedProcessError)
27+
from reframe.core.exceptions import ConfigError, ReframeFatalError
2928
from reframe.core.logging import getlogger
3029
from reframe.utility import ScopedDict
3130

@@ -365,20 +364,19 @@ def load_config_json(self, filename):
365364
self.update_config(config, filename)
366365

367366
def load_config_yaml(self, filename):
368-
def _shell(cmd):
369-
'''Jinja wrapper for executing shell commands'''
370-
try:
371-
completed = osext.run_command(cmd, check=True)
372-
except (FileNotFoundError, SpawnedProcessError) as err:
373-
raise ConfigError('failed to run shell command') from err
374-
else:
375-
return completed.stdout.strip()
367+
bindings = {
368+
'getenv': os.getenv,
369+
'gid': os.getgid(),
370+
'group': osext.osgroup(),
371+
'hostname': socket.gethostname(),
372+
'uid': os.getuid(),
373+
'user': osext.osuser(),
374+
}
376375

377376
with open(filename) as fp:
378-
environment = jinja2.Environment()
377+
environment = SandboxedEnvironment()
379378
template = environment.from_string(fp.read())
380-
yaml_src = template.render(hostname=socket.gethostname(),
381-
sh=_shell, getenv=os.getenv)
379+
yaml_src = template.render(**bindings)
382380
try:
383381
config = yaml.safe_load(io.StringIO(yaml_src))
384382
except Exception as err:
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright 2016-2025 Swiss National Supercomputing Centre (CSCS/ETH Zurich)
2+
# ReFrame Project Developers. See the top-level LICENSE file for details.
3+
#
4+
# SPDX-License-Identifier: BSD-3-Clause
5+
6+
# Config file for testing the YAML bindings
7+
8+
systems:
9+
- name: testsys
10+
hostnames: ['.*']
11+
partitions:
12+
- name: default
13+
scheduler: local
14+
launcher: local
15+
environs: ['builtin']
16+
extras:
17+
getenv: {{ getenv("_FOO_") }}
18+
gid: {{ gid }}
19+
group: {{ group }}
20+
hostname: {{ hostname }}
21+
uid: {{ uid }}
22+
user: {{ user }}

unittests/test_config.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,26 @@ def test_system_create(site_config):
511511
assert system.partitions[0].container_runtime == 'Docker'
512512

513513

514+
def test_yaml_bindings(monkeypatch):
515+
import os
516+
import socket
517+
import reframe.utility.osext as osext
518+
519+
monkeypatch.setenv('_FOO_', 'bar')
520+
site_config = config.load_config(
521+
'unittests/resources/config/bindings.yaml'
522+
)
523+
site_config.select_subconfig('testsys:default')
524+
system = System.create(site_config)
525+
extras = system.partitions[0].extras
526+
assert extras['getenv'] == os.getenv('_FOO_')
527+
assert extras['gid'] == os.getgid()
528+
assert extras['group'] == osext.osgroup()
529+
assert extras['hostname'] == socket.gethostname()
530+
assert extras['uid'] == os.getuid()
531+
assert extras['user'] == osext.osuser()
532+
533+
514534
def test_variables(tmp_path):
515535
# Test that the old syntax using `variables` instead of `env_vars` still
516536
# works

0 commit comments

Comments
 (0)