diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 76790c430..c500ffc84 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -80,7 +80,7 @@ def launch_command(self, stagedir): _VALID_ENV_SYNTAX = rf'^({_NW}|{_FKV}(\s+{_FKV})*)$' _S = rf'({_NW}(:{_NW})?)' # system/partition -_VALID_SYS_SYNTAX = rf'^({_S}|{_FKV}(\s+{_FKV})*)$' +_VALID_SYS_SYNTAX = rf'^(({_FKV}\s+)*{_S}(\s+{_FKV})*|{_FKV}(\s+{_FKV})*)$' _PIPELINE_STAGES = ( diff --git a/reframe/core/runtime.py b/reframe/core/runtime.py index e3fbce980..ad237d5aa 100644 --- a/reframe/core/runtime.py +++ b/reframe/core/runtime.py @@ -289,50 +289,60 @@ def is_env_loaded(environ): def _is_valid_part(part, valid_systems): - for spec in valid_systems: - if spec[0] not in ('+', '-', '%'): - # This is the standard case - sysname, partname = part.fullname.split(':') - valid_matches = ['*', '*:*', sysname, f'{sysname}:*', - f'*:{partname}', f'{part.fullname}'] - if spec in valid_matches: - return True - else: - plus_feats = [] - minus_feats = [] - props = {} - for subspec in spec.split(' '): - if subspec.startswith('+'): - plus_feats.append(subspec[1:]) - elif subspec.startswith('-'): - minus_feats.append(subspec[1:]) - elif subspec.startswith('%'): - key, val = subspec[1:].split('=') - props[key] = val - - have_plus_feats = all( - (ft in part.features or - ft in part.resources or ft in part.extras) - for ft in plus_feats - ) - have_minus_feats = any( - (ft in part.features or - ft in part.resources or ft in part.extras) - for ft in minus_feats - ) - try: - have_props = True - for k, v in props.items(): - extra_value = part.extras[k] - extra_type = type(extra_value) - if extra_value != extra_type(v): - have_props = False - break - except (KeyError, ValueError): - have_props = False + # Get sysname and partname for the partition being checked and construct + # all valid_matches for the partition being checked + sysname, partname = part.fullname.split(':') + valid_matches = ['*', '*:*', sysname, f'{sysname}:*', f'*:{partname}', + f'{part.fullname}'] - if have_plus_feats and not have_minus_feats and have_props: - return True + # If any of the specs in valid_systems matches, this is a valid partition + for spec in valid_systems: + plus_feats = [] + minus_feats = [] + props = {} + syspart_match = True + for subspec in spec.split(' '): + if subspec.startswith('+'): + plus_feats.append(subspec[1:]) + elif subspec.startswith('-'): + minus_feats.append(subspec[1:]) + elif subspec.startswith('%'): + key, val = subspec[1:].split('=') + props[key] = val + elif not subspec.startswith(('+', '-', '%')): + # If there is a system:partition specified, make sure it + # matches one of the items in valid_matches + syspart_match = True if subspec in valid_matches else False + + have_plus_feats = all( + (ft in part.features or + ft in part.resources or ft in part.extras) + for ft in plus_feats + ) + have_minus_feats = any( + (ft in part.features or + ft in part.resources or ft in part.extras) + for ft in minus_feats + ) + try: + have_props = True + for k, v in props.items(): + extra_value = part.extras[k] + extra_type = type(extra_value) + if extra_value != extra_type(v): + have_props = False + break + except (KeyError, ValueError): + have_props = False + + # If the partition has all the plus features, none of the minus + # all of the properties and the system:partition spec (if any) + # matched, this partition is valid + if ( + have_plus_feats and not have_minus_feats and have_props + and syspart_match + ): + return True return False diff --git a/unittests/test_pipeline.py b/unittests/test_pipeline.py index aba0800b4..aedd220f5 100644 --- a/unittests/test_pipeline.py +++ b/unittests/test_pipeline.py @@ -359,6 +359,8 @@ def test_valid_systems_syntax(hellotest): hellotest.valid_systems = ['+x0 -y0 %z0=w0'] hellotest.valid_systems = ['-y0 +x0 %z0=w0'] hellotest.valid_systems = ['%z0=w0 +x0 -y0'] + hellotest.valid_systems = ['sys:part +x0'] + hellotest.valid_systems = ['+x0 sys:part'] with pytest.raises(TypeError): hellotest.valid_systems = ['']