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
7 changes: 6 additions & 1 deletion docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ This document explains the changes made to Iris for this release
#. `@rcomer`_ added handling for string stash codes when saving pp files.
(:issue:`6239`, :pull:`6289`)

#. `@trexfeathers`_ and `@jrackham-mo`_ added a check for dtype castability when
saving NetCDF ``valid_range``, ``valid_min`` and ``valid_max`` attributes -
older NetCDF formats e.g. ``NETCDF4_CLASSIC`` support a maximum precision of
32-bit. (:issue:`6178`, :pull:`6343`)


💣 Incompatible Changes
=======================
Expand All @@ -70,7 +75,7 @@ This document explains the changes made to Iris for this release
🚀 Performance Enhancements
===========================

#. `@bouweandela`_ made loading :class:`~iris.cube.Cube`s from NetCDF files
#. `@bouweandela`_ made loading :class:`~iris.cube.Cube`\s from NetCDF files
faster. (:pull:`6229` and :pull:`6252`)

#. `@fnattino`_ enabled lazy cube interpolation using the linear and
Expand Down
4 changes: 2 additions & 2 deletions lib/iris/fileformats/netcdf/saver.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,8 +655,7 @@ def write(
msg = "cf_profile is available but no {} defined.".format("cf_patch")
warnings.warn(msg, category=iris.warnings.IrisCfSaveWarning)

@staticmethod
def check_attribute_compliance(container, data_dtype):
def check_attribute_compliance(self, container, data_dtype):
"""Check attributte complliance."""

def _coerce_value(val_attr, val_attr_value, data_dtype):
Expand All @@ -681,6 +680,7 @@ def _coerce_value(val_attr, val_attr_value, data_dtype):
val_attr_value = container.attributes.get(val_attr)
if val_attr_value is not None:
val_attr_value = np.asarray(val_attr_value)
self._ensure_valid_dtype(val_attr_value, val_attr, val_attr_value)
if data_dtype.itemsize == 1:
# Allow signed integral type
if val_attr_value.dtype.kind == "i":
Expand Down
24 changes: 22 additions & 2 deletions lib/iris/tests/unit/fileformats/netcdf/saver/test_Saver.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,9 +601,11 @@ def assertAttribute(self, value):
np.asarray(self.container.attributes[self.attribute]).dtype, value
)

def check_attribute_compliance_call(self, value):
def check_attribute_compliance_call(self, value, file_type="NETCDF4"):
self.set_attribute(value)
with Saver("nonexistent test file", "NETCDF4") as saver:
with Saver("nonexistent test file", file_type) as saver:
# Get the Mock to work properly.
saver._dataset.file_format = file_type
saver.check_attribute_compliance(self.container, self.data_dtype)


Expand Down Expand Up @@ -638,6 +640,12 @@ def test_valid_range_not_numpy_array(self):
self.check_attribute_compliance_call(value)
self.assertAttribute(np.int64)

def test_uncastable_dtype(self):
self.data_dtype = np.dtype("int64")
value = [0, np.iinfo(self.data_dtype).max]
with self.assertRaisesRegex(ValueError, "cannot be safely cast"):
self.check_attribute_compliance_call(value, file_type="NETCDF4_CLASSIC")


class Test_check_attribute_compliance__valid_min(
_Common__check_attribute_compliance, tests.IrisTest
Expand Down Expand Up @@ -670,6 +678,12 @@ def test_valid_range_not_numpy_array(self):
self.check_attribute_compliance_call(value)
self.assertAttribute(np.int64)

def test_uncastable_dtype(self):
self.data_dtype = np.dtype("int64")
value = np.iinfo(self.data_dtype).min
with self.assertRaisesRegex(ValueError, "cannot be safely cast"):
self.check_attribute_compliance_call(value, file_type="NETCDF4_CLASSIC")


class Test_check_attribute_compliance__valid_max(
_Common__check_attribute_compliance, tests.IrisTest
Expand Down Expand Up @@ -702,6 +716,12 @@ def test_valid_range_not_numpy_array(self):
self.check_attribute_compliance_call(value)
self.assertAttribute(np.int64)

def test_uncastable_dtype(self):
self.data_dtype = np.dtype("int64")
value = np.iinfo(self.data_dtype).max
with self.assertRaisesRegex(ValueError, "cannot be safely cast"):
self.check_attribute_compliance_call(value, file_type="NETCDF4_CLASSIC")


class Test_check_attribute_compliance__exception_handling(
_Common__check_attribute_compliance, tests.IrisTest
Expand Down
Loading