From f0afe6a6fb9273f08d9f5543ad25c5a21ca9ba1b Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 5 Nov 2026 12:00:15 +0100 Subject: [PATCH] refactor: parse each source YAML once per run --- scripts/validate_schemas.py | 25 +++++++++++++++++++------ tests/test_data_dir_refs.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/scripts/validate_schemas.py b/scripts/validate_schemas.py index dc4bf97aa..bb377a2a6 100644 --- a/scripts/validate_schemas.py +++ b/scripts/validate_schemas.py @@ -39,6 +39,22 @@ def _errors(validator: Draft202012Validator, data: object, label: str) -> list[s return out +_SOURCES: dict[tuple[Path, int, int], object] = {} + + +def _load_source(path: Path) -> object: + """A source YAML, parsed once per run whichever check reads it: the + schema pass, the data directory refs and the layout check each parsed + all 649 profiles again. Keyed by size and mtime too, so a file written + again in the same process is read again.""" + stat = path.stat() + key = (path, stat.st_mtime_ns, stat.st_size) + if key not in _SOURCES: + with path.open(encoding="utf-8") as handle: + _SOURCES[key] = yaml_load(handle) + return _SOURCES[key] + + def _validate_yaml_directory( directory: Path, schema_name: str, @@ -51,8 +67,7 @@ def _validate_yaml_directory( if skip_private and path.name.startswith("_"): continue try: - with path.open(encoding="utf-8") as handle: - data = yaml_load(handle) + data = _load_source(path) except (OSError, yaml.YAMLError) as exc: out.append(f"{path.relative_to(ROOT)}: {exc}") continue @@ -273,8 +288,7 @@ def _unknown_data_dir_refs() -> list[str]: for path in source_yaml_paths(directory): if path.name.startswith("_"): continue - with path.open(encoding="utf-8") as handle: - document = yaml_load(handle) or {} + document = _load_source(path) or {} systems = document.get("systems") holders = [document, *(systems.values() if isinstance(systems, dict) else [])] for holder in holders: @@ -299,8 +313,7 @@ def _repository_layout_in_profiles() -> list[str]: """ out: list[str] = [] for path in source_yaml_paths(ROOT / "emulators"): - with path.open(encoding="utf-8") as handle: - document = yaml_load(handle) or {} + document = _load_source(path) or {} for entry in document.get("files") or []: if not isinstance(entry, dict): continue diff --git a/tests/test_data_dir_refs.py b/tests/test_data_dir_refs.py index ae9fc4f76..df5d00bac 100644 --- a/tests/test_data_dir_refs.py +++ b/tests/test_data_dir_refs.py @@ -23,5 +23,36 @@ class DataDirRefsAreRegistered(unittest.TestCase): self.assertEqual(validate_schemas._unknown_data_dir_refs(), []) + +class EachSourceIsParsedOnce(unittest.TestCase): + """The schema pass, the data directory refs and the layout check each + parsed every profile again: 2,038 parses for about 690 files.""" + + def test_three_checks_one_parse(self): + import tempfile + from unittest import mock + + try: + import validate_schemas + except ImportError as exc: + self.skipTest(f"validate_schemas needs jsonschema: {exc}") + with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp") as tmp: + root = Path(tmp) + (root / "emulators").mkdir() + (root / "platforms").mkdir() + (root / "platforms" / "_data_dirs.yml").write_text("data_directories: {}\n") + (root / "emulators" / "core.yml").write_text( + "emulator: Core\nfiles:\n- name: a.bin\n path: .variants/a.bin\n") + loaded = [] + real = validate_schemas.yaml_load + with mock.patch.object(validate_schemas, "ROOT", root), \ + mock.patch.object(validate_schemas, "yaml_load", + side_effect=lambda h: loaded.append(h.name) or real(h)): + validate_schemas._validate_yaml_directory(root / "emulators", "emulator.schema.json") + validate_schemas._unknown_data_dir_refs() + layout = validate_schemas._repository_layout_in_profiles() + self.assertEqual(sorted(Path(n).name for n in loaded), ["_data_dirs.yml", "core.yml"]) + self.assertEqual(len(layout), 1) + if __name__ == "__main__": unittest.main()