Skip to content

Commit d74a1a0

Browse files
committed
Properly support merge_unique, and add equalise_cubes.
1 parent e812f86 commit d74a1a0

File tree

2 files changed

+37
-26
lines changed

2 files changed

+37
-26
lines changed

lib/iris/_combine.py

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from __future__ import annotations
1515

1616
import threading
17-
from typing import TYPE_CHECKING, List
17+
from typing import TYPE_CHECKING, Any, Dict, List
1818

1919
if TYPE_CHECKING:
2020
from iris.cube import Cube, CubeList
@@ -35,11 +35,15 @@ class CombineOptions(threading.local):
3535
-----
3636
The individual configurable options are :
3737
38+
* ``equalise_cubes_kwargs`` = (dict)
39+
Specifies keywords for a :func:`iris.util.equalise_cubes` call, to be applied
40+
before any merge/concatenate step.
41+
3842
* ``merge_concat_sequence`` = "m" / "c" / "cm" / "mc"
3943
Specifies whether to apply :meth:`~iris.cube.CubeList.merge`, or
4044
:meth:`~iris.cube.CubeList.concatenate` operations, or both, in either order.
4145
42-
* ``merge_uses_unique`` = True / False
46+
* ``merge_unique`` = True / False
4347
When True, any merge operation will error if its result contains multiple
4448
identical cubes. Otherwise (unique=False), that is a permitted result.
4549
@@ -90,29 +94,40 @@ class CombineOptions(threading.local):
9094
# Useful constants
9195
#: Valid option names
9296
OPTION_KEYS = [
97+
"equalise_cubes_kwargs", # N.B. gets special treatment in options checking
9398
"merge_concat_sequence",
99+
"merge_unique",
94100
"repeat_until_unchanged",
95101
] # this is a list, so we can update it in an inheriting class
96102
_OPTIONS_ALLOWED_VALUES = {
97103
"merge_concat_sequence": ("", "m", "c", "mc", "cm"),
104+
"merge_unique": (True, False),
98105
"repeat_until_unchanged": (False, True),
99106
}
100-
#: Settings content
101-
SETTINGS = {
107+
#: Standard settings dictionaries
108+
SETTINGS: Dict[str, Dict[str, Any]] = {
102109
"legacy": dict(
110+
equalise_cubes_kwargs=None,
103111
merge_concat_sequence="m",
112+
merge_unique=False,
104113
repeat_until_unchanged=False,
105114
),
106115
"default": dict(
116+
equalise_cubes_kwargs=None,
107117
merge_concat_sequence="m",
118+
merge_unique=False,
108119
repeat_until_unchanged=False,
109120
),
110121
"recommended": dict(
122+
equalise_cubes_kwargs=None,
111123
merge_concat_sequence="mc",
124+
merge_unique=False,
112125
repeat_until_unchanged=False,
113126
),
114127
"comprehensive": dict(
128+
equalise_cubes_kwargs={"apply_all": True},
115129
merge_concat_sequence="mc",
130+
merge_unique=False,
116131
repeat_until_unchanged=True,
117132
),
118133
}
@@ -128,13 +143,14 @@ def __setattr__(self, key, value):
128143
if key not in self.OPTION_KEYS:
129144
raise KeyError(f"LoadPolicy object has no property '{key}'.")
130145

131-
allowed_values = self._OPTIONS_ALLOWED_VALUES[key]
132-
if value not in allowed_values:
133-
msg = (
134-
f"{value!r} is not a valid setting for LoadPolicy.{key} : "
135-
f"must be one of '{allowed_values}'."
136-
)
137-
raise ValueError(msg)
146+
if key != "equalise_cubes_kwargs":
147+
allowed_values = self._OPTIONS_ALLOWED_VALUES[key]
148+
if value not in allowed_values:
149+
msg = (
150+
f"{value!r} is not a valid setting for LoadPolicy.{key} : "
151+
f"must be one of '{allowed_values}'."
152+
)
153+
raise ValueError(msg)
138154

139155
self.__dict__[key] = value
140156

@@ -157,7 +173,7 @@ def set(self, options: str | dict | None = None, **kwargs):
157173
158174
"""
159175
if options is None:
160-
options_dict = {}
176+
options_dict: dict = {}
161177
elif isinstance(options, str):
162178
if options in self.SETTINGS:
163179
options_dict = self.SETTINGS[options]
@@ -169,12 +185,6 @@ def set(self, options: str | dict | None = None, **kwargs):
169185
raise ValueError(msg)
170186
elif isinstance(options, dict):
171187
options_dict = options
172-
else:
173-
msg = ( # type: ignore[unreachable]
174-
f"arg 'options' has unexpected type {type(options)!r}, "
175-
f"expected one of (None | str | dict)."
176-
) # type: ignore[unreachable]
177-
raise TypeError(msg) # type: ignore[unreachable]
178188

179189
# Override any options with keywords
180190
options_dict = options_dict.copy() # don't modify original
@@ -189,7 +199,7 @@ def set(self, options: str | dict | None = None, **kwargs):
189199
setattr(self, key, value)
190200

191201
def settings(self) -> dict:
192-
"""Return an options dict containing the current settings."""
202+
"""Return a settings dict containing the current options settings."""
193203
return {key: getattr(self, key) for key in self.OPTION_KEYS}
194204

195205
def __repr__(self):
@@ -234,7 +244,14 @@ def _combine_cubes(cubes: List[Cube], options: dict) -> CubeList:
234244
else:
235245
cubelist = CubeList(cubes)
236246

247+
eq_args = options.get("equalise_cubes_kwargs", None)
248+
if eq_args:
249+
from iris.util import equalise_cubes
250+
251+
equalise_cubes(cubelist, **eq_args)
252+
237253
sequence = options["merge_concat_sequence"]
254+
merge_unique = options.get("merge_unique", False)
238255
while True:
239256
n_original_cubes = len(cubelist)
240257

@@ -245,7 +262,7 @@ def _combine_cubes(cubes: List[Cube], options: dict) -> CubeList:
245262
# merge if requested
246263
# NOTE: this needs "unique=False" to make "iris.load()" work correctly.
247264
# TODO: make configurable via options.
248-
cubelist = cubelist.merge(unique=False)
265+
cubelist = cubelist.merge(unique=merge_unique)
249266
if sequence[-1] == "c":
250267
# concat if it comes last
251268
cubelist = cubelist.concatenate()

lib/iris/tests/unit/test_LoadPolicy.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,6 @@ def test_arg_bad_string(self):
8181
with pytest.raises(ValueError, match=expected):
8282
options.set("oddthing")
8383

84-
def test_arg_bad_type(self):
85-
options = LoadPolicy()
86-
expected = r"arg 'options' has unexpected type \<class 'tuple'\>, expected one of \(None \| str \| dict\)\."
87-
with pytest.raises(TypeError, match=expected):
88-
options.set((1, 2, 3))
89-
9084
def test_kwargs(self):
9185
options = LoadPolicy()
9286
assert options.settings()["merge_concat_sequence"] == "m"

0 commit comments

Comments
 (0)