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
51 changes: 50 additions & 1 deletion lib/iris/experimental/ugrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -2772,7 +2772,6 @@ def __init__(
raise ValueError(msg)

# Get the 'coord identity' metadata from the relevant node-coordinate.
# N.B. mesh.coord returns a dict
node_coord = self.mesh.coord(include_nodes=True, axis=self.axis)
# Call parent constructor to handle the common constructor args.
super().__init__(
Expand Down Expand Up @@ -2897,6 +2896,56 @@ def __eq__(self, other):

return eq

def _string_summary(self, repr_style):
# Note: bypass the immediate parent here, which is Coord, because we
# have no interest in reporting coord_system or climatological, or in
# printing out our points/bounds.
# We also want to list our defining properties, i.e. mesh/location/axis
# *first*, before names/units etc, so different from other Coord types.

# First construct a shortform text summary to identify the Mesh.
# IN 'str-mode', this attempts to use Mesh.name() if it is set,
# otherwise uses an object-id style (as also for 'repr-mode').
# TODO: use a suitable method provided by Mesh, e.g. something like
# "Mesh.summary(shorten=True)", when it is available.
mesh_name = None
if not repr_style:
mesh_name = self.mesh.name()
if mesh_name in (None, "", "unknown"):
mesh_name = None
if mesh_name:
# Use a more human-readable form
mesh_string = f"Mesh({mesh_name!r})"
else:
# Mimic the generic object.__str__ style.
mesh_id = id(self.mesh)
mesh_string = f"<Mesh object at {hex(mesh_id)}>"
result = (
f"mesh={mesh_string}"
f", location={self.location!r}"
f", axis={self.axis!r}"
)
# Add 'other' metadata that is drawn from the underlying node-coord.
# But put these *afterward*, unlike other similar classes.
for item in ("standard_name", "units", "long_name", "attributes"):
# NOTE: order of these matches Coord.summary, but omit var_name.
val = getattr(self, item, None)
if item == "attributes":
is_blank = len(val) == 0 # an empty dict is as good as none
else:
is_blank = val is None
if not is_blank:
result += f", {item}={val!r}"

result = f"MeshCoord({result})"
return result

def __str__(self):
return self._string_summary(repr_style=False)

def __repr__(self):
return self._string_summary(repr_style=True)

def _construct_access_arrays(self):
"""
Build lazy points and bounds arrays, providing dynamic access via the
Expand Down
99 changes: 98 additions & 1 deletion lib/iris/tests/unit/experimental/ugrid/test_MeshCoord.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ def _create_test_mesh():
node_x = AuxCoord(
1100 + np.arange(_TEST_N_NODES),
standard_name="longitude",
units="degrees_east",
long_name="long-name",
var_name="var",
var_name="var-name",
attributes={"a": 1, "b": "c"},
)
node_y = AuxCoord(
1200 + np.arange(_TEST_N_NODES), standard_name="latitude"
Expand Down Expand Up @@ -331,6 +333,101 @@ def test_fail_slice_part(self):
meshcoord[:1]


class Test__str_repr(tests.IrisTest):
def setUp(self):
mesh = _create_test_mesh()
self.mesh = mesh
# Give mesh itself a name: makes a difference between str and repr.
self.mesh.rename("test_mesh")
self.meshcoord = _create_test_meshcoord(mesh=mesh)

def _expected_elements_regexp(
self,
mesh_strstyle=True,
standard_name=True,
long_name=True,
attributes=True,
):
regexp = r"^MeshCoord\(mesh="
if mesh_strstyle:
regexp += r"Mesh\('test_mesh'\)"
else:
regexp += "<Mesh object at .*>"
regexp += ", location='face', axis='x'"
if standard_name:
regexp += ", standard_name='longitude'"
regexp += r", units=Unit\('degrees_east'\)"
if long_name:
regexp += ", long_name='long-name'"
if attributes:
regexp += r", attributes={'a': 1, 'b': 'c'}"
regexp += r"\)$"
return regexp

def test_repr(self):
result = repr(self.meshcoord)
re_expected = self._expected_elements_regexp(mesh_strstyle=False)
self.assertRegex(result, re_expected)

def test__str__(self):
result = str(self.meshcoord)
re_expected = self._expected_elements_regexp(mesh_strstyle=True)
self.assertRegex(result, re_expected)

def test_alternative_location_and_axis(self):
meshcoord = _create_test_meshcoord(
mesh=self.mesh, location="edge", axis="y"
)
result = str(meshcoord)
re_expected = r", location='edge', axis='y'"
self.assertRegex(result, re_expected)

def test_str_no_long_name(self):
mesh = self.mesh
# Remove the long_name of the node coord in the mesh.
node_coord = mesh.coord(include_nodes=True, axis="x")
node_coord.long_name = None
# Make a new meshcoord, based on the modified mesh.
meshcoord = _create_test_meshcoord(mesh=self.mesh)
result = str(meshcoord)
re_expected = self._expected_elements_regexp(long_name=False)
self.assertRegex(result, re_expected)

def test_str_no_standard_name(self):
mesh = self.mesh
# Remove the standard_name of the node coord in the mesh.
node_coord = mesh.coord(include_nodes=True, axis="x")
node_coord.standard_name = None
node_coord.axis = "x" # This is required : but it's a kludge !!
# Make a new meshcoord, based on the modified mesh.
meshcoord = _create_test_meshcoord(mesh=self.mesh)
result = str(meshcoord)
re_expected = self._expected_elements_regexp(standard_name=False)
self.assertRegex(result, re_expected)

def test_str_no_attributes(self):
mesh = self.mesh
# No attributes on the node coord in the mesh.
node_coord = mesh.coord(include_nodes=True, axis="x")
node_coord.attributes = None
# Make a new meshcoord, based on the modified mesh.
meshcoord = _create_test_meshcoord(mesh=self.mesh)
result = str(meshcoord)
re_expected = self._expected_elements_regexp(attributes=False)
self.assertRegex(result, re_expected)

def test_str_empty_attributes(self):
mesh = self.mesh
# Empty attributes dict on the node coord in the mesh.
node_coord = mesh.coord(include_nodes=True, axis="x")
node_coord.attributes.clear()
# Make a new meshcoord, based on the modified mesh.
meshcoord = _create_test_meshcoord(mesh=self.mesh)
result = str(meshcoord)
re_expected = self._expected_elements_regexp(attributes=False)
self.assertRegex(result, re_expected)


class Test_cube_containment(tests.IrisTest):
# Check that we can put a MeshCoord into a cube, and have it behave just
# like a regular AuxCoord.
Expand Down