parsing: Add key to matrix stages

This commit adds the `key` property to the templaing context of `matrix`
stages. Similarly to `foreach` stages, the `key` is available using
`${key}` substitution.

Fixes #10043
This commit is contained in:
Piotr Bielak
2023-10-31 08:05:38 -05:00
committed by Dave Berenbaum
parent d60557dd08
commit fcc69ab6eb
2 changed files with 63 additions and 1 deletions
+19 -1
View File
@@ -499,8 +499,26 @@ class MatrixDefinition:
iterable = self.context.resolve(self.matrix_data, unwrap=False)
except (ContextError, ParseError) as exc:
format_and_raise(exc, f"'{self.where}.{self.name}.matrix'", self.relpath)
# Matrix entries will have `key` and `item` added to the context.
# Warn users if these are already in the context from the global vars.
self._warn_if_overwriting(["key", "item"])
return iterable
def _warn_if_overwriting(self, keys: List[str]):
warn_for = [k for k in keys if k in self.context]
if warn_for:
linking_verb = "is" if len(warn_for) == 1 else "are"
logger.warning(
(
"%s %s already specified, "
"will be overwritten for stages generated from '%s'"
),
" and ".join(warn_for),
linking_verb,
self.name,
)
@cached_property
def normalized_iterable(self) -> Dict[str, "DictStrAny"]:
"""Convert sequence to Mapping with keys normalized."""
@@ -540,7 +558,7 @@ class MatrixDefinition:
with reraise(KeyError, EntryNotFound(err_message)):
value = self.normalized_iterable[key]
temp_dict = {self.pair.value: value}
temp_dict = {self.pair.key: key, self.pair.value: value}
with self.context.set_temporarily(temp_dict, reserve=True):
# optimization: item and key can be removed on __exit__() as they
# are top-level values, and are not merged recursively.
+44
View File
@@ -50,3 +50,47 @@ def test_matrix_interpolated(tmp_dir, dvc, matrix):
"[email protected]": {"cmd": "echo linux 3.8 --arg2 2 -- out1 out11"},
"[email protected]": {"cmd": "echo linux 3.8 --arg2 2 -- out2 out22"},
}
@pytest.mark.parametrize(
"matrix",
[
MATRIX_DATA,
{
"os": "${os}",
"pyv": "${pyv}",
"dict": "${dict}",
"list": "${list}",
},
],
)
def test_matrix_key_present(tmp_dir, dvc, matrix):
(tmp_dir / "params.yaml").dump(MATRIX_DATA)
resolver = DataResolver(dvc, tmp_dir.fs_path, {})
data = {
"matrix": matrix,
"cmd": "echo ${key}",
}
definition = MatrixDefinition(resolver, resolver.context, "build", data)
assert definition.resolve_all() == {
f"build@{key}": {"cmd": f"echo {key}"}
for key in (
"win-3.7-dict0-list0",
"win-3.7-dict0-list1",
"win-3.7-dict1-list0",
"win-3.7-dict1-list1",
"win-3.8-dict0-list0",
"win-3.8-dict0-list1",
"win-3.8-dict1-list0",
"win-3.8-dict1-list1",
"linux-3.7-dict0-list0",
"linux-3.7-dict0-list1",
"linux-3.7-dict1-list0",
"linux-3.7-dict1-list1",
"linux-3.8-dict0-list0",
"linux-3.8-dict0-list1",
"linux-3.8-dict1-list0",
"linux-3.8-dict1-list1",
)
}