diff --git a/CHANGES.rst b/CHANGES.rst index 8c173492f..f4930654e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,14 @@ Changelog ========= +v0.49.0 (unreleased) +-------------------- +Contributors to this version: Juliette Lavoie (:user:`juliettelavoie`). + +Bug fixes +^^^^^^^^^ +* Add `measure` to YAML validation schema (for building sdba properties) and allow skipping the YAML validation when building modules. ( :pull:`1664`). + v0.48.1 (2024-02-20) -------------------- Contributors to this version: Trevor James Smith (:user:`Zeitsperre`). diff --git a/tests/test_modules.py b/tests/test_modules.py index 1927fa94f..85eee2f3e 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -224,6 +224,39 @@ def test_realm(tmp_path): assert mod.ice_extent.realm == "ocean" +def test_validate(tmp_path): + # Regression test for #1425 + yml = """ + realm: land + + indicators: + ice_extent: + base: sea_ice_extent + realm: ocean + this_is_not_accepted: True + """ + fh = tmp_path / "test.yml" + fh.write_text(yml) + + with pytest.raises(yamale.YamaleError): + build_indicator_module_from_yaml(fh, name="test") + + build_indicator_module_from_yaml(fh, name="test2", validate=False) + + sch = r""" +realm: str(required=False) +indicators: map(include('indicator'), key=regex(r'^[-\w]+$')) +--- +indicator: + base: str(required=False) + realm: str(required=False) + this_is_not_accepted: bool() +""" + fsch = tmp_path / "schema.yml" + fsch.write_text(sch) + build_indicator_module_from_yaml(fh, name="test3", validate=fsch) + + class TestOfficialYaml(yamale.YamaleTestCase): base_dir = str(Path(__file__).parent.parent / "xclim" / "data") schema = "schema.yml" diff --git a/xclim/core/indicator.py b/xclim/core/indicator.py index 2c2ed5323..deb0125a7 100644 --- a/xclim/core/indicator.py +++ b/xclim/core/indicator.py @@ -1664,6 +1664,7 @@ def build_indicator_module_from_yaml( # noqa: C901 mode: str = "raise", encoding: str = "UTF8", reload: bool = False, + validate: bool | PathLike = True, ) -> ModuleType: """Build or extend an indicator module from a YAML file. @@ -1694,6 +1695,10 @@ def build_indicator_module_from_yaml( # noqa: C901 reload : bool If reload is True and the module already exists, it is first removed before being rebuilt. If False (default), indicators are added or updated, but not removed. + validate : bool or path + If True (default), the yaml module is validated against xclim's schema. + Can also be the path to a yml schema against which to validate. + Or False, in which case validation is simply skipped. Returns ------- @@ -1731,13 +1736,19 @@ def build_indicator_module_from_yaml( # noqa: C901 with ymlpath.open(encoding=encoding) as f: yml = safe_load(f) - # Read schema - schema = yamale.make_schema(Path(__file__).parent.parent / "data" / "schema.yml") + if validate is not False: + # Read schema + if validate is not True: + schema = yamale.make_schema(validate) + else: + schema = yamale.make_schema( + Path(__file__).parent.parent / "data" / "schema.yml" + ) - # Validate - a YamaleError will be raised if the module does not comply with the schema. - yamale.validate( - schema, yamale.make_data(content=ymlpath.read_text(encoding=encoding)) - ) + # Validate - a YamaleError will be raised if the module does not comply with the schema. + yamale.validate( + schema, yamale.make_data(content=ymlpath.read_text(encoding=encoding)) + ) # Load values from top-level in yml. # Priority of arguments differ. diff --git a/xclim/data/schema.yml b/xclim/data/schema.yml index f9d2dd7e1..b36ec5baf 100644 --- a/xclim/data/schema.yml +++ b/xclim/data/schema.yml @@ -15,6 +15,7 @@ indicator: compute: str(required=False) input: map(str(), key=str(), required=False) keywords: str(required=False) + measure: str(required=False) missing: str(required=False) missing_options: map(key=str(), required=False) notes: str(required=False)