Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ dependencies:
- pytest-env
- pytest-html !=2.1.0
- pytest-metadata >=1.5.1
- pytest-mock
- pytest-xdist
# Python packages needed for building docs
- autodocsumm>=0.2.2
Expand Down
1 change: 1 addition & 0 deletions environment_osx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ dependencies:
- pytest-env
- pytest-html !=2.1.0
- pytest-metadata >=1.5.1
- pytest-mock
- pytest-xdist
# Python packages needed for building docs
- autodocsumm>=0.2.2
Expand Down
68 changes: 67 additions & 1 deletion esmvaltool/diag_scripts/monitor/monitor_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import logging
import os
import re

import cartopy
import matplotlib.pyplot as plt
import yaml
from esmvalcore._data_finder import _replace_tags
from iris.analysis import MEAN
from mapgenerator.plotting.timeseries import PlotSeries

Expand All @@ -15,6 +15,72 @@
logger = logging.getLogger(__name__)


def _replace_tags(paths, variable):
"""Replace tags in the config-developer's file with actual values."""
if isinstance(paths, str):
paths = set((paths.strip('/'), ))
else:
paths = set(path.strip('/') for path in paths)
tlist = set()
for path in paths:
tlist = tlist.union(re.findall(r'{([^}]*)}', path))
if 'sub_experiment' in variable:
new_paths = []
for path in paths:
new_paths.extend(
(re.sub(r'(\b{ensemble}\b)', r'{sub_experiment}-\1', path),
re.sub(r'({ensemble})', r'{sub_experiment}-\1', path)))
tlist.add('sub_experiment')
paths = new_paths

for tag in tlist:
original_tag = tag
tag, _, _ = _get_caps_options(tag)

if tag == 'latestversion': # handled separately later
continue
if tag in variable:
replacewith = variable[tag]
else:
raise ValueError(f"Dataset key '{tag}' must be specified for "
f"{variable}, check your recipe entry")
paths = _replace_tag(paths, original_tag, replacewith)
return paths


def _replace_tag(paths, tag, replacewith):
"""Replace tag by replacewith in paths."""
_, lower, upper = _get_caps_options(tag)
result = []
if isinstance(replacewith, (list, tuple)):
for item in replacewith:
result.extend(_replace_tag(paths, tag, item))
else:
text = _apply_caps(str(replacewith), lower, upper)
result.extend(p.replace('{' + tag + '}', text) for p in paths)
return list(set(result))


def _get_caps_options(tag):
lower = False
upper = False
if tag.endswith('.lower'):
lower = True
tag = tag[0:-6]
elif tag.endswith('.upper'):
upper = True
tag = tag[0:-6]
return tag, lower, upper


def _apply_caps(original, lower, upper):
if lower:
return original.lower()
if upper:
return original.upper()
return original


class MonitorBase():
"""Base class for monitoring diagnostic.

Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
'pytest-env',
'pytest-html!=2.1.0',
'pytest-metadata>=1.5.1',
'pytest-mock',
'pytest-xdist',
],
# Documentation dependencies
Expand Down
70 changes: 49 additions & 21 deletions tests/integration/test_recipes_loading.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
"""Test recipes are well formed."""
from pathlib import Path
from unittest.mock import create_autospec

import pytest
import yaml

import esmvalcore
import esmvalcore._config
import esmvalcore._data_finder
import esmvalcore._recipe
import esmvalcore.cmor.check
import pytest
import yaml

import esmvaltool

from .test_diagnostic_run import write_config_user_file
Expand Down Expand Up @@ -40,27 +38,52 @@ def _get_recipes():


@pytest.mark.parametrize('recipe_file', RECIPES, ids=IDS)
def test_recipe_valid(recipe_file, config_user, monkeypatch):
def test_recipe_valid(recipe_file, config_user, mocker):
"""Check that recipe files are valid ESMValTool recipes."""
# Mock input files
find_files = create_autospec(esmvalcore._data_finder.find_files,
spec_set=True)
find_files.side_effect = lambda *_, **__: [
'test_0001-1849.nc',
'test_1850-9999.nc',
]
monkeypatch.setattr(esmvalcore._data_finder, 'find_files', find_files)
try:
# Since ESValCore v2.8.0
import esmvalcore.local
module = esmvalcore.local
method = 'glob'
# The patched_datafinder fixture does not return the correct input
# directory structure, so make sure it is set to flat for every project
from esmvalcore.config import CFG, _config
mocker.patch.dict(CFG, drs={})
for project in _config.CFG:
mocker.patch.dict(_config.CFG[project]['input_dir'], default='/')
except ImportError:
# Prior to ESMValCore v2.8.0
import esmvalcore._data_finder
module = esmvalcore._data_finder
method = 'find_files'

mocker.patch.object(
module,
method,
autospec=True,
side_effect=lambda *_, **__: [
'test_0001-1849.nc',
'test_1850-9999.nc',
],
)

# Mock vertical levels
levels = create_autospec(esmvalcore._recipe.get_reference_levels,
spec_set=True)
levels.side_effect = lambda *_, **__: [1, 2]
monkeypatch.setattr(esmvalcore._recipe, 'get_reference_levels', levels)
mocker.patch.object(
esmvalcore._recipe,
'get_reference_levels',
autospec=True,
spec_set=True,
side_effect=lambda *_, **__: [1, 2],
)

# Mock valid NCL version
ncl_version = create_autospec(esmvalcore._recipe_checks.ncl_version,
spec_set=True)
monkeypatch.setattr(esmvalcore._recipe_checks, 'ncl_version', ncl_version)
mocker.patch.object(
esmvalcore._recipe_checks,
'ncl_version',
autospec=True,
spec_set=True,
)

# Mock interpreters installed
def which(executable):
Expand All @@ -70,7 +93,12 @@ def which(executable):
path = None
return path

monkeypatch.setattr(esmvalcore._task, 'which', which)
mocker.patch.object(
esmvalcore._task,
'which',
autospec=True,
side_effect=which,
)

# Create a shapefile for extract_shape preprocessor if needed
recipe = yaml.safe_load(recipe_file.read_text())
Expand Down