diff --git a/src/zenml/utils/string_utils.py b/src/zenml/utils/string_utils.py index 3cee230e1e2..f0d8519bc92 100644 --- a/src/zenml/utils/string_utils.py +++ b/src/zenml/utils/string_utils.py @@ -183,7 +183,7 @@ def format_name_template( KeyError: If a key in template is missing in the substitutions. ValueError: If the formatted name is empty. """ - substitutions = substitutions or {} + substitutions = dict(substitutions or {}) if ("date" not in substitutions and "{date}" in name_template) or ( "time" not in substitutions and "{time}" in name_template diff --git a/tests/unit/utils/test_string_utils.py b/tests/unit/utils/test_string_utils.py index c2d715e63ac..861862f5555 100644 --- a/tests/unit/utils/test_string_utils.py +++ b/tests/unit/utils/test_string_utils.py @@ -116,3 +116,29 @@ def substitution_func(s): {3: "value_suffix", "key_suffix": model_sub}, set(["set_value_suffix", 4]), ] + + +def test_format_name_template_does_not_mutate_substitutions() -> None: + """The caller's substitutions dict must not be modified. + + `substitutions or {}` only builds a new dict for a falsy argument, so a + non-empty dict was aliased and had date/time injected into it. + """ + substitutions = {"custom": "value"} + + formatted = string_utils.format_name_template( + "run-{custom}-{date}-{time}", substitutions=substitutions + ) + + assert substitutions == {"custom": "value"} + assert formatted.startswith("run-value-") + + +def test_format_name_template_uses_given_substitutions() -> None: + """Explicit date/time substitutions are still honored.""" + formatted = string_utils.format_name_template( + "run-{date}-{time}", + substitutions={"date": "2020_01_01", "time": "00_00_00"}, + ) + + assert formatted == "run-2020_01_01-00_00_00"