SpecList: fix recursion for references (#16897)
* SpecList: fix and refactor variable expansion
This commit is contained in:
parent
11b5fa7170
commit
2421d903b0
@ -121,38 +121,42 @@ def update_reference(self, reference):
|
|||||||
self._constraints = None
|
self._constraints = None
|
||||||
self._specs = None
|
self._specs = None
|
||||||
|
|
||||||
|
def _parse_reference(self, name):
|
||||||
|
sigil = ''
|
||||||
|
name = name[1:]
|
||||||
|
|
||||||
|
# Parse specs as constraints
|
||||||
|
if name.startswith('^') or name.startswith('%'):
|
||||||
|
sigil = name[0]
|
||||||
|
name = name[1:]
|
||||||
|
|
||||||
|
# Make sure the reference is valid
|
||||||
|
if name not in self._reference:
|
||||||
|
msg = 'SpecList %s refers to ' % self.name
|
||||||
|
msg += 'named list %s ' % name
|
||||||
|
msg += 'which does not appear in its reference dict'
|
||||||
|
raise UndefinedReferenceError(msg)
|
||||||
|
|
||||||
|
return (name, sigil)
|
||||||
|
|
||||||
def _expand_references(self, yaml):
|
def _expand_references(self, yaml):
|
||||||
if isinstance(yaml, list):
|
if isinstance(yaml, list):
|
||||||
for idx, item in enumerate(yaml):
|
ret = []
|
||||||
if isinstance(item, string_types) and item.startswith('$'):
|
|
||||||
# Reference type can add a constraint to items
|
|
||||||
if item[1] in ('^', '%'):
|
|
||||||
name = item[2:]
|
|
||||||
sigil = item[1]
|
|
||||||
else:
|
|
||||||
name = item[1:]
|
|
||||||
sigil = ''
|
|
||||||
if name in self._reference:
|
|
||||||
ret = [self._expand_references(i) for i in yaml[:idx]]
|
|
||||||
ret += self._reference[name].specs_as_yaml_list
|
|
||||||
ret += self._expand_references(yaml[idx + 1:])
|
|
||||||
|
|
||||||
# Add the sigil if we're mapping a sigil to a ref
|
for item in yaml:
|
||||||
def sigilify(arg):
|
# if it's a reference, expand it
|
||||||
if isinstance(arg, dict):
|
if isinstance(item, string_types) and item.startswith('$'):
|
||||||
if sigil:
|
# replace the reference and apply the sigil if needed
|
||||||
arg['sigil'] = sigil
|
name, sigil = self._parse_reference(item)
|
||||||
return arg
|
referent = [
|
||||||
else:
|
_sigilify(item, sigil)
|
||||||
return sigil + arg
|
for item in self._reference[name].specs_as_yaml_list
|
||||||
return list(map(sigilify, ret))
|
]
|
||||||
else:
|
ret.extend(referent)
|
||||||
msg = 'SpecList %s refers to ' % self.name
|
else:
|
||||||
msg += 'named list %s ' % name
|
# else just recurse
|
||||||
msg += 'which does not appear in its reference dict'
|
ret.append(self._expand_references(item))
|
||||||
raise UndefinedReferenceError(msg)
|
return ret
|
||||||
# No references in this
|
|
||||||
return [self._expand_references(item) for item in yaml]
|
|
||||||
elif isinstance(yaml, dict):
|
elif isinstance(yaml, dict):
|
||||||
# There can't be expansions in dicts
|
# There can't be expansions in dicts
|
||||||
return dict((name, self._expand_references(val))
|
return dict((name, self._expand_references(val))
|
||||||
@ -216,6 +220,15 @@ def _expand_matrix_constraints(object, specify=True):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def _sigilify(item, sigil):
|
||||||
|
if isinstance(item, dict):
|
||||||
|
if sigil:
|
||||||
|
item['sigil'] = sigil
|
||||||
|
return item
|
||||||
|
else:
|
||||||
|
return sigil + item
|
||||||
|
|
||||||
|
|
||||||
class SpecListError(SpackError):
|
class SpecListError(SpackError):
|
||||||
"""Error class for all errors related to SpecList objects."""
|
"""Error class for all errors related to SpecList objects."""
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
import pytest
|
||||||
import itertools
|
import itertools
|
||||||
from spack.spec_list import SpecList
|
from spack.spec_list import SpecList
|
||||||
from spack.spec import Spec
|
from spack.spec import Spec
|
||||||
@ -157,6 +158,22 @@ def test_spec_list_nested_matrices(self):
|
|||||||
expected = [Spec(' '.join(combo)) for combo in expected_components]
|
expected = [Spec(' '.join(combo)) for combo in expected_components]
|
||||||
assert set(speclist.specs) == set(expected)
|
assert set(speclist.specs) == set(expected)
|
||||||
|
|
||||||
|
@pytest.mark.regression('16897')
|
||||||
|
def test_spec_list_recursion_specs_as_constraints(self):
|
||||||
|
input = ['mpileaks', '$mpis',
|
||||||
|
{'matrix': [['hypre'], ['$%gccs', '$%clangs']]},
|
||||||
|
'libelf']
|
||||||
|
|
||||||
|
reference = {'gccs': SpecList('gccs', ['gcc@4.5.0']),
|
||||||
|
'clangs': SpecList('clangs', ['clang@3.3']),
|
||||||
|
'mpis': SpecList('mpis', ['zmpi@1.0', 'mpich@3.0'])}
|
||||||
|
|
||||||
|
speclist = SpecList('specs', input, reference)
|
||||||
|
|
||||||
|
assert speclist.specs_as_yaml_list == self.default_expansion
|
||||||
|
assert speclist.specs_as_constraints == self.default_constraints
|
||||||
|
assert speclist.specs == self.default_specs
|
||||||
|
|
||||||
def test_spec_list_matrix_exclude(self, mock_packages):
|
def test_spec_list_matrix_exclude(self, mock_packages):
|
||||||
# Test on non-boolean variants for regression for #16841
|
# Test on non-boolean variants for regression for #16841
|
||||||
matrix = [{'matrix': [['multivalue-variant'], ['foo=bar', 'foo=baz']],
|
matrix = [{'matrix': [['multivalue-variant'], ['foo=bar', 'foo=baz']],
|
||||||
|
Loading…
Reference in New Issue
Block a user