diff --git a/eessi/testsuite/utils.py b/eessi/testsuite/utils.py index 3b770d77..dcec83f3 100644 --- a/eessi/testsuite/utils.py +++ b/eessi/testsuite/utils.py @@ -4,7 +4,7 @@ import re import sys -from typing import Iterator +from typing import Iterator, List import reframe as rfm import reframe.core.runtime as rt @@ -57,15 +57,58 @@ def is_cuda_required_module(module_name: str) -> bool: return requires_cuda -def find_modules(substr: str) -> Iterator[str]: - """Return all modules in the current system that contain ``substr`` in their name.""" - if not isinstance(substr, str): +def find_modules(regex: str, name_only = True) -> Iterator[str]: + """ + Return all modules matching the regular expression regex. Note that since we use re.search, + a module matches if the regex matches the module name at any place. I.e. the match does + not have to be at the start of the smodule name + + Arguments: + - regex: a regular expresion + - name_only: regular expressions will only be matched on the module name, not the version (default: True). + + Note: the name_only feature assumes anything after the last forward '/' is the version, + and strips that before doing a match. + + Example + + Suppose we have the following modules on a system: + + gompic/2022a + gompi/2022a + CGAL/4.14.3-gompi-2022a + + The following calls would return the following respective modules + + find_modules('gompi') => [gompic/2022a, gompi/2022a] + find_modules('gompi$') => [gompi/2022a] + find_modules('gompi', name_only = False) => [gompic/2022a, gompi/2022a, CGAL/4.14.3-gompi-2022a] + find_modules('^gompi', name_only = False) => [gompic/2022a, gompi/2022a] + find_modules('^gompi\/', name_only = False) => [gompi/2022a] + find_modules('-gompi-2022a', name_only = False) => [CGAL/4.14.3-gompi-2022a] + + """ + + if not isinstance(regex, str): raise TypeError("'substr' argument must be a string") ms = rt.runtime().modules_system - modules = OrderedSet(ms.available_modules(substr)) - for m in modules: - yield m + # Returns e.g. ['Bison/', 'Bison/3.7.6-GCCcore-10.3.0', 'BLIS/', 'BLIS/0.8.1-GCC-10.3.0'] + modules = ms.available_modules('') + for mod in modules: + # Exclude anything without version, i.e. ending with / (e.g. Bison/) + if re.search('.*\/$', mod): + continue + # The thing we yield should always be the original module name (orig_mod), including version + orig_mod = mod + if name_only: + # Remove part after the last forward slash, as we assume this is the version + mod = re.sub('\/[^\/]*$', '', mod) + # Match the actual regular expression + log(f"Matching module {mod} with regex {regex}") + if re.search(regex, mod): + log(f"Match!") + yield orig_mod def check_proc_attribute_defined(test: rfm.RegressionTest, attribute) -> bool: """ @@ -104,4 +147,4 @@ def check_proc_attribute_defined(test: rfm.RegressionTest, attribute) -> bool: "This is a programming error, please report this issue." ) raise AttributeError(msg) - \ No newline at end of file +