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
6 changes: 3 additions & 3 deletions docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ This document explains the changes made to Iris for this release
:class:`~iris.cube.Cube` :attr:`~iris.cube.Cube.attributes` handling to
better preserve the distinction between dataset-level and variable-level
attributes, allowing file-Cube-file round-tripping of NetCDF attributes. See
:class:`~iris.cube.CubeAttrsDict` and NetCDF
:func:`~iris.fileformats.netcdf.saver.save` for more. (:pull:`5152`,
`split attributes project`_)
:class:`~iris.cube.CubeAttrsDict`, NetCDF
:func:`~iris.fileformats.netcdf.saver.save` and :data:`~iris.Future` for more.
(:pull:`5152`, `split attributes project`_)

#. `@rcomer`_ rewrote :func:`~iris.util.broadcast_to_shape` so it now handles
lazy data. (:pull:`5307`)
Expand Down
12 changes: 12 additions & 0 deletions lib/iris/fileformats/netcdf/saver.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from dask.delayed import Delayed
import numpy as np

from iris._deprecation import warn_deprecated
from iris._lazy_data import _co_realise_lazy_arrays, is_lazy_data
from iris.aux_factory import (
AtmosphereSigmaFactory,
Expand Down Expand Up @@ -2965,6 +2966,17 @@ def attr_values_equal(val1, val2):
else:
# Legacy mode: calculate "local_keys" to control which attributes are local
# and which global.
# TODO: when iris.FUTURE.save_split_attrs is removed, this section can also be
# removed
message = (
"Saving to netcdf with legacy-style attribute handling for backwards "
"compatibility.\n"
"This mode is deprecated since Iris 3.8, and will eventually be removed.\n"
"Please consider enabling the new split-attributes handling mode, by "
"setting 'iris.FUTURE.save_split_attrs = True'."
)
warn_deprecated(message)

if local_keys is None:
local_keys = set()
else:
Expand Down
7 changes: 7 additions & 0 deletions lib/iris/tests/integration/netcdf/test_delayed_save.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@


class Test__lazy_stream_data:
# Ensure all saves are done with split-atttribute saving,
# -- because some of these tests are sensitive to unexpected warnings.
@pytest.fixture(autouse=True)
def all_saves_with_split_attrs(self):
with iris.FUTURE.context(save_split_attrs=True):
yield

@pytest.fixture(autouse=True)
def output_path(self, tmp_path):
# A temporary output netcdf-file path, **unique to each test call**.
Expand Down
36 changes: 33 additions & 3 deletions lib/iris/tests/integration/test_netcdf__loadsaveattrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ def local_attr(request):


def check_captured_warnings(
expected_keys: List[str], captured_warnings: List[warnings.WarningMessage]
expected_keys: List[str],
captured_warnings: List[warnings.WarningMessage],
allow_possible_legacy_warning: bool = False,
):
"""
Compare captured warning messages with a list of regexp-matches.
Expand All @@ -104,6 +106,9 @@ def check_captured_warnings(
comprehend.

"""
# TODO: when iris.FUTURE.save_split_attrs is removed, we can remove the
# 'allow_possible_legacy_warning' arg.

if expected_keys is None:
expected_keys = []
elif hasattr(expected_keys, "upper"):
Expand All @@ -113,6 +118,14 @@ def check_captured_warnings(
return
expected_keys = [expected_keys]

if allow_possible_legacy_warning:
# Allow but do not require a "saving without split-attributes" warning.
legacy_message_key = (
"Saving to netcdf with legacy-style attribute handling for backwards "
"compatibility."
)
expected_keys.append(legacy_message_key)

expected_keys = [re.compile(key) for key in expected_keys]
found_results = [str(warning.message) for warning in captured_warnings]
remaining_keys = expected_keys.copy()
Expand All @@ -126,6 +139,13 @@ def check_captured_warnings(
# skip on to next message
break

if allow_possible_legacy_warning:
# Remove any unused "legacy attribute saving" key.
# N.B. this is the *only* key we will tolerate not being used.
expected_keys = [
key for key in expected_keys if key != legacy_message_key
]

assert set(found_results) == set(expected_keys)


Expand Down Expand Up @@ -726,7 +746,12 @@ def check_roundtrip_results(self, expected, expected_warnings=None):
# which came from different input files.
results = self.fetch_results(filepath=self.result_filepath)
assert results == expected
check_captured_warnings(expected_warnings, self.captured_warnings)
check_captured_warnings(
expected_warnings,
self.captured_warnings,
# N.B. only allow a legacy-attributes warning when NOT saving split-attrs
allow_possible_legacy_warning=not self.save_split_attrs,
)

#######################################################
# Tests on "user-style" attributes.
Expand Down Expand Up @@ -1391,7 +1416,12 @@ def check_save_results(
):
results = self.fetch_results(filepath=self.result_filepath)
assert results == expected
check_captured_warnings(expected_warnings, self.captured_warnings)
check_captured_warnings(
expected_warnings,
self.captured_warnings,
# N.B. only allow a legacy-attributes warning when NOT saving split-attrs
allow_possible_legacy_warning=not self.save_split_attrs,
)

def test_userstyle__single(self, do_split):
self.run_save_testcase_legacytype("random", "value-x")
Expand Down