From 3090e35a542eaf40c06b8df8781ebe314dee8765 Mon Sep 17 00:00:00 2001 From: Manuel Schlund Date: Mon, 22 May 2023 13:21:58 +0200 Subject: [PATCH 1/2] Fixed usage of custom location for custom tables --- esmvalcore/config/_config.py | 2 +- esmvalcore/config/_validated_config.py | 7 ++++--- tests/unit/config/test_config.py | 13 +++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/esmvalcore/config/_config.py b/esmvalcore/config/_config.py index 4cd0b57aa6..01ecfa8714 100644 --- a/esmvalcore/config/_config.py +++ b/esmvalcore/config/_config.py @@ -100,7 +100,7 @@ def load_config_developer(cfg_file): cfg['obs4MIPs'] = cfg.pop('obs4mips') for project, settings in cfg.items(): - for site, drs in settings['input_dir'].items(): + for site, drs in settings.get('input_dir', {}).items(): # Since v2.8, 'version' can be used instead of 'latestversion' if isinstance(drs, list): drs = [d.replace('{latestversion}', '{version}') for d in drs] diff --git a/esmvalcore/config/_validated_config.py b/esmvalcore/config/_validated_config.py index 8c55dacc40..27048397a7 100644 --- a/esmvalcore/config/_validated_config.py +++ b/esmvalcore/config/_validated_config.py @@ -61,13 +61,14 @@ def __init__(self, *args, **kwargs): def __setitem__(self, key, val): """Map key to value.""" + if key not in self._validate: + raise InvalidConfigParameter( + f"`{key}` is not a valid config parameter." + ) try: cval = self._validate[key](val) except ValidationError as verr: raise InvalidConfigParameter(f"Key `{key}`: {verr}") from None - except KeyError: - raise InvalidConfigParameter( - f"`{key}` is not a valid config parameter.") from None if key in self._deprecate: self._deprecate[key](self, val, cval) diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index cb7619970e..2072c7a49d 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -243,3 +243,16 @@ def test_project_obs4mips_case_correction(tmp_path, monkeypatch, mocker): assert 'obs4mips' not in _config.CFG assert _config.CFG['obs4MIPs'] == project_cfg + + +def test_load_config_developer_custom(tmp_path, monkeypatch, mocker): + monkeypatch.setattr(_config, 'CFG', {}) + mocker.patch.object(_config, 'read_cmor_tables', autospec=True) + cfg_file = tmp_path / 'config-developer.yml' + cfg_dev = {'custom': {'cmor_path': '/path/to/tables'}} + with cfg_file.open('w') as file: + yaml.safe_dump(cfg_dev, file) + + _config.load_config_developer(cfg_file) + + assert 'custom' in _config.CFG From ab0b67a9e128b9378e8f7f4eb50f1129dbf8e26f Mon Sep 17 00:00:00 2001 From: Manuel Schlund Date: Mon, 22 May 2023 14:01:21 +0200 Subject: [PATCH 2/2] Added further tests --- tests/unit/config/test_config_validator.py | 51 +++++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/tests/unit/config/test_config_validator.py b/tests/unit/config/test_config_validator.py index ce90f2f0fb..b4d40b5128 100644 --- a/tests/unit/config/test_config_validator.py +++ b/tests/unit/config/test_config_validator.py @@ -2,7 +2,9 @@ import numpy as np import pytest +import yaml +import esmvalcore from esmvalcore import __version__ as current_version from esmvalcore.config._config_validators import ( _handle_deprecation, @@ -10,6 +12,7 @@ validate_bool, validate_bool_or_none, validate_check_level, + validate_config_developer, validate_diagnostics, validate_float, validate_int, @@ -128,8 +131,17 @@ def generate_validator_testcases(valid): }, { 'validator': validate_path_or_none, - 'success': ((None, None), ), - 'fail': (), + 'success': ( + ('a/b/c', Path.cwd() / 'a' / 'b' / 'c'), + ('/a/b/c/', Path('/', 'a', 'b', 'c')), + ('~/', Path.home()), + (None, None), + ), + 'fail': ( + (123, ValueError), + (False, ValueError), + ([], ValueError), + ), }, { 'validator': validate_positive, @@ -240,3 +252,38 @@ def test_handle_deprecation(remove_version): _handle_deprecation( option, deprecated_version, remove_version, more_info ) + + +def test_validate_config_developer_none(): + """Test ``validate_config_developer``.""" + path = validate_config_developer(None) + assert path == Path(esmvalcore.__file__).parent / 'config-developer.yml' + + +def test_validate_config_developer(tmp_path): + """Test ``validate_config_developer``.""" + custom_table_path = ( + Path(esmvalcore.__file__).parent / 'cmor' / 'tables' / 'custom' + ) + cfg_dev = { + 'custom': {'cmor_path': custom_table_path}, + 'CMIP3': {'input_dir': {'default': '/'}}, + 'CMIP5': {'input_dir': {'default': '/'}}, + 'CMIP6': {'input_dir': {'default': '/'}}, + 'CORDEX': {'input_dir': {'default': '/'}}, + 'OBS': {'input_dir': {'default': '/'}}, + 'OBS6': {'input_dir': {'default': '/'}}, + 'obs4MIPs': {'input_dir': {'default': '/'}}, + 'ana4mips': {'input_dir': {'default': '/'}}, + 'native6': {'input_dir': {'default': '/'}}, + 'EMAC': {'input_dir': {'default': '/'}}, + 'IPSLCM': {'input_dir': {'default': '/'}}, + 'ICON': {'input_dir': {'default': '/'}}, + 'CESM': {'input_dir': {'default': '/'}}, + } + cfg_dev_file = tmp_path / 'cfg-developer.yml' + with open(cfg_dev_file, mode='w', encoding='utf-8') as file: + yaml.safe_dump(cfg_dev, file) + + path = validate_config_developer(cfg_dev_file) + assert path == cfg_dev_file