diff --git a/lib/iris/cube.py b/lib/iris/cube.py index 3d0854355c..431d1340d9 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -165,19 +165,25 @@ class CubeList(list): """ - def __new__(cls, list_of_cubes=None): - """Given a :class:`list` of cubes, return a CubeList instance.""" - cube_list = list.__new__(cls, list_of_cubes) - - # Check that all items in the incoming list are cubes. Note that this - # checking does not guarantee that a CubeList instance *always* has - # just cubes in its list as the append & __getitem__ methods have not - # been overridden. - if not all([isinstance(cube, Cube) for cube in cube_list]): + def __init__(self, iterable_of_cubes=None): + """Given an iterable of cubes, return a CubeList instance.""" + if iterable_of_cubes is None: + iterable_of_cubes = [] + # Run the iterable, and capture as a list. + list_of_cubes = list(iterable_of_cubes) + + # Check that all items in the incoming list (if any) are cubes. + # Note that this checking does not (yet) guarantee that a CubeList + # instance *always* contains only cubes, as the append & __setitem__ methods + # (for example), have not been overridden. + if not all(isinstance(cube, Cube) for cube in list_of_cubes): raise ValueError( - "All items in list_of_cubes must be Cube " "instances." + "CubeList create arguments are not all Cube " + "instances : {}".format(list_of_cubes) ) - return cube_list + + # Initialise "as a" list. + super().__init__(list_of_cubes) def __str__(self): """Runs short :meth:`Cube.summary` on every cube.""" diff --git a/lib/iris/tests/unit/cube/test_CubeList.py b/lib/iris/tests/unit/cube/test_CubeList.py index 2e7b110d60..8cd565eaff 100644 --- a/lib/iris/tests/unit/cube/test_CubeList.py +++ b/lib/iris/tests/unit/cube/test_CubeList.py @@ -25,6 +25,53 @@ from iris.fileformats.pp import STASH +class TestCreateCubelist(tests.IrisTest): + def test_create_no_args(self): + result = CubeList() + self.assertIsInstance(result, CubeList) + self.assertEqual(len(result), 0) + + def test_create_empty_arg(self): + # In principle, any iterable should do. So let's use a generator. + def empty_iter(): + if False: + yield False + + result = CubeList(empty_iter()) + self.assertIsInstance(result, CubeList) + self.assertEqual(len(result), 0) + + def test_create_single_cube(self): + cube = Cube([1]) + result = CubeList([cube]) + self.assertIsInstance(result, CubeList) + self.assertEqual(len(result), 1) + self.assertIs(result[0], cube) + + def test_create_cubes(self): + cubes = ( + Cube([1], long_name="a"), + Cube(np.arange(6).reshape((2, 3)), long_name="b"), + ) + result = CubeList(cubes) + self.assertIsInstance(result, CubeList) + self.assertEqual(len(result), 2) + self.assertIs(result[0], cubes[0]) + self.assertIs(result[1], cubes[1]) + + def test_create_fail_noncube_object(self): + with self.assertRaisesRegexp(ValueError, "Cube instance"): + CubeList([Cube([1]), []]) + + def test_create_fail_noncube_None(self): + with self.assertRaisesRegexp(ValueError, "Cube instance"): + CubeList([Cube([1]), None]) + + def test_create_fail_noncube_number(self): + with self.assertRaisesRegexp(ValueError, "Cube instance"): + CubeList([Cube([1]), 123.45]) + + class Test_concatenate_cube(tests.IrisTest): def setUp(self): self.units = Unit( @@ -592,7 +639,9 @@ class TestRealiseData(tests.IrisTest): def test_realise_data(self): # Simply check that calling CubeList.realise_data is calling # _lazy_data.co_realise_cubes. - mock_cubes_list = [mock.Mock(ident=count) for count in range(3)] + mock_cubes_list = [ + mock.Mock(ident=count, spec=Cube) for count in range(3) + ] test_cubelist = CubeList(mock_cubes_list) call_patch = self.patch("iris._lazy_data.co_realise_cubes") test_cubelist.realise_data()