From 754192060f3759bc081aba3cd13545b2a145767f Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Mon, 1 Jun 2026 13:46:44 +0200 Subject: [PATCH] Preserve masked bounds in Coord.cell Coord.cell() used np.array() to extract the bound for a given index, which silently drops the mask and reveals the raw value stored beneath it. The point path was already changed to np.asanyarray for this reason; apply the same treatment to bounds so a masked bound stays masked, both in the returned Cell and in the cube/coordinate printout. Fixes #5158. --- docs/src/whatsnew/latest.rst | 5 ++++- lib/iris/coords.py | 5 ++++- lib/iris/tests/unit/coords/test_Coord.py | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 825508dca3..b7b0cf6cc7 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -36,7 +36,10 @@ This document explains the changes made to Iris for this release 🐛 Bugs Fixed ============= -#. N/A +#. :user:`gaoflow` fixed :meth:`iris.coords.Coord.cell` so that a masked bound + is preserved as masked, instead of revealing the value stored underneath the + mask. This also corrects the cube/coordinate printout for such bounds. + (:issue:`5158`) 💣 Incompatible Changes diff --git a/lib/iris/coords.py b/lib/iris/coords.py index 9593457d28..a7266d7fa4 100644 --- a/lib/iris/coords.py +++ b/lib/iris/coords.py @@ -2235,7 +2235,10 @@ def cell(self, index): bound = None if self.has_bounds(): - bound = tuple(np.array(self.core_bounds()[index], ndmin=1).flatten()) + # Use `np.asanyarray` to preserve any masked values (see #5158): + bound = tuple( + np.atleast_1d(np.asanyarray(self.core_bounds()[index])).flatten() + ) if self.units.is_time_reference(): point = self.units.num2date(point) diff --git a/lib/iris/tests/unit/coords/test_Coord.py b/lib/iris/tests/unit/coords/test_Coord.py index 6ac28fa235..697a6f86d4 100644 --- a/lib/iris/tests/unit/coords/test_Coord.py +++ b/lib/iris/tests/unit/coords/test_Coord.py @@ -445,6 +445,22 @@ def test_time_as_object(self, mocker): mocker.call((mocker.sentinel.lower, mocker.sentinel.upper)), ] + def test_masked_point(self): + # A masked point should be preserved as masked in the cell (#5158). + coord = AuxCoord(ma.masked_array([3.0], mask=[True])) + cell = coord.cell(0) + assert cell.point is ma.masked + + def test_masked_bound(self): + # A masked bound should be preserved as masked in the cell, rather + # than revealing the value underneath the mask (#5158). + coord = AuxCoord( + [5.0], bounds=ma.masked_array([[3.0, 7.0]], mask=[[True, False]]) + ) + cell = coord.cell(0) + assert cell.bound[0] is ma.masked + assert cell.bound[1] == 7.0 + class Test_collapsed(CoordTestMixin): def test_serialize(self):