Skip to content

Commit 59346d7

Browse files
authored
Don't realise lazy coords in netcdf save. (#4397)
* Don't realise lazy coords in netcdf save. * --amend
1 parent 3c087e7 commit 59346d7

File tree

4 files changed

+28
-18
lines changed

4 files changed

+28
-18
lines changed

lib/iris/fileformats/netcdf.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,9 +1190,9 @@ def write(
11901190

11911191
# Ensure that attributes are CF compliant and if possible to make them
11921192
# compliant.
1193-
self.check_attribute_compliance(cube, cube.lazy_data())
1193+
self.check_attribute_compliance(cube, cube.dtype)
11941194
for coord in cube.coords():
1195-
self.check_attribute_compliance(coord, coord.points)
1195+
self.check_attribute_compliance(coord, coord.dtype)
11961196

11971197
# Get suitable dimension names.
11981198
mesh_dimensions, cube_dimensions = self._get_dim_names(cube)
@@ -1280,16 +1280,14 @@ def write(
12801280
warnings.warn(msg)
12811281

12821282
@staticmethod
1283-
def check_attribute_compliance(container, data):
1283+
def check_attribute_compliance(container, data_dtype):
12841284
def _coerce_value(val_attr, val_attr_value, data_dtype):
12851285
val_attr_tmp = np.array(val_attr_value, dtype=data_dtype)
12861286
if (val_attr_tmp != val_attr_value).any():
12871287
msg = '"{}" is not of a suitable value ({})'
12881288
raise ValueError(msg.format(val_attr, val_attr_value))
12891289
return val_attr_tmp
12901290

1291-
data_dtype = data.dtype
1292-
12931291
# Ensure that conflicting attributes are not provided.
12941292
if (
12951293
container.attributes.get("valid_min") is not None

lib/iris/tests/integration/test_netcdf.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,16 @@ def test_lazy_preserved_save(self):
273273
)
274274
acube = iris.load_cube(fpath, "air_temperature")
275275
self.assertTrue(acube.has_lazy_data())
276+
# Also check a coord with lazy points + bounds.
277+
self.assertTrue(acube.coord("forecast_period").has_lazy_points())
278+
self.assertTrue(acube.coord("forecast_period").has_lazy_bounds())
276279
with self.temp_filename(".nc") as nc_path:
277280
with Saver(nc_path, "NETCDF4") as saver:
278281
saver.write(acube)
282+
# Check that cube data is not realised, also coord points + bounds.
279283
self.assertTrue(acube.has_lazy_data())
284+
self.assertTrue(acube.coord("forecast_period").has_lazy_points())
285+
self.assertTrue(acube.coord("forecast_period").has_lazy_bounds())
280286

281287

282288
@tests.skip_data

lib/iris/tests/unit/fileformats/netcdf/test_Saver.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ class _Common__check_attribute_compliance:
612612

613613
def setUp(self):
614614
self.container = mock.Mock(name="container", attributes={})
615-
self.data = self.array_lib.array(1, dtype="int32")
615+
self.data_dtype = np.dtype("int32")
616616

617617
patch = mock.patch("netCDF4.Dataset")
618618
_ = patch.start()
@@ -629,7 +629,7 @@ def assertAttribute(self, value):
629629
def check_attribute_compliance_call(self, value):
630630
self.set_attribute(value)
631631
with Saver(mock.Mock(), "NETCDF4") as saver:
632-
saver.check_attribute_compliance(self.container, self.data)
632+
saver.check_attribute_compliance(self.container, self.data_dtype)
633633

634634

635635
class Test_check_attribute_compliance__valid_range(
@@ -642,10 +642,10 @@ def attribute(self):
642642
def test_valid_range_type_coerce(self):
643643
value = self.array_lib.array([1, 2], dtype="float")
644644
self.check_attribute_compliance_call(value)
645-
self.assertAttribute(self.data.dtype)
645+
self.assertAttribute(self.data_dtype)
646646

647647
def test_valid_range_unsigned_int8_data_signed_range(self):
648-
self.data = self.data.astype("uint8")
648+
self.data_dtype = np.dtype("uint8")
649649
value = self.array_lib.array([1, 2], dtype="int8")
650650
self.check_attribute_compliance_call(value)
651651
self.assertAttribute(value.dtype)
@@ -658,7 +658,7 @@ def test_valid_range_cannot_coerce(self):
658658

659659
def test_valid_range_not_numpy_array(self):
660660
# Ensure we handle the case when not a numpy array is provided.
661-
self.data = self.data.astype("int8")
661+
self.data_dtype = np.dtype("int8")
662662
value = [1, 2]
663663
self.check_attribute_compliance_call(value)
664664
self.assertAttribute(np.int64)
@@ -674,10 +674,10 @@ def attribute(self):
674674
def test_valid_range_type_coerce(self):
675675
value = self.array_lib.array(1, dtype="float")
676676
self.check_attribute_compliance_call(value)
677-
self.assertAttribute(self.data.dtype)
677+
self.assertAttribute(self.data_dtype)
678678

679679
def test_valid_range_unsigned_int8_data_signed_range(self):
680-
self.data = self.data.astype("uint8")
680+
self.data_dtype = np.dtype("uint8")
681681
value = self.array_lib.array(1, dtype="int8")
682682
self.check_attribute_compliance_call(value)
683683
self.assertAttribute(value.dtype)
@@ -690,7 +690,7 @@ def test_valid_range_cannot_coerce(self):
690690

691691
def test_valid_range_not_numpy_array(self):
692692
# Ensure we handle the case when not a numpy array is provided.
693-
self.data = self.data.astype("int8")
693+
self.data_dtype = np.dtype("int8")
694694
value = 1
695695
self.check_attribute_compliance_call(value)
696696
self.assertAttribute(np.int64)
@@ -706,10 +706,10 @@ def attribute(self):
706706
def test_valid_range_type_coerce(self):
707707
value = self.array_lib.array(2, dtype="float")
708708
self.check_attribute_compliance_call(value)
709-
self.assertAttribute(self.data.dtype)
709+
self.assertAttribute(self.data_dtype)
710710

711711
def test_valid_range_unsigned_int8_data_signed_range(self):
712-
self.data = self.data.astype("uint8")
712+
self.data_dtype = np.dtype("uint8")
713713
value = self.array_lib.array(2, dtype="int8")
714714
self.check_attribute_compliance_call(value)
715715
self.assertAttribute(value.dtype)
@@ -722,7 +722,7 @@ def test_valid_range_cannot_coerce(self):
722722

723723
def test_valid_range_not_numpy_array(self):
724724
# Ensure we handle the case when not a numpy array is provided.
725-
self.data = self.data.astype("int8")
725+
self.data_dtype = np.dtype("int8")
726726
value = 2
727727
self.check_attribute_compliance_call(value)
728728
self.assertAttribute(np.int64)
@@ -733,13 +733,15 @@ class Test_check_attribute_compliance__exception_handling(
733733
):
734734
def test_valid_range_and_valid_min_valid_max_provided(self):
735735
# Conflicting attributes should raise a suitable exception.
736-
self.data = self.data.astype("int8")
736+
self.data_dtype = np.dtype("int8")
737737
self.container.attributes["valid_range"] = [1, 2]
738738
self.container.attributes["valid_min"] = [1]
739739
msg = 'Both "valid_range" and "valid_min"'
740740
with Saver(mock.Mock(), "NETCDF4") as saver:
741741
with self.assertRaisesRegex(ValueError, msg):
742-
saver.check_attribute_compliance(self.container, self.data)
742+
saver.check_attribute_compliance(
743+
self.container, self.data_dtype
744+
)
743745

744746

745747
class Test__cf_coord_identity(tests.IrisTest):

lib/iris/tests/unit/fileformats/netcdf/test_Saver__lazy.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,7 @@ def test_lazy_streamed_bounds(self):
122122
self.cube.replace_coord(lazy_coord)
123123
self.save_common(self.cube)
124124
self.assertTrue(self.store_watch.called)
125+
126+
127+
if __name__ == "__main__":
128+
tests.main()

0 commit comments

Comments
 (0)