Skip to content

Commit d164f0b

Browse files
author
Vasileios Karakasis
authored
Merge pull request #1606 from teojgo/feat/use_unuse_path
[feat] Add command-line option to manipulate the module path before executing any tests
2 parents 9354331 + 930904b commit d164f0b

File tree

6 files changed

+136
-12
lines changed

6 files changed

+136
-12
lines changed

docs/manpage.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,16 @@ It does so by leveraging the selected system's environment modules system.
387387
This option can also be set using the :envvar:`RFM_UNLOAD_MODULES` environment variable or the :js:attr:`unload_modules` general configuration parameter.
388388

389389

390+
.. option:: --module-path=PATH
391+
392+
Manipulate the ``MODULEPATH`` environment variable before acting on any tests.
393+
If ``PATH`` starts with the `-` character, it will be removed from the ``MODULEPATH``, whereas if it starts with the `+` character, it will be added to it.
394+
In all other cases, ``PATH`` will completely override MODULEPATH.
395+
This option may be specified multiple times, in which case all the paths specified will be added or removed in order.
396+
397+
.. versionadded:: 3.3
398+
399+
390400
.. option:: --purge-env
391401

392402
Unload all environment modules before acting on any tests.
@@ -883,6 +893,21 @@ Here is an alphabetical list of the environment variables recognized by ReFrame:
883893
================================== ==================
884894

885895

896+
.. versionadded:: 3.3
897+
898+
.. envvar:: RFM_UNUSE_MODULE_PATHS
899+
900+
A colon-separated list of module paths to be unused before acting on any tests.
901+
902+
.. table::
903+
:align: left
904+
905+
================================== ==================
906+
Associated command line option :option:`--unuse-module-path`
907+
Associated configuration parameter :js:attr:`unuse_module_paths` general configuration parameter
908+
================================== ==================
909+
910+
886911
.. envvar:: RFM_USE_LOGIN_SHELL
887912

888913
Use a login shell for the generated job scripts.

reframe/core/modules.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,22 @@ class ModulesSystemImpl(abc.ABC):
400400
:meta private:
401401
'''
402402

403-
@abc.abstractmethod
404403
def execute(self, cmd, *args):
404+
'''Execute an arbitrary module command using the modules backend.
405+
406+
:arg cmd: The command to execute, e.g., ``load``, ``restore`` etc.
407+
:arg args: The arguments to pass to the command.
408+
:returns: The command output.
409+
'''
410+
try:
411+
exec_output = self._execute(cmd, *args)
412+
except SpawnedProcessError as e:
413+
raise EnvironError('could not execute module operation') from e
414+
415+
return exec_output
416+
417+
@abc.abstractmethod
418+
def _execute(self, cmd, *args):
405419
'''Execute an arbitrary command of the module system.'''
406420

407421
@abc.abstractmethod
@@ -539,7 +553,7 @@ def version(self):
539553
def modulecmd(self, *args):
540554
return ' '.join(['modulecmd', 'python', *args])
541555

542-
def execute(self, cmd, *args):
556+
def _execute(self, cmd, *args):
543557
modulecmd = self.modulecmd(cmd, *args)
544558
completed = osext.run_command(modulecmd)
545559
if re.search(r'ERROR', completed.stderr) is not None:
@@ -661,7 +675,7 @@ def name(self):
661675
def modulecmd(self, *args):
662676
return ' '.join([self._command, *args])
663677

664-
def execute(self, cmd, *args):
678+
def _execute(self, cmd, *args):
665679
modulecmd = self.modulecmd(cmd, *args)
666680
completed = osext.run_command(modulecmd)
667681
if re.search(r'ERROR', completed.stderr) is not None:
@@ -722,7 +736,7 @@ def name(self):
722736
def modulecmd(self, *args):
723737
return ' '.join(['modulecmd', 'python', *args])
724738

725-
def execute(self, cmd, *args):
739+
def _execute(self, cmd, *args):
726740
modulecmd = self.modulecmd(cmd, *args)
727741
completed = osext.run_command(modulecmd, check=False)
728742
namespace = {}
@@ -883,7 +897,7 @@ def loaded_modules(self):
883897
def conflicted_modules(self, module):
884898
return []
885899

886-
def execute(self, cmd, *args):
900+
def _execute(self, cmd, *args):
887901
return ''
888902

889903
def load_module(self, module):

reframe/frontend/cli.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,11 @@ def main():
359359
help='Unload module MOD before running any regression check',
360360
envvar='RFM_UNLOAD_MODULES ,', configvar='general/unload_modules'
361361
)
362+
env_options.add_argument(
363+
'--module-path', action='append', metavar='PATH',
364+
dest='module_paths', default=[],
365+
help='(Un)use module path PATH before running any regression check',
366+
)
362367
env_options.add_argument(
363368
'--purge-env', action='store_true', dest='purge_env', default=False,
364369
help='Unload all modules before running any regression check',
@@ -733,6 +738,35 @@ def print_infoline(param, value):
733738
printer.debug(str(e))
734739
raise
735740

741+
printer.debug('(Un)using module paths from command line')
742+
for d in options.module_paths:
743+
if d.startswith('-'):
744+
try:
745+
rt.modules_system.searchpath_remove(d[1:])
746+
except errors.EnvironError as e:
747+
printer.warning(
748+
f'could not remove module path {d} correctly; '
749+
f'skipping...'
750+
)
751+
printer.verbose(str(e))
752+
elif d.startswith('+'):
753+
try:
754+
rt.modules_system.searchpath_add(d[1:])
755+
except errors.EnvironError as e:
756+
printer.warning(
757+
f'could not add module path {d} correctly; '
758+
f'skipping...'
759+
)
760+
printer.verbose(str(e))
761+
else:
762+
# Here we make sure that we don't try to remove an empty path
763+
# from the searchpath
764+
searchpath = [p for p in rt.modules_system.searchpath if p]
765+
if searchpath:
766+
rt.modules_system.searchpath_remove(*searchpath)
767+
768+
rt.modules_system.searchpath_add(d)
769+
736770
printer.debug('Loading user modules from command line')
737771
for m in site_config.get('general/0/user_modules'):
738772
try:

unittests/test_cli.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,59 @@ def test_unload_module(run_reframe, user_exec_ctx):
591591
assert returncode == 0
592592

593593

594+
def test_unuse_module_path(run_reframe, user_exec_ctx, monkeypatch):
595+
ms = rt.runtime().modules_system
596+
if ms.name == 'nomod':
597+
pytest.skip('no modules system found')
598+
599+
module_path = 'unittests/modules'
600+
monkeypatch.setenv('MODULEPATH', module_path)
601+
returncode, stdout, stderr = run_reframe(
602+
more_options=[f'--module-path=-{module_path}', '--module=testmod_foo'],
603+
config_file=fixtures.USER_CONFIG_FILE, action='run',
604+
system=rt.runtime().system.name
605+
)
606+
assert "could not load module 'testmod_foo' correctly" in stdout
607+
assert 'Traceback' not in stderr
608+
assert returncode == 0
609+
610+
611+
def test_use_module_path(run_reframe, user_exec_ctx):
612+
ms = rt.runtime().modules_system
613+
if ms.name == 'nomod':
614+
pytest.skip('no modules system found')
615+
616+
module_path = 'unittests/modules'
617+
returncode, stdout, stderr = run_reframe(
618+
more_options=[f'--module-path=+{module_path}', '--module=testmod_foo'],
619+
config_file=fixtures.USER_CONFIG_FILE, action='run',
620+
system=rt.runtime().system.name
621+
)
622+
623+
assert 'Traceback' not in stdout
624+
assert 'Traceback' not in stderr
625+
assert "could not load module 'testmod_foo' correctly" not in stdout
626+
assert returncode == 0
627+
628+
629+
def test_overwrite_module_path(run_reframe, user_exec_ctx):
630+
ms = rt.runtime().modules_system
631+
if ms.name == 'nomod':
632+
pytest.skip('no modules system found')
633+
634+
module_path = 'unittests/modules'
635+
returncode, stdout, stderr = run_reframe(
636+
more_options=[f'--module-path={module_path}', '--module=testmod_foo'],
637+
config_file=fixtures.USER_CONFIG_FILE, action='run',
638+
system=rt.runtime().system.name
639+
)
640+
641+
assert 'Traceback' not in stdout
642+
assert 'Traceback' not in stderr
643+
assert "could not load module 'testmod_foo' correctly" not in stdout
644+
assert returncode == 0
645+
646+
594647
def test_failure_stats(run_reframe):
595648
returncode, stdout, stderr = run_reframe(
596649
checkpath=['unittests/resources/checks/frontend_checks.py'],

unittests/test_environments.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import reframe.core.environments as env
1111
import reframe.core.runtime as rt
1212
import unittests.fixtures as fixtures
13-
from reframe.core.exceptions import (EnvironError, SpawnedProcessError)
13+
from reframe.core.exceptions import EnvironError
1414

1515

1616
@pytest.fixture
@@ -275,7 +275,7 @@ def test_emit_loadenv_failure(user_runtime):
275275

276276
# Suppress the module load error and verify that the original environment
277277
# is preserved
278-
with contextlib.suppress(SpawnedProcessError):
278+
with contextlib.suppress(EnvironError):
279279
rt.emit_loadenv_commands(environ)
280280

281281
assert rt.snapshot() == snap

unittests/test_modules.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
import reframe.utility as util
1313
import reframe.utility.osext as osext
1414
import unittests.fixtures as fixtures
15-
from reframe.core.exceptions import (ConfigError,
16-
EnvironError,
17-
SpawnedProcessError)
15+
from reframe.core.exceptions import (ConfigError, EnvironError)
1816
from reframe.core.runtime import runtime
1917

2018

@@ -80,7 +78,7 @@ def test_module_load(modules_system):
8078
modules_system.load_module('foo')
8179
modules_system.unload_module('foo')
8280
else:
83-
with pytest.raises(SpawnedProcessError):
81+
with pytest.raises(EnvironError):
8482
modules_system.load_module('foo')
8583

8684
assert not modules_system.is_module_loaded('foo')
@@ -307,7 +305,7 @@ def loaded_modules(self):
307305
def conflicted_modules(self, module):
308306
return []
309307

310-
def execute(self, cmd, *args):
308+
def _execute(self, cmd, *args):
311309
return ''
312310

313311
def load_module(self, module):

0 commit comments

Comments
 (0)