Skip to content

Commit 33d0073

Browse files
authored
Merge pull request #3216 from Flamefire/improve_module_exist
make ModulesTool.exist more robust w.r.t. module wrappers, aliases, defaults, etc.
2 parents 42aaf63 + 8cb5517 commit 33d0073

File tree

4 files changed

+218
-65
lines changed

4 files changed

+218
-65
lines changed

easybuild/tools/modules.py

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,11 @@ def module_wrapper_exists(self, mod_name, modulerc_fn='.modulerc', mod_wrapper_r
487487
"""
488488
Determine whether a module wrapper with specified name exists.
489489
Only .modulerc file in Tcl syntax is considered here.
490+
DEPRECATED. Use exists()
490491
"""
492+
self.log.deprecated('module_wrapper_exists is unreliable and should no longer be used. ' +
493+
'Use exists instead to check for an existing module or alias.', '5.0')
494+
491495
if mod_wrapper_regex_template is None:
492496
mod_wrapper_regex_template = "^[ ]*module-version (?P<wrapped_mod>[^ ]*) %s$"
493497

@@ -528,24 +532,40 @@ def module_wrapper_exists(self, mod_name, modulerc_fn='.modulerc', mod_wrapper_r
528532

529533
return wrapped_mod
530534

531-
def exist(self, mod_names, mod_exists_regex_template=r'^\s*\S*/%s.*:\s*$', skip_avail=False, maybe_partial=True):
535+
def exist(self, mod_names, mod_exists_regex_template=None, skip_avail=False, maybe_partial=True):
532536
"""
533537
Check if modules with specified names exists.
534538
535539
:param mod_names: list of module names
536-
:param mod_exists_regex_template: template regular expression to search 'module show' output with
540+
:param mod_exists_regex_template: DEPRECATED and unused
537541
:param skip_avail: skip checking through 'module avail', only check via 'module show'
538542
:param maybe_partial: indicates if the module name may be a partial module name
539543
"""
544+
if mod_exists_regex_template is not None:
545+
self.log.deprecated('mod_exists_regex_template is no longer used', '5.0')
546+
540547
def mod_exists_via_show(mod_name):
541548
"""
542549
Helper function to check whether specified module name exists through 'module show'.
543550
544551
:param mod_name: module name
545552
"""
546-
mod_exists_regex = mod_exists_regex_template % re.escape(mod_name)
547-
txt = self.show(mod_name)
548-
return bool(re.search(mod_exists_regex, txt, re.M))
553+
stderr = self.show(mod_name)
554+
res = False
555+
# Parse the output:
556+
# - Skip whitespace
557+
# - Any error -> Module does not exist
558+
# - Check first non-whitespace line for something that looks like an absolute path terminated by a colon
559+
mod_exists_regex = r'\s*/.+:\s*'
560+
for line in stderr.split('\n'):
561+
if OUTPUT_MATCHES['whitespace'].search(line):
562+
continue
563+
if OUTPUT_MATCHES['error'].search(line):
564+
break
565+
if re.match(mod_exists_regex, line):
566+
res = True
567+
break
568+
return res
549569

550570
if skip_avail:
551571
avail_mod_names = []
@@ -570,15 +590,6 @@ def mod_exists_via_show(mod_name):
570590
self.log.debug("checking whether hidden module %s exists via 'show'..." % mod_name)
571591
mod_exists = mod_exists_via_show(mod_name)
572592

573-
# if no module file was found, check whether specified module name can be a 'wrapper' module...
574-
if not mod_exists:
575-
self.log.debug("Module %s not found via module avail/show, checking whether it is a wrapper", mod_name)
576-
wrapped_mod = self.module_wrapper_exists(mod_name)
577-
if wrapped_mod is not None:
578-
# module wrapper only really exists if the wrapped module file is also available
579-
mod_exists = wrapped_mod in avail_mod_names or mod_exists_via_show(wrapped_mod)
580-
self.log.debug("Result for existence check of wrapped module %s: %s", wrapped_mod, mod_exists)
581-
582593
self.log.debug("Result for existence check of %s module: %s", mod_name, mod_exists)
583594

584595
mods_exist.append(mod_exists)
@@ -643,7 +654,7 @@ def show(self, mod_name):
643654
ans = MODULE_SHOW_CACHE[key]
644655
self.log.debug("Found cached result for 'module show %s' with key '%s': %s", mod_name, key, ans)
645656
else:
646-
ans = self.run_module('show', mod_name, check_output=False, return_output=True)
657+
ans = self.run_module('show', mod_name, check_output=False, return_stderr=True)
647658
MODULE_SHOW_CACHE[key] = ans
648659
self.log.debug("Cached result for 'module show %s' with key '%s': %s", mod_name, key, ans)
649660

@@ -759,13 +770,15 @@ def run_module(self, *args, **kwargs):
759770
# also catch and check exit code
760771
exit_code = proc.returncode
761772
if kwargs.get('check_exit_code', True) and exit_code != 0:
762-
raise EasyBuildError("Module command 'module %s' failed with exit code %s; stderr: %s; stdout: %s",
763-
' '.join(cmd_list[2:]), exit_code, stderr, stdout)
773+
raise EasyBuildError("Module command '%s' failed with exit code %s; stderr: %s; stdout: %s",
774+
' '.join(cmd_list), exit_code, stderr, stdout)
764775

765776
if kwargs.get('check_output', True):
766777
self.check_module_output(full_cmd, stdout, stderr)
767778

768-
if kwargs.get('return_output', False):
779+
if kwargs.get('return_stderr', False):
780+
return stderr
781+
elif kwargs.get('return_output', False):
769782
return stdout + stderr
770783
else:
771784
# the module command was run with an outdated selected environment variables (see LD_ENV_VAR_KEYS list)
@@ -1394,7 +1407,7 @@ def prepend_module_path(self, path, set_mod_paths=True, priority=None):
13941407
def module_wrapper_exists(self, mod_name):
13951408
"""
13961409
Determine whether a module wrapper with specified name exists.
1397-
First check for wrapper defined in .modulerc.lua, fall back to also checking .modulerc (Tcl syntax).
1410+
DEPRECATED. Use exists()
13981411
"""
13991412
res = None
14001413

@@ -1410,19 +1423,6 @@ def module_wrapper_exists(self, mod_name):
14101423

14111424
return res
14121425

1413-
def exist(self, mod_names, skip_avail=False, maybe_partial=True):
1414-
"""
1415-
Check if modules with specified names exists.
1416-
1417-
:param mod_names: list of module names
1418-
:param skip_avail: skip checking through 'module avail', only check via 'module show'
1419-
"""
1420-
# module file may be either in Tcl syntax (no file extension) or Lua sytax (.lua extension);
1421-
# the current configuration for matters little, since the module may have been installed with a different cfg;
1422-
# Lmod may pick up both Tcl and Lua module files, regardless of the EasyBuild configuration
1423-
return super(Lmod, self).exist(mod_names, mod_exists_regex_template=r'^\s*\S*/%s.*(\.lua)?:\s*$',
1424-
skip_avail=skip_avail, maybe_partial=maybe_partial)
1425-
14261426
def get_setenv_value_from_modulefile(self, mod_name, var_name):
14271427
"""
14281428
Get value for specific 'setenv' statement from module file for the specified module.

0 commit comments

Comments
 (0)