Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cubelist contain only cubes -- resurrected #4767

Merged
merged 17 commits into from
May 27, 2022
Merged
63 changes: 28 additions & 35 deletions lib/iris/cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,26 @@ def merged(self, unique=False):
[pair.merged(unique) for pair in self.pairs]
)

def _check_iscube(obj):
"""
Raise a warning if obj does not look like a cube.
"""
if not hasattr(obj, 'add_aux_coord'):
msg = ("Cubelist now contains object of type '{}'. This may "
"adversely affect subsequent operations.")
warnings.warn(msg.format(type(obj).__name__))

def _check_cube_sequence(sequence):
"""
Raise one or more warnings if sequence contains elements that are not
Cubes (or cube-like). Skip this if the sequence is a CubeList, as we can
assume it was already checked.
"""
if (isinstance(sequence, collections.Iterable) and
not isinstance(sequence, Cube) and
not isinstance(sequence, CubeList)):
for obj in sequence:
_check_iscube(obj)

class CubeList(list):
"""
Expand Down Expand Up @@ -177,34 +197,7 @@ def __str__(self):

def __repr__(self):
"""Runs repr on every cube."""
return "[%s]" % ",\n".join([repr(cube) for cube in self])

def _repr_html_(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I ask why the HTML representation for CubeList was removed please?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I think was a plain mistake.
On closer inspection, it looks like we test the usage of CubeListRepresentation(cubelist).repr_html, but not actually "cubelist.repr_html()".

I think this needs fixing : #4973

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you @pp-mo!

from iris.experimental.representation import CubeListRepresentation

representer = CubeListRepresentation(self)
return representer.repr_html()

def _check_iscube(self, obj):
"""
Raise a warning if obj is not a cube.
"""
if not isinstance(obj, Cube):
msg = ('Cubelist now contains object of type {}. This may '
'adversely affect subsequent operations.')
warnings.warn(msg.format(type(obj)))

def _check_cube_sequence(self, sequence):
"""
Raise one or more warnings if sequence contains elements that are not
Cubes. Skip this if the sequence is a CubeList, as we can assume it
was already checked.
"""
if (isinstance(sequence, collections.Iterable) and
not isinstance(sequence, Cube) and
not isinstance(sequence, CubeList)):
for obj in sequence:
self._check_iscube(obj)
return '[%s]' % ',\n'.join([repr(cube) for cube in self])

# TODO #370 Which operators need overloads?

Expand Down Expand Up @@ -233,31 +226,31 @@ def __iadd__(self, other_cubes):
"""
Add a sequence of cubes to the cubelist in place.
"""
self._check_cube_sequence(other_cubes)
_check_cube_sequence(other_cubes)
super(CubeList, self).__iadd__(other_cubes)

def __setitem__(self, key, cube_or_sequence):
"""Set self[key] to cube or sequence of cubes"""
if isinstance(key, int):
# should have single cube.
self._check_iscube(cube_or_sequence)
_check_iscube(cube_or_sequence)
else:
# key is a slice (or exception will come from list method).
self._check_cube_sequence(cube_or_sequence)
_check_cube_sequence(cube_or_sequence)

super(CubeList, self).__setitem__(key, cube_or_sequence)

# __setslice__ is only required for python2.7 compatibility.
def __setslice__(self, *args):
cubes = args[-1]
self._check_cube_sequence(cubes)
_check_cube_sequence(cubes)
super(CubeList, self).__setslice__(*args)

def append(self, cube):
"""
Append a cube.
"""
self._check_iscube(cube)
_check_iscube(cube)
super(CubeList, self).append(cube)

def extend(self, other_cubes):
Expand All @@ -269,14 +262,14 @@ def extend(self, other_cubes):
* other_cubes:
A cubelist or other sequence of cubes.
"""
self._check_cube_sequence(other_cubes)
_check_cube_sequence(other_cubes)
super(CubeList, self).extend(other_cubes)

def insert(self, index, cube):
"""
Insert a cube before index.
"""
self._check_iscube(cube)
_check_iscube(cube)
super(CubeList, self).insert(index, cube)

def xml(self, checksum=False, order=True, byteorder=True):
Expand Down
14 changes: 7 additions & 7 deletions lib/iris/tests/unit/cube/test_CubeList.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from iris.fileformats.pp import STASH
import iris.tests.stock

NOT_CUBE_MSG = 'Cubelist now contains object of type '
NOT_CUBE_MSG = "Cubelist now contains object of type '{}'"


class Test_append(tests.IrisTest):
Expand All @@ -41,7 +41,7 @@ def test_pass(self):
self.assertEqual(self.cubelist[-1], self.cube2)

def test_warn(self):
with self.assertWarnsRegexp(NOT_CUBE_MSG):
with self.assertWarnsRegexp(NOT_CUBE_MSG.format('NoneType')):
self.cubelist.append(None)


Expand Down Expand Up @@ -113,7 +113,7 @@ def test_fail(self):
self.cubelist1.extend(None)

def test_warn(self):
with self.assertWarnsRegexp(NOT_CUBE_MSG):
with self.assertWarnsRegexp(NOT_CUBE_MSG.format('int')):
self.cubelist1.extend(range(3))


Expand Down Expand Up @@ -200,7 +200,7 @@ def test_fail(self):
self.cubelist1 += 1.

def test_warn(self):
with self.assertWarnsRegexp(NOT_CUBE_MSG):
with self.assertWarnsRegexp(NOT_CUBE_MSG.format('int')):
self.cubelist1 += range(3)


Expand All @@ -215,7 +215,7 @@ def test_pass(self):
self.assertEqual(self.cubelist[1], self.cube2)

def test_warn(self):
with self.assertWarnsRegexp(NOT_CUBE_MSG):
with self.assertWarnsRegexp(NOT_CUBE_MSG.format('NoneType')):
self.cubelist.insert(0, None)


Expand Down Expand Up @@ -379,9 +379,9 @@ def test_pass(self):
iris.cube.CubeList([self.cube2, self.cube3, self.cube1]))

def test_warn(self):
with self.assertWarnsRegexp(NOT_CUBE_MSG):
with self.assertWarnsRegexp(NOT_CUBE_MSG.format('NoneType')):
self.cubelist[0] = None
with self.assertWarnsRegexp(NOT_CUBE_MSG):
with self.assertWarnsRegexp(NOT_CUBE_MSG.format('NoneType')):
self.cubelist[0:2] = [self.cube3, None]

def test_fail(self):
Expand Down