Skip to content

Commit a5fe0e9

Browse files
authored
Merge pull request ESMValGroup#1568 from ESMValGroup/version
Increase version to v2.0.0b3
2 parents d4f3f82 + 202ced5 commit a5fe0e9

File tree

6 files changed

+59
-139
lines changed

6 files changed

+59
-139
lines changed

CITATION.cff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,5 +308,5 @@ license: "Apache-2.0"
308308
message: "If you use this software, please cite it using these metadata."
309309
repository-code: "https://github.com/ESMValGroup/ESMValTool/"
310310
title: ESMValTool
311-
version: "v2.0.0b2"
311+
version: "v2.0.0b3"
312312
...

environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ channels:
77
dependencies:
88
# Python packages that cannot be installed from PyPI:
99
- gdal
10-
- esmvalcore>=2.0.0b6,<2.1
10+
- esmvalcore>=2.0.0b7,<2.1
1111
# Non-Python dependencies
1212
- graphviz
1313
- cdo>=1.9.7

esmvaltool/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""ESMValTool diagnostics package."""
2-
__version__ = '2.0.0b2'
2+
__version__ = '2.0.0b3'

meta.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# conda build . -c conda-forge -c esmvalgroup
66

77
# Package version number
8-
{% set version = "2.0.0b2" %}
8+
{% set version = "2.0.0b3" %}
99

1010
package:
1111
name: esmvaltool
@@ -51,7 +51,7 @@ requirements:
5151
- ecmwf-api-client # in esmvalgroup channel
5252
- eofs
5353
- esmpy
54-
- esmvalcore>=2.0.0b6,<2.1 # in esmvalgroup channel
54+
- esmvalcore>=2.0.0b7,<2.1 # in esmvalgroup channel
5555
- jinja2
5656
- matplotlib
5757
- nc-time-axis

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
'cython',
3131
'ecmwf-api-client',
3232
'eofs',
33-
'esmvalcore>=2.0.0b6,<2.1',
33+
'esmvalcore>=2.0.0b7,<2.1',
3434
'fiona',
3535
'jinja2',
3636
'matplotlib<3',
Lines changed: 53 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,77 @@
11
"""Test recipes are well formed."""
2-
3-
import os
42
from pathlib import Path
3+
from unittest.mock import create_autospec
54

6-
import iris
7-
import numpy as np
85
import pytest
6+
import yaml
97

108
import esmvalcore
119
import esmvaltool
12-
from esmvalcore import _data_finder, _recipe_checks
13-
from esmvalcore._config import read_config_user_file
14-
from esmvalcore._recipe import read_recipe_file
1510

1611
from .test_diagnostic_run import write_config_user_file
1712

1813

19-
def _get_recipes():
20-
recipes_path = Path(esmvaltool.__file__).absolute().parent / 'recipes'
21-
recipes = recipes_path.glob("**/recipe*.yml")
22-
return recipes
23-
24-
25-
def _tracking_ids(i=0):
26-
while True:
27-
yield i
28-
i += 1
29-
30-
31-
def _create_test_file(filename, tracking_id=None):
32-
"""Generate dummy data file for recipe checker."""
33-
dirname = os.path.dirname(filename)
34-
if not os.path.exists(dirname):
35-
os.makedirs(dirname)
36-
37-
attributes = {}
38-
if tracking_id is not None:
39-
attributes['tracking_id'] = tracking_id
40-
41-
xcoord = iris.coords.DimCoord(np.linspace(0, 5, 5),
42-
standard_name="longitude")
43-
ycoord = iris.coords.DimCoord(np.linspace(0, 5, 12),
44-
standard_name="latitude")
45-
zcoord = iris.coords.DimCoord(np.linspace(0, 5, 17),
46-
standard_name="height",
47-
attributes={'positive': 'up'})
48-
cube = iris.cube.Cube(np.zeros((5, 12, 17), np.float32),
49-
dim_coords_and_dims=[(xcoord, 0), (ycoord, 1),
50-
(zcoord, 2)],
51-
attributes=attributes)
52-
iris.save(cube, filename)
53-
54-
55-
def _get_dummy_filenames(drs):
56-
"""Generate list of realistic dummy filename(s) according to drs.
57-
58-
drs is the directory structure used to find input files in ESMValTool
59-
"""
60-
dummy_filenames = []
61-
62-
# Time-invariant (fx) variables don't have years in their filename
63-
if 'fx' in drs:
64-
if drs.endswith('[_.]*nc'):
65-
dummy_filename = drs.replace('[_.]*', '.')
66-
elif drs.endswith('*.nc'):
67-
dummy_filename = drs.replace('*', '')
68-
dummy_filenames.append(dummy_filename)
69-
# For other variables, add custom (large) intervals in dummy filename
70-
elif '*' in drs:
71-
if drs.endswith('[_.]*nc'):
72-
dummy_filename = drs[:-len('[_.]*nc')]
73-
elif drs.endswith('*.nc'):
74-
dummy_filename = drs[:-len('*.nc')]
75-
# Spread dummy data over multiple files for realistic test
76-
# Note: adding too many intervals here makes the tests really slow!
77-
for interval in ['0000_1849', '1850_9999']:
78-
dummy_filenames.append(dummy_filename + '_' + interval + '.nc')
79-
# Provide for the possibility of filename drss without *.
80-
else:
81-
dummy_filename = drs
82-
dummy_filenames.append(dummy_filename)
83-
return dummy_filenames
84-
85-
8614
@pytest.fixture
8715
def config_user(tmp_path):
8816
"""Generate dummy config-user file for testing purposes."""
8917
filename = write_config_user_file(tmp_path)
90-
cfg = read_config_user_file(filename, 'recipe_test')
18+
cfg = esmvalcore._config.read_config_user_file(filename, 'recipe_test')
9119
cfg['synda_download'] = False
20+
cfg['auxiliary_data_dir'] = str(tmp_path / 'auxiliary_data_dir')
9221
return cfg
9322

9423

95-
@pytest.fixture
96-
def patched_datafinder(tmp_path, monkeypatch):
97-
"""Replace `_datafinder.find_files()`.
98-
99-
Creates and points to dummy data input files instead of searching for
100-
existing data.
101-
"""
102-
def find_files(_, filenames):
103-
drs = filenames[0]
104-
dummyfiles = str(tmp_path / 'input' / drs)
105-
filenames = _get_dummy_filenames(dummyfiles)
106-
107-
for file in filenames:
108-
_create_test_file(file, next(tracking_id))
109-
110-
return filenames
111-
112-
tracking_id = _tracking_ids()
113-
monkeypatch.setattr(_data_finder, 'find_files', find_files)
114-
115-
116-
@pytest.fixture
117-
def patched_extract_shape(monkeypatch):
118-
"""Replace `_recipe_checks.extract_shape`.
119-
120-
Skips check that shapefile exists.
121-
"""
122-
def extract_shape(settings):
123-
valid = {
124-
'method': {'contains', 'representative'},
125-
'crop': {True, False},
126-
}
127-
128-
for key in valid:
129-
value = settings.get(key)
130-
if not (value is None or value in valid[key]):
131-
raise _recipe_checks.RecipeError(
132-
"In preprocessor function `extract_shape`: Invalid value"
133-
f"'{value}' for argument '{key}', choose from "
134-
"{}".format(', '.join(f"'{k}'".lower()
135-
for k in valid[key])))
136-
137-
monkeypatch.setattr(_recipe_checks, 'extract_shape', extract_shape)
138-
139-
140-
@pytest.fixture
141-
def patched_get_reference_levels(monkeypatch):
142-
"""Replace `_regrid.get_reference_levels`.
24+
def _get_recipes():
25+
recipes_path = Path(esmvaltool.__file__).absolute().parent / 'recipes'
26+
recipes = tuple(recipes_path.glob("**/recipe*.yml"))
27+
ids = tuple(str(p.relative_to(recipes_path)) for p in recipes)
28+
return recipes, ids
14329

144-
Return a random set of reference levels
145-
"""
146-
def get_reference_levels(*_, **_a):
147-
return [1, 2]
14830

149-
monkeypatch.setattr(esmvalcore._recipe, 'get_reference_levels',
150-
get_reference_levels)
31+
RECIPES, IDS = _get_recipes()
15132

15233

153-
@pytest.mark.parametrize('recipe_file', _get_recipes())
154-
def test_diagnostic_run(recipe_file, config_user, patched_datafinder,
155-
patched_extract_shape, patched_get_reference_levels):
34+
@pytest.mark.parametrize('recipe_file', RECIPES, ids=IDS)
35+
def test_recipe_valid(recipe_file, config_user, monkeypatch):
15636
"""Check that recipe files are valid ESMValTool recipes."""
157-
read_recipe_file(recipe_file, config_user)
37+
# Mock input files
38+
find_files = create_autospec(esmvalcore._data_finder.find_files,
39+
spec_set=True)
40+
find_files.side_effect = lambda *_, **__: [
41+
'test_0000-1849.nc',
42+
'test_1850-9999.nc',
43+
]
44+
monkeypatch.setattr(esmvalcore._data_finder, 'find_files', find_files)
45+
46+
# Mock vertical levels
47+
levels = create_autospec(esmvalcore._recipe.get_reference_levels,
48+
spec_set=True)
49+
levels.side_effect = lambda *_, **__: [1, 2]
50+
monkeypatch.setattr(esmvalcore._recipe, 'get_reference_levels', levels)
51+
52+
# Mock valid NCL version
53+
ncl_version = create_autospec(esmvalcore._recipe_checks.ncl_version,
54+
spec_set=True)
55+
monkeypatch.setattr(esmvalcore._recipe_checks, 'ncl_version', ncl_version)
56+
57+
# Mock interpreters installed
58+
def which(executable):
59+
if executable in ('julia', 'ncl', 'python', 'Rscript'):
60+
path = '/path/to/' + executable
61+
else:
62+
path = None
63+
return path
64+
65+
monkeypatch.setattr(esmvalcore._task, 'which', which)
66+
67+
# Create a shapefile for extract_shape preprocessor if needed
68+
recipe = yaml.safe_load(recipe_file.read_text())
69+
for preproc in recipe.get('preprocessors', {}).values():
70+
extract_shape = preproc.get('extract_shape')
71+
if extract_shape and 'shapefile' in extract_shape:
72+
filename = Path(
73+
config_user['auxiliary_data_dir']) / extract_shape['shapefile']
74+
filename.parent.mkdir(parents=True, exist_ok=True)
75+
filename.touch()
76+
77+
esmvalcore._recipe.read_recipe_file(recipe_file, config_user)

0 commit comments

Comments
 (0)