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
157 changes: 139 additions & 18 deletions lib/iris/experimental/ugrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"MeshFaceCoords",
"MeshNodeCoords",
"MeshMetadata",
"MeshCoordMetadata",
]


Expand Down Expand Up @@ -1989,25 +1990,145 @@ def face_node(self):
return self._members["face_node_connectivity"]


#: Convenience collection of lenient metadata combine services.
_services = [ConnectivityMetadata.combine, MeshMetadata.combine]
SERVICES_COMBINE.extend(_services)
SERVICES.extend(_services)
class MeshCoordMetadata(BaseMetadata):
"""
Metadata container for a :class:`~iris.coords.MeshCoord`.
"""

_members = ("location", "axis")
# NOTE: in future, we may add 'mesh' as part of this metadata,
# as the Mesh seems part of the 'identity' of a MeshCoord.
# For now we omit it, particularly as we don't yet implement Mesh.__eq__.
#
# Thus, for now, the MeshCoord class will need to handle 'mesh' explicitly
# in identity / comparison, but in future that may be simplified.

#: Convenience collection of lenient metadata difference services.
_services = [ConnectivityMetadata.difference, MeshMetadata.difference]
SERVICES_DIFFERENCE.extend(_services)
SERVICES.extend(_services)
__slots__ = ()

#: Convenience collection of lenient metadata equality services.
_services = [
ConnectivityMetadata.__eq__,
ConnectivityMetadata.equal,
MeshMetadata.__eq__,
MeshMetadata.equal,
]
SERVICES_EQUAL.extend(_services)
SERVICES.extend(_services)
@wraps(BaseMetadata.__eq__, assigned=("__doc__",), updated=())
@lenient_service
def __eq__(self, other):
return super().__eq__(other)

def _combine_lenient(self, other):
"""
Perform lenient combination of metadata members for MeshCoord.

Args:

* other (MeshCoordMetadata):
The other metadata participating in the lenient combination.

Returns:
A list of combined metadata member values.

"""
# It is actually "strict" : return None except where members are equal.
def func(field):
left = getattr(self, field)
right = getattr(other, field)
return left if left == right else None

# Note that, we use "_members" not "_fields".
values = [func(field) for field in self._members]
# Perform lenient combination of the other parent members.
result = super()._combine_lenient(other)
result.extend(values)

return result

def _compare_lenient(self, other):
"""
Perform lenient equality of metadata members for MeshCoord.

Args:

* other (MeshCoordMetadata):
The other metadata participating in the lenient comparison.

Returns:
Boolean.

"""
# Perform "strict" comparison for the MeshCoord specific members
# 'location', 'axis' : for equality, they must all match.
result = all(
[
getattr(self, field) == getattr(other, field)
for field in self._members
]
)
if result:
# Perform lenient comparison of the other parent members.
result = super()._compare_lenient(other)

return result

def _difference_lenient(self, other):
"""
Perform lenient difference of metadata members for MeshCoord.

Args:

* other (MeshCoordMetadata):
The other MeshCoord metadata participating in the lenient
difference.

Returns:
A list of different metadata member values.

"""
# Perform "strict" difference for location / axis.
def func(field):
left = getattr(self, field)
right = getattr(other, field)
return None if left == right else (left, right)

# Note that, we use "_members" not "_fields".
values = [func(field) for field in self._members]
# Perform lenient difference of the other parent members.
result = super()._difference_lenient(other)
result.extend(values)

return result

@wraps(BaseMetadata.combine, assigned=("__doc__",), updated=())
@lenient_service
def combine(self, other, lenient=None):
return super().combine(other, lenient=lenient)

del _services
@wraps(BaseMetadata.difference, assigned=("__doc__",), updated=())
@lenient_service
def difference(self, other, lenient=None):
return super().difference(other, lenient=lenient)

@wraps(BaseMetadata.equal, assigned=("__doc__",), updated=())
@lenient_service
def equal(self, other, lenient=None):
return super().equal(other, lenient=lenient)


# Add our new optional metadata operations into the 'convenience collections'
# of lenient metadata services.
# TODO: when included in 'iris.common.metadata', install each one directly ?
_op_names_and_service_collections = [
("combine", SERVICES_COMBINE),
("difference", SERVICES_DIFFERENCE),
("__eq__", SERVICES_EQUAL),
("equal", SERVICES_EQUAL),
]
_metadata_classes = [ConnectivityMetadata, MeshMetadata, MeshCoordMetadata]
for _cls in _metadata_classes:
for _name, _service_collection in _op_names_and_service_collections:
_method = getattr(_cls, _name)
_service_collection.append(_method)
SERVICES.append(_method)

del (
_op_names_and_service_collections,
_metadata_classes,
_cls,
_name,
_service_collection,
_method,
)
8 changes: 6 additions & 2 deletions lib/iris/tests/unit/common/lenient/test_Lenient.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ def setUp(self):
self.lenient = Lenient()

def test_in(self):
self.assertTrue("maths", self.lenient)
self.assertIn("maths", self.lenient)

def test_not_in(self):
self.assertTrue(("concatenate", self.lenient))
self.assertNotIn("concatenate", self.lenient)


class Test___getitem__(tests.IrisTest):
Expand Down Expand Up @@ -180,3 +180,7 @@ def test_maths_enable__lenient_false(self):
# still synchronised
self.assertFalse(_LENIENT.enable)
self.assertFalse(self.lenient["maths"])


if __name__ == "__main__":
tests.main()
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,8 @@ def test_op_lenient_same_measure_none(self):
expected = right.copy()

with mock.patch("iris.common.metadata._LENIENT", return_value=True):
self.assertTrue(expected, lmetadata.combine(rmetadata)._asdict())
self.assertTrue(expected, rmetadata.combine(lmetadata)._asdict())
self.assertEqual(expected, lmetadata.combine(rmetadata)._asdict())
self.assertEqual(expected, rmetadata.combine(lmetadata)._asdict())

def test_op_lenient_different(self):
lmetadata = self.cls(**self.values)
Expand Down
4 changes: 2 additions & 2 deletions lib/iris/tests/unit/common/metadata/test_CubeMetadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,8 @@ def test_op_lenient_same_cell_methods_none(self):
expected = right.copy()

with mock.patch("iris.common.metadata._LENIENT", return_value=True):
self.assertTrue(expected, lmetadata.combine(rmetadata)._asdict())
self.assertTrue(expected, rmetadata.combine(lmetadata)._asdict())
self.assertEqual(expected, lmetadata.combine(rmetadata)._asdict())
self.assertEqual(expected, rmetadata.combine(lmetadata)._asdict())

def test_op_lenient_different(self):
lmetadata = self.cls(**self.values)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,10 +389,10 @@ def test_op_lenient_same_members_none(self):
with mock.patch(
"iris.common.metadata._LENIENT", return_value=True
):
self.assertTrue(
self.assertEqual(
expected, lmetadata.combine(rmetadata)._asdict()
)
self.assertTrue(
self.assertEqual(
expected, rmetadata.combine(lmetadata)._asdict()
)

Expand Down
Loading