From 4fc797dd95e978ebc5b30325d5ff5d765259f9a2 Mon Sep 17 00:00:00 2001 From: Ruth Comer Date: Tue, 5 Nov 2019 15:00:27 +0000 Subject: [PATCH] add cube attributes decorator --- lib/iris/cube.py | 27 +++++++++++++++++++++++++++ lib/iris/io/__init__.py | 1 + 2 files changed, 28 insertions(+) diff --git a/lib/iris/cube.py b/lib/iris/cube.py index a1b6eca680..584ec09322 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -59,6 +59,23 @@ __all__ = ['Cube', 'CubeList', 'CubeMetadata'] +def cube_attributes(func): + """ + Wrap functions that operate on CubeLists so they produce a more informative + error message if they find an element that is not a cube. + """ + def inner(*args, **kwargs): + try: + result = func(*args, **kwargs) + except AttributeError: + raise TypeError( + 'Cubelist appears to contain objects that are not cubes') + else: + return result + + return inner + + class CubeMetadata(namedtuple('CubeMetadata', ['standard_name', 'long_name', @@ -206,6 +223,7 @@ def __new__(cls, list_of_cubes=None): 'instances.') return cube_list + @cube_attributes def __str__(self): """Runs short :meth:`Cube.summary` on every cube.""" result = ['%s: %s' % (i, cube.summary(shorten=True)) for i, cube in @@ -216,10 +234,12 @@ def __str__(self): result = '< No cubes >' return result + @cube_attributes def __repr__(self): """Runs repr on every cube.""" return '[%s]' % ',\n'.join([repr(cube) for cube in self]) + @cube_attributes def _repr_html_(self): from iris.experimental.representation import CubeListRepresentation representer = CubeListRepresentation(self) @@ -247,6 +267,7 @@ def __getslice__(self, start, stop): result = CubeList(result) return result + @cube_attributes def xml(self, checksum=False, order=True, byteorder=True): """Return a string of the XML that this list of cubes represents.""" doc = Document() @@ -263,6 +284,7 @@ def xml(self, checksum=False, order=True, byteorder=True): # return our newly created XML string return doc.toprettyxml(indent=" ") + @cube_attributes def extract(self, constraints, strict=False): """ Filter each of the cubes which can be filtered by the given @@ -351,6 +373,7 @@ def overlap_fn(cell): return self.extract(iris.Constraint(coord_values=coord_values)) + @cube_attributes def merge_cube(self): """ Return the merged contents of the :class:`CubeList` as a single @@ -388,6 +411,7 @@ def merge_cube(self): merged_cube, = proto_cube.merge() return merged_cube + @cube_attributes def merge(self, unique=True): """ Returns the :class:`CubeList` resulting from merging this @@ -476,6 +500,7 @@ def _none_sort(item): return merged_cubes + @cube_attributes def concatenate_cube(self, check_aux_coords=True): """ Return the concatenated contents of the :class:`CubeList` as a single @@ -521,6 +546,7 @@ def concatenate_cube(self, check_aux_coords=True): names[1])) raise iris.exceptions.ConcatenateError(msgs) + @cube_attributes def concatenate(self, check_aux_coords=True): """ Concatenate the cubes over their common dimensions. @@ -599,6 +625,7 @@ def concatenate(self, check_aux_coords=True): return iris._concatenate.concatenate(self, check_aux_coords=check_aux_coords) + @cube_attributes def realise_data(self): """ Fetch 'real' data for all cubes, in a shared calculation. diff --git a/lib/iris/io/__init__.py b/lib/iris/io/__init__.py index e7064f65aa..6df5002473 100644 --- a/lib/iris/io/__init__.py +++ b/lib/iris/io/__init__.py @@ -308,6 +308,7 @@ def find_saver(filespec): return _savers[matches[0]] if matches else None +@iris.cube.cube_attributes def save(source, target, saver=None, **kwargs): """ Save one or more Cubes to file (or other writable).