diff --git a/doc/quickstart/configure.rst b/doc/quickstart/configure.rst index fc30aea5e8..c8f4d1e550 100644 --- a/doc/quickstart/configure.rst +++ b/doc/quickstart/configure.rst @@ -80,23 +80,6 @@ Within a directory, files are sorted lexicographically, and later files (e.g., files (like the old ``config-developer.yml`` files) will lead to errors. Make sure to move these files to a different directory. -.. deprecated:: 2.12.0 - - If a single configuration file is present at its deprecated location - ``~/.esmvaltool/config-user.yml`` or specified via the deprecated command - line argument ``--config_file``, all potentially available new configuration - files at ``~/.config/esmvaltool/`` and/or the location specified via - ``--config_dir`` are ignored. - This ensures full backwards-compatibility. - To switch to the new configuration system outlined here, move your old - configuration file to ``~/.config/esmvaltool/`` or to the location specified - via ``--config_dir``, remove ``~/.esmvaltool/config-user.yml``, and omit the - command line argument ``--config_file``. - Alternatively, specifying the environment variable ``ESMVALTOOL_CONFIG_DIR`` - will also force the usage of the new configuration system regardless of the - presence of any potential old configuration files. - Support for the deprecated configuration will be removed in version 2.14.0. - To get a copy of the default configuration file, you can run .. code-block:: bash diff --git a/esmvalcore/_main.py b/esmvalcore/_main.py index 4f31b4cec3..2f8d95d84e 100644 --- a/esmvalcore/_main.py +++ b/esmvalcore/_main.py @@ -32,14 +32,12 @@ import logging import os import sys -import warnings from importlib.metadata import entry_points from pathlib import Path import fire from esmvalcore.config._config import warn_if_old_extra_facets_exist -from esmvalcore.exceptions import ESMValCoreDeprecationWarning # set up logging logger = logging.getLogger(__name__) @@ -401,7 +399,6 @@ def run(self, recipe, **kwargs): """ from .config import CFG - from .config._dask import warn_if_old_dask_config_exists from .exceptions import InvalidConfigParameter cli_config_dir = kwargs.pop("config_dir", None) @@ -413,38 +410,6 @@ def run(self, recipe, **kwargs): f"existing directory" ) raise NotADirectoryError(msg) - - # TODO: remove in v2.14.0 - # At this point, --config_file is already parsed if a valid file has - # been given (see - # https://github.com/ESMValGroup/ESMValCore/issues/2280), but no error - # has been raised if the file does not exist. Thus, reload the file - # here with `load_from_file` to make sure a proper error is raised. - if "config_file" in kwargs: - if os.environ.get("ESMVALTOOL_CONFIG_DIR"): - deprecation_msg = ( - "Usage of a single configuration file specified via CLI " - "argument `--config_file` has been deprecated in " - "ESMValCore version 2.12.0 and is scheduled for removal " - "in version 2.14.0. Since the environment variable " - "ESMVALTOOL_CONFIG_DIR is set, old configuration files " - "present at ~/.esmvaltool/config-user.yml and/or " - "specified via `--config_file` are currently ignored. To " - "silence this warning, omit CLI argument `--config_file`." - ) - warnings.warn( - deprecation_msg, - ESMValCoreDeprecationWarning, - stacklevel=2, - ) - kwargs.pop("config_file") - else: - cli_config_dir = kwargs["config_file"] - CFG.load_from_file(kwargs["config_file"]) - - # New in v2.12.0: read additional configuration directory given by CLI - # argument - if CFG.get("config_file") is None and cli_config_dir is not None: try: CFG.update_from_dirs([cli_config_dir]) except InvalidConfigParameter as exc: @@ -475,7 +440,6 @@ def run(self, recipe, **kwargs): CFG.update_from_dirs([cli_config_dir]) CFG.nested_update(kwargs) - warn_if_old_dask_config_exists() warn_if_old_extra_facets_exist() @staticmethod @@ -577,35 +541,24 @@ def _get_recipe(recipe) -> Path: @staticmethod def _get_config_info(cli_config_dir): """Get information about config files for logging.""" - from .config import CFG from .config._config_object import ( - DEFAULT_CONFIG_DIR, _get_all_config_dirs, _get_all_config_sources, ) - # TODO: remove in v2.14.0 - if CFG.get("config_file") is not None: - config_info = [ - (DEFAULT_CONFIG_DIR, "defaults"), - (CFG["config_file"], "single configuration file [deprecated]"), - ] - - # New in v2.12.0 - else: - config_dirs = [] - for path in _get_all_config_dirs(cli_config_dir): - if not path.is_dir(): - config_dirs.append(f"{path} [NOT AN EXISTING DIRECTORY]") - else: - config_dirs.append(str(path)) - config_info = list( - zip( - config_dirs, - _get_all_config_sources(cli_config_dir), - strict=False, - ), - ) + config_dirs = [] + for path in _get_all_config_dirs(cli_config_dir): + if not path.is_dir(): + config_dirs.append(f"{path} [NOT AN EXISTING DIRECTORY]") + else: + config_dirs.append(str(path)) + config_info = list( + zip( + config_dirs, + _get_all_config_sources(cli_config_dir), + strict=False, + ), + ) return "\n".join(f"{i[0]} ({i[1]})" for i in config_info) @@ -623,6 +576,12 @@ def _log_header(self, log_files, cli_config_dir): "Reading configuration files from:\n%s", self._get_config_info(cli_config_dir), ) + old_config_file = Path.home() / ".esmvaltool" / "config-user.yml" + if old_config_file.exists(): + logger.warning( + "Ignoring old configuration file at %s", + old_config_file, + ) logger.info("Writing program log files to:\n%s", "\n".join(log_files)) diff --git a/esmvalcore/config/_config_object.py b/esmvalcore/config/_config_object.py index 2fee096fa5..bb3dcaba0d 100644 --- a/esmvalcore/config/_config_object.py +++ b/esmvalcore/config/_config_object.py @@ -4,13 +4,11 @@ import datetime import os -import sys import warnings from pathlib import Path from typing import TYPE_CHECKING import dask.config -import yaml import esmvalcore from esmvalcore.config._config_validators import ( @@ -20,7 +18,6 @@ ) from esmvalcore.config._validated_config import ValidatedConfig from esmvalcore.exceptions import ( - ESMValCoreDeprecationWarning, InvalidConfigParameter, ) @@ -50,9 +47,7 @@ def _get_user_config_dir() -> Path: f"ESMVALTOOL_CONFIG_DIR environment variable: " f"{user_config_dir} is not an existing directory" ) - raise NotADirectoryError( - msg, - ) + raise NotADirectoryError(msg) return user_config_dir return Path.home() / ".config" / "esmvaltool" @@ -79,9 +74,6 @@ class Config(ValidatedConfig): """ - # TODO: remove in v2.14.0 - _DEFAULT_USER_CONFIG_DIR = Path.home() / ".esmvaltool" - _validate = _validators _deprecate = _deprecators _deprecated_defaults = _deprecated_options_defaults @@ -99,227 +91,6 @@ def __init__(self, *args, **kwargs): ) warnings.warn(msg, UserWarning, stacklevel=2) - # TODO: remove in v2.14.0 - @classmethod - def _load_user_config( - cls, - filename: os.PathLike | str | None = None, - raise_exception: bool = True, - ): - """Load user configuration from the given file. - - The config is cleared and updated in-place. - - Parameters - ---------- - filename: - Name of the user configuration file (must be YAML format). If - `None`, use the rules given in `Config._get_config_user_path` to - determine the path. - raise_exception : bool - If ``True``, raise an exception if `filename` cannot be found. If - ``False``, silently pass and use the default configuration. This - setting is necessary during the loading of this module when no - configuration file is given (relevant if used within a script or - notebook). - """ - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", - message="Do not instantiate `Config` objects directly", - category=UserWarning, - module="esmvalcore", - ) - new = cls() - new.update(Config._load_default_config()) - - config_user_path = cls._get_config_user_path(filename) - - try: - mapping = cls._read_config_file(config_user_path) - mapping["config_file"] = config_user_path - except FileNotFoundError: - if raise_exception: - raise - mapping = {} - - try: - new.update(mapping) - new.check_missing() - except InvalidConfigParameter as exc: - msg = ( - f"Failed to parse user configuration file {config_user_path}: " - f"{exc!s}" - ) - raise InvalidConfigParameter(msg) from exc - - return new - - # TODO: remove in v2.14.0 - @classmethod - def _load_default_config(cls): - """Load the default configuration.""" - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", - message="Do not instantiate `Config` objects directly", - category=UserWarning, - module="esmvalcore", - ) - new = cls() - - paths = [DEFAULT_CONFIG_DIR] - mapping = dask.config.collect(paths=paths, env={}) - new.update(mapping) - - return new - - # TODO: remove in v2.14.0 - @staticmethod - def _read_config_file(config_user_path: Path) -> dict: - """Read configuration file and store settings in a dictionary.""" - if not config_user_path.is_file(): - msg = f"Config file '{config_user_path}' does not exist" - raise FileNotFoundError( - msg, - ) - - with open(config_user_path, encoding="utf-8") as file: - return yaml.safe_load(file) - - # TODO: remove in v2.14.0 - @staticmethod - def _get_config_user_path( - filename: os.PathLike | str | None = None, - ) -> Path: - """Get path to user configuration file. - - `filename` can be given as absolute or relative path. In the latter - case, search in the current working directory and `~/.esmvaltool` (in - that order). - - If `filename` is not given, try to get user configuration file from the - following locations (sorted by descending priority): - - 1. Internal `_ESMVALTOOL_USER_CONFIG_FILE_` environment variable - (this ensures that any subprocess spawned by the esmvaltool program - will use the correct user configuration file). - 2. Command line arguments `--config-file` or `--config_file` (both - variants are allowed by the fire module), but only if script name is - `esmvaltool`. - 3. `config-user.yml` within default ESMValTool configuration directory - `~/.esmvaltool`. - - Note - ---- - This will NOT check if the returned file actually exists to allow - loading the module without any configuration file (this is relevant if - the module is used within a script or notebook). To check if the file - actually exists, use the method `load_from_file` (this is done when - using the `esmvaltool` CLI). - - If used within the esmvaltool program, set the - _ESMVALTOOL_USER_CONFIG_FILE_ at the end of this method to make sure - that subsequent calls of this method (also in suprocesses) use the - correct user configuration file. - - """ - # (1) Try to get user configuration file from `filename` argument - config_user = filename - - # (2) Try to get user configuration file from internal - # _ESMVALTOOL_USER_CONFIG_FILE_ environment variable - if ( - config_user is None - and "_ESMVALTOOL_USER_CONFIG_FILE_" in os.environ - ): - config_user = os.environ["_ESMVALTOOL_USER_CONFIG_FILE_"] - - # (3) Try to get user configuration file from CLI arguments - if config_user is None: - config_user = Config._get_config_path_from_cli() - - # (4) Default location - if config_user is None: - config_user = Config._DEFAULT_USER_CONFIG_DIR / "config-user.yml" - - config_user = Path(config_user).expanduser() - - # Also search path relative to ~/.esmvaltool if necessary - if not (config_user.is_file() or config_user.is_absolute()): - config_user = Config._DEFAULT_USER_CONFIG_DIR / config_user - config_user = config_user.absolute() - - # If used within the esmvaltool program, make sure that subsequent - # calls of this method (also in suprocesses) use the correct user - # configuration file - if Path(sys.argv[0]).name == "esmvaltool": - os.environ["_ESMVALTOOL_USER_CONFIG_FILE_"] = str(config_user) - - return config_user - - # TODO: remove in v2.14.0 - @staticmethod - def _get_config_path_from_cli() -> None | str: - """Try to get configuration path from CLI arguments. - - The hack of directly parsing the CLI arguments here (instead of using - the fire or argparser module) ensures that the correct user - configuration file is used. This will always work, regardless of when - this module has been imported in the code. - - Note - ---- - This only works if the script name is `esmvaltool`. Does not check if - file exists. - - """ - if Path(sys.argv[0]).name != "esmvaltool": - return None - - for arg in sys.argv: - for opt in ("--config-file", "--config_file"): - if opt in arg: - # Parse '--config-file=/file.yml' or '--config_file=/file.yml' - partition = arg.partition("=") - if partition[2]: - return partition[2] - - # Parse '--config-file /file.yml' or '--config_file /file.yml' - config_idx = sys.argv.index(opt) - if config_idx == len(sys.argv) - 1: # no file given - return None - return sys.argv[config_idx + 1] - - return None - - # TODO: remove in v2.14.0 - def load_from_file( - self, - filename: os.PathLike | str | None = None, - ) -> None: - """Load user configuration from the given file. - - .. deprecated:: 2.12.0 - This method has been deprecated in ESMValCore version 2.14.0 and is - scheduled for removal in version 2.14.0. Please use - `CFG.load_from_dirs()` instead. - - Parameters - ---------- - filename: - YAML file to load. - - """ - msg = ( - "The method `CFG.load_from_file()` has been deprecated in " - "ESMValCore version 2.12.0 and is scheduled for removal in " - "version 2.14.0. Please use `CFG.load_from_dirs()` instead." - ) - warnings.warn(msg, ESMValCoreDeprecationWarning, stacklevel=2) - self.clear() - self.update(Config._load_user_config(filename)) - @staticmethod def _get_config_dict_from_dirs(dirs: Iterable[str | Path]) -> dict: """Get configuration :obj:`dict` from directories.""" @@ -388,36 +159,6 @@ def reload(self) -> None: Invalid configuration option given. """ - # TODO: remove in v2.14.0 - self.clear() - _deprecated_config_user_path = Config._get_config_user_path() - if _deprecated_config_user_path.is_file() and not os.environ.get( - "ESMVALTOOL_CONFIG_DIR", - ): - deprecation_msg = ( - f"Usage of the single configuration file " - f"~/.esmvaltool/config-user.yml or specifying it via CLI " - f"argument `--config_file` has been deprecated in ESMValCore " - f"version 2.12.0 and is scheduled for removal in version " - f"2.14.0. To switch to the new configuration system, (1) run " - f"`mkdir -p ~/.config/esmvaltool && mv " - f"{_deprecated_config_user_path} ~/.config/esmvaltool` (or " - f"alternatively use a custom `--config_dir`) and omit " - f"`--config_file`, or (2) use the environment variable " - f"ESMVALTOOL_CONFIG_DIR to specify a custom user " - f"configuration directory. New configuration files present at " - f"~/.config/esmvaltool or specified via `--config_dir` are " - f"currently ignored." - ) - warnings.warn( - deprecation_msg, - ESMValCoreDeprecationWarning, - stacklevel=2, - ) - self.update(Config._load_user_config(raise_exception=False)) - return - - # New since v2.12.0 try: self.load_from_dirs([USER_CONFIG_DIR]) except InvalidConfigParameter as exc: @@ -587,26 +328,6 @@ def run_dir(self): """Return run directory.""" return self.session_dir / self.relative_run_dir - # TODO: remove in v2.14.0 - @property - def config_dir(self): - """Return user config directory. - - .. deprecated:: 2.12.0 - This attribute has been deprecated in ESMValCore version 2.12.0 and - is scheduled for removal in version 2.14.0. - - """ - msg = ( - "The attribute `Session.config_dir` has been deprecated in " - "ESMValCore version 2.12.0 and is scheduled for removal in " - "version 2.14.0." - ) - warnings.warn(msg, ESMValCoreDeprecationWarning, stacklevel=2) - if self.get("config_file") is None: - return None - return Path(self["config_file"]).parent - @property def main_log(self): """Return main log file.""" diff --git a/esmvalcore/config/_config_validators.py b/esmvalcore/config/_config_validators.py index c61e4ea309..1b995c6d32 100644 --- a/esmvalcore/config/_config_validators.py +++ b/esmvalcore/config/_config_validators.py @@ -329,14 +329,14 @@ def validate_diagnostics( return validated_diagnostics -# TODO: remove in v2.14.0 +# TODO: remove in v2.15.0 def validate_extra_facets_dir(value): """Validate extra_facets_dir.""" if isinstance(value, tuple): msg = ( "Specifying `extra_facets_dir` as tuple has been deprecated in " - "ESMValCore version 2.12.0 and is scheduled for removal in " - "version 2.14.0. Please use a list instead." + "ESMValCore version 2.13.0 and is scheduled for removal in " + "version 2.15.0. Please use a list instead." ) warnings.warn(msg, ESMValCoreDeprecationWarning, stacklevel=2) value = list(value) @@ -390,9 +390,6 @@ def validate_projects(value: Any) -> Any: "skip_nonexistent": validate_bool, # From recipe "write_ncl_interface": validate_bool, - # TODO: remove in v2.14.0 - # config location - "config_file": validate_path, # TODO: remove in v2.15.0 "extra_facets_dir": validate_extra_facets_dir, } @@ -424,34 +421,6 @@ def _handle_deprecation( warnings.warn(deprecation_msg, ESMValCoreDeprecationWarning, stacklevel=2) -# TODO: remove in v2.14.0 -def deprecate_config_file( - validated_config: ValidatedConfig, - value: Any, - validated_value: Any, -) -> None: - """Deprecate ``config_file`` option. - - Parameters - ---------- - validated_config: - ``ValidatedConfig`` instance which will be modified in place. - value: - Raw input value for ``config_file`` option. - validated_value: - Validated value for ``config_file`` option. - - """ - validated_config # noqa: B018 - value # noqa: B018 - validated_value # noqa: B018 - option = "config_file" - deprecated_version = "2.12.0" - remove_version = "2.14.0" - more_info = " Please use the option `config_dir` instead." - _handle_deprecation(option, deprecated_version, remove_version, more_info) - - # TODO: remove in v2.15.0 def deprecate_extra_facets_dir( validated_config: ValidatedConfig, @@ -493,7 +462,6 @@ def deprecate_extra_facets_dir( # Example usage: see removed files in # https://github.com/ESMValGroup/ESMValCore/pull/2213 _deprecators: dict[str, Callable] = { - "config_file": deprecate_config_file, # TODO: remove in v2.14.0 "extra_facets_dir": deprecate_extra_facets_dir, # TODO: remove in v2.15.0 } @@ -502,6 +470,5 @@ def deprecate_extra_facets_dir( # Example usage: see removed files in # https://github.com/ESMValGroup/ESMValCore/pull/2213 _deprecated_options_defaults: dict[str, Any] = { - "config_file": None, # TODO: remove in v2.14.0 "extra_facets_dir": [], # TODO: remove in v2.15.0 } diff --git a/esmvalcore/config/_dask.py b/esmvalcore/config/_dask.py index 95378f65af..6ffed159c4 100644 --- a/esmvalcore/config/_dask.py +++ b/esmvalcore/config/_dask.py @@ -3,20 +3,15 @@ import contextlib import importlib import logging -import os -import warnings from collections.abc import Generator, Mapping from copy import deepcopy -from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING import dask.config -import yaml from distributed import Client from esmvalcore.config import CFG from esmvalcore.exceptions import ( - ESMValCoreDeprecationWarning, InvalidConfigParameter, ) @@ -25,34 +20,6 @@ logger = logging.getLogger(__name__) -# TODO: Remove in v2.14.0 -CONFIG_FILE = Path.home() / ".esmvaltool" / "dask.yml" - - -# TODO: Remove in v2.14.0 -def warn_if_old_dask_config_exists() -> None: - """Warn user if deprecated dask configuration file exists.""" - if CONFIG_FILE.exists() and not os.environ.get( - "ESMVALTOOL_USE_NEW_DASK_CONFIG", - ): - deprecation_msg = ( - "Usage of Dask configuration file ~/.esmvaltool/dask.yml " - "has been deprecated in ESMValCore version 2.12.0 and is " - "scheduled for removal in version 2.14.0. Please use the " - "configuration option `dask` instead (see " - "https://docs.esmvaltool.org/projects/ESMValCore/en/latest/" - "quickstart/configure.html#dask-configuration for details). " - "Ignoring all existing `dask` configuration options for this run. " - "To enable the new `dask` configuration options, delete or move " - "the file ~/.esmvaltool/dask.yml or set the environment variable " - "ESMVALTOOL_USE_NEW_DASK_CONFIG=1." - ) - warnings.warn( - deprecation_msg, - ESMValCoreDeprecationWarning, - stacklevel=2, - ) - def validate_dask_config(dask_config: Mapping) -> None: """Validate dask configuration options.""" @@ -104,76 +71,12 @@ def validate_dask_config(dask_config: Mapping) -> None: raise InvalidConfigParameter(msg) -# TODO: Remove in v2.14.0 -def _get_old_dask_config() -> dict: - """Get dask configuration dict from old dask configuration file.""" - dask_config: dict[str, Any] = { - "use": "local_threaded", - "profiles": {"local_threaded": {"scheduler": "threads"}}, - } - config = yaml.safe_load(CONFIG_FILE.read_text(encoding="utf-8")) - - # Use settings from file if this is not empty - if config is not None: - client_kwargs = config.get("client", {}) - cluster_kwargs = config.get("cluster", {}) - - # Externally managed cluster - if "address" in client_kwargs: - if cluster_kwargs: - logger.warning( - "Not using Dask 'cluster' settings from %s because a " - "cluster 'address' is already provided in 'client'.", - CONFIG_FILE, - ) - dask_config = { - "use": "external", - "profiles": { - "external": { - "scheduler_address": client_kwargs.pop("address"), - }, - }, - } - - # Dask distributed cluster - elif cluster_kwargs: - cluster_kwargs.setdefault("type", "distributed.LocalCluster") - dask_config = { - "use": "cluster_from_file", - "profiles": { - "cluster_from_file": { - "cluster": cluster_kwargs, - }, - }, - } - - dask_config["client"] = client_kwargs - - return dask_config - - -# TODO: Remove in v2.14.0; used deepcopy(CFG["dask"]) instead -def _get_dask_config() -> dict: - """Get Dask configuration dictionary.""" - if CONFIG_FILE.exists() and not os.environ.get( - "ESMVALTOOL_USE_NEW_DASK_CONFIG", - ): - dask_config = _get_old_dask_config() - else: - dask_config = deepcopy(CFG["dask"]) - return dask_config - - @contextlib.contextmanager def get_distributed_client() -> Generator[None | Client]: """Get a Dask distributed client.""" - warn_if_old_dask_config_exists() - dask_config = _get_dask_config() + dask_config = deepcopy(CFG["dask"]) validate_dask_config(dask_config) - # TODO: Remove in v2.14.0 - client_kwargs = dask_config.get("client", {}) - # Set up cluster and client according to the selected profile # Note: we already ensured earlier that the selected profile (via `use`) # actually exists in `profiles`, so we don't have to check that again here @@ -211,7 +114,7 @@ def get_distributed_client() -> Generator[None | Client]: "scheduler.", ) else: - client = Client(**client_kwargs) + client = Client() logger.info( "Using Dask distributed scheduler (address: %s, dashboard link: " "%s)", diff --git a/tests/conftest.py b/tests/conftest.py index fc6a39c7b2..aaea60b9be 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,7 +15,6 @@ ) from iris.cube import Cube -import esmvalcore.config._dask from esmvalcore.config import CFG, Config @@ -55,30 +54,6 @@ def session(tmp_path: Path, ignore_existing_user_config, monkeypatch): return CFG.start_session("recipe_test") -# TODO: remove in v2.14.0 -@pytest.fixture(autouse=True) -def ignore_old_config_user(tmp_path, monkeypatch): - """Ignore potentially existing old config-user.yml file in all tests.""" - nonexistent_config_dir = tmp_path / "nonexistent_config_dir" - monkeypatch.setattr( - Config, - "_DEFAULT_USER_CONFIG_DIR", - nonexistent_config_dir, - ) - - -# TODO: remove in v2.14.0 -@pytest.fixture(autouse=True) -def ignore_old_dask_config_file(tmp_path, monkeypatch): - """Ignore potentially existing old dask.yml file in all tests.""" - nonexistent_file = tmp_path / "nonexistent_file.yml" - monkeypatch.setattr( - esmvalcore.config._dask, - "CONFIG_FILE", - nonexistent_file, - ) - - @pytest.fixture def realistic_4d_cube(): """Create a realistic 4D cube.""" diff --git a/tests/integration/test_diagnostic_run.py b/tests/integration/test_diagnostic_run.py index 1d148d5c28..c261240843 100644 --- a/tests/integration/test_diagnostic_run.py +++ b/tests/integration/test_diagnostic_run.py @@ -188,68 +188,3 @@ def test_diagnostic_run(tmp_path, script_file, script): run() check(result_file) - - -# TODO: remove in v2.14.0 -@pytest.mark.parametrize( - ("script_file", "script"), - [ - pytest.param( - script_file, - script, - marks=[ - pytest.mark.installation, - pytest.mark.xfail( - interpreter_not_installed(script_file), - run=False, - reason="Interpreter not available", - ), - ], - ) - for script_file, script in SCRIPTS.items() - if script_file != "null" - ], -) -def test_diagnostic_run_old_config(tmp_path, script_file, script): - recipe_file = tmp_path / "recipe_test.yml" - script_file = tmp_path / script_file - result_file = tmp_path / "result.yml" - - # Write script to file - script_file.write_text(str(script)) - - # Create recipe - recipe = dedent( - f""" - documentation: - title: Recipe without data - description: Recipe with no data. - authors: [andela_bouwe] - - diagnostics: - diagnostic_name: - scripts: - script_name: - script: {script_file} - setting_name: {result_file} - """, - ) - recipe_file.write_text(str(recipe)) - - # ensure that tags are cleared - TAGS.clear() - - config_dir = tmp_path / "config" - config_dir.mkdir(parents=True, exist_ok=True) - config_file = write_config_user_file(config_dir) - - with arguments( - "esmvaltool", - "run", - "--config_file", - str(config_file), - str(recipe_file), - ): - run() - - check(result_file) diff --git a/tests/integration/test_main.py b/tests/integration/test_main.py index 61b946c113..a9cfce6366 100644 --- a/tests/integration/test_main.py +++ b/tests/integration/test_main.py @@ -16,7 +16,7 @@ from fire.core import FireExit from esmvalcore._main import Config, ESMValTool, Recipes, run -from esmvalcore.exceptions import ESMValCoreDeprecationWarning, RecipeError +from esmvalcore.exceptions import RecipeError def wrapper(f): @@ -101,100 +101,6 @@ def test_empty_run(tmp_path): assert not filled_recipe.exists() -# TODO: remove in v2.14.0 -def test_empty_run_old_config(tmp_path): - """Test real run with no diags.""" - recipe_file = tmp_path / "recipe.yml" - content = dedent(""" - documentation: - title: Test recipe - description: This is a test recipe. - authors: - - andela_bouwe - references: - - contact_authors - - acknow_project - projects: - - c3s-magic - diagnostics: null - """) - recipe_file.write_text(content) - out_dir = tmp_path / "esmvaltool_output" - config_dir = tmp_path / "config" - config_dir.mkdir(parents=True, exist_ok=True) - config_file = config_dir / "config.yml" - config_file.write_text(f"output_dir: {out_dir}") - - err_msg = "The given recipe does not have any diagnostic." - warn_msg = "Please use the option `config_dir` instead" - with ( - pytest.raises(RecipeError, match=err_msg), - pytest.warns(ESMValCoreDeprecationWarning, match=warn_msg), - ): - ESMValTool().run(recipe_file, config_file=config_file) - - run_dir = out_dir / next(out_dir.iterdir()) / "run" - log_file = run_dir / "main_log.txt" - filled_recipe = run_dir / "recipe_filled.yml" - - assert log_file.exists() - assert not filled_recipe.exists() - - -# TODO: remove in v2.14.0 -def test_empty_run_ignore_old_config(tmp_path, monkeypatch): - """Test real run with no diags.""" - recipe_file = tmp_path / "recipe.yml" - content = dedent(""" - documentation: - title: Test recipe - description: This is a test recipe. - authors: - - andela_bouwe - references: - - contact_authors - - acknow_project - projects: - - c3s-magic - diagnostics: null - """) - recipe_file.write_text(content) - out_dir = tmp_path / "esmvaltool_output" - new_config_dir = tmp_path / "new_config" - new_config_dir.mkdir(parents=True, exist_ok=True) - new_config_file = new_config_dir / "config.yml" - new_config_file.write_text(f"output_dir: {out_dir}") - old_config_dir = tmp_path / "old_config" - old_config_dir.mkdir(parents=True, exist_ok=True) - old_config_file = old_config_dir / "config.yml" - old_config_file.write_text("invalid_option: will be ignored") - - # Note: old config file will be ignored since ESMVALTOOL_CONFIG_DIR is set, - # but its actual value will be ignored since - # esmvalcore.config._config_object.USER_CONFIG_DIR has already been set to - # its default value when loading this module - monkeypatch.setenv("ESMVALTOOL_CONFIG_DIR", "value_does_not_matter") - - err_msg = "The given recipe does not have any diagnostic." - warn_msg = "Since the environment variable ESMVALTOOL_CONFIG_DIR is set" - with ( - pytest.raises(RecipeError, match=err_msg), - pytest.warns(ESMValCoreDeprecationWarning, match=warn_msg), - ): - ESMValTool().run( - recipe_file, - config_file=old_config_file, - config_dir=new_config_dir, - ) - - run_dir = out_dir / next(out_dir.iterdir()) / "run" - log_file = run_dir / "main_log.txt" - filled_recipe = run_dir / "recipe_filled.yml" - - assert log_file.exists() - assert not filled_recipe.exists() - - def test_recipes_get(tmp_path, monkeypatch): """Test version command.""" src_recipe = tmp_path / "recipe.yml" diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index 7795189b3d..7c5ed0e5c3 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -196,7 +196,6 @@ def test_load_default_config(cfg_default, monkeypatch): "preproc_dir", "run_dir", "work_dir", - "config_dir", } # Check that only allowed keys are in it assert set(default_cfg) == set(session) @@ -215,8 +214,6 @@ def test_load_default_config(cfg_default, monkeypatch): for path in ("preproc", "work", "run"): assert getattr(session, path + "_dir") == session.session_dir / path assert session.plot_dir == session.session_dir / "plots" - with pytest.warns(ESMValCoreDeprecationWarning): - assert session.config_dir is None # Check that projects were configured assert project_cfg diff --git a/tests/unit/config/test_config_object.py b/tests/unit/config/test_config_object.py index fd0a4dc393..f974ea574d 100644 --- a/tests/unit/config/test_config_object.py +++ b/tests/unit/config/test_config_object.py @@ -1,5 +1,3 @@ -import os -import warnings from collections.abc import MutableMapping from pathlib import Path from textwrap import dedent @@ -11,10 +9,8 @@ from esmvalcore.config import CFG, Config, Session from esmvalcore.config._config_object import DEFAULT_CONFIG_DIR from esmvalcore.exceptions import ( - ESMValCoreDeprecationWarning, InvalidConfigParameter, ) -from tests.integration.test_main import arguments def test_config_class(): @@ -61,64 +57,14 @@ def test_config_init(): assert isinstance(config, MutableMapping) -# TODO: remove in v2.14.0 -def test_load_from_file(monkeypatch): - default_config_file = DEFAULT_CONFIG_DIR / "config-user.yml" - config = Config() - assert not config - with pytest.warns(ESMValCoreDeprecationWarning): - config.load_from_file(default_config_file) - assert config - - -# TODO: remove in v2.14.0 -def test_load_from_file_filenotfound(monkeypatch, tmp_path): - """Test `Config.load_from_file`.""" - config = Config() - assert not config - - expected_path = ( - tmp_path / "nonexistent_config_dir" / "not_existent_file.yml" - ) - msg = f"Config file '{expected_path}' does not exist" - with pytest.raises(FileNotFoundError, match=msg): - config.load_from_file("not_existent_file.yml") - - -# TODO: remove in v2.14.0 -def test_load_from_file_invalidconfigparameter(monkeypatch, tmp_path): - """Test `Config.load_from_file`.""" - monkeypatch.chdir(tmp_path) - cfg_path = tmp_path / "test.yml" - cfg_path.write_text("invalid_param: 42") - - config = Config() - assert not config - - msg = ( - f"Failed to parse user configuration file {cfg_path}: `invalid_param` " - f"is not a valid config parameter." - ) - with pytest.raises(InvalidConfigParameter, match=msg): - config.load_from_file(cfg_path) - - def test_config_key_error(): config = Config() with pytest.raises(KeyError): config["invalid_key"] -def test_reload(cfg_default, monkeypatch, tmp_path): +def test_reload(cfg_default): """Test `Config.reload`.""" - # TODO: remove in v2.14.0 - monkeypatch.delenv("_ESMVALTOOL_USER_CONFIG_FILE_", raising=False) - - monkeypatch.setattr( - esmvalcore.config._config_object, - "USER_CONFIG_DIR", - tmp_path / "this" / "is" / "an" / "empty" / "dir", - ) cfg = Config() cfg.reload() @@ -128,8 +74,7 @@ def test_reload(cfg_default, monkeypatch, tmp_path): def test_reload_fail(monkeypatch, tmp_path): """Test `Config.reload`.""" - # TODO: remove in v2.14.0 - monkeypatch.delenv("_ESMVALTOOL_USER_CONFIG_FILE_", raising=False) + monkeypatch.setenv("ESMVALTOOL_CONFIG_DIR", tmp_path) config_file = tmp_path / "invalid_config_file.yml" config_file.write_text("invalid_option: 1") @@ -169,213 +114,6 @@ def test_session_key_error(): session["invalid_key"] -# TODO: remove in v2.14.0 -def test_session_config_dir(): - session = Session({"config_file": "/path/to/config.yml"}) - with pytest.warns(ESMValCoreDeprecationWarning): - config_dir = session.config_dir - assert config_dir == Path("/path/to") - - -TEST_GET_CFG_PATH = [ - ( - None, - None, - None, - "{tmp_path}/nonexistent_config_dir/config-user.yml", - False, - ), - ( - None, - None, - ("any_other_module", "--config_file=cli.yml"), - "{tmp_path}/nonexistent_config_dir/config-user.yml", - False, - ), - ( - None, - None, - ("esmvaltool", "run", "--max_parallel_tasks=4"), - "{tmp_path}/nonexistent_config_dir/config-user.yml", - True, - ), - ( - None, - None, - ("esmvaltool", "--config_file"), - "{tmp_path}/nonexistent_config_dir/config-user.yml", - True, - ), - ( - None, - None, - ("esmvaltool", "run", "--config_file=/cli.yml"), - "/cli.yml", - True, - ), - ( - None, - None, - ("esmvaltool", "run", "--config_file=/cli.yml"), - "/cli.yml", - True, - ), - ( - None, - None, - ("esmvaltool", "run", "--config-file", "/cli.yml"), - "/cli.yml", - True, - ), - ( - None, - None, - ("esmvaltool", "run", "--config-file=/cli.yml"), - "/cli.yml", - True, - ), - ( - None, - None, - ("esmvaltool", "run", "--config-file=relative_cli.yml"), - "{tmp_path}/nonexistent_config_dir/relative_cli.yml", - True, - ), - ( - None, - None, - ("esmvaltool", "run", "--config-file=existing_cfg.yml"), - "existing_cfg.yml", - True, - ), - ( - None, - {"_ESMVALTOOL_USER_CONFIG_FILE_": "/env.yml"}, - ("esmvaltool", "run", "--config-file=/cli.yml"), - "/env.yml", - True, - ), - ( - None, - {"_ESMVALTOOL_USER_CONFIG_FILE_": "/env.yml"}, - None, - "/env.yml", - True, - ), - ( - None, - {"_ESMVALTOOL_USER_CONFIG_FILE_": "existing_cfg.yml"}, - ("esmvaltool", "run", "--config-file=/cli.yml"), - "existing_cfg.yml", - True, - ), - ( - "/filename.yml", - {"_ESMVALTOOL_USER_CONFIG_FILE_": "/env.yml"}, - ("esmvaltool", "run", "--config-file=/cli.yml"), - "/filename.yml", - True, - ), - ( - "/filename.yml", - None, - ("esmvaltool", "run", "--config-file=/cli.yml"), - "/filename.yml", - True, - ), - ("/filename.yml", None, None, "/filename.yml", False), - ( - "filename.yml", - None, - None, - "{tmp_path}/nonexistent_config_dir/filename.yml", - False, - ), - ( - "existing_cfg.yml", - {"_ESMVALTOOL_USER_CONFIG_FILE_": "/env.yml"}, - ("esmvaltool", "run", "--config-file=/cli.yml"), - "existing_cfg.yml", - True, - ), -] - - -# TODO: remove in v2.14.0 -@pytest.mark.parametrize( - ("filename", "env", "cli_args", "output", "env_var_set"), - TEST_GET_CFG_PATH, -) -def test_get_config_user_path( - filename, - env, - cli_args, - output, - env_var_set, - monkeypatch, - tmp_path, -): - """Test `Config._get_config_user_path`.""" - output = output.format(tmp_path=tmp_path) - monkeypatch.delenv("_ESMVALTOOL_USER_CONFIG_FILE_", raising=False) - - # Create empty test file - monkeypatch.chdir(tmp_path) - (tmp_path / "existing_cfg.yml").write_text("") - - if output == "existing_cfg.yml": - output = tmp_path / "existing_cfg.yml" - else: - output = Path(output).expanduser() - - if env is not None: - for key, val in env.items(): - monkeypatch.setenv(key, val) - if cli_args is None: - cli_args = ["python"] - - with arguments(*cli_args): - config_path = Config._get_config_user_path(filename) - if env_var_set: - assert os.environ["_ESMVALTOOL_USER_CONFIG_FILE_"] == str(output) - else: - assert "_ESMVALTOOL_USER_CONFIG_FILE_" not in os.environ - assert isinstance(config_path, Path) - assert config_path == output - - -# TODO: remove in v2.14.0 -def test_load_user_config_filenotfound(tmp_path): - """Test `Config._load_user_config`.""" - expected_path = ( - tmp_path / "nonexistent_config_dir" / "not_existent_file.yml" - ) - msg = f"Config file '{expected_path}' does not exist" - with pytest.raises(FileNotFoundError, match=msg): - Config._load_user_config("not_existent_file.yml") - - -# TODO: remove in v2.14.0 -def test_load_user_config_no_exception(): - """Test `Config._load_user_config`.""" - Config._load_user_config("not_existent_file.yml", raise_exception=False) - - -# TODO: remove in v2.14.0 -def test_load_user_config_invalidconfigparameter(monkeypatch, tmp_path): - """Test `Config._load_user_config`.""" - monkeypatch.chdir(tmp_path) - cfg_path = tmp_path / "test.yml" - cfg_path.write_text("invalid_param: 42") - - msg = ( - f"Failed to parse user configuration file {cfg_path}: `invalid_param` " - f"is not a valid config parameter." - ) - with pytest.raises(InvalidConfigParameter, match=msg): - Config._load_user_config(cfg_path) - - def test_get_user_config_dir_and_source_with_env(tmp_path, monkeypatch): """Test `_get_user_config_dir` and `_get_user_config_source`.""" monkeypatch.setenv("ESMVALTOOL_CONFIG_DIR", str(tmp_path)) @@ -411,48 +149,6 @@ def test_get_user_config_dir_with_env_fail(tmp_path, monkeypatch): esmvalcore.config._config_object._get_user_config_dir() -# TODO: remove in v2.14.0 -def test_get_global_config_force_new_config(mocker, tmp_path, monkeypatch): - """Test ``_get_global_config``.""" - monkeypatch.setenv("ESMVALTOOL_CONFIG_DIR", "/path/to/config/file") - - # Create invalid old config file to ensure that this is not used - config_file = tmp_path / "old_config_user.yml" - config_file.write_text("invalid_option: /new/output/dir") - mocker.patch.object( - esmvalcore.config._config_object.Config, - "_get_config_user_path", - return_value=config_file, - ) - - # No deprecation message should be raised - # Note: _get_global_config will ignore the old config since - # ESMVALTOOL_CONFIG_DIR is set, but not actually use its value since - # esmvalcore.config._config_object.USER_CONFIG_DIR has already been set to - # its default value when loading this module - with warnings.catch_warnings(): - warnings.simplefilter("error") - esmvalcore.config._config_object._get_global_config() - - -# TODO: remove in v2.14.0 -def test_get_global_config_deprecated(mocker, tmp_path, monkeypatch): - """Test ``_get_global_config``.""" - monkeypatch.delenv("ESMVALTOOL_CONFIG_DIR", raising=False) - - config_file = tmp_path / "old_config_user.yml" - config_file.write_text("output_dir: /new/output/dir") - mocker.patch.object( - esmvalcore.config._config_object.Config, - "_get_config_user_path", - return_value=config_file, - ) - with pytest.warns(ESMValCoreDeprecationWarning): - cfg = esmvalcore.config._config_object._get_global_config() - - assert cfg["output_dir"] == Path("/new/output/dir") - - def _setup_config_dirs(tmp_path): """Set up test configuration directories.""" config1 = tmp_path / "config1" / "1.yml" diff --git a/tests/unit/config/test_config_validator.py b/tests/unit/config/test_config_validator.py index 92fecc2d03..7b4ddde924 100644 --- a/tests/unit/config/test_config_validator.py +++ b/tests/unit/config/test_config_validator.py @@ -335,7 +335,7 @@ def test_validate_config_developer(tmp_path): validate_config_developer(None) -# TODO: remove in v2.14.0 +# TODO: remove in v2.15.0 def test_extra_facets_dir_tuple_deprecated(monkeypatch): """Test extra_facets_dir.""" with pytest.warns(ESMValCoreDeprecationWarning): diff --git a/tests/unit/config/test_dask.py b/tests/unit/config/test_dask.py index 3221540619..70f5ca9ef8 100644 --- a/tests/unit/config/test_dask.py +++ b/tests/unit/config/test_dask.py @@ -1,9 +1,7 @@ import pytest -import yaml from esmvalcore.config import CFG, _dask from esmvalcore.exceptions import ( - ESMValCoreDeprecationWarning, InvalidConfigParameter, ) @@ -23,106 +21,6 @@ def test_get_no_distributed_client(ignore_existing_user_config): assert client is None -# TODO: Remove in v2.14.0 -def test_get_distributed_client_empty_dask_file(mocker, tmp_path): - # Create mock client configuration. - cfg_file = tmp_path / "dask.yml" - with cfg_file.open("w", encoding="utf-8") as file: - file.write("") - mocker.patch.object(_dask, "CONFIG_FILE", cfg_file) - - # Create mock distributed.Client - with pytest.warns(ESMValCoreDeprecationWarning): - with _dask.get_distributed_client() as client: - assert client is None - - -# TODO: Remove in v2.14.0 -@pytest.mark.parametrize("use_new_dask_config", ["", "1"]) -def test_force_new_dask_config( - monkeypatch, - mocker, - tmp_path, - mock_dask_config_set, - ignore_existing_user_config, - use_new_dask_config, -): - # Old config -> threaded scheduler - cfg_file = tmp_path / "dask.yml" - with cfg_file.open("w", encoding="utf-8") as file: - file.write("") - mocker.patch.object(_dask, "CONFIG_FILE", cfg_file) - - # New config -> distributed scheduler - slurm_cluster = { - "type": "dask_jobqueue.SLURMCluster", - "queue": "interactive", - "cores": "8", - "memory": "16GiB", - } - monkeypatch.setitem( - CFG, - "dask", - { - "use": "slurm_cluster", - "profiles": {"slurm_cluster": {"cluster": slurm_cluster}}, - }, - ) - - # Create mock distributed.Client - mock_client = mocker.Mock() - mocker.patch.object( - _dask, - "Client", - create_autospec=True, - return_value=mock_client, - ) - - mock_module = mocker.Mock() - mock_cluster_cls = mocker.Mock() - mock_module.SLURMCluster = mock_cluster_cls - mocker.patch.object( - _dask.importlib, - "import_module", - create_autospec=True, - return_value=mock_module, - ) - - monkeypatch.setenv("ESMVALTOOL_USE_NEW_DASK_CONFIG", use_new_dask_config) - - with _dask.get_distributed_client() as client: - if use_new_dask_config: - assert client is not None - else: - assert client is None - - -# TODO: Remove in v2.14.0 -def test_get_old_dask_config(mocker, tmp_path): - # Create mock client configuration. - cfg = {"cluster": {"n_workers": 2}} - cfg_file = tmp_path / "dask.yml" - with cfg_file.open("w", encoding="utf-8") as file: - yaml.safe_dump(cfg, file) - mocker.patch.object(_dask, "CONFIG_FILE", cfg_file) - - dask_cfg = _dask._get_old_dask_config() - - expected_cfg = { - "use": "cluster_from_file", - "profiles": { - "cluster_from_file": { - "cluster": { - "n_workers": 2, - "type": "distributed.LocalCluster", - }, - }, - }, - "client": {}, - } - assert dask_cfg == expected_cfg - - def test_get_distributed_client_external( monkeypatch, mocker, @@ -161,48 +59,6 @@ def test_get_distributed_client_external( ) -# TODO: Remove in v2.14.0 -@pytest.mark.parametrize("warn_unused_args", [False, True]) -def test_get_distributed_client_external_old( - mocker, - tmp_path, - mock_dask_config_set, - warn_unused_args, -): - # Create mock client configuration. - cfg = { - "client": { - "address": "tcp://127.0.0.1:42021", - "other_client_options": 1, - }, - } - if warn_unused_args: - cfg["cluster"] = {"n_workers": 2} - cfg_file = tmp_path / "dask.yml" - with cfg_file.open("w", encoding="utf-8") as file: - yaml.safe_dump(cfg, file) - mocker.patch.object(_dask, "CONFIG_FILE", cfg_file) - - # Create mock distributed.Client - mock_client = mocker.Mock() - mocker.patch.object( - _dask, - "Client", - create_autospec=True, - return_value=mock_client, - ) - - with pytest.warns(ESMValCoreDeprecationWarning): - with _dask.get_distributed_client() as client: - assert client is mock_client - _dask.Client.assert_called_once_with(other_client_options=1) - mock_client.close.assert_called_once_with() - assert ( - mocker.call({"scheduler_address": "tcp://127.0.0.1:42021"}) - in mock_dask_config_set.mock_calls - ) - - @pytest.mark.parametrize("shutdown_timeout", [False, True]) def test_get_distributed_client_slurm( monkeypatch, @@ -266,62 +122,6 @@ def test_get_distributed_client_slurm( ) -# TODO: Remove in v2.14.0 -@pytest.mark.parametrize("shutdown_timeout", [False, True]) -def test_get_distributed_client_slurm_old( - mocker, - tmp_path, - mock_dask_config_set, - shutdown_timeout, -): - cfg = { - "cluster": { - "type": "dask_jobqueue.SLURMCluster", - "queue": "interactive", - "cores": "8", - "memory": "16GiB", - }, - } - cfg_file = tmp_path / "dask.yml" - with cfg_file.open("w", encoding="utf-8") as file: - yaml.safe_dump(cfg, file) - mocker.patch.object(_dask, "CONFIG_FILE", cfg_file) - - # Create mock distributed.Client - mock_client = mocker.Mock() - mocker.patch.object( - _dask, - "Client", - create_autospec=True, - return_value=mock_client, - ) - - mock_module = mocker.Mock() - mock_cluster_cls = mocker.Mock() - mock_module.SLURMCluster = mock_cluster_cls - mocker.patch.object( - _dask.importlib, - "import_module", - create_autospec=True, - return_value=mock_module, - ) - mock_cluster = mock_cluster_cls.return_value - if shutdown_timeout: - mock_cluster.close.side_effect = TimeoutError - with pytest.warns(ESMValCoreDeprecationWarning): - with _dask.get_distributed_client() as client: - assert client is mock_client - mock_client.close.assert_called_once_with() - _dask.Client.assert_called_once_with() - args = {k: v for k, v in cfg["cluster"].items() if k != "type"} - mock_cluster_cls.assert_called_once_with(**args) - mock_cluster.close.assert_called() - assert ( - mocker.call({"scheduler_address": mock_cluster.scheduler_address}) - in mock_dask_config_set.mock_calls - ) - - def test_custom_default_scheduler( monkeypatch, mock_dask_config_set, diff --git a/tests/unit/main/test_esmvaltool.py b/tests/unit/main/test_esmvaltool.py index b41a4726e0..b329998ee1 100644 --- a/tests/unit/main/test_esmvaltool.py +++ b/tests/unit/main/test_esmvaltool.py @@ -192,13 +192,8 @@ def test_run_invalid_config_dir(tmp_path): program.run("/recipe_dir/recipe_test.yml", config_dir=tmp_path) -def test_run_invalid_cli_arg(monkeypatch, tmp_path): +def test_run_invalid_cli_arg(): """Test `ESMValTool.run`.""" - monkeypatch.delitem( # TODO: remove in v2.14.0 - esmvalcore.config.CFG._mapping, - "config_file", - raising=False, - ) program = ESMValTool() msg = r"Invalid command line argument given:" @@ -228,25 +223,24 @@ def test_do_not_clean_preproc_dir(session): assert session._fixed_file_dir.exists() -@mock.patch("esmvalcore._main.ESMValTool._get_config_info") @mock.patch("esmvalcore._main.entry_points") def test_header( - mock_entry_points, - mock_get_config_info, - monkeypatch, - tmp_path, - caplog, -): - tmp_path.mkdir(parents=True, exist_ok=True) + mock_entry_points: mock.Mock, + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + caplog: pytest.LogCaptureFixture, +) -> None: + user_config_dir = tmp_path monkeypatch.setattr( esmvalcore.config._config_object, "USER_CONFIG_DIR", - tmp_path, + user_config_dir, ) + esmvalcore.config.CFG.reload() monkeypatch.setattr( esmvalcore.config._config_object, "USER_CONFIG_SOURCE", - "SOURCE", + "MOCK_SOURCE", ) entry_point = mock.Mock() entry_point.dist.name = "MyEntry" @@ -255,9 +249,6 @@ def test_header( mock_entry_points.return_value = [entry_point] cli_config_dir = tmp_path / "this" / "does" / "not" / "exist" - # TODO: remove in v2.14.0 - mock_get_config_info.return_value = "config_dir (SOURCE)" - with caplog.at_level(logging.INFO): ESMValTool()._log_header( ["path_to_log_file1", "path_to_log_file2"], @@ -271,8 +262,13 @@ def test_header( assert caplog.messages[3] == f"ESMValCore: {__version__}" assert caplog.messages[4] == "MyEntry: v42.42.42" assert caplog.messages[5] == "----------------" - assert caplog.messages[6] == ( - "Reading configuration files from:\nconfig_dir (SOURCE)" + assert caplog.messages[6] == "\n".join( + [ + "Reading configuration files from:", + f"{Path(esmvalcore.config.__file__).parent}/configurations/defaults (defaults)", + f"{user_config_dir} (MOCK_SOURCE)", + f"{cli_config_dir} [NOT AN EXISTING DIRECTORY] (command line argument)", + ], ) assert caplog.messages[7] == ( "Writing program log files to:\npath_to_log_file1\npath_to_log_file2"