diff --git a/src/CSET/recipes/__init__.py b/src/CSET/recipes/__init__.py index cef45c433..3c92d83fb 100644 --- a/src/CSET/recipes/__init__.py +++ b/src/CSET/recipes/__init__.py @@ -14,9 +14,11 @@ """Operations on recipes.""" +import hashlib import importlib.resources import logging from collections.abc import Iterator +from io import StringIO from pathlib import Path from typing import Any @@ -214,10 +216,18 @@ def parbake(self, ROSE_DATAC: Path, SHARE_DIR: Path) -> None: # Parbake this recipe, saving into recipe_dir. recipe = parse_recipe(Path(self.recipe), self.variables) - output = recipe_dir / f"{slugify(recipe['title'])}.yaml" - with open(output, "wt") as fp: - with YAML(pure=True, output=fp) as yaml: + + # Serialise into memory, as we use the serialised value twice. + with StringIO() as s: + with YAML(pure=True, output=s) as yaml: yaml.dump(recipe) + serialised_recipe = s.getvalue().encode() + # Include shortened hash in filename to avoid collisions between recipes + # with the same title. + digest = hashlib.sha256(serialised_recipe).hexdigest() + output_filename = recipe_dir / f"{slugify(recipe['title'])}_{digest[:12]}.yaml" + with open(output_filename, "wb") as fp: + fp.write(serialised_recipe) class Config: diff --git a/tests/test_recipes.py b/tests/test_recipes.py index 9a866ca65..ab263a1cf 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -209,7 +209,6 @@ def test_RawRecipe_parbake(tmp_working_dir): ) ) # Expected. - parbaked_recipe_file = rose_datac / "recipes/recipe_value.yaml" expected = dedent( f"""\ title: Recipe value @@ -222,6 +221,7 @@ def test_RawRecipe_parbake(tmp_working_dir): # Act. r.parbake(rose_datac, tmp_working_dir) # Assert. + parbaked_recipe_file = next((rose_datac / "recipes").glob("recipe_value_*.yaml")) assert parbaked_recipe_file.exists() assert parbaked_recipe_file.read_text() == expected @@ -248,7 +248,6 @@ def test_RawRecipe_parbake_aggregation(tmp_working_dir): ) ) # Expected. - parbaked_recipe_file = rose_datac / "aggregation_recipes/recipe_value.yaml" expected = dedent( f"""\ title: Recipe value @@ -263,6 +262,9 @@ def test_RawRecipe_parbake_aggregation(tmp_working_dir): # Act. r.parbake(rose_datac, tmp_working_dir) # Assert. + parbaked_recipe_file = next( + (rose_datac / "aggregation_recipes").glob("recipe_value_*.yaml") + ) assert parbaked_recipe_file.exists() assert parbaked_recipe_file.read_text() == expected