Skip to content

Commit

Permalink
More small cosmetic changes, plus minimal bounds in repr.
Browse files Browse the repository at this point in the history
  • Loading branch information
pp-mo committed Jan 21, 2022
1 parent e8bef5f commit a2a6015
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 61 deletions.
5 changes: 4 additions & 1 deletion lib/iris/coords.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def summary(
convert_dates=True,
):
"""
Make a printable text summary of a :class:.
Make a printable text summary.
Parameters
----------
Expand Down Expand Up @@ -446,6 +446,9 @@ def flatten_array_str(array_str):
# "placeholder" representation.
data_str = "[...]"

if self.has_bounds():
data_str += "+bnds"

if self.shape != (1,):
# Anything non-scalar : show shape as well.
data_str += f" shape{shape_str}"
Expand Down
26 changes: 9 additions & 17 deletions lib/iris/experimental/ugrid/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -1030,7 +1030,7 @@ def line(text, i_indent=0):
if optional_conns:
line("optional connectivities", 1)
for name, conn in optional_conns.items():
conn_string = conn.summary(shorten=True, max_array_width=0)
conn_string = conn.summary(shorten=True, linewidth=0)
line(f"{name}: {conn_string}", 2)

# Output the detail properties, basically those from CFVariableMixin
Expand Down Expand Up @@ -2953,31 +2953,22 @@ def summary(self, *args, **kwargs):
else:
shorten = kwargs.get("shorten", False)

# We need a short one-line printout to identify the mesh, but at
# present this is tricky, because the Mesh class itself doesn't
# provide one.
# So for now we "fake" it, in a rather preliminary way...
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 = mesh_name
else:
# Mimic the generic object.__str__ style.
mesh_id = id(self.mesh)
mesh_string = f"<Mesh object at {hex(mesh_id)}>"

# Get the default-form result.
if shorten:
# NOTE: we simply aren't interested in the values for the repr,
# so fix linewidth to suppress them
kwargs["linewidth"] = 1

result = super().summary(*args, **kwargs)

# Modify the generic 'default-form' result to produce what we want.
if shorten:
# Single-line form : insert the mesh+location before the array part
i_array = result.index("[")
mesh_string = self.mesh.name()
if mesh_string == "unknown":
# If no name, replace with the one-line summary
mesh_string = self.mesh.summary(shorten=True)
extra_str = f"mesh({mesh_string}) location({self.location}) "
result = result[:i_array] + extra_str + result[i_array:]
# NOTE: this invalidates the original width calculation and may
Expand All @@ -2997,7 +2988,8 @@ def summary(self, *args, **kwargs):
i_namestart = location_line.index("location:")
indent = location_line[:i_namestart]
# Construct a suitable 'mesh' line
mesh_line = f"{indent}mesh: '{mesh_string}'"
mesh_string = self.mesh.summary(shorten=True)
mesh_line = f"{indent}mesh: {mesh_string}"
# Move the 'location' line, putting it and the 'mesh' line
# immediately after the header
del lines[i_location]
Expand Down
24 changes: 12 additions & 12 deletions lib/iris/tests/unit/coords/test__DimensionalMetadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def test_names(self):
def test_bounded(self):
result = self.coord_representations(shape=(3,), bounded=True)
expected = [
"<AuxCoord: x / (m) [0., 1., 2.] shape(3,)>",
"<AuxCoord: x / (m) [0., 1., 2.]+bnds shape(3,)>",
"AuxCoord : x / (m)",
" points: [0., 1., 2.]",
" bounds: [",
Expand Down Expand Up @@ -360,7 +360,7 @@ def test_lazy_points(self):
def test_lazy_bounds(self):
result = self.coord_representations(lazy_bounds=True)
expected = [
"<AuxCoord: x / (m) [0., 1., 2., 3., 4.] shape(5,)>",
"<AuxCoord: x / (m) [0., 1., 2., 3., 4.]+bnds shape(5,)>",
"AuxCoord : x / (m)",
" points: [0., 1., 2., 3., 4.]",
" bounds: <lazy>",
Expand All @@ -386,7 +386,7 @@ def test_lazy_points_and_bounds(self):
def test_scalar(self):
result = self.coord_representations(shape=(1,), bounded=True)
expected = [
"<AuxCoord: x / (m) [0.]>",
"<AuxCoord: x / (m) [0.]+bnds>",
"AuxCoord : x / (m)",
" points: [0.]",
" bounds: [[-10., 10.]]",
Expand All @@ -401,7 +401,7 @@ def test_scalar_masked(self):
shape=(1,), bounded=True, masked=True
)
expected = [
"<AuxCoord: x / (m) [--]>",
"<AuxCoord: x / (m) [--]+bnds>",
"AuxCoord : x / (m)",
" points: [--]",
" bounds: [[--, --]]",
Expand All @@ -414,7 +414,7 @@ def test_scalar_masked(self):
def test_length_short(self):
result = self.coord_representations(shape=(2,), bounded=True)
expected = [
"<AuxCoord: x / (m) [0., 1.] shape(2,)>",
"<AuxCoord: x / (m) [0., 1.]+bnds shape(2,)>",
"AuxCoord : x / (m)",
" points: [0., 1.]",
" bounds: [",
Expand All @@ -430,7 +430,7 @@ def test_length_medium(self):
# Where bounds are truncated, but points not.
result = self.coord_representations(shape=(14,), bounded=True)
expected = [
"<AuxCoord: x / (m) [ 0., 1., ..., 12., 13.] shape(14,)>",
"<AuxCoord: x / (m) [ 0., 1., ..., 12., 13.]+bnds shape(14,)>",
"AuxCoord : x / (m)",
" points: [",
" 0., 1., 2., 3., 4., 5., 6., 7., 8.,",
Expand All @@ -451,7 +451,7 @@ def test_length_long(self):
# Completely truncated representations
result = self.coord_representations(shape=(150,), bounded=True)
expected = [
"<AuxCoord: x / (m) [ 0., 1., ..., 148., 149.] shape(150,)>",
"<AuxCoord: x / (m) [ 0., 1., ..., 148., 149.]+bnds shape(150,)>",
"AuxCoord : x / (m)",
" points: [ 0., 1., ..., 148., 149.]",
" bounds: [",
Expand Down Expand Up @@ -542,7 +542,7 @@ def test_dates_scalar(self):
def test_dates_bounds(self):
result = self.coord_representations(dates=True, bounded=True)
expected = [
"<AuxCoord: x / (days since 1970-03-5) [...] shape(5,)>",
"<AuxCoord: x / (days since 1970-03-5) [...]+bnds shape(5,)>",
"AuxCoord : x / (days since 1970-03-5, gregorian calendar)",
" points: [",
" 1970-03-05 00:00:00, 1970-03-06 00:00:00,",
Expand Down Expand Up @@ -582,7 +582,7 @@ def test_untypical_bounds(self):
coord.bounds = bounds
result = self.repr_str_strings(coord)
expected = [
"<AuxCoord: x / (m) [0., 1., 2., 3., 4.] shape(5,)>",
"<AuxCoord: x / (m) [0., 1., 2., 3., 4.]+bnds shape(5,)>",
"AuxCoord : x / (m)",
" points: [0., 1., 2., 3., 4.]",
" bounds: [",
Expand Down Expand Up @@ -733,7 +733,7 @@ def test_climatological(self):
coord = coord[:1] # Just to make it a bit shorter
result = self.repr_str_strings(coord)
expected = [
"<DimCoord: time / (days since 1970-01-01 00:00:00-00) [...]>",
"<DimCoord: time / (days since 1970-01-01 00:00:00-00) [...]+bnds>",
"DimCoord : time / (days since 1970-01-01 00:00:00-00, gregorian calendar)",
" points: [2001-01-10 00:00:00]",
" bounds: [[2001-01-10 00:00:00, 2011-01-10 00:00:00]]",
Expand Down Expand Up @@ -897,10 +897,10 @@ def test_meshcoord(self):
(
"<MeshCoord: longitude / (degrees_east) "
"mesh(test_mesh) location(face) "
"[...] shape(3,)>"
"[...]+bnds shape(3,)>"
),
"MeshCoord : longitude / (degrees_east)",
" mesh: 'test_mesh'",
" mesh: <Mesh: 'test_mesh'>",
" location: 'face'",
" points: [3100, 3101, 3102]",
" bounds: [",
Expand Down
4 changes: 4 additions & 0 deletions lib/iris/tests/unit/experimental/ugrid/mesh/test_Mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,10 @@ def setUp(self):
}
self.mesh = mesh.Mesh(**self.kwargs)

def test___repr__basic(self):
expected = "<Mesh: 'my_topology_mesh'>"
self.assertEqual(expected, repr(self.mesh))

def test___repr__varname(self):
self.mesh.long_name = None
expected = "<Mesh: 'mesh'>"
Expand Down
72 changes: 41 additions & 31 deletions lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ def setUp(self):

def _expected_elements_regexp(
self,
mesh_reprstyle=False,
standard_name="longitude",
long_name="long-name",
attributes=True,
Expand All @@ -278,45 +277,56 @@ def _expected_elements_regexp(
):
# Printed name is standard or long -- we don't have a case with neither
coord_name = standard_name or long_name
if mesh_reprstyle:
regexp = f"^<MeshCoord: {coord_name} / .*>$"
else:
# Construct regexp in 'sections'
# NB each consumes upto first non-space in the next line
regexp = f"MeshCoord : {coord_name} / [^\n]+\n *"
regexp += "mesh: 'test_mesh'\n *"
regexp += f"location: '{location}'\n *"
# Now some optional sections : whichever comes first will match
# arbitrary content leading up to it.
matched_any_upto = False
if standard_name:
# Construct regexp in 'sections'
# NB each consumes upto first non-space in the next line
regexp = f"MeshCoord : {coord_name} / [^\n]+\n *"
regexp += r"mesh: \<Mesh: 'test_mesh'>\n *"
regexp += f"location: '{location}'\n *"
# Now some optional sections : whichever comes first will match
# arbitrary content leading up to it.
matched_any_upto = False
if standard_name:
regexp += ".*"
matched_any_upto = True
regexp += f"standard_name: '{standard_name}'\n *"
if long_name:
if not matched_any_upto:
regexp += ".*"
matched_any_upto = True
regexp += f"standard_name: '{standard_name}'\n *"
if long_name:
if not matched_any_upto:
regexp += ".*"
matched_any_upto = True
regexp += f"long_name: '{long_name}'\n *"
if attributes:
# if we expected attributes, they should come next
# TODO: change this when each attribute goes on a new line
if not matched_any_upto:
regexp += ".*"
matched_any_upto = True
regexp += "attributes: {[^}]*}\n *"
# After those items, expect 'axis' next
# N.B. this FAILS if we had attributes when we didn't expect them
regexp += f"axis: '{axis}'$" # N.B. this is always the end
regexp += f"long_name: '{long_name}'\n *"
if attributes:
# if we expected attributes, they should come next
# TODO: change this when each attribute goes on a new line
if not matched_any_upto:
regexp += ".*"
matched_any_upto = True
regexp += "attributes: {[^}]*}\n *"
# After those items, expect 'axis' next
# N.B. this FAILS if we had attributes when we didn't expect them
regexp += f"axis: '{axis}'$" # N.B. this is always the end

# Compile regexp, also allowing matches across newlines
regexp = re.compile(regexp, flags=re.DOTALL)
return regexp

def test_repr(self):
# One simple check for the condensed form.
# A simple check for the condensed form.
result = repr(self.meshcoord)
expected = (
"<MeshCoord: longitude / (degrees_east) "
"mesh(test_mesh) location(face) [...]+bnds shape(3,)>"
)
self.assertEqual(expected, result)

def test_repr__nameless_mesh(self):
# Check what it does when the Mesh doesn't have a name.
self.mesh.long_name = None
assert self.mesh.name() == "unknown"
result = repr(self.meshcoord)
re_expected = self._expected_elements_regexp(mesh_reprstyle=True)
re_expected = (
r".MeshCoord: longitude / \(degrees_east\) "
r"mesh\(.Mesh object at 0x[^>]+.\) location\(face\) "
)
self.assertRegex(result, re_expected)

def test__str__(self):
Expand Down

0 comments on commit a2a6015

Please sign in to comment.