From 67f171e8972d5f4dcb247f10b491bfcd380eea12 Mon Sep 17 00:00:00 2001 From: marqh Date: Tue, 4 Apr 2017 08:29:19 +0000 Subject: [PATCH 01/11] functional code transfer --- lib/iris/fileformats/__init__.py | 10 +- lib/iris/fileformats/grib/__init__.py | 837 +++++++----------- .../{load_rules.py => _grib1_load_rules.py} | 184 +--- lib/iris/fileformats/grib/_grib_cf_map.py | 27 +- lib/iris/fileformats/grib/_load_convert.py | 106 ++- lib/iris/fileformats/grib/_save_rules.py | 199 +++-- .../grib/grib_phenom_translation.py | 14 +- lib/iris/fileformats/grib/grib_save_rules.py | 695 --------------- lib/iris/fileformats/grib/message.py | 2 +- lib/iris/tests/test_coding_standards.py | 3 +- 10 files changed, 540 insertions(+), 1537 deletions(-) rename lib/iris/fileformats/grib/{load_rules.py => _grib1_load_rules.py} (56%) delete mode 100644 lib/iris/fileformats/grib/grib_save_rules.py diff --git a/lib/iris/fileformats/__init__.py b/lib/iris/fileformats/__init__.py index ce15901285..4eba3548c3 100644 --- a/lib/iris/fileformats/__init__.py +++ b/lib/iris/fileformats/__init__.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2016, Met Office +# (C) British Crown Copyright 2010 - 2017, Met Office # # This file is part of Iris. # @@ -27,13 +27,11 @@ UriProtocol, LeadingLine) from . import abf from . import um + try: - import iris_grib as igrib + from . import grib as igrib except ImportError: - try: - from . import grib as igrib - except ImportError: - igrib = None + igrib = None from . import name from . import netcdf diff --git a/lib/iris/fileformats/grib/__init__.py b/lib/iris/fileformats/grib/__init__.py index f31e20dd1f..8898ed0506 100644 --- a/lib/iris/fileformats/grib/__init__.py +++ b/lib/iris/fileformats/grib/__init__.py @@ -17,13 +17,7 @@ """ Conversion of cubes to/from GRIB. -See also: `ECMWF GRIB API `_. - -.. deprecated:: 1.10 - - This module is now deprecated. For GRIB file support in iris, please use - the separate package - `iris_grib `_ instead. +See: `ECMWF GRIB API `_. """ @@ -32,7 +26,7 @@ import six import datetime -import math #for fmod +import math # for fmod import warnings import biggus @@ -41,56 +35,44 @@ import gribapi import numpy as np import numpy.ma as ma -import scipy.interpolate -from iris._deprecation import warn_deprecated -from iris.analysis._interpolate_private import Linear1dExtrapolator +import iris import iris.coord_systems as coord_systems -from iris.exceptions import TranslationError +from iris.exceptions import TranslationError, NotYetImplementedError + # NOTE: careful here, to avoid circular imports (as iris imports grib) -from iris.fileformats.grib import grib_phenom_translation as gptx -from iris.fileformats.grib import _save_rules -import iris.fileformats.grib._load_convert -from iris.fileformats.grib.message import GribMessage -import iris.fileformats.grib.load_rules +from . import grib_phenom_translation as gptx +from . import _save_rules +from ._load_convert import convert as load_convert +from .message import GribMessage -# Issue a blanket deprecation for this module. -warn_deprecated( - "The module iris.fileformats.grib is deprecated since v1.10. " - "Please install the package 'iris_grib' package instead.") +__version__ = '0.10.0-dev' __all__ = ['load_cubes', 'save_grib2', 'load_pairs_from_fields', - 'save_pairs_from_cube', 'save_messages', 'GribWrapper', - 'as_messages', 'as_pairs', 'grib_generator', 'reset_load_rules', - 'hindcast_workaround'] - - -#: Set this flag to True to enable support of negative forecast periods -#: when loading and saving GRIB files. -#: -#: .. deprecated:: 1.10 -hindcast_workaround = False + 'save_pairs_from_cube', 'save_messages'] CENTRE_TITLES = {'egrr': 'U.K. Met Office - Exeter', 'ecmf': 'European Centre for Medium Range Weather Forecasts', 'rjtd': 'Tokyo, Japan Meteorological Agency', - '55' : 'San Francisco', - 'kwbc': 'US National Weather Service, National Centres for Environmental Prediction'} - -TIME_RANGE_INDICATORS = {0:'none', 1:'none', 3:'time mean', 4:'time sum', - 5:'time _difference', 10:'none', - # TODO #567 Further exploration of the following mappings - 51:'time mean', 113:'time mean', 114:'time sum', - 115:'time mean', 116:'time sum', 117:'time mean', - 118:'time _covariance', 123:'time mean', - 124:'time sum', 125:'time standard_deviation'} - -PROCESSING_TYPES = {0:'time mean', 1:'time sum', 2:'time maximum', 3:'time minimum', - 4:'time _difference', 5:'time _root mean square', - 6:'time standard_deviation', 7:'time _convariance', - 8:'time _difference', 9:'time _ratio'} + '55': 'San Francisco', + 'kwbc': ('US National Weather Service, National Centres for ' + 'Environmental Prediction')} + +TIME_RANGE_INDICATORS = {0: 'none', 1: 'none', 3: 'time mean', 4: 'time sum', + 5: 'time _difference', 10: 'none', + # TODO #567 Further exploration of following mappings + 51: 'time mean', 113: 'time mean', 114: 'time sum', + 115: 'time mean', 116: 'time sum', 117: 'time mean', + 118: 'time _covariance', 123: 'time mean', + 124: 'time sum', 125: 'time standard_deviation'} + +PROCESSING_TYPES = {0: 'time mean', 1: 'time sum', 2: 'time maximum', + 3: 'time minimum', 4: 'time _difference', + 5: 'time _root mean square', 6: 'time standard_deviation', + 7: 'time _convariance', 8: 'time _difference', + 9: 'time _ratio'} TIME_CODES_EDITION1 = { 0: ('minutes', 60), @@ -111,48 +93,20 @@ 254: ('seconds', 1), } -TIME_CODES_EDITION2 = { - 0: ('minutes', 60), - 1: ('hours', 60*60), - 2: ('days', 24*60*60), - # NOTE: do *not* support calendar-dependent units at all. - # So the following possible keys remain unsupported: - # 3: 'months', - # 4: 'years', - # 5: 'decades', - # 6: '30 years', - # 7: 'century', - 10: ('3 hours', 3*60*60), - 11: ('6 hours', 6*60*60), - 12: ('12 hours', 12*60*60), - 13: ('seconds', 1), -} - unknown_string = "???" -def reset_load_rules(): - """ - Resets the GRIB load process to use only the standard conversion rules. - - .. deprecated:: 1.7 - - """ - warn_deprecated('reset_load_rules was deprecated in v1.7.') - - class GribDataProxy(object): """A reference to the data payload of a single Grib message.""" - __slots__ = ('shape', 'dtype', 'fill_value', 'path', 'offset', 'regularise') + __slots__ = ('shape', 'dtype', 'fill_value', 'path', 'offset') - def __init__(self, shape, dtype, fill_value, path, offset, regularise): + def __init__(self, shape, dtype, fill_value, path, offset): self.shape = shape self.dtype = dtype self.fill_value = fill_value self.path = path self.offset = offset - self.regularise = regularise @property def ndim(self): @@ -162,10 +116,6 @@ def __getitem__(self, keys): with open(self.path, 'rb') as grib_fh: grib_fh.seek(self.offset) grib_message = gribapi.grib_new_from_file(grib_fh) - - if self.regularise and _is_quasi_regular_grib(grib_message): - _regularise(grib_message) - data = _message_values(grib_message, self.shape) gribapi.grib_release(grib_message) @@ -173,13 +123,12 @@ def __getitem__(self, keys): def __repr__(self): msg = '<{self.__class__.__name__} shape={self.shape} ' \ - 'dtype={self.dtype!r} fill_value={self.fill_value!r} ' \ - 'path={self.path!r} offset={self.offset} ' \ - 'regularise={self.regularise}>' + 'dtype={self.dtype!r} fill_value={self.fill_value!r} ' \ + 'path={self.path!r} offset={self.offset}>' return msg.format(self=self) def __getstate__(self): - return {attr:getattr(self, attr) for attr in self.__slots__} + return {attr: getattr(self, attr) for attr in self.__slots__} def __setstate__(self, state): for key, value in six.iteritems(state): @@ -190,33 +139,29 @@ class GribWrapper(object): """ Contains a pygrib object plus some extra keys of our own. - .. deprecated:: 1.10 - The class :class:`iris.fileformats.grib.message.GribMessage` provides alternative means of working with GRIB message instances. """ - def __init__(self, grib_message, grib_fh=None, auto_regularise=True): - warn_deprecated('Deprecated at version 1.10') + def __init__(self, grib_message, grib_fh=None): """Store the grib message and compute our extra keys.""" self.grib_message = grib_message + + if self.edition != 1: + emsg = 'GRIB edition {} is not supported by {!r}.' + raise TranslationError(emsg.format(self.edition, + type(self).__name__)) + deferred = grib_fh is not None # Store the file pointer and message length from the current # grib message before it's changed by calls to the grib-api. if deferred: - # Note that, the grib-api has already read this message and + # Note that, the grib-api has already read this message and # advanced the file pointer to the end of the message. offset = grib_fh.tell() message_length = gribapi.grib_get_long(grib_message, 'totalLength') - if auto_regularise and _is_quasi_regular_grib(grib_message): - warnings.warn('Regularising GRIB message.') - if deferred: - self._regularise_shape(grib_message) - else: - _regularise(grib_message) - # Initialise the key-extension dictionary. # NOTE: this attribute *must* exist, or the the __getattr__ overload # can hit an infinite loop. @@ -237,102 +182,72 @@ def __init__(self, grib_message, grib_fh=None, auto_regularise=True): # Wrap the reference to the data payload within the data proxy # in order to support deferred data loading. # The byte offset requires to be reset back to the first byte - # of this message. The file pointer offset is always at the end + # of this message. The file pointer offset is always at the end # of the current message due to the grib-api reading the message. proxy = GribDataProxy(shape, np.zeros(0).dtype, np.nan, grib_fh.name, - offset - message_length, - auto_regularise) + offset - message_length) self._data = biggus.NumpyArrayAdapter(proxy) else: self.data = _message_values(grib_message, shape) - @staticmethod - def _regularise_shape(grib_message): - """ - Calculate the regularised shape of the reduced message and push - dummy regularised values into the message to force the gribapi - to update the message grid type from reduced to regular. - - """ - # Make sure to read any missing values as NaN. - gribapi.grib_set_double(grib_message, "missingValue", np.nan) - - # Get full longitude values, these describe the longitude value of - # *every* point in the grid, they are not 1d monotonic coordinates. - lons = gribapi.grib_get_double_array(grib_message, "longitudes") - - # Compute the new longitude coordinate for the regular grid. - new_nx = max(gribapi.grib_get_long_array(grib_message, "pl")) - new_x_step = (max(lons) - min(lons)) / (new_nx - 1) - if gribapi.grib_get_long(grib_message, "iScansNegatively"): - new_x_step *= -1 - - gribapi.grib_set_long(grib_message, "Nx", int(new_nx)) - gribapi.grib_set_double(grib_message, "iDirectionIncrementInDegrees", - float(new_x_step)) - # Spoof gribapi with false regularised values. - nj = gribapi.grib_get_long(grib_message, 'Nj') - temp = np.zeros((nj * new_nx,), dtype=np.float) - gribapi.grib_set_double_array(grib_message, 'values', temp) - gribapi.grib_set_long(grib_message, "jPointsAreConsecutive", 0) - gribapi.grib_set_long(grib_message, "PLPresent", 0) - def _confirm_in_scope(self): """Ensure we have a grib flavour that we choose to support.""" - #forbid alternate row scanning - #(uncommon entry from GRIB2 flag table 3.4, also in GRIB1) + # forbid alternate row scanning + # (uncommon entry from GRIB2 flag table 3.4, also in GRIB1) if self.alternativeRowScanning == 1: - raise iris.exceptions.IrisError("alternativeRowScanning == 1 not handled.") + raise ValueError("alternativeRowScanning == 1 not handled.") def __getattr__(self, key): """Return a grib key, or one of our extra keys.""" # is it in the grib message? try: - # we just get as the type of the "values" array...special case here... + # we just get as the type of the "values" + # array...special case here... if key in ["values", "pv", "latitudes", "longitudes"]: res = gribapi.grib_get_double_array(self.grib_message, key) - elif key in ('typeOfFirstFixedSurface', 'typeOfSecondFixedSurface'): + elif key in ('typeOfFirstFixedSurface', + 'typeOfSecondFixedSurface'): res = np.int32(gribapi.grib_get_long(self.grib_message, key)) else: key_type = gribapi.grib_get_native_type(self.grib_message, key) if key_type == int: - res = np.int32(gribapi.grib_get_long(self.grib_message, key)) + res = np.int32(gribapi.grib_get_long(self.grib_message, + key)) elif key_type == float: # Because some computer keys are floats, like - # longitudeOfFirstGridPointInDegrees, a float32 is not always enough... - res = np.float64(gribapi.grib_get_double(self.grib_message, key)) + # longitudeOfFirstGridPointInDegrees, a float32 + # is not always enough... + res = np.float64(gribapi.grib_get_double(self.grib_message, + key)) elif key_type == str: res = gribapi.grib_get_string(self.grib_message, key) else: - raise ValueError("Unknown type for %s : %s" % (key, str(key_type))) + emsg = "Unknown type for {} : {}" + raise ValueError(emsg.format(key, str(key_type))) except gribapi.GribInternalError: res = None - #...or is it in our list of extras? + # ...or is it in our list of extras? if res is None: if key in self.extra_keys: res = self.extra_keys[key] else: - #must raise an exception for the hasattr() mechanism to work + # must raise an exception for the hasattr() mechanism to work raise AttributeError("Cannot find GRIB key %s" % key) return res def _timeunit_detail(self): """Return the (string, seconds) describing the message time unit.""" - if self.edition == 1: - code_to_detail = TIME_CODES_EDITION1 - else: - code_to_detail = TIME_CODES_EDITION2 unit_code = self.indicatorOfUnitOfTimeRange - if unit_code not in code_to_detail: + if unit_code not in TIME_CODES_EDITION1: message = 'Unhandled time unit for forecast ' \ 'indicatorOfUnitOfTimeRange : ' + str(unit_code) - raise iris.exceptions.NotYetImplementedError(message) - return code_to_detail[unit_code] + raise NotYetImplementedError(message) + return TIME_CODES_EDITION1[unit_code] def _timeunit_string(self): """Get the udunits string for the message time unit.""" @@ -347,79 +262,56 @@ def _compute_extra_keys(self): global unknown_string self.extra_keys = {} + forecastTime = self.startStep - # work out stuff based on these values from the message - edition = self.edition - - # time-processed forcast time is from reference time to start of period - if edition == 2: - forecastTime = self.forecastTime - - uft = np.uint32(forecastTime) - BILL = 2**30 - - # Workaround grib api's assumption that forecast time is positive. - # Handles correctly encoded -ve forecast times up to one -1 billion. - if hindcast_workaround: - if 2 * BILL < uft < 3 * BILL: - msg = "Re-interpreting negative forecastTime from " \ - + str(forecastTime) - forecastTime = -(uft - 2 * BILL) - msg += " to " + str(forecastTime) - warnings.warn(msg) - - else: - forecastTime = self.startStep - - #regular or rotated grid? + # regular or rotated grid? try: - longitudeOfSouthernPoleInDegrees = self.longitudeOfSouthernPoleInDegrees - latitudeOfSouthernPoleInDegrees = self.latitudeOfSouthernPoleInDegrees + longitudeOfSouthernPoleInDegrees = \ + self.longitudeOfSouthernPoleInDegrees + latitudeOfSouthernPoleInDegrees = \ + self.latitudeOfSouthernPoleInDegrees except AttributeError: longitudeOfSouthernPoleInDegrees = 0.0 latitudeOfSouthernPoleInDegrees = 90.0 centre = gribapi.grib_get_string(self.grib_message, "centre") - - #default values - self.extra_keys = {'_referenceDateTime':-1.0, '_phenomenonDateTime':-1.0, - '_periodStartDateTime':-1.0, '_periodEndDateTime':-1.0, - '_levelTypeName':unknown_string, - '_levelTypeUnits':unknown_string, '_firstLevelTypeName':unknown_string, - '_firstLevelTypeUnits':unknown_string, '_firstLevel':-1.0, - '_secondLevelTypeName':unknown_string, '_secondLevel':-1.0, - '_originatingCentre':unknown_string, - '_forecastTime':None, '_forecastTimeUnit':unknown_string, - '_coord_system':None, '_x_circular':False, - '_x_coord_name':unknown_string, '_y_coord_name':unknown_string, - # These are here to avoid repetition in the rules files, - # and reduce the very long line lengths. - '_x_points':None, '_y_points':None, - '_cf_data':None} + # default values + self.extra_keys = {'_referenceDateTime': -1.0, + '_phenomenonDateTime': -1.0, + '_periodStartDateTime': -1.0, + '_periodEndDateTime': -1.0, + '_levelTypeName': unknown_string, + '_levelTypeUnits': unknown_string, + '_firstLevelTypeName': unknown_string, + '_firstLevelTypeUnits': unknown_string, + '_firstLevel': -1.0, + '_secondLevelTypeName': unknown_string, + '_secondLevel': -1.0, + '_originatingCentre': unknown_string, + '_forecastTime': None, + '_forecastTimeUnit': unknown_string, + '_coord_system': None, + '_x_circular': False, + '_x_coord_name': unknown_string, + '_y_coord_name': unknown_string, + # These are here to avoid repetition in the rules + # files, and reduce the very long line lengths. + '_x_points': None, + '_y_points': None, + '_cf_data': None} # cf phenomenon translation - if edition == 1: - # Get centre code (N.B. self.centre has default type = string) - centre_number = gribapi.grib_get_long(self.grib_message, "centre") - # Look for a known grib1-to-cf translation (or None). - cf_data = gptx.grib1_phenom_to_cf_info( - table2_version=self.table2Version, - centre_number=centre_number, - param_number=self.indicatorOfParameter) - self.extra_keys['_cf_data'] = cf_data - elif edition == 2: - # Don't attempt to interpret params if 'master tables version' is - # 255, as local params may then have same codes as standard ones. - if self.tablesVersion != 255: - # Look for a known grib2-to-cf translation (or None). - cf_data = gptx.grib2_phenom_to_cf_info( - param_discipline=self.discipline, - param_category=self.parameterCategory, - param_number=self.parameterNumber) - self.extra_keys['_cf_data'] = cf_data - - #reference date + # Get centre code (N.B. self.centre has default type = string) + centre_number = gribapi.grib_get_long(self.grib_message, "centre") + # Look for a known grib1-to-cf translation (or None). + cf_data = gptx.grib1_phenom_to_cf_info( + table2_version=self.table2Version, + centre_number=centre_number, + param_number=self.indicatorOfParameter) + self.extra_keys['_cf_data'] = cf_data + + # reference date self.extra_keys['_referenceDateTime'] = \ datetime.datetime(int(self.year), int(self.month), int(self.day), int(self.hour), int(self.minute)) @@ -427,62 +319,55 @@ def _compute_extra_keys(self): # forecast time with workarounds self.extra_keys['_forecastTime'] = forecastTime - #verification date + # verification date processingDone = self._get_processing_done() - #time processed? + # time processed? if processingDone.startswith("time"): - if self.edition == 1: - validityDate = str(self.validityDate) - validityTime = "{:04}".format(int(self.validityTime)) - endYear = int(validityDate[:4]) - endMonth = int(validityDate[4:6]) - endDay = int(validityDate[6:8]) - endHour = int(validityTime[:2]) - endMinute = int(validityTime[2:4]) - elif self.edition == 2: - endYear = self.yearOfEndOfOverallTimeInterval - endMonth = self.monthOfEndOfOverallTimeInterval - endDay = self.dayOfEndOfOverallTimeInterval - endHour = self.hourOfEndOfOverallTimeInterval - endMinute = self.minuteOfEndOfOverallTimeInterval + validityDate = str(self.validityDate) + validityTime = "{:04}".format(int(self.validityTime)) + endYear = int(validityDate[:4]) + endMonth = int(validityDate[4:6]) + endDay = int(validityDate[6:8]) + endHour = int(validityTime[:2]) + endMinute = int(validityTime[2:4]) # fixed forecastTime in hours self.extra_keys['_periodStartDateTime'] = \ (self.extra_keys['_referenceDateTime'] + datetime.timedelta(hours=int(forecastTime))) self.extra_keys['_periodEndDateTime'] = \ - datetime.datetime(endYear, endMonth, endDay, endHour, endMinute) + datetime.datetime(endYear, endMonth, endDay, endHour, + endMinute) else: - self.extra_keys['_phenomenonDateTime'] = self._get_verification_date() + self.extra_keys['_phenomenonDateTime'] = \ + self._get_verification_date() - - #originating centre - #TODO #574 Expand to include sub-centre + # originating centre + # TODO #574 Expand to include sub-centre self.extra_keys['_originatingCentre'] = CENTRE_TITLES.get( - centre, "unknown centre %s" % centre) + centre, "unknown centre %s" % centre) - #forecast time unit as a cm string - #TODO #575 Do we want PP or GRIB style forecast delta? + # forecast time unit as a cm string + # TODO #575 Do we want PP or GRIB style forecast delta? self.extra_keys['_forecastTimeUnit'] = self._timeunit_string() + # shape of the earth - #shape of the earth - - #pre-defined sphere + # pre-defined sphere if self.shapeOfTheEarth == 0: geoid = coord_systems.GeogCS(semi_major_axis=6367470) - #custom sphere + # custom sphere elif self.shapeOfTheEarth == 1: geoid = coord_systems.GeogCS( self.scaledValueOfRadiusOfSphericalEarth * 10 ** -self.scaleFactorOfRadiusOfSphericalEarth) - #IAU65 oblate sphere + # IAU65 oblate sphere elif self.shapeOfTheEarth == 2: geoid = coord_systems.GeogCS(6378160, inverse_flattening=297.0) - #custom oblate spheroid (km) + # custom oblate spheroid (km) elif self.shapeOfTheEarth == 3: geoid = coord_systems.GeogCS( semi_major_axis=self.scaledValueOfEarthMajorAxis * @@ -490,20 +375,20 @@ def _compute_extra_keys(self): semi_minor_axis=self.scaledValueOfEarthMinorAxis * 10 ** -self.scaleFactorOfEarthMinorAxis * 1000.) - #IAG-GRS80 oblate spheroid + # IAG-GRS80 oblate spheroid elif self.shapeOfTheEarth == 4: geoid = coord_systems.GeogCS(6378137, None, 298.257222101) - #WGS84 + # WGS84 elif self.shapeOfTheEarth == 5: geoid = \ coord_systems.GeogCS(6378137, inverse_flattening=298.257223563) - #pre-defined sphere + # pre-defined sphere elif self.shapeOfTheEarth == 6: geoid = coord_systems.GeogCS(6371229) - #custom oblate spheroid (m) + # custom oblate spheroid (m) elif self.shapeOfTheEarth == 7: geoid = coord_systems.GeogCS( semi_major_axis=self.scaledValueOfEarthMajorAxis * @@ -519,7 +404,8 @@ def _compute_extra_keys(self): gridType = gribapi.grib_get_string(self.grib_message, "gridType") - if gridType in ["regular_ll", "regular_gg", "reduced_ll", "reduced_gg"]: + if gridType in ["regular_ll", "regular_gg", "reduced_ll", + "reduced_gg"]: self.extra_keys['_x_coord_name'] = "longitude" self.extra_keys['_y_coord_name'] = "latitude" self.extra_keys['_coord_system'] = geoid @@ -531,10 +417,10 @@ def _compute_extra_keys(self): southPoleLon = longitudeOfSouthernPoleInDegrees southPoleLat = latitudeOfSouthernPoleInDegrees self.extra_keys['_coord_system'] = \ - iris.coord_systems.RotatedGeogCS( - -southPoleLat, - math.fmod(southPoleLon + 180.0, 360.0), - self.angleOfRotation, geoid) + coord_systems.RotatedGeogCS( + -southPoleLat, + math.fmod(southPoleLon + 180.0, 360.0), + self.angleOfRotation, geoid) elif gridType == 'polar_stereographic': self.extra_keys['_x_coord_name'] = "projection_x_coordinate" self.extra_keys['_y_coord_name'] = "projection_y_coordinate" @@ -548,7 +434,7 @@ def _compute_extra_keys(self): # Note: I think the grib api defaults LaDInDegrees to 60 for grib1. self.extra_keys['_coord_system'] = \ - iris.coord_systems.Stereographic( + coord_systems.Stereographic( pole_lat, self.orientationOfTheGridInDegrees, 0, 0, self.LaDInDegrees, ellipsoid=geoid) @@ -556,10 +442,7 @@ def _compute_extra_keys(self): self.extra_keys['_x_coord_name'] = "projection_x_coordinate" self.extra_keys['_y_coord_name'] = "projection_y_coordinate" - if self.edition == 1: - flag_name = "projectionCenterFlag" - else: - flag_name = "projectionCentreFlag" + flag_name = "projectionCenterFlag" if getattr(self, flag_name) == 0: pole_lat = 90 @@ -568,7 +451,7 @@ def _compute_extra_keys(self): else: raise TranslationError("Unhandled projectionCentreFlag") - LambertConformal = iris.coord_systems.LambertConformal + LambertConformal = coord_systems.LambertConformal self.extra_keys['_coord_system'] = LambertConformal( self.LaDInDegrees, self.LoVInDegrees, 0, 0, secant_latitudes=(self.Latin1InDegrees, self.Latin2InDegrees), @@ -603,9 +486,9 @@ def _compute_extra_keys(self): # convert the starting latlon into meters cartopy_crs = self.extra_keys['_coord_system'].as_cartopy_crs() x1, y1 = cartopy_crs.transform_point( - self.longitudeOfFirstGridPointInDegrees, - self.latitudeOfFirstGridPointInDegrees, - ccrs.Geodetic()) + self.longitudeOfFirstGridPointInDegrees, + self.latitudeOfFirstGridPointInDegrees, + ccrs.Geodetic()) if not np.all(np.isfinite([x1, y1])): raise TranslationError("Could not determine the first latitude" @@ -638,67 +521,114 @@ def _get_processing_done(self): """Determine the type of processing that was done on the data.""" processingDone = 'unknown' - edition = self.edition - - #grib1 - if edition == 1: - timeRangeIndicator = self.timeRangeIndicator - processingDone = TIME_RANGE_INDICATORS.get(timeRangeIndicator, - 'time _grib1_process_unknown_%i' % timeRangeIndicator) - - #grib2 - else: - - pdt = self.productDefinitionTemplateNumber - - #pdt 4.0? (standard forecast) - if pdt == 0: - processingDone = 'none' - - #pdt 4.8 or 4.9? (time-processed) - elif pdt in (8, 9): - typeOfStatisticalProcessing = self.typeOfStatisticalProcessing - processingDone = PROCESSING_TYPES.get(typeOfStatisticalProcessing, - 'time _grib2_process_unknown_%i' % typeOfStatisticalProcessing) + timeRangeIndicator = self.timeRangeIndicator + default = 'time _grib1_process_unknown_%i' % timeRangeIndicator + processingDone = TIME_RANGE_INDICATORS.get(timeRangeIndicator, default) return processingDone def _get_verification_date(self): reference_date_time = self._referenceDateTime - # calculate start time (edition-dependent) - if self.edition == 1: - time_range_indicator = self.timeRangeIndicator - P1 = self.P1 - P2 = self.P2 - if time_range_indicator == 0: time_diff = P1 #Forecast product valid at reference time + P1 P1>0), or Uninitialized analysis product for reference time (P1=0). Or Image product for reference time (P1=0) - elif time_range_indicator == 1: time_diff = P1 #Initialized analysis product for reference time (P1=0). - elif time_range_indicator == 2: time_diff = (P1 + P2) * 0.5 #Product with a valid time ranging between reference time + P1 and reference time + P2 - elif time_range_indicator == 3: time_diff = (P1 + P2) * 0.5 #Average(reference time + P1 to reference time + P2) - elif time_range_indicator == 4: time_diff = P2 #Accumulation (reference time + P1 to reference time + P2) product considered valid at reference time + P2 - elif time_range_indicator == 5: time_diff = P2 #Difference(reference time + P2 minus reference time + P1) product considered valid at reference time + P2 - elif time_range_indicator == 10: time_diff = P1 * 256 + P2 #P1 occupies octets 19 and 20; product valid at reference time + P1 - elif time_range_indicator == 51: #Climatological Mean Value: multiple year averages of quantities which are themselves means over some period of time (P2) less than a year. The reference time (R) indicates the date and time of the start of a period of time, given by R to R + P2, over which a mean is formed; N indicates the number of such period-means that are averaged together to form the climatological value, assuming that the N period-mean fields are separated by one year. The reference time indicates the start of the N-year climatology. N is given in octets 22-23 of the PDS. If P1 = 0 then the data averaged in the basic interval P2 are assumed to be continuous, i.e., all available data are simply averaged together. If P1 = 1 (the units of time - octet 18, code table 4 - are not relevant here) then the data averaged together in the basic interval P2 are valid only at the time (hour, minute) given in the reference time, for all the days included in the P2 period. The units of P2 are given by the contents of octet 18 and Table 4. - raise TranslationError("unhandled grib1 timeRangeIndicator " - "= 51 (avg of avgs)") - elif time_range_indicator == 113: time_diff = P1 #Average of N forecasts (or initialized analyses); each product has forecast period of P1 (P1=0 for initialized analyses); products have reference times at intervals of P2, beginning at the given reference time. - elif time_range_indicator == 114: time_diff = P1 #Accumulation of N forecasts (or initialized analyses); each product has forecast period of P1 (P1=0 for initialized analyses); products have reference times at intervals of P2, beginning at the given reference time. - elif time_range_indicator == 115: time_diff = P1 #Average of N forecasts, all with the same reference time; the first has a forecast period of P1, the remaining forecasts follow at intervals of P2. - elif time_range_indicator == 116: time_diff = P1 #Accumulation of N forecasts, all with the same reference time; the first has a forecast period of P1, the remaining follow at intervals of P2. - elif time_range_indicator == 117: time_diff = P1 #Average of N forecasts, the first has a period of P1, the subsequent ones have forecast periods reduced from the previous one by an interval of P2; the reference time for the first is given in octets 13-17, the subsequent ones have reference times increased from the previous one by an interval of P2. Thus all the forecasts have the same valid time, given by the initial reference time + P1. - elif time_range_indicator == 118: time_diff = P1 #Temporal variance, or covariance, of N initialized analyses; each product has forecast period P1=0; products have reference times at intervals of P2, beginning at the given reference time. - elif time_range_indicator == 123: time_diff = P1 #Average of N uninitialized analyses, starting at the reference time, at intervals of P2. - elif time_range_indicator == 124: time_diff = P1 #Accumulation of N uninitialized analyses, starting at the reference time, at intervals of P2. - else: - raise TranslationError("unhandled grib1 timeRangeIndicator " - "= %i" % time_range_indicator) - elif self.edition == 2: - time_diff = int(self.stepRange) # gribapi gives us a string! - + # calculate start time + time_range_indicator = self.timeRangeIndicator + P1 = self.P1 + P2 = self.P2 + if time_range_indicator == 0: + # Forecast product valid at reference time + P1 P1>0), + # or Uninitialized analysis product for reference time (P1=0). + # Or Image product for reference time (P1=0) + time_diff = P1 + elif time_range_indicator == 1: + # Initialized analysis product for reference time (P1=0). + time_diff = P1 + elif time_range_indicator == 2: + # Product with a valid time ranging between reference time + P1 + # and reference time + P2 + time_diff = (P1 + P2) * 0.5 + elif time_range_indicator == 3: + # Average(reference time + P1 to reference time + P2) + time_diff = (P1 + P2) * 0.5 + elif time_range_indicator == 4: + # Accumulation (reference time + P1 to reference time + P2) + # product considered valid at reference time + P2 + time_diff = P2 + elif time_range_indicator == 5: + # Difference(reference time + P2 minus reference time + P1) + # product considered valid at reference time + P2 + time_diff = P2 + elif time_range_indicator == 10: + # P1 occupies octets 19 and 20; product valid at + # reference time + P1 + time_diff = P1 * 256 + P2 + elif time_range_indicator == 51: + # Climatological Mean Value: multiple year averages of + # quantities which are themselves means over some period of + # time (P2) less than a year. The reference time (R) indicates + # the date and time of the start of a period of time, given by + # R to R + P2, over which a mean is formed; N indicates the number + # of such period-means that are averaged together to form the + # climatological value, assuming that the N period-mean fields + # are separated by one year. The reference time indicates the + # start of the N-year climatology. N is given in octets 22-23 + # of the PDS. If P1 = 0 then the data averaged in the basic + # interval P2 are assumed to be continuous, i.e., all available + # data are simply averaged together. If P1 = 1 (the units of + # time - octet 18, code table 4 - are not relevant here) then + # the data averaged together in the basic interval P2 are valid + # only at the time (hour, minute) given in the reference time, + # for all the days included in the P2 period. The units of P2 + # are given by the contents of octet 18 and Table 4. + raise TranslationError("unhandled grib1 timeRangeIndicator " + "= 51 (avg of avgs)") + elif time_range_indicator == 113: + # Average of N forecasts (or initialized analyses); each + # product has forecast period of P1 (P1=0 for initialized + # analyses); products have reference times at intervals of P2, + # beginning at the given reference time. + time_diff = P1 + elif time_range_indicator == 114: + # Accumulation of N forecasts (or initialized analyses); each + # product has forecast period of P1 (P1=0 for initialized + # analyses); products have reference times at intervals of P2, + # beginning at the given reference time. + time_diff = P1 + elif time_range_indicator == 115: + # Average of N forecasts, all with the same reference time; + # the first has a forecast period of P1, the remaining + # forecasts follow at intervals of P2. + time_diff = P1 + elif time_range_indicator == 116: + # Accumulation of N forecasts, all with the same reference + # time; the first has a forecast period of P1, the remaining + # follow at intervals of P2. + time_diff = P1 + elif time_range_indicator == 117: + # Average of N forecasts, the first has a period of P1, the + # subsequent ones have forecast periods reduced from the + # previous one by an interval of P2; the reference time for + # the first is given in octets 13-17, the subsequent ones + # have reference times increased from the previous one by + # an interval of P2. Thus all the forecasts have the same + # valid time, given by the initial reference time + P1. + time_diff = P1 + elif time_range_indicator == 118: + # Temporal variance, or covariance, of N initialized analyses; + # each product has forecast period P1=0; products have + # reference times at intervals of P2, beginning at the given + # reference time. + time_diff = P1 + elif time_range_indicator == 123: + # Average of N uninitialized analyses, starting at the + # reference time, at intervals of P2. + time_diff = P1 + elif time_range_indicator == 124: + # Accumulation of N uninitialized analyses, starting at + # the reference time, at intervals of P2. + time_diff = P1 else: - raise TranslationError( - "unhandled grib edition = {}".format(self.edition) - ) + raise TranslationError("unhandled grib1 timeRangeIndicator " + "= %i" % time_range_indicator) # Get the timeunit interval. interval_secs = self._timeunit_seconds() @@ -718,7 +648,7 @@ def phenomenon_points(self, time_unit): """ time_reference = '%s since epoch' % time_unit return cf_units.date2num(self._phenomenonDateTime, time_reference, - cf_units.CALENDAR_GREGORIAN) + cf_units.CALENDAR_GREGORIAN) def phenomenon_bounds(self, time_unit): """ @@ -760,184 +690,44 @@ def _message_values(grib_message, shape): return data -def _is_quasi_regular_grib(grib_message): - """Detect GRIB 'thinned' a.k.a 'reduced' a.k.a 'quasi-regular' grid.""" - reduced_grids = ("reduced_ll", "reduced_gg") - return gribapi.grib_get(grib_message, 'gridType') in reduced_grids - - -def _regularise(grib_message): - """ - Transform a reduced grid to a regular grid using interpolation. +def _load_generate(filename): + messages = GribMessage.messages_from_filename(filename) + for message in messages: + editionNumber = message.sections[0]['editionNumber'] + if editionNumber == 1: + message_id = message._raw_message._message_id + grib_fh = message._file_ref.open_file + message = GribWrapper(message_id, grib_fh=grib_fh) + elif editionNumber != 2: + emsg = 'GRIB edition {} is not supported by {!r}.' + raise TranslationError(emsg.format(editionNumber, + type(message).__name__)) + yield message - Uses 1d linear interpolation at constant latitude to make the grid - regular. If the longitude dimension is circular then this is taken - into account by the interpolation. If the longitude dimension is not - circular then extrapolation is allowed to make sure all end regular - grid points get a value. In practice this extrapolation is likely to - be minimal. - """ - # Make sure to read any missing values as NaN. - gribapi.grib_set_double(grib_message, "missingValue", np.nan) - - # Get full longitude values, these describe the longitude value of - # *every* point in the grid, they are not 1d monotonic coordinates. - lons = gribapi.grib_get_double_array(grib_message, "longitudes") - - # Compute the new longitude coordinate for the regular grid. - new_nx = max(gribapi.grib_get_long_array(grib_message, "pl")) - new_x_step = (max(lons) - min(lons)) / (new_nx - 1) - if gribapi.grib_get_long(grib_message, "iScansNegatively"): - new_x_step *= -1 - - new_lons = np.arange(new_nx) * new_x_step + lons[0] - # Get full latitude and data values, these describe the latitude and - # data values of *every* point in the grid, they are not 1d monotonic - # coordinates. - lats = gribapi.grib_get_double_array(grib_message, "latitudes") - values = gribapi.grib_get_double_array(grib_message, "values") - - # Retrieve the distinct latitudes from the GRIB message. GRIBAPI docs - # don't specify if these points are guaranteed to be oriented correctly so - # the safe option is to sort them into ascending (south-to-north) order - # and then reverse the order if necessary. - new_lats = gribapi.grib_get_double_array(grib_message, "distinctLatitudes") - new_lats.sort() - if not gribapi.grib_get_long(grib_message, "jScansPositively"): - new_lats = new_lats[::-1] - ny = new_lats.shape[0] - - # Use 1d linear interpolation along latitude circles to regularise the - # reduced data. - cyclic = _longitude_is_cyclic(new_lons) - new_values = np.empty([ny, new_nx], dtype=values.dtype) - for ilat, lat in enumerate(new_lats): - idx = np.where(lats == lat) - llons = lons[idx] - vvalues = values[idx] - if cyclic: - # For cyclic data we insert dummy points at each end to ensure - # we can interpolate to all output longitudes using pure - # interpolation. - cgap = (360 - llons[-1] - llons[0]) - llons = np.concatenate( - (llons[0:1] - cgap, llons, llons[-1:] + cgap)) - vvalues = np.concatenate( - (vvalues[-1:], vvalues, vvalues[0:1])) - fixed_latitude_interpolator = scipy.interpolate.interp1d( - llons, vvalues) - else: - # Allow extrapolation for non-cyclic data sets to ensure we can - # interpolate to all output longitudes. - fixed_latitude_interpolator = Linear1dExtrapolator( - scipy.interpolate.interp1d(llons, vvalues)) - new_values[ilat] = fixed_latitude_interpolator(new_lons) - new_values = new_values.flatten() - - # Set flags for the regularised data. - if np.isnan(new_values).any(): - # Account for any missing data. - gribapi.grib_set_double(grib_message, "missingValue", np.inf) - gribapi.grib_set(grib_message, "bitmapPresent", 1) - new_values = np.where(np.isnan(new_values), np.inf, new_values) - - gribapi.grib_set_long(grib_message, "Nx", int(new_nx)) - gribapi.grib_set_double(grib_message, - "iDirectionIncrementInDegrees", float(new_x_step)) - gribapi.grib_set_double_array(grib_message, "values", new_values) - gribapi.grib_set_long(grib_message, "jPointsAreConsecutive", 0) - gribapi.grib_set_long(grib_message, "PLPresent", 0) - - -def grib_generator(filename, auto_regularise=True): - """ - Returns a generator of :class:`~iris.fileformats.grib.GribWrapper` - fields from the given filename. - - .. deprecated:: 1.10 - - The function: - :meth:`iris.fileformats.grib.message.GribMessage.messages_from_filename` - provides alternative means of obtainig GRIB messages from a file. - - Args: - - * filename (string): - Name of the file to generate fields from. - - Kwargs: - - * auto_regularise (*True* | *False*): - If *True*, any field defined on a reduced grid will be interpolated - to an equivalent regular grid. If *False*, any field defined on a - reduced grid will be loaded on the raw reduced grid with no shape - information. The default behaviour is to interpolate fields on a - reduced grid to an equivalent regular grid. - - """ - warn_deprecated('Deprecated at version 1.10') - with open(filename, 'rb') as grib_fh: - while True: - grib_message = gribapi.grib_new_from_file(grib_fh) - if grib_message is None: - break - - grib_wrapper = GribWrapper(grib_message, grib_fh, auto_regularise) - - yield grib_wrapper - - # finished with the grib message - claimed by the ecmwf c library. - gribapi.grib_release(grib_message) - - -def load_cubes(filenames, callback=None, auto_regularise=True): +def load_cubes(filenames, callback=None): """ Returns a generator of cubes from the given list of filenames. Args: - * filenames (string/list): + * filenames: One or more GRIB filenames to load from. Kwargs: - * callback (callable function): + * callback: Function which can be passed on to :func:`iris.io.run_callback`. - * auto_regularise (*True* | *False*): - If *True*, any cube defined on a reduced grid will be interpolated - to an equivalent regular grid. If *False*, any cube defined on a - reduced grid will be loaded on the raw reduced grid with no shape - information. If `iris.FUTURE.strict_grib_load` is `True` then this - keyword has no effect, raw grids are always used. If the older GRIB - loader is in use then the default behaviour is to interpolate cubes - on a reduced grid to an equivalent regular grid. - - .. deprecated:: 1.8. Please use strict_grib_load and regrid instead. - + Returns: + A generator containing Iris cubes loaded from the GRIB files. """ - if iris.FUTURE.strict_grib_load: - grib_loader = iris.fileformats.rules.Loader( - GribMessage.messages_from_filename, - {}, - iris.fileformats.grib._load_convert.convert) - else: - if auto_regularise is not None: - # The old loader supports the auto_regularise keyword, but in - # deprecation mode, so warning if it is found. - msg = ('the`auto_regularise` kwarg is deprecated and ' - 'will be removed in a future release. Resampling ' - 'quasi-regular grids on load will no longer be ' - 'available. Resampling should be done on the ' - 'loaded cube instead using Cube.regrid.') - warn_deprecated(msg) - - grib_loader = iris.fileformats.rules.Loader( - grib_generator, {'auto_regularise': auto_regularise}, - iris.fileformats.grib.load_rules.convert) - return iris.fileformats.rules.load_cubes(filenames, callback, grib_loader) + import iris.fileformats.rules as iris_rules + grib_loader = iris_rules.Loader(_load_generate, + {}, + load_convert) + return iris_rules.load_cubes(filenames, callback, grib_loader) def load_pairs_from_fields(grib_messages): @@ -945,15 +735,6 @@ def load_pairs_from_fields(grib_messages): Convert an iterable of GRIB messages into an iterable of (Cube, Grib message) tuples. - Args: - - * grib_messages: - An iterable of :class:`iris.fileformats.grib.message.GribMessage`. - - Returns: - An iterable of tuples of (:class:`iris.cube.Cube`, - :class:`iris.fileformats.grib.message.GribMessage`). - This capability can be used to filter out fields before they are passed to the load pipeline, and amend the cubes once they are created, using GRIB metadata conditions. Where the filtering @@ -986,49 +767,45 @@ def load_pairs_from_fields(grib_messages): ... message.sections[1]['productionStatusOfProcessedData'] = 4 >>> cubes = load_pairs_from_fields(cleaned_messages) + Args: + + * grib_messages: + An iterable of :class:`iris.fileformats.grib.message.GribMessage`. + + Returns: + An iterable of tuples of (:class:`iris.cube.Cube`, + :class:`iris.fileformats.grib.message.GribMessage`). + """ - grib_conv = iris.fileformats.grib._load_convert.convert - return iris.fileformats.rules.load_pairs_from_fields(grib_messages, - grib_conv) + import iris.fileformats.rules as iris_rules + return iris_rules.load_pairs_from_fields(grib_messages, load_convert) -def save_grib2(cube, target, append=False, **kwargs): +def save_grib2(cube, target, append=False): """ Save a cube or iterable of cubes to a GRIB2 file. Args: - * cube - A :class:`iris.cube.Cube`, :class:`iris.cube.CubeList` or - list of cubes. - * target - A filename or open file handle. + * cube: + The :class:`iris.cube.Cube`, :class:`iris.cube.CubeList` or list of + cubes to save to a GRIB2 file. + * target: + A filename or open file handle specifying the GRIB2 file to save + to. Kwargs: - * append - Whether to start a new file afresh or add the cube(s) to - the end of the file. - Only applicable when target is a filename, not a file - handle. Default is False. - - See also :func:`iris.io.save`. + * append: + Whether to start a new file afresh or add the cube(s) to the end of + the file. Only applicable when target is a filename, not a file + handle. Default is False. """ - messages = as_messages(cube) + messages = (message for _, message in save_pairs_from_cube(cube)) save_messages(messages, target, append=append) -def as_pairs(cube): - """ - .. deprecated:: 1.10 - Please use :func:`iris.fileformats.grib.save_pairs_from_cube` - for the same functionality. - - - """ - warn_deprecated('as_pairs is deprecated in v1.10; please use' - ' save_pairs_from_cube instead.') - return save_pairs_from_cube(cube) - - def save_pairs_from_cube(cube): """ Convert one or more cubes to (2D cube, GRIB message) pairs. @@ -1037,7 +814,9 @@ def save_pairs_from_cube(cube): save rules. Args: - * cube - A :class:`iris.cube.Cube`, :class:`iris.cube.CubeList` or + + * cube: + A :class:`iris.cube.Cube`, :class:`iris.cube.CubeList` or list of cubes. """ @@ -1053,22 +832,6 @@ def save_pairs_from_cube(cube): yield (slice2D, grib_message) -def as_messages(cube): - """ - .. deprecated:: 1.10 - Please use :func:`iris.fileformats.grib.save_pairs_from_cube` instead. - - Convert one or more cubes to GRIB messages. - Returns an iterable of grib_api GRIB messages. - - Args: - * cube - A :class:`iris.cube.Cube`, :class:`iris.cube.CubeList` or - list of cubes. - - """ - return (message for cube, message in save_pairs_from_cube(cube)) - - def save_messages(messages, target, append=False): """ Save messages to a GRIB2 file. @@ -1076,15 +839,17 @@ def save_messages(messages, target, append=False): Args: - * messages - An iterable of grib_api message IDs. - * target - A filename or open file handle. + * messages: + An iterable of grib_api message IDs. + * target: + A filename or open file handle. Kwargs: - * append - Whether to start a new file afresh or add the cube(s) to - the end of the file. - Only applicable when target is a filename, not a file - handle. Default is False. + * append: + Whether to start a new file afresh or add the cube(s) to the end of + the file. Only applicable when target is a filename, not a file + handle. Default is False. """ # grib file (this bit is common to the pp and grib savers...) diff --git a/lib/iris/fileformats/grib/load_rules.py b/lib/iris/fileformats/grib/_grib1_load_rules.py similarity index 56% rename from lib/iris/fileformats/grib/load_rules.py rename to lib/iris/fileformats/grib/_grib1_load_rules.py index ceea13374f..295f0d523e 100644 --- a/lib/iris/fileformats/grib/load_rules.py +++ b/lib/iris/fileformats/grib/_grib1_load_rules.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2016, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -26,16 +26,16 @@ from cf_units import CALENDAR_GREGORIAN, Unit import numpy as np -from iris._deprecation import warn_deprecated from iris.aux_factory import HybridPressureFactory from iris.coords import AuxCoord, CellMethod, DimCoord +from iris.exceptions import TranslationError from iris.fileformats.rules import (ConversionMetadata, Factory, Reference, ReferenceTarget) -def convert(grib): +def grib1_convert(grib): """ - Converts a GRIB message into the corresponding items of Cube metadata. + Converts a GRIB1 message into the corresponding items of Cube metadata. Args: @@ -46,6 +46,11 @@ def convert(grib): A :class:`iris.fileformats.rules.ConversionMetadata` object. """ + if grib.edition != 1: + emsg = 'GRIB edition {} is not supported by {!r}.' + raise TranslationError(emsg.format(grib.edition, + type(grib).__name__)) + factories = [] references = [] standard_name = None @@ -56,18 +61,6 @@ def convert(grib): dim_coords_and_dims = [] aux_coords_and_dims = [] - # deprecation warning for this code path for edition 2 messages - if grib.edition == 2: - msg = ('This GRIB loader is deprecated and will be removed in ' - 'a future release. Please consider using the new ' - 'GRIB loader by setting the :class:`iris.Future` ' - 'option `strict_grib_load` to True; e.g.:\n' - 'iris.FUTURE.strict_grib_load = True\n' - 'Please report issues you experience to:\n' - 'https://groups.google.com/forum/#!topic/scitools-iris-dev/' - 'lMsOusKNfaU') - warn_deprecated(msg) - if \ (grib.gridType=="reduced_gg"): aux_coords_and_dims.append((AuxCoord(grib._y_points, grib._y_coord_name, units='degrees', coord_system=grib._coord_system), 0)) @@ -114,7 +107,6 @@ def convert(grib): dim_coords_and_dims.append((DimCoord(grib._x_points, grib._x_coord_name, units="m", coord_system=grib._coord_system), 1)) if \ - (grib.edition == 1) and \ (grib.table2Version < 128) and \ (grib.indicatorOfParameter == 11) and \ (grib._cf_data is None): @@ -122,7 +114,6 @@ def convert(grib): units = "kelvin" if \ - (grib.edition == 1) and \ (grib.table2Version < 128) and \ (grib.indicatorOfParameter == 33) and \ (grib._cf_data is None): @@ -130,7 +121,6 @@ def convert(grib): units = "m s-1" if \ - (grib.edition == 1) and \ (grib.table2Version < 128) and \ (grib.indicatorOfParameter == 34) and \ (grib._cf_data is None): @@ -138,35 +128,24 @@ def convert(grib): units = "m s-1" if \ - (grib.edition == 1) and \ (grib._cf_data is not None): standard_name = grib._cf_data.standard_name long_name = grib._cf_data.standard_name or grib._cf_data.long_name units = grib._cf_data.units if \ - (grib.edition == 1) and \ (grib.table2Version >= 128) and \ (grib._cf_data is None): long_name = "UNKNOWN LOCAL PARAM " + str(grib.indicatorOfParameter) + "." + str(grib.table2Version) units = "???" if \ - (grib.edition == 1) and \ (grib.table2Version == 1) and \ (grib.indicatorOfParameter >= 128): long_name = "UNKNOWN LOCAL PARAM " + str(grib.indicatorOfParameter) + "." + str(grib.table2Version) units = "???" if \ - (grib.edition == 2) and \ - (grib._cf_data is not None): - standard_name = grib._cf_data.standard_name - long_name = grib._cf_data.long_name - units = grib._cf_data.units - - if \ - (grib.edition == 1) and \ (grib._phenomenonDateTime != -1.0): aux_coords_and_dims.append((DimCoord(points=grib.startStep, standard_name='forecast_period', units=grib._forecastTimeUnit), None)) aux_coords_and_dims.append((DimCoord(points=grib.phenomenon_points('hours'), standard_name='time', units=Unit('hours since epoch', CALENDAR_GREGORIAN)), None)) @@ -189,166 +168,79 @@ def add_bounded_time_coords(aux_coords_and_dims, grib): None)) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 2): add_bounded_time_coords(aux_coords_and_dims, grib) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 3): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("mean", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 4): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("sum", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 5): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("_difference", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 51): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("mean", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 113): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("mean", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 114): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("sum", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 115): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("mean", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 116): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("sum", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 117): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("mean", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 118): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("_covariance", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 123): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("mean", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 124): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("sum", coords="time")) if \ - (grib.edition == 1) and \ (grib.timeRangeIndicator == 125): add_bounded_time_coords(aux_coords_and_dims, grib) cell_methods.append(CellMethod("standard_deviation", coords="time")) if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 0): - aux_coords_and_dims.append((DimCoord(points=Unit(grib._forecastTimeUnit).convert(np.int32(grib._forecastTime), "hours"), standard_name='forecast_period', units="hours"), None)) - aux_coords_and_dims.append((DimCoord(points=grib.phenomenon_points('hours'), standard_name='time', units=Unit('hours since epoch', CALENDAR_GREGORIAN)), None)) - - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber in (8, 9)): - add_bounded_time_coords(aux_coords_and_dims, grib) - - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 8) and \ - (grib.typeOfStatisticalProcessing == 0): - cell_methods.append(CellMethod("mean", coords="time")) - - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 8) and \ - (grib.typeOfStatisticalProcessing == 1): - cell_methods.append(CellMethod("sum", coords="time")) - - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 8) and \ - (grib.typeOfStatisticalProcessing == 2): - cell_methods.append(CellMethod("maximum", coords="time")) - - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 8) and \ - (grib.typeOfStatisticalProcessing == 3): - cell_methods.append(CellMethod("minimum", coords="time")) - - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 8) and \ - (grib.typeOfStatisticalProcessing == 4): - cell_methods.append(CellMethod("_difference", coords="time")) - - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 8) and \ - (grib.typeOfStatisticalProcessing == 5): - cell_methods.append(CellMethod("_root_mean_square", coords="time")) - - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 8) and \ - (grib.typeOfStatisticalProcessing == 6): - cell_methods.append(CellMethod("standard_deviation", coords="time")) - - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 8) and \ - (grib.typeOfStatisticalProcessing == 7): - cell_methods.append(CellMethod("_convariance", coords="time")) - - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 8) and \ - (grib.typeOfStatisticalProcessing == 8): - cell_methods.append(CellMethod("_difference", coords="time")) - - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 8) and \ - (grib.typeOfStatisticalProcessing == 9): - cell_methods.append(CellMethod("_ratio", coords="time")) - - if \ - (grib.edition == 1) and \ (grib.levelType == 'pl'): aux_coords_and_dims.append((DimCoord(points=grib.level, long_name="pressure", units="hPa"), None)) if \ - (grib.edition == 1) and \ (grib.levelType == 'sfc'): if (grib._cf_data is not None) and \ @@ -358,7 +250,6 @@ def add_bounded_time_coords(aux_coords_and_dims, grib): aux_coords_and_dims.append((DimCoord(points=grib.level, long_name="height", units="m", attributes={'positive':'up'}), None)) if \ - (grib.edition == 1) and \ (grib.levelType == 'ml') and \ (hasattr(grib, 'pv')): aux_coords_and_dims.append((AuxCoord(grib.level, standard_name='model_level_number', attributes={'positive': 'up'}), None)) @@ -366,66 +257,9 @@ def add_bounded_time_coords(aux_coords_and_dims, grib): aux_coords_and_dims.append((AuxCoord(grib.pv[grib.numberOfCoordinatesValues//2 + grib.level], long_name='sigma'), None)) factories.append(Factory(HybridPressureFactory, [{'long_name': 'level_pressure'}, {'long_name': 'sigma'}, Reference('surface_pressure')])) - if \ - (grib.edition == 2) and \ - (grib.typeOfFirstFixedSurface != grib.typeOfSecondFixedSurface): - warnings.warn("Different vertical bound types not yet handled.") - - if \ - (grib.edition == 2) and \ - (grib.typeOfFirstFixedSurface == 103) and \ - (grib.typeOfSecondFixedSurface == 255): - aux_coords_and_dims.append((DimCoord(points=grib.scaledValueOfFirstFixedSurface/(10.0**grib.scaleFactorOfFirstFixedSurface), standard_name="height", units="m"), None)) - - if \ - (grib.edition == 2) and \ - (grib.typeOfFirstFixedSurface == 103) and \ - (grib.typeOfSecondFixedSurface != 255): - aux_coords_and_dims.append((DimCoord(points=0.5*(grib.scaledValueOfFirstFixedSurface/(10.0**grib.scaleFactorOfFirstFixedSurface) + grib.scaledValueOfSecondFixedSurface/(10.0**grib.scaleFactorOfSecondFixedSurface)), standard_name="height", units="m", bounds=[grib.scaledValueOfFirstFixedSurface/(10.0**grib.scaleFactorOfFirstFixedSurface), grib.scaledValueOfSecondFixedSurface/(10.0**grib.scaleFactorOfSecondFixedSurface)]), None)) - - if \ - (grib.edition == 2) and \ - (grib.typeOfFirstFixedSurface == 100) and \ - (grib.typeOfSecondFixedSurface == 255): - aux_coords_and_dims.append((DimCoord(points=grib.scaledValueOfFirstFixedSurface/(10.0**grib.scaleFactorOfFirstFixedSurface), long_name="pressure", units="Pa"), None)) - - if \ - (grib.edition == 2) and \ - (grib.typeOfFirstFixedSurface == 100) and \ - (grib.typeOfSecondFixedSurface != 255): - aux_coords_and_dims.append((DimCoord(points=0.5*(grib.scaledValueOfFirstFixedSurface/(10.0**grib.scaleFactorOfFirstFixedSurface) + grib.scaledValueOfSecondFixedSurface/(10.0**grib.scaleFactorOfSecondFixedSurface)), long_name="pressure", units="Pa", bounds=[grib.scaledValueOfFirstFixedSurface/(10.0**grib.scaleFactorOfFirstFixedSurface), grib.scaledValueOfSecondFixedSurface/(10.0**grib.scaleFactorOfSecondFixedSurface)]), None)) - - if \ - (grib.edition == 2) and \ - (grib.typeOfFirstFixedSurface in [105, 119]) and \ - (grib.numberOfCoordinatesValues > 0): - aux_coords_and_dims.append((AuxCoord(grib.scaledValueOfFirstFixedSurface, standard_name='model_level_number', attributes={'positive': 'up'}), None)) - aux_coords_and_dims.append((DimCoord(grib.pv[grib.scaledValueOfFirstFixedSurface], long_name='level_pressure', units='Pa'), None)) - aux_coords_and_dims.append((AuxCoord(grib.pv[grib.numberOfCoordinatesValues//2 + grib.scaledValueOfFirstFixedSurface], long_name='sigma'), None)) - factories.append(Factory(HybridPressureFactory, [{'long_name': 'level_pressure'}, {'long_name': 'sigma'}, Reference('surface_air_pressure')])) - if grib._originatingCentre != 'unknown': aux_coords_and_dims.append((AuxCoord(points=grib._originatingCentre, long_name='originating_centre', units='no_unit'), None)) - if \ - (grib.edition == 2) and \ - (grib.productDefinitionTemplateNumber == 1): - aux_coords_and_dims.append((DimCoord(points=grib.perturbationNumber, long_name='ensemble_member', units='no_unit'), None)) - - if \ - (grib.edition == 2) and \ - grib.productDefinitionTemplateNumber not in (0, 8): - attributes["GRIB_LOAD_WARNING"] = ("unsupported GRIB%d ProductDefinitionTemplate: #4.%d" % (grib.edition, grib.productDefinitionTemplateNumber)) - - if \ - (grib.edition == 2) and \ - (grib.centre == 'ecmf') and \ - (grib.discipline == 0) and \ - (grib.parameterCategory == 3) and \ - (grib.parameterNumber == 25) and \ - (grib.typeOfFirstFixedSurface == 105): - references.append(ReferenceTarget('surface_air_pressure', lambda cube: {'standard_name': 'surface_air_pressure', 'units': 'Pa', 'data': np.exp(cube.data)})) - return ConversionMetadata(factories, references, standard_name, long_name, units, attributes, cell_methods, dim_coords_and_dims, aux_coords_and_dims) diff --git a/lib/iris/fileformats/grib/_grib_cf_map.py b/lib/iris/fileformats/grib/_grib_cf_map.py index 71c523e431..8f9418e55c 100644 --- a/lib/iris/fileformats/grib/_grib_cf_map.py +++ b/lib/iris/fileformats/grib/_grib_cf_map.py @@ -1,26 +1,26 @@ -# (C) British Crown Copyright 2013 - 2016, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # -# This file is part of Iris. +# This file is part of iris. # -# Iris is free software: you can redistribute it and/or modify it under +# iris is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # -# Iris is distributed in the hope that it will be useful, +# iris is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License -# along with Iris. If not, see . +# along with iris. If not, see . # # DO NOT EDIT: AUTO-GENERATED -# Created on 12 February 2016 17:02 from +# Created on 14 October 2016 15:10 from # http://www.metarelate.net/metOcean -# at commit cf419fba84a70fba5f394f1481cfcdbba28877ff +# at commit 3cde018acc4303203ff006a26f7b96a64e6ed3fb -# https://github.com/metarelate/metOcean/commit/cf419fba84a70fba5f394f1481cfcdbba28877ff +# https://github.com/metarelate/metOcean/commit/3cde018acc4303203ff006a26f7b96a64e6ed3fb """ Provides GRIB/CF phenomenon translations. @@ -88,6 +88,7 @@ G2Param(2, 0, 1, 49): CFName('precipitation_amount', None, 'kg m-2'), G2Param(2, 0, 1, 51): CFName('atmosphere_mass_content_of_water', None, 'kg m-2'), G2Param(2, 0, 1, 53): CFName('snowfall_flux', None, 'kg m-2 s-1'), + G2Param(2, 0, 1, 60): CFName('snowfall_amount', None, 'kg m-2'), G2Param(2, 0, 1, 64): CFName('atmosphere_mass_content_of_water_vapor', None, 'kg m-2'), G2Param(2, 0, 2, 0): CFName('wind_from_direction', None, 'degrees'), G2Param(2, 0, 2, 1): CFName('wind_speed', None, 'm s-1'), @@ -107,8 +108,8 @@ G2Param(2, 0, 4, 7): CFName('surface_downwelling_shortwave_flux_in_air', None, 'W m-2'), G2Param(2, 0, 4, 9): CFName('surface_net_downward_shortwave_flux', None, 'W m-2'), G2Param(2, 0, 5, 3): CFName('surface_downwelling_longwave_flux_in_air', None, 'W m-2'), - G2Param(2, 0, 5, 5): CFName('toa_outgoing_longwave_flux', None, 'W m-2'), - G2Param(2, 0, 6, 1): CFName('cloud_area_fraction', None, '%'), + G2Param(2, 0, 5, 5): CFName('surface_net_downward_longwave_flux', None, 'W m-2'), + G2Param(2, 0, 6, 1): CFName(None, 'cloud_area_fraction_assuming_maximum_random_overlap', '1'), G2Param(2, 0, 6, 3): CFName('low_type_cloud_area_fraction', None, '%'), G2Param(2, 0, 6, 4): CFName('medium_type_cloud_area_fraction', None, '%'), G2Param(2, 0, 6, 5): CFName('high_type_cloud_area_fraction', None, '%'), @@ -119,6 +120,7 @@ G2Param(2, 0, 7, 8): CFName(None, 'storm_relative_helicity', 'J kg-1'), G2Param(2, 0, 14, 0): CFName('atmosphere_mole_content_of_ozone', None, 'Dobson'), G2Param(2, 0, 19, 1): CFName(None, 'grib_physical_atmosphere_albedo', '%'), + G2Param(2, 2, 0, 0): CFName('land_binary_mask', None, '1'), G2Param(2, 2, 0, 0): CFName('land_area_fraction', None, '1'), G2Param(2, 2, 0, 1): CFName('surface_roughness_length', None, 'm'), G2Param(2, 2, 0, 2): CFName('soil_temperature', None, 'K'), @@ -170,6 +172,7 @@ CFName(None, 'storm_relative_helicity', 'J kg-1'): G2Param(2, 0, 7, 8), CFName('air_potential_temperature', None, 'K'): G2Param(2, 0, 0, 2), CFName('air_pressure', None, 'Pa'): G2Param(2, 0, 3, 0), + CFName('air_pressure_at_sea_level', None, 'Pa'): G2Param(2, 0, 3, 0), CFName('air_pressure_at_sea_level', None, 'Pa'): G2Param(2, 0, 3, 1), CFName('air_temperature', None, 'K'): G2Param(2, 0, 0, 0), CFName('altitude', None, 'm'): G2Param(2, 0, 3, 6), @@ -179,7 +182,6 @@ CFName('atmosphere_mass_content_of_water_vapor', None, 'kg m-2'): G2Param(2, 0, 1, 64), CFName('atmosphere_mole_content_of_ozone', None, 'Dobson'): G2Param(2, 0, 14, 0), CFName('atmosphere_specific_convective_available_potential_energy', None, 'J kg-1'): G2Param(2, 0, 7, 6), - CFName('cloud_area_fraction', None, '%'): G2Param(2, 0, 6, 1), CFName('cloud_area_fraction_in_atmosphere_layer', None, '%'): G2Param(2, 0, 6, 7), CFName('dew_point_temperature', None, 'K'): G2Param(2, 0, 0, 6), CFName('geopotential', None, 'm2 s-2'): G2Param(2, 0, 3, 4), @@ -201,6 +203,7 @@ CFName('sea_surface_temperature', None, 'K'): G2Param(2, 10, 3, 0), CFName('sea_water_x_velocity', None, 'm s-1'): G2Param(2, 10, 1, 2), CFName('sea_water_y_velocity', None, 'm s-1'): G2Param(2, 10, 1, 3), + CFName('snowfall_amount', None, 'kg m-2'): G2Param(2, 0, 1, 60), CFName('snowfall_flux', None, 'kg m-2 s-1'): G2Param(2, 0, 1, 53), CFName('soil_temperature', None, 'K'): G2Param(2, 2, 0, 2), CFName('specific_humidity', None, 'kg kg-1'): G2Param(2, 0, 1, 0), @@ -209,6 +212,7 @@ CFName('surface_downwelling_longwave_flux_in_air', None, 'W m-2'): G2Param(2, 0, 5, 3), CFName('surface_downwelling_shortwave_flux_in_air', None, 'W m-2'): G2Param(2, 0, 4, 7), CFName('surface_net_downward_longwave_flux', None, 'W m-2'): G2Param(2, 0, 5, 5), + CFName('surface_net_downward_longwave_flux', None, 'W m-2'): G2Param(2, 0, 5, 5), CFName('surface_net_downward_shortwave_flux', None, 'W m-2'): G2Param(2, 0, 4, 9), CFName('surface_roughness_length', None, 'm'): G2Param(2, 2, 0, 1), CFName('surface_runoff_flux', None, 'kg m-2 s-1'): G2Param(2, 2, 0, 34), @@ -216,7 +220,6 @@ CFName('surface_upward_latent_heat_flux', None, 'W m-2'): G2Param(2, 0, 0, 10), CFName('surface_upward_sensible_heat_flux', None, 'W m-2'): G2Param(2, 0, 0, 11), CFName('thickness_of_snowfall_amount', None, 'm'): G2Param(2, 0, 1, 11), - CFName('toa_outgoing_longwave_flux', None, 'W m-2'): G2Param(2, 0, 5, 5), CFName('wind_from_direction', None, 'degrees'): G2Param(2, 0, 2, 0), CFName('wind_speed', None, 'm s-1'): G2Param(2, 0, 2, 1), CFName('wind_speed_of_gust', None, 'm s-1'): G2Param(2, 0, 2, 22), diff --git a/lib/iris/fileformats/grib/_load_convert.py b/lib/iris/fileformats/grib/_load_convert.py index 54a3cbcd38..ce553e60c8 100644 --- a/lib/iris/fileformats/grib/_load_convert.py +++ b/lib/iris/fileformats/grib/_load_convert.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -38,10 +38,13 @@ import iris.coord_systems as icoord_systems from iris.coords import AuxCoord, DimCoord, CellMethod from iris.exceptions import TranslationError -from iris.fileformats.grib import grib_phenom_translation as itranslation +from . import grib_phenom_translation as itranslation from iris.fileformats.rules import ConversionMetadata, Factory, Reference from iris.util import _is_circular +from ._grib1_load_rules import grib1_convert +from .message import GribMessage + # Restrict the names imported from this namespace. __all__ = ['convert'] @@ -274,7 +277,7 @@ def reference_time_coord(section): """ # Look-up standard name by significanceOfReferenceTime. - _lookup = {0: 'time', + _lookup = {0: 'forecast_reference_time', 1: 'forecast_reference_time', 2: 'time', 3: 'time'} @@ -1007,10 +1010,10 @@ def grid_definition_template_40_regular(section, metadata, cs): # Create lat/lon coordinates. x_coord = DimCoord(x_points, standard_name='longitude', - units='degrees_east', coord_system=cs, + units='degrees', coord_system=cs, circular=circular) y_coord = DimCoord(y_points, standard_name='latitude', - units='degrees_north', coord_system=cs) + units='degrees', coord_system=cs) # Determine the lat/lon dimensions. y_dim, x_dim = 0, 1 @@ -1042,9 +1045,9 @@ def grid_definition_template_40_reduced(section, metadata, cs): # Create lat/lon coordinates. x_coord = AuxCoord(x_points, standard_name='longitude', - units='degrees_east', coord_system=cs) + units='degrees', coord_system=cs) y_coord = AuxCoord(y_points, standard_name='latitude', - units='degrees_north', coord_system=cs) + units='degrees', coord_system=cs) # Add the lat/lon coordinates to the metadata dim coords. metadata['aux_coords_and_dims'].append((y_coord, 0)) @@ -1847,8 +1850,11 @@ def product_definition_template_8(section, metadata, frt_coord): # Add the forecast cell method to the metadata. metadata['cell_methods'].append(time_statistic_cell_method) - # Add the forecast reference time coordinate to the metadata aux coords. - metadata['aux_coords_and_dims'].append((frt_coord, None)) + # Add the forecast reference time coordinate to the metadata aux coords, + # if it is a forecast reference time, not a time coord, as defined by + # significanceOfReferenceTime. + if frt_coord.name() != 'time': + metadata['aux_coords_and_dims'].append((frt_coord, None)) # Add a bounded forecast period coordinate. fp_coord = statistical_forecast_period_coord(section, frt_coord) @@ -1923,6 +1929,35 @@ def product_definition_template_9(section, metadata, frt_coord): return probability_type +def product_definition_template_10(section, metadata, frt_coord): + """ + Translate template representing percentile forecasts at a horizontal level + or in a horizontal layer in a continuous or non-continuous time interval. + + Updates the metadata in-place with the translations. + + Args: + + * section: + Dictionary of coded key/value pairs from section 4 of the message. + + * metadata: + :class:`collections.OrderedDict` of metadata. + + * frt_coord: + The scalar forecast reference time :class:`iris.coords.DimCoord`. + + """ + product_definition_template_8(section, metadata, frt_coord) + + percentile = DimCoord(section['percentileValue'], + long_name='percentile_over_time', + units='no_unit') + + # Add the percentile data info + metadata['aux_coords_and_dims'].append((percentile, None)) + + def product_definition_template_11(section, metadata, frt_coord): """ Translate template representing individual ensemble forecast, control @@ -2035,6 +2070,7 @@ def product_definition_template_40(section, metadata, frt_coord): # Perform identical message processing. product_definition_template_0(section, metadata, frt_coord) + # Reference GRIB2 Code Table 4.230. constituent_type = section['constituentType'] # Add the constituent type as an attribute. @@ -2085,6 +2121,8 @@ def product_definition_section(section, metadata, discipline, tablesVersion, elif template == 9: probability = \ product_definition_template_9(section, metadata, rt_coord) + elif template == 10: + product_definition_template_10(section, metadata, rt_coord) elif template == 11: product_definition_template_11(section, metadata, rt_coord) elif template == 31: @@ -2220,24 +2258,38 @@ def convert(field): A :class:`iris.fileformats.rules.ConversionMetadata` object. """ - editionNumber = field.sections[0]['editionNumber'] - if editionNumber != 2: - msg = 'GRIB edition {} is not supported'.format(editionNumber) - raise TranslationError(msg) + if hasattr(field, 'sections'): + editionNumber = field.sections[0]['editionNumber'] - # Initialise the cube metadata. - metadata = OrderedDict() - metadata['factories'] = [] - metadata['references'] = [] - metadata['standard_name'] = None - metadata['long_name'] = None - metadata['units'] = None - metadata['attributes'] = {} - metadata['cell_methods'] = [] - metadata['dim_coords_and_dims'] = [] - metadata['aux_coords_and_dims'] = [] + if editionNumber != 2: + emsg = 'GRIB edition {} is not supported by {!r}.' + raise TranslationError(emsg.format(editionNumber, + type(field).__name__)) + + # Initialise the cube metadata. + metadata = OrderedDict() + metadata['factories'] = [] + metadata['references'] = [] + metadata['standard_name'] = None + metadata['long_name'] = None + metadata['units'] = None + metadata['attributes'] = {} + metadata['cell_methods'] = [] + metadata['dim_coords_and_dims'] = [] + metadata['aux_coords_and_dims'] = [] - # Convert GRIB2 message to cube metadata. - grib2_convert(field, metadata) + # Convert GRIB2 message to cube metadata. + grib2_convert(field, metadata) + + result = ConversionMetadata._make(metadata.values()) + else: + editionNumber = field.edition - return ConversionMetadata._make(metadata.values()) + if editionNumber != 1: + emsg = 'GRIB edition {} is not supported by {!r}.' + raise TranslationError(emsg.format(editionNumber, + type(field).__name__)) + + result = grib1_convert(field) + + return result diff --git a/lib/iris/fileformats/grib/_save_rules.py b/lib/iris/fileformats/grib/_save_rules.py index b9a224be2b..522d97dcd0 100644 --- a/lib/iris/fileformats/grib/_save_rules.py +++ b/lib/iris/fileformats/grib/_save_rules.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2016, Met Office +# (C) British Crown Copyright 2010 - 2017, Met Office # # This file is part of Iris. # @@ -17,10 +17,9 @@ """ Grib save implementation. -This module replaces the deprecated -:mod:`iris.fileformats.grib.grib_save_rules`. It is a private module -with no public API. It is invoked from -:meth:`iris.fileformats.grib.save_grib2`. +:mod:`iris.fileformats.grib._save_rules` is a private module with +no public API. +It is invoked from :meth:`iris.fileformats.grib.save_grib2`. """ @@ -37,9 +36,8 @@ import iris import iris.exceptions from iris.coord_systems import GeogCS, RotatedGeogCS, TransverseMercator -from iris.fileformats.grib import grib_phenom_translation as gptx -from iris.fileformats.grib._load_convert import (_STATISTIC_TYPE_NAMES, - _TIME_RANGE_UNITS) +from . import grib_phenom_translation as gptx +from ._load_convert import (_STATISTIC_TYPE_NAMES, _TIME_RANGE_UNITS) from iris.util import is_regular, regular_step @@ -573,14 +571,6 @@ def _non_missing_forecast_period(cube): "scaling required.") fp = int(fp) - # Turn negative forecast times into grib negative numbers? - from iris.fileformats.grib import hindcast_workaround - if hindcast_workaround and fp < 0: - msg = "Encoding negative forecast period from {} to ".format(fp) - fp = 2**31 + abs(fp) - msg += "{}".format(np.int32(fp)) - warnings.warn(msg) - return rt, rt_meaning, fp, grib_time_code @@ -808,33 +798,55 @@ def _cube_is_time_statistic(cube): """ Test whether we can identify this cube as a statistic over time. - At present, accept anything whose latest cell method operates over a single - coordinate that "looks like" a time factor (i.e. some specific names). - In particular, we recognise the coordinate names defined in - :py:mod:`iris.coord_categorisation`. + We need to know whether our cube represents a time statistic. This is + almost always captured in the cell methods. The exception is when a + percentage statistic has been calculated (i.e. for PDT10). This is + captured in a `percentage_over_time` scalar coord, which must be handled + here too. """ - # The *only* relevant information is in cell_methods, as coordinates or - # dimensions of aggregation may no longer exist. So it's not possible to - # be definitive, but we handle *some* useful cases. - # In other cases just say "no", which is safe even when not ideal. + result = False + stat_coord_name = 'percentile_over_time' + cube_coord_names = [coord.name() for coord in cube.coords()] + + # Check our cube for time statistic indicators. + has_percentile_statistic = stat_coord_name in cube_coord_names + has_cell_methods = cube.cell_methods + + # Determine whether we have a time statistic. + if has_percentile_statistic: + result = True + elif has_cell_methods: + # Define accepted time names, including from coord_categorisations. + recognised_time_names = ['time', 'year', 'month', 'day', 'weekday', + 'season'] + latest_coordnames = cube.cell_methods[-1].coord_names + if len(latest_coordnames) != 1: + result = False + else: + coord_name = latest_coordnames[0] + result = coord_name in recognised_time_names + else: + result = False - # Identify a single coordinate from the latest cell_method. - if not cube.cell_methods: - return False - latest_coordnames = cube.cell_methods[-1].coord_names - if len(latest_coordnames) != 1: - return False - coord_name = latest_coordnames[0] + return result - # Define accepted time names, including those from coord_categorisations. - recognised_time_names = ['time', 'year', 'month', 'day', 'weekday', - 'season'] - # Accept it if the name is recognised. - # Currently does *not* recognise related names like 'month_number' or - # 'years', as that seems potentially unsafe. - return coord_name in recognised_time_names +def set_ensemble(cube, grib): + """ + Set keys in the provided grib based message relating to ensemble + information. + + """ + if not (cube.coords('realization') and + len(cube.coord('realization').points) == 1): + raise ValueError("A cube 'realization' coordinate with one " + "point is required, but not present") + gribapi.grib_set(grib, "perturbationNumber", + int(cube.coord('realization').points[0])) + # no encoding at present in iris, set to missing + gribapi.grib_set(grib, "numberOfForecastsInEnsemble", 255) + gribapi.grib_set(grib, "typeOfEnsembleForecast", 255) def product_definition_template_common(cube, grib): @@ -870,6 +882,21 @@ def product_definition_template_0(cube, grib): product_definition_template_common(cube, grib) +def product_definition_template_1(cube, grib): + """ + Set keys within the provided grib message based on Product + Definition Template 4.1. + + Template 4.1 is used to represent an individual ensemble forecast, control + and perturbed, at a horizontal level or in a horizontal layer at a point + in time. + + """ + gribapi.grib_set(grib, "productDefinitionTemplateNumber", 1) + product_definition_template_common(cube, grib) + set_ensemble(cube, grib) + + def product_definition_template_8(cube, grib): """ Set keys within the provided grib message based on Product @@ -880,32 +907,43 @@ def product_definition_template_8(cube, grib): """ gribapi.grib_set(grib, "productDefinitionTemplateNumber", 8) - _product_definition_template_8_and_11(cube, grib) + _product_definition_template_8_10_and_11(cube, grib) + + +def product_definition_template_10(cube, grib): + """ + Set keys within the provided grib message based on Product Definition + Template 4.10. + + Template 4.10 is used to represent a percentile forecast over a time + interval. + + """ + gribapi.grib_set(grib, "productDefinitionTemplateNumber", 10) + if not (cube.coords('percentile_over_time') and + len(cube.coord('percentile_over_time').points) == 1): + raise ValueError("A cube 'percentile_over_time' coordinate with one " + "point is required, but not present.") + gribapi.grib_set(grib, "percentileValue", + int(cube.coord('percentile_over_time').points[0])) + _product_definition_template_8_10_and_11(cube, grib) def product_definition_template_11(cube, grib): """ Set keys within the provided grib message based on Product - Definition Template 4.8. + Definition Template 4.11. - Template 4.8 is used to represent an aggregation over a time - interval. + Template 4.11 is used to represent an aggregation over a time + interval for an ensemble member. """ gribapi.grib_set(grib, "productDefinitionTemplateNumber", 11) - if not (cube.coords('realization') and - len(cube.coord('realization').points) == 1): - raise ValueError("A cube 'realization' coordinate with one" - "point is required, but not present") - gribapi.grib_set(grib, "perturbationNumber", - int(cube.coord('realization').points[0])) - # no encoding at present in Iris, set to missing - gribapi.grib_set(grib, "numberOfForecastsInEnsemble", 255) - gribapi.grib_set(grib, "typeOfEnsembleForecast", 255) - _product_definition_template_8_and_11(cube, grib) + set_ensemble(cube, grib) + _product_definition_template_8_10_and_11(cube, grib) -def _product_definition_template_8_and_11(cube, grib): +def _product_definition_template_8_10_and_11(cube, grib): """ Set keys within the provided grib message based on common aspects of Product Definition Templates 4.8 and 4.11. @@ -927,22 +965,6 @@ def _product_definition_template_8_and_11(cube, grib): msg = 'Expected time coordinate with two bounds, got {} bounds' raise ValueError(msg.format(time_coord.nbounds)) - # Check that there is one and only one cell method related to the - # time coord. - time_cell_methods = [cell_method for cell_method in cube.cell_methods if - 'time' in cell_method.coord_names] - if not time_cell_methods: - raise ValueError("Expected a cell method with a coordinate name " - "of 'time'") - if len(time_cell_methods) > 1: - raise ValueError("Cannot handle multiple 'time' cell methods") - cell_method, = time_cell_methods - - if len(cell_method.coord_names) > 1: - raise ValueError("Cannot handle multiple coordinate names in " - "the time related cell method. Expected ('time',), " - "got {!r}".format(cell_method.coord_names)) - # Extract the datetime-like object corresponding to the end of # the overall processing interval. end = time_coord.units.num2date(time_coord.bounds[0, -1]) @@ -962,15 +984,34 @@ def _product_definition_template_8_and_11(cube, grib): gribapi.grib_set(grib, "numberOfTimeRange", 1) gribapi.grib_set(grib, "numberOfMissingInStatisticalProcess", 0) - # Type of statistical process (see code table 4.10) - statistic_type = _STATISTIC_TYPE_NAMES.get(cell_method.method, 255) - gribapi.grib_set(grib, "typeOfStatisticalProcessing", statistic_type) - # Period over which statistical processing is performed. set_time_range(time_coord, grib) - # Time increment i.e. interval of cell method (if any) - set_time_increment(cell_method, grib) + # Check that there is one and only one cell method related to the + # time coord. + if cube.cell_methods: + time_cell_methods = [ + cell_method for cell_method in cube.cell_methods if 'time' in + cell_method.coord_names] + if not time_cell_methods: + raise ValueError("Expected a cell method with a coordinate name " + "of 'time'") + if len(time_cell_methods) > 1: + raise ValueError("Cannot handle multiple 'time' cell methods") + cell_method, = time_cell_methods + + if len(cell_method.coord_names) > 1: + raise ValueError("Cannot handle multiple coordinate names in " + "the time related cell method. Expected " + "('time',), got {!r}".format( + cell_method.coord_names)) + + # Type of statistical process (see code table 4.10) + statistic_type = _STATISTIC_TYPE_NAMES.get(cell_method.method, 255) + gribapi.grib_set(grib, "typeOfStatisticalProcessing", statistic_type) + + # Time increment i.e. interval of cell method (if any) + set_time_increment(cell_method, grib) def product_definition_template_40(cube, grib): @@ -996,7 +1037,10 @@ def product_definition_section(cube, grib): """ if not cube.coord("time").has_bounds(): - if 'WMO_constituent_type' in cube.attributes: + if cube.coords('realization'): + # ensemble forecast (template 4.1) + pdt = product_definition_template_1(cube, grib) + elif 'WMO_constituent_type' in cube.attributes: # forecast for atmospheric chemical constiuent (template 4.40) product_definition_template_40(cube, grib) else: @@ -1006,6 +1050,9 @@ def product_definition_section(cube, grib): if cube.coords('realization'): # time processed (template 4.11) pdt = product_definition_template_11 + elif cube.coords('percentile_over_time'): + # time processed as percentile (template 4.10) + pdt = product_definition_template_10 else: # time processed (template 4.8) pdt = product_definition_template_8 diff --git a/lib/iris/fileformats/grib/grib_phenom_translation.py b/lib/iris/fileformats/grib/grib_phenom_translation.py index 1d2507163f..0fce03a8fb 100644 --- a/lib/iris/fileformats/grib/grib_phenom_translation.py +++ b/lib/iris/fileformats/grib/grib_phenom_translation.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -37,11 +37,11 @@ import cf_units -from iris.fileformats.grib import _grib_cf_map as grcf +from . import _grib_cf_map as grcf import iris.std_names -class LookupTable(dict): +class _LookupTable(dict): """ Specialised dictionary object for making lookup tables. @@ -51,7 +51,7 @@ class LookupTable(dict): """ def __init__(self, *args, **kwargs): - self._super = super(LookupTable, self) + self._super = super(_LookupTable, self) self._super.__init__(*args, **kwargs) def __getitem__(self, key): @@ -83,7 +83,7 @@ def __setitem__(self, key, value): def _make_grib1_cf_table(): """ Build the Grib1 to CF phenomenon translation table. """ - table = LookupTable() + table = _LookupTable() def _make_grib1_cf_entry(table2_version, centre_number, param_number, standard_name, long_name, units, set_height=None): @@ -170,7 +170,7 @@ def _make_grib1_cf_entry(table2_version, centre_number, param_number, def _make_grib2_to_cf_table(): """ Build the Grib2 to CF phenomenon translation table. """ - table = LookupTable() + table = _LookupTable() def _make_grib2_cf_entry(param_discipline, param_category, param_number, standard_name, long_name, units): @@ -233,7 +233,7 @@ def _make_grib2_cf_entry(param_discipline, param_category, param_number, def _make_cf_to_grib2_table(): """ Build the Grib1 to CF phenomenon translation table. """ - table = LookupTable() + table = _LookupTable() def _make_cf_grib2_entry(standard_name, long_name, param_discipline, param_category, param_number, diff --git a/lib/iris/fileformats/grib/grib_save_rules.py b/lib/iris/fileformats/grib/grib_save_rules.py deleted file mode 100644 index e6ecc45ef4..0000000000 --- a/lib/iris/fileformats/grib/grib_save_rules.py +++ /dev/null @@ -1,695 +0,0 @@ -# (C) British Crown Copyright 2010 - 2015, Met Office -# -# This file is part of Iris. -# -# Iris is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Iris is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Iris. If not, see . -""" -Grib save implementation. - -..deprecated:: 1.8 - -This module is for legacy requirements only. -It has been superceded by :mod:`iris.fileformats.grib._save_rules', which has -no public API. - -""" - -from __future__ import (absolute_import, division, print_function) -from six.moves import (filter, input, map, range, zip) # noqa - -import warnings - -import cf_units -import gribapi -import numpy as np -import numpy.ma as ma - -import iris -import iris.exceptions -from iris.fileformats.rules import is_regular, regular_step -from iris.fileformats.grib import grib_phenom_translation as gptx - - -def gribbability_check(cube): - "We always need the following things for grib saving." - - # GeogCS exists? - cs0 = cube.coord(dimensions=[0]).coord_system - cs1 = cube.coord(dimensions=[1]).coord_system - if cs0 is None or cs1 is None: - raise iris.exceptions.TranslationError("CoordSystem not present") - if cs0 != cs1: - raise iris.exceptions.TranslationError("Inconsistent CoordSystems") - - # Regular? - y_coord = cube.coord(dimensions=[0]) - x_coord = cube.coord(dimensions=[1]) - if not is_regular(x_coord) or not is_regular(y_coord): - raise iris.exceptions.TranslationError( - "Cannot save irregular grids to grib") - - # Time period exists? - if not cube.coords("time"): - raise iris.exceptions.TranslationError("time coord not found") - - -############################################################################### -# -# Identification Section 1 -# -############################################################################### - -def centre(cube, grib): - # TODO: read centre from cube - gribapi.grib_set_long(grib, "centre", 74) # UKMO - gribapi.grib_set_long(grib, "subCentre", 0) # exeter is not in the spec - - -def reference_time(cube, grib): - # Set the reference time. - # (analysis, forecast start, verify time, obs time, etc) - try: - fp_coord = cube.coord("forecast_period") - except iris.exceptions.CoordinateNotFoundError: - fp_coord = None - - if fp_coord is not None: - rt, rt_meaning, _, _ = _non_missing_forecast_period(cube) - else: - rt, rt_meaning, _, _ = _missing_forecast_period(cube) - - gribapi.grib_set_long(grib, "significanceOfReferenceTime", rt_meaning) - gribapi.grib_set_long( - grib, "dataDate", "%04d%02d%02d" % (rt.year, rt.month, rt.day)) - gribapi.grib_set_long( - grib, "dataTime", "%02d%02d" % (rt.hour, rt.minute)) - - # TODO: Set the calendar, when we find out what happened to the proposal! - # http://tinyurl.com/oefqgv6 - # I was sure it was approved for pre-operational use but it's not there. - - -def identification(cube, grib): - centre(cube, grib) - reference_time(cube, grib) - - # operational product, operational test, research product, etc - # (missing for now) - gribapi.grib_set_long(grib, "productionStatusOfProcessedData", 255) - # analysis, forecast, processed satellite, processed radar, - # (analysis and forecast products for now) - gribapi.grib_set_long(grib, "typeOfProcessedData", 2) - - -############################################################################### -# -# Grid Definition Section 3 -# -############################################################################### - -def shape_of_the_earth(cube, grib): - - # assume latlon - cs = cube.coord(dimensions=[0]).coord_system - - # Turn them all missing to start with (255 for byte, -1 for long) - gribapi.grib_set_long(grib, "scaleFactorOfRadiusOfSphericalEarth", 255) - gribapi.grib_set_long(grib, "scaledValueOfRadiusOfSphericalEarth", -1) - gribapi.grib_set_long(grib, "scaleFactorOfEarthMajorAxis", 255) - gribapi.grib_set_long(grib, "scaledValueOfEarthMajorAxis", -1) - gribapi.grib_set_long(grib, "scaleFactorOfEarthMinorAxis", 255) - gribapi.grib_set_long(grib, "scaledValueOfEarthMinorAxis", -1) - - ellipsoid = cs - if isinstance(cs, iris.coord_systems.RotatedGeogCS): - ellipsoid = cs.ellipsoid - - if ellipsoid.inverse_flattening == 0.0: - gribapi.grib_set_long(grib, "shapeOfTheEarth", 1) - gribapi.grib_set_long(grib, "scaleFactorOfRadiusOfSphericalEarth", 0) - gribapi.grib_set_long(grib, "scaledValueOfRadiusOfSphericalEarth", - ellipsoid.semi_major_axis) - else: - gribapi.grib_set_long(grib, "shapeOfTheEarth", 7) - gribapi.grib_set_long(grib, "scaleFactorOfEarthMajorAxis", 0) - gribapi.grib_set_long(grib, "scaledValueOfEarthMajorAxis", - ellipsoid.semi_major_axis) - gribapi.grib_set_long(grib, "scaleFactorOfEarthMinorAxis", 0) - gribapi.grib_set_long(grib, "scaledValueOfEarthMinorAxis", - ellipsoid.semi_minor_axis) - - -def grid_dims(x_coord, y_coord, grib): - gribapi.grib_set_long(grib, "Ni", x_coord.shape[0]) - gribapi.grib_set_long(grib, "Nj", y_coord.shape[0]) - - -def latlon_first_last(x_coord, y_coord, grib): - if x_coord.has_bounds() or y_coord.has_bounds(): - warnings.warn("Ignoring xy bounds") - -# XXX Pending #1125 -# gribapi.grib_set_double(grib, "latitudeOfFirstGridPointInDegrees", -# float(y_coord.points[0])) -# gribapi.grib_set_double(grib, "latitudeOfLastGridPointInDegrees", -# float(y_coord.points[-1])) -# gribapi.grib_set_double(grib, "longitudeOfFirstGridPointInDegrees", -# float(x_coord.points[0])) -# gribapi.grib_set_double(grib, "longitudeOfLastGridPointInDegrees", -# float(x_coord.points[-1])) -# WORKAROUND - gribapi.grib_set_long(grib, "latitudeOfFirstGridPoint", - int(y_coord.points[0]*1000000)) - gribapi.grib_set_long(grib, "latitudeOfLastGridPoint", - int(y_coord.points[-1]*1000000)) - gribapi.grib_set_long(grib, "longitudeOfFirstGridPoint", - int((x_coord.points[0] % 360)*1000000)) - gribapi.grib_set_long(grib, "longitudeOfLastGridPoint", - int((x_coord.points[-1] % 360)*1000000)) - - -def dx_dy(x_coord, y_coord, grib): - x_step = regular_step(x_coord) - y_step = regular_step(y_coord) - # TODO: THIS USED BE "Dx" and "Dy"!!! DID THE API CHANGE AGAIN??? - gribapi.grib_set_double(grib, "DxInDegrees", float(abs(x_step))) - gribapi.grib_set_double(grib, "DyInDegrees", float(abs(y_step))) - - -def scanning_mode_flags(x_coord, y_coord, grib): - gribapi.grib_set_long(grib, "iScansPositively", - int(x_coord.points[1] - x_coord.points[0] > 0)) - gribapi.grib_set_long(grib, "jScansPositively", - int(y_coord.points[1] - y_coord.points[0] > 0)) - - -def latlon_common(cube, grib): - y_coord = cube.coord(dimensions=[0]) - x_coord = cube.coord(dimensions=[1]) - shape_of_the_earth(cube, grib) - grid_dims(x_coord, y_coord, grib) - latlon_first_last(x_coord, y_coord, grib) - dx_dy(x_coord, y_coord, grib) - scanning_mode_flags(x_coord, y_coord, grib) - - -def rotated_pole(cube, grib): - cs = cube.coord(dimensions=[0]).coord_system - -# XXX Pending #1125 -# gribapi.grib_set_double(grib, "latitudeOfSouthernPoleInDegrees", -# float(cs.n_pole.latitude)) -# gribapi.grib_set_double(grib, "longitudeOfSouthernPoleInDegrees", -# float(cs.n_pole.longitude)) -# gribapi.grib_set_double(grib, "angleOfRotationInDegrees", 0) -# WORKAROUND - latitude = -int(cs.grid_north_pole_latitude*1000000) - longitude = int(((cs.grid_north_pole_longitude+180) % 360)*1000000) - gribapi.grib_set_long(grib, "latitudeOfSouthernPole", latitude) - gribapi.grib_set_long(grib, "longitudeOfSouthernPole", longitude) - gribapi.grib_set_long(grib, "angleOfRotation", 0) - - -def grid_template(cube, grib): - cs = cube.coord(dimensions=[0]).coord_system - if isinstance(cs, iris.coord_systems.GeogCS): - # template 3.0 - gribapi.grib_set_long(grib, "gridDefinitionTemplateNumber", 0) - latlon_common(cube, grib) - - # rotated - elif isinstance(cs, iris.coord_systems.RotatedGeogCS): - # template 3.1 - gribapi.grib_set_long(grib, "gridDefinitionTemplateNumber", 1) - latlon_common(cube, grib) - rotated_pole(cube, grib) - else: - raise ValueError("Currently unhandled CoordSystem: %s" % cs) - - -############################################################################### -# -# Product Definition Section 4 -# -############################################################################### - -def param_code(cube, grib): - # NOTE: for now, can match by *either* standard_name or long_name. - # This allows workarounds for data with no identified standard_name. - grib2_info = gptx.cf_phenom_to_grib2_info(cube.standard_name, - cube.long_name) - if grib2_info is not None: - gribapi.grib_set_long(grib, "discipline", - int(grib2_info.discipline)) - gribapi.grib_set_long(grib, "parameterCategory", - int(grib2_info.category)) - gribapi.grib_set_long(grib, "parameterNumber", - int(grib2_info.number)) - else: - gribapi.grib_set_long(grib, "discipline", 255) - gribapi.grib_set_long(grib, "parameterCategory", 255) - gribapi.grib_set_long(grib, "parameterNumber", 255) - warnings.warn('Unable to determine Grib2 parameter code for cube.\n' - 'discipline, parameterCategory and parameterNumber ' - 'have been set to "missing".') - - -def generating_process_type(cube, grib): - # analysis = 0 - # initialisation = 1 - # forecast = 2 - # more... - - # missing - gribapi.grib_set_long(grib, "typeOfGeneratingProcess", 255) - - -def background_process_id(cube, grib): - # locally defined - gribapi.grib_set_long(grib, "backgroundProcess", 255) - - -def generating_process_id(cube, grib): - # locally defined - gribapi.grib_set_long(grib, "generatingProcessIdentifier", 255) - - -def obs_time_after_cutoff(cube, grib): - # nothing stored in iris for this at present - gribapi.grib_set_long(grib, "hoursAfterDataCutoff", 0) - gribapi.grib_set_long(grib, "minutesAfterDataCutoff", 0) - - -def _non_missing_forecast_period(cube): - # Calculate "model start time" to use as the reference time. - fp_coord = cube.coord("forecast_period") - - # Convert fp and t to hours so we can subtract to calculate R. - cf_fp_hrs = fp_coord.units.convert(fp_coord.points[0], 'hours') - t_coord = cube.coord("time").copy() - hours_since = cf_units.Unit("hours since epoch", - calendar=t_coord.units.calendar) - t_coord.convert_units(hours_since) - - rt_num = t_coord.points[0] - cf_fp_hrs - rt = hours_since.num2date(rt_num) - rt_meaning = 1 # "start of forecast" - - # Forecast period - if fp_coord.units == cf_units.Unit("hours"): - grib_time_code = 1 - elif fp_coord.units == cf_units.Unit("minutes"): - grib_time_code = 0 - elif fp_coord.units == cf_units.Unit("seconds"): - grib_time_code = 13 - else: - raise iris.exceptions.TranslationError( - "Unexpected units for 'forecast_period' : %s" % fp_coord.units) - - if not t_coord.has_bounds(): - fp = fp_coord.points[0] - else: - if not fp_coord.has_bounds(): - raise iris.exceptions.TranslationError( - "bounds on 'time' coordinate requires bounds on" - " 'forecast_period'.") - fp = fp_coord.bounds[0][0] - - if fp - int(fp): - warnings.warn("forecast_period encoding problem: " - "scaling required.") - fp = int(fp) - - # Turn negative forecast times into grib negative numbers? - from iris.fileformats.grib import hindcast_workaround - if hindcast_workaround and fp < 0: - msg = "Encoding negative forecast period from {} to ".format(fp) - fp = 2**31 + abs(fp) - msg += "{}".format(np.int32(fp)) - warnings.warn(msg) - - return rt, rt_meaning, fp, grib_time_code - - -def _missing_forecast_period(cube): - # We have no way of knowing the CF forecast reference time. - # Set GRIB reference time to "verifying time of forecast", - # and the forecast period to 0h. - warnings.warn('No CF forecast_period. Setting reference time to mean ' - '"verifying time of forecast", "forecast time" = 0h') - - t_coord = cube.coord("time") - t = t_coord.bounds[0, 0] if t_coord.has_bounds() else t_coord.points[0] - rt = t_coord.units.num2date(t) - rt_meaning = 2 # "verification time of forecast" - - fp = 0 - fp_meaning = 1 # hours - - return rt, rt_meaning, fp, fp_meaning - - -def time_range(cube, grib): - """Grib encoding of forecast_period.""" - try: - fp_coord = cube.coord("forecast_period") - except iris.exceptions.CoordinateNotFoundError: - fp_coord = None - - if fp_coord is not None: - _, _, fp, grib_time_code = _non_missing_forecast_period(cube) - else: - _, _, fp, grib_time_code = _missing_forecast_period(cube) - - gribapi.grib_set_long(grib, "indicatorOfUnitOfTimeRange", grib_time_code) - gribapi.grib_set_long(grib, "forecastTime", fp) - - -def hybrid_surfaces(cube, grib): - is_hybrid = False -# XXX Addressed in #1118 pending #1039 for hybrid levels -# -# # hybrid height? (assume points) -# if cube.coords("model_level") and cube.coords("level_height") and \ -# cube.coords("sigma") and \ -# isinstance(cube.coord("sigma").coord_system, -# iris.coord_systems.HybridHeightCS): -# is_hybrid = True -# gribapi.grib_set_long(grib, "typeOfFirstFixedSurface", 118) -# gribapi.grib_set_long(grib, "scaledValueOfFirstFixedSurface", -# long(cube.coord("model_level").points[0])) -# gribapi.grib_set_long(grib, "PVPresent", 1) -# gribapi.grib_set_long(grib, "numberOfVerticalCoordinateValues", 2) -# level_height = cube.coord("level_height").points[0] -# sigma = cube.coord("sigma").points[0] -# gribapi.grib_set_double_array(grib, "pv", [level_height, sigma]) -# -# # hybrid pressure? -# if XXX: -# pass - return is_hybrid - - -def non_hybrid_surfaces(cube, grib): - - # Look for something we can export - v_coord = grib_v_code = output_unit = None - - # pressure - if cube.coords("air_pressure") or cube.coords("pressure"): - grib_v_code = 100 - output_unit = cf_units.Unit("Pa") - v_coord = (cube.coords("air_pressure") or cube.coords("pressure"))[0] - - # altitude - elif cube.coords("altitude"): - grib_v_code = 102 - output_unit = cf_units.Unit("m") - v_coord = cube.coord("altitude") - - # height - elif cube.coords("height"): - grib_v_code = 103 - output_unit = cf_units.Unit("m") - v_coord = cube.coord("height") - - # unknown / absent - else: - # check for *ANY* height coords at all... - v_coords = cube.coords(axis='z') - if v_coords: - # There are vertical coordinate(s), but we don't understand them... - v_coords_str = ' ,'.join(["'{}'".format(c.name()) - for c in v_coords]) - raise iris.exceptions.TranslationError( - 'The vertical-axis coordinate(s) ({}) ' - 'are not recognised or handled.'.format(v_coords_str)) - - # What did we find? - if v_coord is None: - # No vertical coordinate: record as 'surface' level (levelType=1). - # NOTE: may *not* be truly correct, but seems to be common practice. - # Still under investigation : - # See https://github.com/SciTools/iris/issues/519 - gribapi.grib_set_long(grib, "typeOfFirstFixedSurface", 1) - gribapi.grib_set_long(grib, "scaleFactorOfFirstFixedSurface", 0) - gribapi.grib_set_long(grib, "scaledValueOfFirstFixedSurface", 0) - # Set secondary surface = 'missing'. - gribapi.grib_set_long(grib, "typeOfSecondFixedSurface", -1) - gribapi.grib_set_long(grib, "scaleFactorOfSecondFixedSurface", 255) - gribapi.grib_set_long(grib, "scaledValueOfSecondFixedSurface", -1) - elif not v_coord.has_bounds(): - # No second surface - output_v = v_coord.units.convert(v_coord.points[0], output_unit) - if output_v - abs(output_v): - warnings.warn("Vertical level encoding problem: scaling required.") - output_v = int(output_v) - - gribapi.grib_set_long(grib, "typeOfFirstFixedSurface", grib_v_code) - gribapi.grib_set_long(grib, "scaleFactorOfFirstFixedSurface", 0) - gribapi.grib_set_long(grib, "scaledValueOfFirstFixedSurface", output_v) - gribapi.grib_set_long(grib, "typeOfSecondFixedSurface", -1) - gribapi.grib_set_long(grib, "scaleFactorOfSecondFixedSurface", 255) - gribapi.grib_set_long(grib, "scaledValueOfSecondFixedSurface", -1) - else: - # bounded : set lower+upper surfaces - output_v = v_coord.units.convert(v_coord.bounds[0], output_unit) - if output_v[0] - abs(output_v[0]) or output_v[1] - abs(output_v[1]): - warnings.warn("Vertical level encoding problem: scaling required.") - gribapi.grib_set_long(grib, "typeOfFirstFixedSurface", grib_v_code) - gribapi.grib_set_long(grib, "typeOfSecondFixedSurface", grib_v_code) - gribapi.grib_set_long(grib, "scaleFactorOfFirstFixedSurface", 0) - gribapi.grib_set_long(grib, "scaleFactorOfSecondFixedSurface", 0) - gribapi.grib_set_long(grib, "scaledValueOfFirstFixedSurface", - int(output_v[0])) - gribapi.grib_set_long(grib, "scaledValueOfSecondFixedSurface", - int(output_v[1])) - - -def surfaces(cube, grib): - if not hybrid_surfaces(cube, grib): - non_hybrid_surfaces(cube, grib) - - -def product_common(cube, grib): - param_code(cube, grib) - generating_process_type(cube, grib) - background_process_id(cube, grib) - generating_process_id(cube, grib) - obs_time_after_cutoff(cube, grib) - time_range(cube, grib) - surfaces(cube, grib) - - -def type_of_statistical_processing(cube, grib, coord): - """Search for processing over the given coord.""" - # if the last cell method applies only to the given coord... - cell_method = cube.cell_methods[-1] - coord_names = cell_method.coord_names - if len(coord_names) != 1: - raise ValueError('There are multiple coord names referenced by ' - 'the primary cell method: {!r}. Multiple coordinate ' - 'names are not supported.'.format(coord_names)) - if coord_names[0] != coord.name(): - raise ValueError('The coord name referenced by the primary cell method' - ', {!r}, is not the expected coord name {!r}.' - ''.format(coord_names[0], coord.name())) - stat_codes = {'mean': 0, 'sum': 1, 'maximum': 2, 'minimum': 3, - 'standard_deviation': 6} - # 255 is the code in template 4.8 for 'unknown' statistical method - stat_code = stat_codes.get(cell_method.method, 255) - gribapi.grib_set_long(grib, "typeOfStatisticalProcessing", stat_code) - - -def time_processing_period(cube, grib): - """ - For template 4.8 (time mean, time max, etc). - - The time range is taken from the 'time' coordinate bounds. - If the cell-method coordinate is not 'time' itself, the type of statistic - will not be derived and the save process will be aborted. - - """ - # We could probably split this function up a bit - - # Can safely assume bounded pt. - pt_coord = cube.coord("time") - end = cf_units.num2date(pt_coord.bounds[0, 1], pt_coord.units.name, - pt_coord.units.calendar) - - gribapi.grib_set_long(grib, "yearOfEndOfOverallTimeInterval", end.year) - gribapi.grib_set_long(grib, "monthOfEndOfOverallTimeInterval", end.month) - gribapi.grib_set_long(grib, "dayOfEndOfOverallTimeInterval", end.day) - gribapi.grib_set_long(grib, "hourOfEndOfOverallTimeInterval", end.hour) - gribapi.grib_set_long(grib, "minuteOfEndOfOverallTimeInterval", end.minute) - gribapi.grib_set_long(grib, "secondOfEndOfOverallTimeInterval", end.second) - - gribapi.grib_set_long(grib, "numberOfTimeRange", 1) - gribapi.grib_set_long(grib, "numberOfMissingInStatisticalProcess", 0) - - type_of_statistical_processing(cube, grib, pt_coord) - - # Type of time increment, e.g incrementing fp, incrementing ref - # time, etc. (code table 4.11) - gribapi.grib_set_long(grib, "typeOfTimeIncrement", 255) - # time unit for period over which statistical processing is done (hours) - gribapi.grib_set_long(grib, "indicatorOfUnitForTimeRange", 1) - # period over which statistical processing is done - gribapi.grib_set_long(grib, "lengthOfTimeRange", - float(pt_coord.bounds[0, 1] - pt_coord.bounds[0, 0])) - # time unit between successive source fields (not setting this at present) - gribapi.grib_set_long(grib, "indicatorOfUnitForTimeIncrement", 255) - # between successive source fields (just set to 0 for now) - gribapi.grib_set_long(grib, "timeIncrement", 0) - - -def _cube_is_time_statistic(cube): - """ - Test whether we can identify this cube as a statistic over time. - - At present, accept anything whose latest cell method operates over a single - coordinate that "looks like" a time factor (i.e. some specific names). - In particular, we recognise the coordinate names defined in - :py:mod:`iris.coord_categorisation`. - - """ - # The *only* relevant information is in cell_methods, as coordinates or - # dimensions of aggregation may no longer exist. So it's not possible to - # be definitive, but we handle *some* useful cases. - # In other cases just say "no", which is safe even when not ideal. - - # Identify a single coordinate from the latest cell_method. - if not cube.cell_methods: - return False - latest_coordnames = cube.cell_methods[-1].coord_names - if len(latest_coordnames) != 1: - return False - coord_name = latest_coordnames[0] - - # Define accepted time names, including those from coord_categorisations. - recognised_time_names = ['time', 'year', 'month', 'day', 'weekday', - 'season'] - - # Accept it if the name is recognised. - # Currently does *not* recognise related names like 'month_number' or - # 'years', as that seems potentially unsafe. - return coord_name in recognised_time_names - - -def product_template(cube, grib): - # This will become more complex if we cover more templates, such as 4.15 - - # forecast (template 4.0) - if not cube.coord("time").has_bounds(): - gribapi.grib_set_long(grib, "productDefinitionTemplateNumber", 0) - product_common(cube, grib) - return - - # time processed (template 4.8) - if _cube_is_time_statistic(cube): - gribapi.grib_set_long(grib, "productDefinitionTemplateNumber", 8) - product_common(cube, grib) - try: - time_processing_period(cube, grib) - except ValueError as e: - raise ValueError('Saving to GRIB2 failed: the cube is not suitable' - ' for saving as a time processed statistic GRIB' - ' message. {}'.format(e)) - return - - # Don't know how to handle this kind of data - raise iris.exceptions.TranslationError( - 'A suitable product template could not be deduced') - - -############################################################################### -# -# Data Representation Section 5 -# -############################################################################### - -def data(cube, grib): - # Masked data? - if isinstance(cube.data, ma.core.MaskedArray): - # What missing value shall we use? - if not np.isnan(cube.data.fill_value): - # Use the data's fill value. - fill_value = float(cube.data.fill_value) - else: - # We can't use the data's fill value if it's NaN, - # the GRIB API doesn't like it. - # Calculate an MDI outside the data range. - min, max = cube.data.min(), cube.data.max() - fill_value = min - (max - min) * 0.1 - # Prepare the unmaksed data array, using fill_value as the MDI. - data = cube.data.filled(fill_value) - else: - fill_value = None - data = cube.data - - # units scaling - grib2_info = gptx.cf_phenom_to_grib2_info(cube.standard_name, - cube.long_name) - if grib2_info is None: - # for now, just allow this - warnings.warn('Unable to determine Grib2 parameter code for cube.\n' - 'Message data may not be correctly scaled.') - else: - if cube.units != grib2_info.units: - data = cube.units.convert(data, grib2_info.units) - if fill_value is not None: - fill_value = cube.units.convert(fill_value, grib2_info.units) - - if fill_value is None: - # Disable missing values in the grib message. - gribapi.grib_set(grib, "bitmapPresent", 0) - else: - # Enable missing values in the grib message. - gribapi.grib_set(grib, "bitmapPresent", 1) - gribapi.grib_set_double(grib, "missingValue", fill_value) - gribapi.grib_set_double_array(grib, "values", data.flatten()) - - # todo: check packing accuracy? -# print("packingError", gribapi.getb_get_double(grib, "packingError")) - - -############################################################################### - -def run(cube, grib): - """ - Sets the keys of the grib message based on the contents of the cube. - - Args: - - * cube: - An instance of :class:`iris.cube.Cube`. - - * grib_message_id: - ID of a grib message in memory. This is typically the return value of - :func:`gribapi.grib_new_from_samples`. - - """ - gribbability_check(cube) - - # Section 1 - Identification Section. - identification(cube, grib) - - # Section 3 - Grid Definition Section (Grid Definition Template) - grid_template(cube, grib) - - # Section 4 - Product Definition Section (Product Definition Template) - product_template(cube, grib) - - # Section 5 - Data Representation Section (Data Representation Template) - data(cube, grib) diff --git a/lib/iris/fileformats/grib/message.py b/lib/iris/fileformats/grib/message.py index 5c6cb04009..b70a8dd17a 100644 --- a/lib/iris/fileformats/grib/message.py +++ b/lib/iris/fileformats/grib/message.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # diff --git a/lib/iris/tests/test_coding_standards.py b/lib/iris/tests/test_coding_standards.py index 1985d88206..1f96ff404c 100644 --- a/lib/iris/tests/test_coding_standards.py +++ b/lib/iris/tests/test_coding_standards.py @@ -85,9 +85,8 @@ class StandardReportWithExclusions(pep8.StandardReport): '*/iris/analysis/_interpolate_private.py', '*/iris/fileformats/cf.py', '*/iris/fileformats/dot.py', - '*/iris/fileformats/grib/__init__.py', '*/iris/fileformats/grib/_grib_cf_map.py', - '*/iris/fileformats/grib/load_rules.py', + '*/iris/fileformats/grib/_grib1_load_rules.py', '*/iris/fileformats/pp_rules.py', '*/iris/fileformats/rules.py', '*/iris/fileformats/um_cf_map.py', From bf6b2665acbcc2459588c7ded94dca3dee277119 Mon Sep 17 00:00:00 2001 From: marqh Date: Tue, 4 Apr 2017 09:21:14 +0000 Subject: [PATCH 02/11] bring in unit tests --- lib/iris/tests/__init__.py | 2 + .../load_cubes/load_cubes/reduced_raw.cml | 15 +- .../tests/unit/fileformats/grib/__init__.py | 210 +++++++++++++++++- .../__init__.py | 4 +- .../test_grib1_convert.py} | 82 ++----- .../grib/grib_phenom_translation/__init__.py | 21 ++ .../test_grib_phenom_translation.py | 168 ++++++++++++++ .../fileformats/grib/load_convert/__init__.py | 6 +- .../grib/load_convert/test__hindcast_fix.py | 10 +- .../grib/load_convert/test_bitmap_section.py | 7 +- .../grib/load_convert/test_convert.py | 44 +++- .../grib/load_convert/test_data_cutoff.py | 9 +- .../grib/load_convert/test_ellipsoid.py | 7 +- .../load_convert/test_ellipsoid_geometry.py | 6 +- .../load_convert/test_ensemble_identifier.py | 9 +- .../test_fixup_float32_from_int32.py | 4 +- .../test_fixup_int32_from_uint32.py | 4 +- .../test_forecast_period_coord.py | 7 +- .../load_convert/test_generating_process.py | 6 +- .../grib/load_convert/test_grib2_convert.py | 10 +- .../test_grid_definition_template_0_and_1.py | 11 +- .../test_grid_definition_template_12.py | 7 +- .../test_grid_definition_template_20.py | 7 +- .../test_grid_definition_template_30.py | 7 +- .../test_grid_definition_template_40.py | 9 +- .../test_grid_definition_template_4_and_5.py | 26 +-- .../test_grid_definition_template_5.py | 39 ++-- .../test_grid_definition_template_90.py | 7 +- .../load_convert/test_other_time_coord.py | 9 +- .../test_product_definition_template_0.py | 9 +- .../test_product_definition_template_1.py | 16 +- .../test_product_definition_template_10.py | 82 +++++++ .../test_product_definition_template_11.py | 18 +- .../test_product_definition_template_31.py | 23 +- .../test_product_definition_template_40.py | 38 ++-- .../test_product_definition_template_8.py | 11 +- .../test_product_definition_template_9.py | 10 +- .../load_convert/test_projection_centre.py | 6 +- .../load_convert/test_reference_time_coord.py | 12 +- .../load_convert/test_resolution_flags.py | 10 +- .../grib/load_convert/test_scanning_mode.py | 7 +- .../test_statistical_cell_method.py | 6 +- .../test_statistical_forecast_period_coord.py | 10 +- .../grib/load_convert/test_time_range_unit.py | 8 +- .../load_convert/test_translate_phenomenon.py | 17 +- .../grib/load_convert/test_unscale.py | 6 +- .../load_convert/test_validity_time_coord.py | 9 +- .../grib/load_convert/test_vertical_coords.py | 16 +- .../unit/fileformats/grib/message/__init__.py | 2 +- .../grib/message/test_GribMessage.py | 34 ++- .../fileformats/grib/message/test_Section.py | 8 +- .../grib/message/test__DataProxy.py | 7 +- .../grib/message/test__MessageLocation.py | 14 +- .../grib/message/test__RawGribMessage.py | 4 +- .../fileformats/grib/save_rules/__init__.py | 9 +- .../test__missing_forecast_period.py | 16 +- .../test__non_missing_forecast_period.py | 5 +- ...roduct_definition_template_8_10_and_11.py} | 44 ++-- .../grib/save_rules/test_data_section.py | 11 +- .../save_rules/test_fixup_float32_as_int32.py | 4 +- .../save_rules/test_fixup_int32_as_uint32.py | 4 +- .../test_grid_definition_section.py | 6 +- .../test_grid_definition_template_0.py | 6 +- .../test_grid_definition_template_1.py | 6 +- .../test_grid_definition_template_12.py | 6 +- .../test_grid_definition_template_5.py | 6 +- .../grib/save_rules/test_identification.py | 11 +- .../test_product_definition_template_1.py | 77 +++++++ .../test_product_definition_template_10.py | 75 +++++++ .../test_product_definition_template_11.py | 7 +- .../test_product_definition_template_40.py | 13 +- .../test_product_definition_template_8.py | 7 +- .../grib/save_rules/test_reference_time.py | 48 ++-- .../save_rules/test_set_fixed_surfaces.py | 8 +- .../save_rules/test_set_time_increment.py | 10 +- .../grib/save_rules/test_set_time_range.py | 10 +- .../unit/fileformats/grib/test_GribWrapper.py | 53 +++-- .../fileformats/grib/test__load_generate.py | 80 +++++++ .../unit/fileformats/grib/test_as_messages.py | 50 ----- .../unit/fileformats/grib/test_as_pairs.py | 51 ----- .../unit/fileformats/grib/test_load_cubes.py | 66 ++---- .../unit/fileformats/grib/test_save_grib2.py | 62 ++++++ .../fileformats/grib/test_save_messages.py | 16 +- 83 files changed, 1281 insertions(+), 622 deletions(-) rename lib/iris/tests/unit/fileformats/grib/{load_rules => grib1_load_rules}/__init__.py (85%) rename lib/iris/tests/unit/fileformats/grib/{load_rules/test_convert.py => grib1_load_rules/test_grib1_convert.py} (64%) create mode 100644 lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/__init__.py create mode 100644 lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/test_grib_phenom_translation.py create mode 100644 lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py rename lib/iris/tests/unit/fileformats/grib/save_rules/{test__product_definition_template_8_and_11.py => test__product_definition_template_8_10_and_11.py} (84%) create mode 100644 lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py create mode 100644 lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py create mode 100644 lib/iris/tests/unit/fileformats/grib/test__load_generate.py delete mode 100644 lib/iris/tests/unit/fileformats/grib/test_as_messages.py delete mode 100644 lib/iris/tests/unit/fileformats/grib/test_as_pairs.py create mode 100644 lib/iris/tests/unit/fileformats/grib/test_save_grib2.py diff --git a/lib/iris/tests/__init__.py b/lib/iris/tests/__init__.py index e7cd49c4b6..cb06a1a17b 100644 --- a/lib/iris/tests/__init__.py +++ b/lib/iris/tests/__init__.py @@ -932,6 +932,8 @@ class IrisTest(six.with_metaclass(_TestTimingsMetaclass, IrisTest_nometa)): # @iristest_timing_decorator explicitly to your new testclass. pass +class IrisGribTest(IrisTest): + pass get_result_path = IrisTest.get_result_path diff --git a/lib/iris/tests/results/unit/fileformats/grib/load_cubes/load_cubes/reduced_raw.cml b/lib/iris/tests/results/unit/fileformats/grib/load_cubes/load_cubes/reduced_raw.cml index 896fcaa9bb..1c3ea9062c 100644 --- a/lib/iris/tests/results/unit/fileformats/grib/load_cubes/load_cubes/reduced_raw.cml +++ b/lib/iris/tests/results/unit/fileformats/grib/load_cubes/load_cubes/reduced_raw.cml @@ -1,9 +1,15 @@ + + + - + + + + + - - - - + diff --git a/lib/iris/tests/unit/fileformats/grib/__init__.py b/lib/iris/tests/unit/fileformats/grib/__init__.py index 970757abcb..5e04304b8b 100644 --- a/lib/iris/tests/unit/fileformats/grib/__init__.py +++ b/lib/iris/tests/unit/fileformats/grib/__init__.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2016, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -19,11 +19,217 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa +# import iris.tests first so that some things can be initialised +# before importing anything else. +import iris.tests as tests + +import gribapi +import mock +import numpy as np + +import iris + +import iris.fileformats.grib from iris.fileformats.grib.message import GribMessage -from iris.tests import mock def _make_test_message(sections): raw_message = mock.Mock(sections=sections) recreate_raw = mock.Mock(return_value=raw_message) return GribMessage(raw_message, recreate_raw) + + +def _mock_gribapi_fetch(message, key): + """ + Fake the gribapi key-fetch. + + Fetch key-value from the fake message (dictionary). + If the key is not present, raise the diagnostic exception. + + """ + if key in message: + return message[key] + else: + raise _mock_gribapi.GribInternalError + + +def _mock_gribapi__grib_is_missing(grib_message, keyname): + """ + Fake the gribapi key-existence enquiry. + + Return whether the key exists in the fake message (dictionary). + + """ + return (keyname not in grib_message) + + +def _mock_gribapi__grib_get_native_type(grib_message, keyname): + """ + Fake the gribapi type-discovery operation. + + Return type of key-value in the fake message (dictionary). + If the key is not present, raise the diagnostic exception. + + """ + if keyname in grib_message: + return type(grib_message[keyname]) + raise _mock_gribapi.GribInternalError(keyname) + + +# Construct a mock object to mimic the gribapi for GribWrapper testing. +_mock_gribapi = mock.Mock(spec=gribapi) +_mock_gribapi.GribInternalError = Exception + +_mock_gribapi.grib_get_long = mock.Mock(side_effect=_mock_gribapi_fetch) +_mock_gribapi.grib_get_string = mock.Mock(side_effect=_mock_gribapi_fetch) +_mock_gribapi.grib_get_double = mock.Mock(side_effect=_mock_gribapi_fetch) +_mock_gribapi.grib_get_double_array = mock.Mock( + side_effect=_mock_gribapi_fetch) +_mock_gribapi.grib_is_missing = mock.Mock( + side_effect=_mock_gribapi__grib_is_missing) +_mock_gribapi.grib_get_native_type = mock.Mock( + side_effect=_mock_gribapi__grib_get_native_type) + + +class FakeGribMessage(dict): + """ + A 'fake grib message' object, for testing GribWrapper construction. + + Behaves as a dictionary, containing key-values for message keys. + + """ + def __init__(self, **kwargs): + """ + Create a fake message object. + + General keys can be set/add as required via **kwargs. + The 'time_code' key is specially managed. + + """ + # Start with a bare dictionary + dict.__init__(self) + # Extract specially-recognised keys. + time_code = kwargs.pop('time_code', None) + # Set the minimally required keys. + self._init_minimal_message() + # Also set a time-code, if given. + if time_code is not None: + self.set_timeunit_code(time_code) + # Finally, add any remaining passed key-values. + self.update(**kwargs) + + def _init_minimal_message(self): + # Set values for all the required keys. + self.update({ + 'edition': 1, + 'Ni': 1, + 'Nj': 1, + 'numberOfValues': 1, + 'alternativeRowScanning': 0, + 'centre': 'ecmf', + 'year': 2007, + 'month': 3, + 'day': 23, + 'hour': 12, + 'minute': 0, + 'indicatorOfUnitOfTimeRange': 1, + 'shapeOfTheEarth': 6, + 'gridType': 'rotated_ll', + 'angleOfRotation': 0.0, + 'iDirectionIncrementInDegrees': 0.036, + 'jDirectionIncrementInDegrees': 0.036, + 'iScansNegatively': 0, + 'jScansPositively': 1, + 'longitudeOfFirstGridPointInDegrees': -5.70, + 'latitudeOfFirstGridPointInDegrees': -4.452, + 'jPointsAreConsecutive': 0, + 'values': np.array([[1.0]]), + 'indicatorOfParameter': 9999, + 'parameterNumber': 9999, + 'startStep': 24, + 'timeRangeIndicator': 1, + 'P1': 2, 'P2': 0, + # time unit - needed AS WELL as 'indicatorOfUnitOfTimeRange' + 'unitOfTime': 1, + 'table2Version': 9999, + }) + + def set_timeunit_code(self, timecode): + self['indicatorOfUnitOfTimeRange'] = timecode + # for some odd reason, GRIB1 code uses *both* of these + # NOTE kludge -- the 2 keys are really the same thing + self['unitOfTime'] = timecode + + +class TestField(tests.IrisGribTest): + def _test_for_coord(self, field, convert, coord_predicate, expected_points, + expected_bounds): + (factories, references, standard_name, long_name, units, + attributes, cell_methods, dim_coords_and_dims, + aux_coords_and_dims) = convert(field) + + # Check for one and only one matching coordinate. + coords_and_dims = dim_coords_and_dims + aux_coords_and_dims + matching_coords = [coord for coord, _ in coords_and_dims if + coord_predicate(coord)] + self.assertEqual(len(matching_coords), 1, str(matching_coords)) + coord = matching_coords[0] + + # Check points and bounds. + if expected_points is not None: + self.assertArrayEqual(coord.points, expected_points) + + if expected_bounds is None: + self.assertIsNone(coord.bounds) + else: + self.assertArrayEqual(coord.bounds, expected_bounds) + + def assertCoordsAndDimsListsMatch(self, coords_and_dims_got, + coords_and_dims_expected): + """ + Check that coords_and_dims lists are equivalent. + + The arguments are lists of pairs of (coordinate, dimensions). + The elements are compared one-to-one, by coordinate name (so the order + of the lists is _not_ significant). + It also checks that the coordinate types (DimCoord/AuxCoord) match. + + """ + def sorted_by_coordname(list): + return sorted(list, key=lambda item: item[0].name()) + + coords_and_dims_got = sorted_by_coordname(coords_and_dims_got) + coords_and_dims_expected = sorted_by_coordname( + coords_and_dims_expected) + self.assertEqual(coords_and_dims_got, coords_and_dims_expected) + # Also check coordinate type equivalences (as Coord.__eq__ does not). + self.assertEqual( + [type(coord) for coord, dims in coords_and_dims_got], + [type(coord) for coord, dims in coords_and_dims_expected]) + + +class TestGribSimple(tests.IrisGribTest): + # A testing class that does not need the test data. + def mock_grib(self): + # A mock grib message, with attributes that can't be Mocks themselves. + grib = mock.Mock() + grib.startStep = 0 + grib.phenomenon_points = lambda unit: 3 + grib._forecastTimeUnit = "hours" + grib.productDefinitionTemplateNumber = 0 + # define a level type (NB these 2 are effectively the same) + grib.levelType = 1 + grib.typeOfFirstFixedSurface = 1 + grib.typeOfSecondFixedSurface = 1 + return grib + + def cube_from_message(self, grib): + # Parameter translation now uses the GribWrapper, so we must convert + # the Mock-based fake message to a FakeGribMessage. + with mock.patch('iris.fileformats.grib.gribapi', _mock_gribapi): + grib_message = FakeGribMessage(**grib.__dict__) + wrapped_msg = iris.fileformats.grib.GribWrapper(grib_message) + cube, _, _ = iris.fileformats.rules._make_cube( + wrapped_msg, + iris.fileformats.grib._grib1_load_rules.grib1_convert) + return cube diff --git a/lib/iris/tests/unit/fileformats/grib/load_rules/__init__.py b/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/__init__.py similarity index 85% rename from lib/iris/tests/unit/fileformats/grib/load_rules/__init__.py rename to lib/iris/tests/unit/fileformats/grib/grib1_load_rules/__init__.py index 39a7f0e0a5..946065ac18 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_rules/__init__.py +++ b/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/__init__.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -14,7 +14,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . -"""Unit tests for the :mod:`iris.fileformats.grib.load_rules` module.""" +"""Unit tests for the :mod:`iris.fileformats.grib._grib1_load_rules` module.""" from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa diff --git a/lib/iris/tests/unit/fileformats/grib/load_rules/test_convert.py b/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/test_grib1_convert.py similarity index 64% rename from lib/iris/tests/unit/fileformats/grib/load_rules/test_convert.py rename to lib/iris/tests/unit/fileformats/grib/grib1_load_rules/test_grib1_convert.py index cd53ebf88f..a22f84cafa 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_rules/test_convert.py +++ b/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/test_grib1_convert.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2016, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -14,70 +14,36 @@ # # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . -"""Unit tests for :func:`iris.fileformats.grib.load_rules.convert`.""" +""" +Unit tests for :func:`iris.fileformats.grib._grib1_load_rules.grib1_convert`. +""" from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# Import iris tests first so that some things can be initialised before +# Import iris.tests first so that some things can be initialised before # importing anything else import iris.tests as tests import cf_units import gribapi +import mock import iris +from iris.exceptions import TranslationError from iris.fileformats.rules import Reference -from iris.tests import mock -from iris.tests.test_grib_load_translations import TestGribSimple -from iris.tests.unit.fileformats import TestField from iris.fileformats.grib import GribWrapper -from iris.fileformats.grib.load_rules import convert - - -class Test_GribLevels_Mock(TestGribSimple): - # Unit test levels with mocking. - def test_grib2_height(self): - grib = self.mock_grib() - grib.edition = 2 - grib.typeOfFirstFixedSurface = 103 - grib.scaledValueOfFirstFixedSurface = 12345 - grib.scaleFactorOfFirstFixedSurface = 0 - grib.typeOfSecondFixedSurface = 255 - cube = self.cube_from_message(grib) - self.assertEqual( - cube.coord('height'), - iris.coords.DimCoord(12345, standard_name="height", units="m")) - - def test_grib2_bounded_height(self): - grib = self.mock_grib() - grib.edition = 2 - grib.typeOfFirstFixedSurface = 103 - grib.scaledValueOfFirstFixedSurface = 12345 - grib.scaleFactorOfFirstFixedSurface = 0 - grib.typeOfSecondFixedSurface = 103 - grib.scaledValueOfSecondFixedSurface = 54321 - grib.scaleFactorOfSecondFixedSurface = 0 - cube = self.cube_from_message(grib) - self.assertEqual( - cube.coord('height'), - iris.coords.DimCoord(33333, standard_name="height", units="m", - bounds=[[12345, 54321]])) - - def test_grib2_diff_bound_types(self): - grib = self.mock_grib() - grib.edition = 2 - grib.typeOfFirstFixedSurface = 103 - grib.scaledValueOfFirstFixedSurface = 12345 - grib.scaleFactorOfFirstFixedSurface = 0 - grib.typeOfSecondFixedSurface = 102 - grib.scaledValueOfSecondFixedSurface = 54321 - grib.scaleFactorOfSecondFixedSurface = 0 - with mock.patch('warnings.warn') as warn: - cube = self.cube_from_message(grib) - warn.assert_called_with( - "Different vertical bound types not yet handled.") +from iris.fileformats.grib._grib1_load_rules import grib1_convert +from iris.tests.unit.fileformats.grib import TestField + + +class TestBadEdition(tests.IrisGribTest): + def test(self): + message = mock.Mock(edition=2) + emsg = 'GRIB edition 2 is not supported' + with self.assertRaisesRegexp(TranslationError, emsg): + grib1_convert(message) class TestBoundedTime(TestField): @@ -100,10 +66,10 @@ def assert_bounded_message(self, **kwargs): 'table2Version': 9999} attributes.update(kwargs) message = mock.Mock(**attributes) - self._test_for_coord(message, convert, self.is_forecast_period, + self._test_for_coord(message, grib1_convert, self.is_forecast_period, expected_points=[35], expected_bounds=[[15, 55]]) - self._test_for_coord(message, convert, self.is_time, + self._test_for_coord(message, grib1_convert, self.is_time, expected_points=[100], expected_bounds=[[80, 120]]) @@ -149,20 +115,12 @@ def test_time_range_indicator_124(self): def test_time_range_indicator_125(self): self.assert_bounded_message(timeRangeIndicator=125) - def test_product_template_8(self): - self.assert_bounded_message(edition=2, - productDefinitionTemplateNumber=8) - - def test_product_template_9(self): - self.assert_bounded_message(edition=2, - productDefinitionTemplateNumber=9) - class Test_GribLevels(tests.IrisTest): def test_grib1_hybrid_height(self): gm = gribapi.grib_new_from_samples('regular_gg_ml_grib1') gw = GribWrapper(gm) - results = convert(gw) + results = grib1_convert(gw) factory, = results[0] self.assertEqual(factory.factory_class, diff --git a/lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/__init__.py b/lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/__init__.py new file mode 100644 index 0000000000..0bc5a8a92b --- /dev/null +++ b/lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/__init__.py @@ -0,0 +1,21 @@ +# (C) British Crown Copyright 2014 - 2017, Met Office +# +# This file is part of Iris. +# +# Iris is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Iris. If not, see . +"""Unit tests for the +:mod:`iris.fileformats.grib.grib_phenom_translation` package.""" + +from __future__ import (absolute_import, division, print_function) +from six.moves import (filter, input, map, range, zip) # noqa diff --git a/lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/test_grib_phenom_translation.py b/lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/test_grib_phenom_translation.py new file mode 100644 index 0000000000..8ebdb21ca7 --- /dev/null +++ b/lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/test_grib_phenom_translation.py @@ -0,0 +1,168 @@ +# (C) British Crown Copyright 2014 - 2017, Met Office +# +# This file is part of Iris. +# +# Iris is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Iris. If not, see . +''' +Unit tests for the mod:`iris.fileformats.grib.grib_phenom_translation` module. + +Carried over from old iris/tests/test_grib_phenom_translation.py. +Code is out of step with current test conventions and standards. + +''' +from __future__ import (absolute_import, division, print_function) +from six.moves import (filter, input, map, range, zip) # noqa + +# Import iris.tests first so that some things can be initialised before +# importing anything else. +import iris.tests as tests + +import cf_units + +import iris.fileformats.grib.grib_phenom_translation as gptx + + +class TestGribLookupTableType(tests.IrisTest): + def test_lookuptable_type(self): + ll = gptx._LookupTable([('a', 1), ('b', 2)]) + assert ll['a'] == 1 + assert ll['q'] is None + ll['q'] = 15 + assert ll['q'] == 15 + ll['q'] = 15 + assert ll['q'] == 15 + with self.assertRaises(KeyError): + ll['q'] = 7 + del ll['q'] + ll['q'] = 7 + assert ll['q'] == 7 + + +class TestGribPhenomenonLookup(tests.IrisTest): + def test_grib1_cf_lookup(self): + def check_grib1_cf(param, + standard_name, long_name, units, + height=None, + t2version=128, centre=98, expect_none=False): + a_cf_unit = cf_units.Unit(units) + cfdata = gptx.grib1_phenom_to_cf_info(param_number=param, + table2_version=t2version, + centre_number=centre) + if expect_none: + self.assertIsNone(cfdata) + else: + self.assertEqual(cfdata.standard_name, standard_name) + self.assertEqual(cfdata.long_name, long_name) + self.assertEqual(cfdata.units, a_cf_unit) + if height is None: + self.assertIsNone(cfdata.set_height) + else: + self.assertEqual(cfdata.set_height, float(height)) + + check_grib1_cf(165, 'x_wind', None, 'm s-1', 10.0) + check_grib1_cf(168, 'dew_point_temperature', None, 'K', 2) + check_grib1_cf(130, 'air_temperature', None, 'K') + check_grib1_cf(235, None, "grib_skin_temperature", "K") + check_grib1_cf(235, None, "grib_skin_temperature", "K", + t2version=9999, expect_none=True) + check_grib1_cf(235, None, "grib_skin_temperature", "K", + centre=9999, expect_none=True) + check_grib1_cf(9999, None, "grib_skin_temperature", "K", + expect_none=True) + + def test_grib2_cf_lookup(self): + def check_grib2_cf(discipline, category, number, + standard_name, long_name, units, + expect_none=False): + a_cf_unit = cf_units.Unit(units) + cfdata = gptx.grib2_phenom_to_cf_info(param_discipline=discipline, + param_category=category, + param_number=number) + if expect_none: + self.assertIsNone(cfdata) + else: + self.assertEqual(cfdata.standard_name, standard_name) + self.assertEqual(cfdata.long_name, long_name) + self.assertEqual(cfdata.units, a_cf_unit) + + # These should work + check_grib2_cf(0, 0, 2, "air_potential_temperature", None, "K") + check_grib2_cf(0, 19, 1, None, "grib_physical_atmosphere_albedo", "%") + check_grib2_cf(2, 0, 2, "soil_temperature", None, "K") + check_grib2_cf(10, 2, 0, "sea_ice_area_fraction", None, 1) + check_grib2_cf(2, 0, 0, "land_area_fraction", None, 1) + check_grib2_cf(0, 19, 1, None, "grib_physical_atmosphere_albedo", "%") + check_grib2_cf(0, 1, 64, + "atmosphere_mass_content_of_water_vapor", None, + "kg m-2") + check_grib2_cf(2, 0, 7, "surface_altitude", None, "m") + + # These should fail + check_grib2_cf(9999, 2, 0, "sea_ice_area_fraction", None, 1, + expect_none=True) + check_grib2_cf(10, 9999, 0, "sea_ice_area_fraction", None, 1, + expect_none=True) + check_grib2_cf(10, 2, 9999, "sea_ice_area_fraction", None, 1, + expect_none=True) + + def test_cf_grib2_lookup(self): + def check_cf_grib2(standard_name, long_name, + discipline, category, number, units, + expect_none=False): + a_cf_unit = cf_units.Unit(units) + gribdata = gptx.cf_phenom_to_grib2_info(standard_name, long_name) + if expect_none: + self.assertIsNone(gribdata) + else: + self.assertEqual(gribdata.discipline, discipline) + self.assertEqual(gribdata.category, category) + self.assertEqual(gribdata.number, number) + self.assertEqual(gribdata.units, a_cf_unit) + + # These should work + check_cf_grib2("sea_surface_temperature", None, + 10, 3, 0, 'K') + check_cf_grib2("air_temperature", None, + 0, 0, 0, 'K') + check_cf_grib2("soil_temperature", None, + 2, 0, 2, "K") + check_cf_grib2("land_area_fraction", None, + 2, 0, 0, '1') + check_cf_grib2("land_binary_mask", None, + 2, 0, 0, '1') + check_cf_grib2("atmosphere_mass_content_of_water_vapor", None, + 0, 1, 64, "kg m-2") + check_cf_grib2("surface_altitude", None, + 2, 0, 7, "m") + + # These should fail + check_cf_grib2("air_temperature", "user_long_UNRECOGNISED", + 0, 0, 0, 'K') + check_cf_grib2("air_temperature_UNRECOGNISED", None, + 0, 0, 0, 'K', + expect_none=True) + check_cf_grib2(None, "user_long_UNRECOGNISED", + 0, 0, 0, 'K', + expect_none=True) + check_cf_grib2(None, "precipitable_water", + 0, 1, 3, 'kg m-2') + check_cf_grib2("invalid_unknown", "precipitable_water", + 0, 1, 3, 'kg m-2', + expect_none=True) + check_cf_grib2(None, None, 0, 0, 0, '', + expect_none=True) + + +if __name__ == '__main__': + tests.main() diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/__init__.py b/lib/iris/tests/unit/fileformats/grib/load_convert/__init__.py index b6afad2e24..b7bcee25c3 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/__init__.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/__init__.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -19,7 +19,7 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -40,7 +40,7 @@ def empty_metadata(): return metadata -class LoadConvertTest(tests.IrisTest): +class LoadConvertTest(tests.IrisGribTest): def assertMetadataEqual(self, result, expected): # Compare two metadata dictionaries. Gives slightly more # helpful error message than: self.assertEqual(result, expected) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test__hindcast_fix.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test__hindcast_fix.py index 186a431074..55b897aa01 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test__hindcast_fix.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test__hindcast_fix.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,7 +22,7 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -31,7 +31,7 @@ from iris.fileformats.grib._load_convert import _hindcast_fix as hindcast_fix -class TestHindcastFix(tests.IrisTest): +class TestHindcastFix(tests.IrisGribTest): # setup tests : provided value, fix-applies, expected-fixed FixTest = namedtuple('FixTest', ('given', 'fixable', 'fixed')) test_values = [ @@ -56,8 +56,8 @@ def test_fix(self): def test_fix_warning(self): # Check warning appears when enabled. - self.patch('iris.fileformats.grib._load_convert.options' - '.warn_on_unsupported', True) + pm = 'iris.fileformats.grib._load_convert.options.warn_on_unsupported' + self.patch(pm, True) hindcast_fix(2 * 2**30 + 5) self.assertEqual(self.patch_warn.call_count, 1) self.assertIn('Re-interpreting large grib forecastTime', diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py index dbfef702c5..57b0168fba 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,16 +22,17 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from iris.exceptions import TranslationError + from iris.fileformats.grib._load_convert import bitmap_section from iris.tests.unit.fileformats.grib import _make_test_message -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test_bitmap_unsupported(self): # bitMapIndicator in range 1-254. # Note that bitMapIndicator = 1-253 and bitMapIndicator = 254 mean two diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py index 2ab05af4f4..631e405d52 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -19,26 +19,27 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests +import mock + from iris.exceptions import TranslationError + from iris.fileformats.grib._load_convert import convert -from iris.tests import mock from iris.tests.unit.fileformats.grib import _make_test_message -class Test(tests.IrisTest): - def test_call(self): +class TestGribMessage(tests.IrisGribTest): + def test_edition_2(self): + def func(field, metadata): + return metadata['factories'].append(factory) + sections = [{'editionNumber': 2}] field = _make_test_message(sections) this = 'iris.fileformats.grib._load_convert.grib2_convert' factory = mock.sentinel.factory - - def func(field, metadata): - return metadata['factories'].append(factory) - with mock.patch(this, side_effect=func) as grib2_convert: # The call being tested. result = convert(field) @@ -46,13 +47,32 @@ def func(field, metadata): metadata = ([factory], [], None, None, None, {}, [], [], []) self.assertEqual(result, metadata) - def test_edition_1(self): + def test_edition_1_bad(self): sections = [{'editionNumber': 1}] field = _make_test_message(sections) - with self.assertRaisesRegexp(TranslationError, - 'edition 1 is not supported'): + emsg = 'edition 1 is not supported' + with self.assertRaisesRegexp(TranslationError, emsg): + convert(field) + + +class TestGribWrapper(tests.IrisGribTest): + def test_edition_2_bad(self): + # Test object with no '.sections', and '.edition' ==2. + field = mock.Mock(edition=2, spec=('edition')) + emsg = 'edition 2 is not supported' + with self.assertRaisesRegexp(TranslationError, emsg): convert(field) + def test_edition_1(self): + # Test object with no '.sections', and '.edition' ==1. + field = mock.Mock(edition=1, spec=('edition')) + func = 'iris.fileformats.grib._load_convert.grib1_convert' + metadata = mock.sentinel.metadata + with mock.patch(func, return_value=metadata) as grib1_convert: + result = convert(field) + grib1_convert.assert_called_once_with(field) + self.assertEqual(result, metadata) + if __name__ == '__main__': tests.main() diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py index 729d2b9b30..e263391834 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,16 +22,17 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests +import mock + from iris.fileformats.grib._load_convert import _MDI as MDI from iris.fileformats.grib._load_convert import data_cutoff -from iris.tests import mock -class TestDataCutoff(tests.IrisTest): +class TestDataCutoff(tests.IrisGribTest): def _check(self, hours, minutes, request_warning, expect_warning=False): # Setup the environment. patch_target = 'iris.fileformats.grib._load_convert.options' diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py index 3c3ce51026..8cf724458f 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,7 +22,7 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -30,6 +30,7 @@ import iris.coord_systems as icoord_systems from iris.exceptions import TranslationError + from iris.fileformats.grib._load_convert import ellipsoid @@ -39,7 +40,7 @@ MDI = ma.masked -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test_shape_unsupported(self): unsupported = [2, 4, 5, 8, 9, 10, MDI] emsg = 'unsupported shape of the earth' diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid_geometry.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid_geometry.py index 25252da835..f0bbbb6565 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid_geometry.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid_geometry.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,14 +22,14 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from iris.fileformats.grib._load_convert import ellipsoid_geometry -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): self.section = {'scaledValueOfEarthMajorAxis': 10, 'scaleFactorOfEarthMajorAxis': 1, diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py index 8dea616fc8..c42ff985fc 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2016, Met Office +# (C) British Crown Copyright 2016 - 2017, Met Office # # This file is part of Iris. # @@ -23,19 +23,20 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from copy import deepcopy +import mock import warnings from iris.coords import DimCoord + from iris.fileformats.grib._load_convert import ensemble_identifier -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): module = 'iris.fileformats.grib._load_convert' self.patch('warnings.warn') diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_float32_from_int32.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_float32_from_int32.py index 4d98ffdfb9..d8597804d8 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_float32_from_int32.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_float32_from_int32.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -29,7 +29,7 @@ from iris.fileformats.grib._load_convert import fixup_float32_from_int32 -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test_negative(self): result = fixup_float32_from_int32(-0x3f000000) self.assertEqual(result, -0.5) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_int32_from_uint32.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_int32_from_uint32.py index f1144f14fc..6bc28305a6 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_int32_from_uint32.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_int32_from_uint32.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -29,7 +29,7 @@ from iris.fileformats.grib._load_convert import fixup_int32_from_uint32 -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test_negative(self): result = fixup_int32_from_uint32(0x80000005) self.assertEqual(result, -5) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py index 3c516a1bff..1eeeb693ba 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,15 +22,16 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from iris.coords import DimCoord + from iris.fileformats.grib._load_convert import forecast_period_coord -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test(self): # (indicatorOfUnitOfTimeRange, forecastTime, expected-hours) times = [(0, 60, 1), # minutes diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_generating_process.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_generating_process.py index fb5942f3ff..b57a62aa7a 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_generating_process.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_generating_process.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -23,14 +23,14 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from iris.fileformats.grib._load_convert import generating_process -class TestGeneratingProcess(tests.IrisTest): +class TestGeneratingProcess(tests.IrisGribTest): def setUp(self): self.warn_patch = self.patch('warnings.warn') diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py index 1e785eb248..579910a085 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -19,19 +19,19 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests import copy +import mock -import iris +import iris.fileformats.grib from iris.fileformats.grib._load_convert import grib2_convert -from iris.tests import mock from iris.tests.unit.fileformats.grib import _make_test_message -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): this = 'iris.fileformats.grib._load_convert' self.patch('{}.reference_time_coord'.format(this), return_value=None) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py index 0d0e4bc9ac..ff6364f619 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2015, Met Office +# (C) British Crown Copyright 2016 - 2017, Met Office # # This file is part of Iris. # @@ -23,16 +23,17 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# Import iris tests first so that some things can be initialised +# Import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from iris.exceptions import TranslationError -from iris.fileformats.grib._load_convert import \ - grid_definition_template_0_and_1 +from iris.fileformats.grib._load_convert \ + import grid_definition_template_0_and_1 -class Test(tests.IrisTest): + +class Test(tests.IrisGribTest): def test_unsupported_quasi_regular__number_of_octets(self): section = {'numberOfOctectsForNumberOfPoints': 1} diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py index a9a5346344..e07d24f3dd 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -23,7 +23,7 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -32,6 +32,7 @@ import iris.coord_systems import iris.coords import iris.exceptions + from iris.tests.unit.fileformats.grib.load_convert import empty_metadata from iris.fileformats.grib._load_convert import grid_definition_template_12 @@ -39,7 +40,7 @@ MDI = 2 ** 32 - 1 -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def section_3(self): section = { 'shapeOfTheEarth': 7, diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py index b22bab70be..6dabcc6cfc 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2016, Met Office +# (C) British Crown Copyright 2016 - 2017, Met Office # # This file is part of Iris. # @@ -22,7 +22,7 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -31,6 +31,7 @@ import iris.coord_systems import iris.coords + from iris.tests.unit.fileformats.grib.load_convert import empty_metadata from iris.fileformats.grib._load_convert import grid_definition_template_20 @@ -38,7 +39,7 @@ MDI = 2 ** 32 - 1 -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def section_3(self): section = { diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py index 515cf95896..b4808c7fb9 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2015, Met Office +# (C) British Crown Copyright 2016 - 2017, Met Office # # This file is part of Iris. # @@ -22,7 +22,7 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -31,6 +31,7 @@ import iris.coord_systems import iris.coords + from iris.tests.unit.fileformats.grib.load_convert import empty_metadata from iris.fileformats.grib._load_convert import grid_definition_template_30 @@ -38,7 +39,7 @@ MDI = 2 ** 32 - 1 -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def section_3(self): section = { diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py index ddddb8190c..dbb4aa7456 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2015 - 2016, Met Office +# (C) British Crown Copyright 2015 - 2017, Met Office # # This file is part of Iris. # @@ -23,7 +23,7 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -31,6 +31,7 @@ import iris.coord_systems import iris.coords + from iris.tests.unit.fileformats.grib.load_convert import empty_metadata from iris.fileformats.grib._load_convert import grid_definition_template_40 @@ -43,7 +44,7 @@ def get_computed_key(self, key): return self.get(key) -class Test_regular(tests.IrisTest): +class Test_regular(tests.IrisGribTest): def section_3(self): section = _Section({ @@ -117,7 +118,7 @@ def test_reverse_latitude(self): self.assertEqual(metadata, expected) -class Test_reduced(tests.IrisTest): +class Test_reduced(tests.IrisGribTest): def section_3(self): section = _Section({ diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py index ab78dfd041..8fb604b0e2 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -23,32 +23,29 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from copy import deepcopy -import warnings - +import mock import numpy as np +import warnings from iris.coords import DimCoord + from iris.fileformats.grib._load_convert import \ - grid_definition_template_4_and_5, \ - _MDI as MDI -from iris.tests import mock + grid_definition_template_4_and_5, _MDI as MDI RESOLUTION = 1e6 -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): - patch = [] - patch.append(mock.patch('warnings.warn')) - module = 'iris.fileformats.grib._load_convert' - this = '{}._is_circular'.format(module) - patch.append(mock.patch(this, return_value=False)) + self.patch('warnings.warn') + self.patch('iris.fileformats.grib._load_convert._is_circular', + return_value=False) self.metadata = {'factories': [], 'references': [], 'standard_name': None, 'long_name': None, 'units': None, 'attributes': {}, @@ -56,9 +53,6 @@ def setUp(self): 'aux_coords_and_dims': []} self.cs = mock.sentinel.coord_system self.data = np.arange(10, dtype=np.float64) - for p in patch: - p.start() - self.addCleanup(p.stop) def _check(self, section, request_warning, expect_warning=False, y_dim=0, x_dim=1): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py index e799bf4d72..48e23adac3 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -23,49 +23,50 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from copy import deepcopy +import mock from iris.fileformats.grib._load_convert import grid_definition_template_5 -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): + def func(s, m, y, x, c): + return m['dim_coords_and_dims'].append(item) + module = 'iris.fileformats.grib._load_convert' - patch = [] + self.major = mock.sentinel.major self.minor = mock.sentinel.minor self.radius = mock.sentinel.radius - this = '{}.ellipsoid_geometry'.format(module) + + mfunc = '{}.ellipsoid_geometry'.format(module) return_value = (self.major, self.minor, self.radius) - patch.append(mock.patch(this, return_value=return_value)) - this = '{}.ellipsoid'.format(module) + self.patch(mfunc, return_value=return_value) + + mfunc = '{}.ellipsoid'.format(module) self.ellipsoid = mock.sentinel.ellipsoid - patch.append(mock.patch(this, return_value=self.ellipsoid)) - this = '{}.grid_definition_template_4_and_5'.format(module) + self.patch(mfunc, return_value=self.ellipsoid) + + mfunc = '{}.grid_definition_template_4_and_5'.format(module) self.coord = mock.sentinel.coord self.dim = mock.sentinel.dim item = (self.coord, self.dim) + self.patch(mfunc, side_effect=func) - def func(s, m, y, x, c): - return m['dim_coords_and_dims'].append(item) - patch.append(mock.patch(this, side_effect=func)) - - this = 'iris.coord_systems.RotatedGeogCS' + mclass = 'iris.coord_systems.RotatedGeogCS' self.cs = mock.sentinel.cs - patch.append(mock.patch(this, return_value=self.cs)) + self.patch(mclass, return_value=self.cs) + self.metadata = {'factories': [], 'references': [], 'standard_name': None, 'long_name': None, 'units': None, 'attributes': {}, 'cell_methods': [], 'dim_coords_and_dims': [], 'aux_coords_and_dims': []} - for p in patch: - p.start() - self.addCleanup(p.stop) def test(self): metadata = deepcopy(self.metadata) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py index 04883dd990..1bee9c600c 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -24,7 +24,7 @@ from six.moves import (filter, input, map, range, zip) # noqa import six -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -33,6 +33,7 @@ import iris.coord_systems import iris.coords import iris.exceptions + from iris.tests.unit.fileformats.grib.load_convert import empty_metadata from iris.fileformats.grib._load_convert import grid_definition_template_90 @@ -40,7 +41,7 @@ MDI = 2 ** 32 - 1 -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def uk(self): section = { 'shapeOfTheEarth': 3, diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py index 4b47d99c4f..e602d054f6 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,15 +22,16 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests import iris.coords + from iris.fileformats.grib._load_convert import other_time_coord -class TestValid(tests.IrisTest): +class TestValid(tests.IrisGribTest): def test_t(self): rt = iris.coords.DimCoord(48, 'time', units='hours since epoch') fp = iris.coords.DimCoord(6, 'forecast_period', units='hours') @@ -48,7 +49,7 @@ def test_frt(self): self.assertEqual(result, expected) -class TestInvalid(tests.IrisTest): +class TestInvalid(tests.IrisGribTest): def test_t_with_bounds(self): rt = iris.coords.DimCoord(48, 'time', units='hours since epoch', bounds=[36, 60]) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_0.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_0.py index 649aa3c2b6..a4de342aa5 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_0.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_0.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -23,15 +23,18 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests +import mock + import iris.coords + +import iris.fileformats.grib from iris.tests.unit.fileformats.grib.load_convert import (LoadConvertTest, empty_metadata) from iris.fileformats.grib._load_convert import product_definition_template_0 -from iris.tests import mock MDI = 0xffffffff diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py index d8c8f5344e..cb6b157e6d 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -23,29 +23,29 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from copy import deepcopy +import mock import warnings from iris.coords import DimCoord + from iris.fileformats.grib._load_convert import product_definition_template_1 -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): + def func(s, m, f): + return m['cell_methods'].append(self.cell_method) + module = 'iris.fileformats.grib._load_convert' self.patch('warnings.warn') this = '{}.product_definition_template_0'.format(module) self.cell_method = mock.sentinel.cell_method - - def func(s, m, f): - return m['cell_methods'].append(self.cell_method) self.patch(this, side_effect=func) - self.metadata = {'factories': [], 'references': [], 'standard_name': None, 'long_name': None, 'units': None, 'attributes': {}, diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py new file mode 100644 index 0000000000..914e5cd01e --- /dev/null +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py @@ -0,0 +1,82 @@ +# (C) British Crown Copyright 2016 - 2017, Met Office +# +# This file is part of Iris. +# +# Iris is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Iris. If not, see . +""" +Test function +:func:`iris.fileformats.grib._load_convert.product_definition_template_10`. + +""" + +from __future__ import (absolute_import, division, print_function) +from six.moves import (filter, input, map, range, zip) # noqa + +# import iris.tests first so that some things can be initialised +# before importing anything else. +import iris.tests as tests + +from copy import deepcopy +import mock + +from iris.coords import DimCoord +from iris.fileformats.grib._load_convert import product_definition_template_10 +from iris.tests.unit.fileformats.grib.load_convert import empty_metadata + + +class Test(tests.IrisGribTest): + def setUp(self): + module = 'iris.fileformats.grib._load_convert' + this_module = '{}.product_definition_template_10'.format(module) + self.patch_statistical_fp_coord = self.patch( + module + '.statistical_forecast_period_coord', + return_value=mock.sentinel.dummy_fp_coord) + self.patch_time_coord = self.patch( + module + '.validity_time_coord', + return_value=mock.sentinel.dummy_time_coord) + self.patch_vertical_coords = self.patch(module + '.vertical_coords') + + def test_percentile_coord(self): + metadata = empty_metadata() + percentileValue = 75 + section = {'percentileValue': percentileValue, + 'hoursAfterDataCutoff': 1, + 'minutesAfterDataCutoff': 1, + 'numberOfTimeRange': 1, + 'typeOfStatisticalProcessing': 1, + 'typeOfTimeIncrement': 2, + 'timeIncrement': 0, + 'yearOfEndOfOverallTimeInterval': 2000, + 'monthOfEndOfOverallTimeInterval': 1, + 'dayOfEndOfOverallTimeInterval': 1, + 'hourOfEndOfOverallTimeInterval': 1, + 'minuteOfEndOfOverallTimeInterval': 0, + 'secondOfEndOfOverallTimeInterval': 1} + forecast_reference_time = mock.Mock() + # The called being tested. + product_definition_template_10(section, metadata, + forecast_reference_time) + + expected = {'aux_coords_and_dims': []} + percentile = DimCoord(percentileValue, + long_name='percentile_over_time', + units='no_unit') + expected['aux_coords_and_dims'].append((percentile, None)) + + self.assertEqual(metadata['aux_coords_and_dims'][-1], + expected['aux_coords_and_dims'][0]) + + +if __name__ == '__main__': + tests.main() diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py index a20cb4cf75..542555a793 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2016, Met Office +# (C) British Crown Copyright 2016 - 2017, Met Office # # This file is part of Iris. # @@ -23,29 +23,29 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from copy import deepcopy +import mock import warnings from iris.coords import DimCoord, CellMethod + from iris.fileformats.grib._load_convert import product_definition_template_11 -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): + def func(s, m, f): + return m['cell_methods'].append(self.cell_method) + module = 'iris.fileformats.grib._load_convert' self.patch('warnings.warn') this_module = '{}.product_definition_template_11'.format(module) self.cell_method = mock.sentinel.cell_method - - def func(s, m, f): - return m['cell_methods'].append(self.cell_method) self.patch(this_module, side_effect=func) - self.patch_statistical_fp_coord = self.patch( module + '.statistical_forecast_period_coord', return_value=mock.sentinel.dummy_fp_coord) @@ -77,7 +77,7 @@ def _check(self, request_warning): 'hourOfEndOfOverallTimeInterval': 1, 'minuteOfEndOfOverallTimeInterval': 0, 'secondOfEndOfOverallTimeInterval': 1} - forecast_reference_time = mock.sentinel.forecast_reference_time + forecast_reference_time = mock.Mock() # The called being tested. product_definition_template_11(section, metadata, forecast_reference_time) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py index 59f6f327f8..7283984527 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,25 +22,23 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from copy import deepcopy -import warnings - +import mock import numpy as np +import warnings from iris.coords import AuxCoord + from iris.fileformats.grib._load_convert import product_definition_template_31 -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): - patch = mock.patch('warnings.warn') - patch.start() - self.addCleanup(patch.stop) + self.patch('warnings.warn') self.metadata = {'factories': [], 'references': [], 'standard_name': None, 'long_name': None, 'units': None, 'attributes': None, @@ -49,6 +47,9 @@ def setUp(self): def _check(self, request_warning=False, value=10, factor=1): # Prepare the arguments. + def unscale(v, f): + return v / 10.0 ** f + series = mock.sentinel.satelliteSeries number = mock.sentinel.satelliteNumber instrument = mock.sentinel.instrumentType @@ -64,11 +65,7 @@ def _check(self, request_warning=False, value=10, factor=1): with mock.patch(this, warn_on_unsupported=request_warning): # The call being tested. product_definition_template_31(section, metadata, rt_coord) - # Check the result. - def unscale(v, f): - return v / 10.0 ** f - expected = deepcopy(self.metadata) coord = AuxCoord(series, long_name='satellite_series') expected['aux_coords_and_dims'].append((coord, None)) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py index 5625be870e..715e9becd1 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2016, Met Office +# (C) British Crown Copyright 2016 - 2017, Met Office # # This file is part of Iris. # @@ -23,37 +23,35 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests import iris.coords -from iris.fileformats.grib._load_convert import product_definition_template_40 -from iris.tests.unit.fileformats.grib.load_convert import empty_metadata - - -MDI = 0xffffffff +from iris.fileformats.grib._load_convert import \ + product_definition_template_40, _MDI +from iris.tests.unit.fileformats.grib.load_convert import empty_metadata -def section_4(): - return {'hoursAfterDataCutoff': MDI, - 'minutesAfterDataCutoff': MDI, - 'constituentType': 1, - 'indicatorOfUnitOfTimeRange': 0, # minutes - 'startStep': 360, - 'NV': 0, - 'typeOfFirstFixedSurface': 103, - 'scaleFactorOfFirstFixedSurface': 0, - 'scaledValueOfFirstFixedSurface': 9999, - 'typeOfSecondFixedSurface': 255} +class Test(tests.IrisGribTest): + def setUp(self): + self.section_4 = {'hoursAfterDataCutoff': _MDI, + 'minutesAfterDataCutoff': _MDI, + 'constituentType': 1, + 'indicatorOfUnitOfTimeRange': 0, # minutes + 'startStep': 360, + 'NV': 0, + 'typeOfFirstFixedSurface': 103, + 'scaleFactorOfFirstFixedSurface': 0, + 'scaledValueOfFirstFixedSurface': 9999, + 'typeOfSecondFixedSurface': 255} -class Test(tests.IrisTest): def test_constituent_type(self): metadata = empty_metadata() rt_coord = iris.coords.DimCoord(24, 'forecast_reference_time', units='hours since epoch') - product_definition_template_40(section_4(), metadata, rt_coord) + product_definition_template_40(self.section_4, metadata, rt_coord) expected = empty_metadata() expected['attributes']['WMO_constituent_type'] = 1 self.assertEqual(metadata['attributes'], expected['attributes']) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py index fb0d550813..092a050973 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -24,15 +24,16 @@ from six.moves import (filter, input, map, range, zip) # noqa import six -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests +import mock + from iris.fileformats.grib._load_convert import product_definition_template_8 -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): module = 'iris.fileformats.grib._load_convert' self.module = module @@ -54,7 +55,7 @@ def setUp(self): self.section = {} self.section['hoursAfterDataCutoff'] = mock.sentinel.cutoff_hours self.section['minutesAfterDataCutoff'] = mock.sentinel.cutoff_mins - self.frt_coord = mock.sentinel.frt_coord + self.frt_coord = mock.Mock() self.metadata = {'cell_methods': [], 'aux_coords_and_dims': []} def test_basic(self): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py index e1aede2dbf..b41cad4f20 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -23,17 +23,19 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests +import mock + from iris.exceptions import TranslationError + from iris.fileformats.grib._load_convert import product_definition_template_9 from iris.fileformats.grib._load_convert import Probability, _MDI -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): # Create patches for called routines module = 'iris.fileformats.grib._load_convert' diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_projection_centre.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_projection_centre.py index 8426655fab..5b70f714a5 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_projection_centre.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_projection_centre.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2015, Met Office +# (C) British Crown Copyright 2016 - 2017, Met Office # # This file is part of Iris. # @@ -22,7 +22,7 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -30,7 +30,7 @@ ProjectionCentre) -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test_unset(self): expected = ProjectionCentre(False, False) self.assertEqual(projection_centre(0x0), expected) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py index d8dcdd7a03..57c073892b 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -24,7 +24,7 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -35,10 +35,11 @@ from iris.coords import DimCoord from iris.exceptions import TranslationError + from iris.fileformats.grib._load_convert import reference_time_coord -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): self.section = {'year': 2007, 'month': 1, @@ -59,6 +60,11 @@ def _check(self, section, standard_name=None): coord = reference_time_coord(section) self.assertEqual(coord, expected) + def test_start_of_forecast(self): + section = deepcopy(self.section) + section['significanceOfReferenceTime'] = 0 + self._check(section, 'forecast_reference_time') + def test_start_of_forecast(self): section = deepcopy(self.section) section['significanceOfReferenceTime'] = 1 diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_resolution_flags.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_resolution_flags.py index 29713cffc5..e274461815 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_resolution_flags.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_resolution_flags.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,15 +22,15 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests -from iris.fileformats.grib._load_convert import \ - resolution_flags, ResolutionFlags +from iris.fileformats.grib._load_convert import (resolution_flags, + ResolutionFlags) -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test_unset(self): expected = ResolutionFlags(False, False, False) self.assertEqual(resolution_flags(0x0), expected) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py index 97cbe69d4e..1cdfb94da2 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,15 +22,16 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from iris.exceptions import TranslationError + from iris.fileformats.grib._load_convert import scanning_mode, ScanningMode -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test_unset(self): expected = ScanningMode(False, False, False, False) self.assertEqual(scanning_mode(0x0), expected) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py index e89f998ed8..c300abad10 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -23,7 +23,7 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -32,7 +32,7 @@ from iris.fileformats.grib._load_convert import statistical_cell_method -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): self.section = {} self.section['numberOfTimeRange'] = 1 diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py index 4bf3fb4a61..e5150c71f5 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -23,18 +23,18 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests import datetime +import mock from iris.fileformats.grib._load_convert import \ - statistical_forecast_period_coord -from iris.tests import mock + statistical_forecast_period_coord -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): module = 'iris.fileformats.grib._load_convert' self.module = module diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py index 648a68d587..7cc6eaeb23 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,17 +22,17 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from cf_units import Unit - from iris.exceptions import TranslationError + from iris.fileformats.grib._load_convert import time_range_unit -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): self.unit_by_indicator = {0: Unit('minutes'), 1: Unit('hours'), diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py index ea8cee55db..8f66f59ee9 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -15,30 +15,29 @@ # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . """ -Tests for function -:func:`iris.fileformats.grib._load_convert.translate_phenomenon`. +Tests for +function :func:`iris.fileformats.grib._load_convert.translate_phenomenon`. """ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from copy import deepcopy from cf_units import Unit - from iris.coords import DimCoord -from iris.fileformats.grib._load_convert import Probability -from iris.fileformats.grib.grib_phenom_translation import _GribToCfDataClass -from iris.fileformats.grib._load_convert import translate_phenomenon +from iris.fileformats.grib._load_convert import (Probability, + translate_phenomenon) +from iris.fileformats.grib.grib_phenom_translation import _GribToCfDataClass -class Test_probability(tests.IrisTest): +class Test_probability(tests.IrisGribTest): def setUp(self): # Patch inner call to return a given phenomenon type. target_module = 'iris.fileformats.grib._load_convert' diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py index 1f219477f4..9e26446d56 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,7 +22,7 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests @@ -34,7 +34,7 @@ # Reference GRIB2 Regulation 92.1.12. -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test_single(self): self.assertEqual(unscale(123, 1), 12.3) self.assertEqual(unscale(123, -1), 1230.0) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py index 9d9cc9858f..4845389d56 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,19 +22,20 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from cf_units import Unit +import mock import numpy as np from iris.coords import DimCoord + from iris.fileformats.grib._load_convert import validity_time_coord -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): self.fp = DimCoord(5, standard_name='forecast_period', units='hours') self.fp_test_bounds = np.array([[1.0, 9.0]]) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py index 51f5c68407..de65b0df54 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -22,22 +22,23 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised +# import iris.tests first so that some things can be initialised # before importing anything else. import iris.tests as tests from copy import deepcopy +import mock from iris.coords import DimCoord from iris.exceptions import TranslationError + from iris.fileformats.grib._load_convert import vertical_coords from iris.fileformats.grib._load_convert import \ _TYPE_OF_FIXED_SURFACE_MISSING as MISSING_SURFACE, \ _MDI as MISSING_LEVEL -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): self.metadata = {'factories': [], 'references': [], 'standard_name': None, @@ -46,14 +47,13 @@ def setUp(self): 'aux_coords_and_dims': []} def test_hybrid_factories(self): + def func(section, metadata): + return metadata['factories'].append(factory) + metadata = deepcopy(self.metadata) section = {'NV': 1} this = 'iris.fileformats.grib._load_convert.hybrid_factories' factory = mock.sentinel.factory - - def func(section, metadata): - return metadata['factories'].append(factory) - with mock.patch(this, side_effect=func) as hybrid_factories: vertical_coords(section, metadata) self.assertTrue(hybrid_factories.called) diff --git a/lib/iris/tests/unit/fileformats/grib/message/__init__.py b/lib/iris/tests/unit/fileformats/grib/message/__init__.py index 3162608b42..800584bf5e 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/__init__.py +++ b/lib/iris/tests/unit/fileformats/grib/message/__init__.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # diff --git a/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py b/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py index d0dd419ff6..0c986f354a 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py @@ -30,11 +30,12 @@ from abc import ABCMeta, abstractmethod import biggus +import mock import numpy as np from iris.exceptions import TranslationError + from iris.fileformats.grib.message import GribMessage -from iris.tests import mock from iris.tests.unit.fileformats.grib import _make_test_message @@ -42,7 +43,7 @@ @tests.skip_data -class Test_messages_from_filename(tests.IrisTest): +class Test_messages_from_filename(tests.IrisGribTest): def test(self): filename = tests.get_data_path(('GRIB', '3_layer_viz', '3_layer.grib2')) @@ -60,7 +61,7 @@ def test_release_file(self): self.assertTrue(my_file.closed) -class Test_sections(tests.IrisTest): +class Test_sections(tests.IrisGribTest): def test(self): # Check that the `sections` attribute defers to the `sections` # attribute on the underlying _RawGribMessage. @@ -68,7 +69,7 @@ def test(self): self.assertIs(message.sections, mock.sentinel.SECTIONS) -class Test_data__masked(tests.IrisTest): +class Test_data__masked(tests.IrisGribTest): def setUp(self): self.bitmap = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1]) self.shape = (3, 4) @@ -126,7 +127,7 @@ def test_bitmap__invalid_indicator(self): message.data.ndarray() -class Test_data__unsupported(tests.IrisTest): +class Test_data__unsupported(tests.IrisGribTest): def test_unsupported_grid_definition(self): message = _make_test_message({3: {'sourceOfGridDefinition': 1}, 6: SECTION_6_NO_BITMAP}) @@ -211,36 +212,31 @@ def _example_section_3(grib_definition_template_number, scanning_mode): 'Ni': 4} -@tests.iristest_timing_decorator -class Test_data__grid_template_0(tests.IrisTest_nometa, +class Test_data__grid_template_0(tests.IrisGribTest, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(0, scanning_mode) -@tests.iristest_timing_decorator -class Test_data__grid_template_1(tests.IrisTest_nometa, +class Test_data__grid_template_1(tests.IrisGribTest, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(1, scanning_mode) -@tests.iristest_timing_decorator -class Test_data__grid_template_5(tests.IrisTest_nometa, +class Test_data__grid_template_5(tests.IrisGribTest, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(5, scanning_mode) -@tests.iristest_timing_decorator -class Test_data__grid_template_12(tests.IrisTest_nometa, +class Test_data__grid_template_12(tests.IrisGribTest, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(12, scanning_mode) -@tests.iristest_timing_decorator -class Test_data__grid_template_30(tests.IrisTest_nometa, +class Test_data__grid_template_30(tests.IrisGribTest, Mixin_data__grid_template): def section_3(self, scanning_mode): section_3 = _example_section_3(30, scanning_mode) @@ -252,15 +248,13 @@ def section_3(self, scanning_mode): return section_3 -@tests.iristest_timing_decorator -class Test_data__grid_template_40_regular(tests.IrisTest_nometa, +class Test_data__grid_template_40_regular(tests.IrisGribTest, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(40, scanning_mode) -@tests.iristest_timing_decorator -class Test_data__grid_template_90(tests.IrisTest_nometa, +class Test_data__grid_template_90(tests.IrisGribTest, Mixin_data__grid_template): def section_3(self, scanning_mode): section_3 = _example_section_3(90, scanning_mode) @@ -272,7 +266,7 @@ def section_3(self, scanning_mode): return section_3 -class Test_data__unknown_grid_template(tests.IrisTest): +class Test_data__unknown_grid_template(tests.IrisGribTest): def test(self): message = _make_test_message( {3: _example_section_3(999, 0), diff --git a/lib/iris/tests/unit/fileformats/grib/message/test_Section.py b/lib/iris/tests/unit/fileformats/grib/message/test_Section.py index 07cee31dff..7a4b581784 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test_Section.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test_Section.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -33,7 +33,7 @@ @tests.skip_data -class Test___getitem__(tests.IrisTest): +class Test___getitem__(tests.IrisGribTest): def setUp(self): filename = tests.get_data_path(('GRIB', 'uk_t', 'uk_t.grib2')) with open(filename, 'rb') as grib_fh: @@ -66,7 +66,7 @@ def test_invalid(self): @tests.skip_data -class Test__getitem___pdt_31(tests.IrisTest): +class Test__getitem___pdt_31(tests.IrisGribTest): def setUp(self): filename = tests.get_data_path(('GRIB', 'umukv', 'ukv_chan9.grib2')) with open(filename, 'rb') as grib_fh: @@ -84,7 +84,7 @@ def test_array(self): @tests.skip_data -class Test_get_computed_key(tests.IrisTest): +class Test_get_computed_key(tests.IrisGribTest): def test_gdt40_computed(self): fname = tests.get_data_path(('GRIB', 'gaussian', 'regular_gg.grib2')) with open(fname, 'rb') as grib_fh: diff --git a/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py b/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py index 29dcd72120..1231f4ec35 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -15,7 +15,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . """ -Unit tests for the `iris.fileformats.grib.message._DataProxy` class. +Unit tests for the `iris.message._DataProxy` class. """ @@ -30,10 +30,11 @@ from numpy.random import randint from iris.exceptions import TranslationError + from iris.fileformats.grib.message import _DataProxy -class Test__bitmap(tests.IrisTest): +class Test__bitmap(tests.IrisGribTest): def test_no_bitmap(self): section_6 = {'bitMapIndicator': 255, 'bitmap': None} data_proxy = _DataProxy(0, 0, 0, 0) diff --git a/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py b/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py index b7e05de87b..13fd819300 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -15,7 +15,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . """ -Unit tests for the `iris.fileformats.grib.message._MessageLocation` class. +Unit tests for the `iris.message._MessageLocation` class. """ @@ -26,18 +26,18 @@ # importing anything else. import iris.tests as tests +import mock + from iris.fileformats.grib.message import _MessageLocation -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test(self): message_location = _MessageLocation(mock.sentinel.filename, mock.sentinel.location) - patch_target = 'iris.fileformats.grib.message._RawGribMessage.' \ - 'from_file_offset' + pt = 'iris.fileformats.grib.message._RawGribMessage.from_file_offset' expected = mock.sentinel.message - with mock.patch(patch_target, return_value=expected) as rgm: + with mock.patch(pt, return_value=expected) as rgm: result = message_location() rgm.assert_called_once_with(mock.sentinel.filename, mock.sentinel.location) diff --git a/lib/iris/tests/unit/fileformats/grib/message/test__RawGribMessage.py b/lib/iris/tests/unit/fileformats/grib/message/test__RawGribMessage.py index c192ee48d9..45c78ce813 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test__RawGribMessage.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test__RawGribMessage.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -32,7 +32,7 @@ @tests.skip_data -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): filename = tests.get_data_path(('GRIB', 'uk_t', 'uk_t.grib2')) with open(filename, 'rb') as grib_fh: diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/__init__.py b/lib/iris/tests/unit/fileformats/grib/save_rules/__init__.py index 0fbe2245ad..7457e0019e 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/__init__.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/__init__.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -19,12 +19,15 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa +# Import iris.tests first so that some things can be initialised before +# importing anything else. import iris.tests as tests +import mock +import numpy as np + import iris from iris.fileformats.pp import EARTH_RADIUS as PP_DEFAULT_EARTH_RADIUS -from iris.tests import mock -import numpy as np class GdtTestMixin(object): diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py index 5bd2c47127..b952ddda94 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -28,20 +28,22 @@ import iris.tests as tests import datetime +import numpy as np from iris.cube import Cube from iris.coords import DimCoord + from iris.fileformats.grib._save_rules import _missing_forecast_period -class TestNoForecastReferenceTime(tests.IrisTest): +class TestNoForecastReferenceTime(tests.IrisGribTest): def test_no_bounds(self): t_coord = DimCoord(15, 'time', units='hours since epoch') cube = Cube(23) cube.add_aux_coord(t_coord) res = _missing_forecast_period(cube) - expected_rt = datetime.datetime(1970, 1, 1, 15, 0) + expected_rt = t_coord.units.num2date(15) expected_rt_type = 3 expected_fp = 0 expected_fp_type = 1 @@ -58,7 +60,7 @@ def test_with_bounds(self): cube.add_aux_coord(t_coord) res = _missing_forecast_period(cube) - expected_rt = datetime.datetime(1970, 1, 1, 14, 0) + expected_rt = t_coord.units.num2date(14) expected_rt_type = 3 expected_fp = 0 expected_fp_type = 1 @@ -69,7 +71,7 @@ def test_with_bounds(self): self.assertEqual(res, expected) -class TestWithForecastReferenceTime(tests.IrisTest): +class TestWithForecastReferenceTime(tests.IrisGribTest): def test_no_bounds(self): t_coord = DimCoord(3, 'time', units='days since epoch') frt_coord = DimCoord(8, 'forecast_reference_time', @@ -79,7 +81,7 @@ def test_no_bounds(self): cube.add_aux_coord(frt_coord) res = _missing_forecast_period(cube) - expected_rt = datetime.datetime(1970, 1, 1, 8, 0) + expected_rt = frt_coord.units.num2date(8) expected_rt_type = 1 expected_fp = 3 * 24 - 8 expected_fp_type = 1 @@ -98,7 +100,7 @@ def test_with_bounds(self): cube.add_aux_coord(frt_coord) res = _missing_forecast_period(cube) - expected_rt = datetime.datetime(1970, 1, 1, 8, 0) + expected_rt = frt_coord.units.num2date(8) expected_rt_type = 1 expected_fp = 2 * 24 - 8 expected_fp_type = 1 diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py index 25ac6690d8..c386a6e623 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -26,10 +26,11 @@ import cf_units import iris + from iris.fileformats.grib._save_rules import _non_missing_forecast_period -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def _cube(self, t_bounds=False): time_coord = iris.coords.DimCoord(15, standard_name='time', units='hours since epoch') diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_and_11.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_10_and_11.py similarity index 84% rename from lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_and_11.py rename to lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_10_and_11.py index 4cabb3be19..48bffbbd02 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_and_11.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_10_and_11.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -16,7 +16,8 @@ # along with Iris. If not, see . """ Unit tests for -:func:`iris.fileformats.grib._save_rules._product_definition_template_8_and_11` +:func: +`iris.fileformats.grib._save_rules._product_definition_template_8_10_and_11` """ @@ -29,15 +30,16 @@ from cf_units import Unit import gribapi +import mock from iris.coords import CellMethod, DimCoord -from iris.tests import mock import iris.tests.stock as stock + from iris.fileformats.grib._save_rules import \ - _product_definition_template_8_and_11 + _product_definition_template_8_10_and_11 -class TestTypeOfStatisticalProcessing(tests.IrisTest): +class TestTypeOfStatisticalProcessing(tests.IrisGribTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. @@ -52,7 +54,7 @@ def test_sum(self, mock_set): cell_method = CellMethod(method='sum', coords=['time']) cube.add_cell_method(cell_method) - _product_definition_template_8_and_11(cube, mock.sentinel.grib) + _product_definition_template_8_10_and_11(cube, mock.sentinel.grib) mock_set.assert_any_call(mock.sentinel.grib, "typeOfStatisticalProcessing", 1) @@ -62,7 +64,7 @@ def test_unrecognised(self, mock_set): cell_method = CellMethod(method='95th percentile', coords=['time']) cube.add_cell_method(cell_method) - _product_definition_template_8_and_11(cube, mock.sentinel.grib) + _product_definition_template_8_10_and_11(cube, mock.sentinel.grib) mock_set.assert_any_call(mock.sentinel.grib, "typeOfStatisticalProcessing", 255) @@ -74,7 +76,7 @@ def test_multiple_cell_method_coords(self, mock_set): cube.add_cell_method(cell_method) with self.assertRaisesRegexp(ValueError, 'Cannot handle multiple coordinate name'): - _product_definition_template_8_and_11(cube, mock.sentinel.grib) + _product_definition_template_8_10_and_11(cube, mock.sentinel.grib) @mock.patch.object(gribapi, 'grib_set') def test_cell_method_coord_name_fail(self, mock_set): @@ -84,10 +86,10 @@ def test_cell_method_coord_name_fail(self, mock_set): with self.assertRaisesRegexp( ValueError, "Expected a cell method with a coordinate " "name of 'time'"): - _product_definition_template_8_and_11(cube, mock.sentinel.grib) + _product_definition_template_8_10_and_11(cube, mock.sentinel.grib) -class TestTimeCoordPrerequisites(tests.IrisTest): +class TestTimeCoordPrerequisites(tests.IrisGribTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. @@ -102,8 +104,8 @@ def test_multiple_points(self, mock_set): self.cube.add_aux_coord(coord, 0) with self.assertRaisesRegexp( ValueError, 'Expected length one time coordinate'): - _product_definition_template_8_and_11(self.cube, - mock.sentinel.grib) + _product_definition_template_8_10_and_11(self.cube, + mock.sentinel.grib) @mock.patch.object(gribapi, 'grib_set') def test_no_bounds(self, mock_set): @@ -114,8 +116,8 @@ def test_no_bounds(self, mock_set): with self.assertRaisesRegexp( ValueError, 'Expected time coordinate with two bounds, ' 'got 0 bounds'): - _product_definition_template_8_and_11(self.cube, - mock.sentinel.grib) + _product_definition_template_8_10_and_11(self.cube, + mock.sentinel.grib) @mock.patch.object(gribapi, 'grib_set') def test_more_than_two_bounds(self, mock_set): @@ -126,11 +128,11 @@ def test_more_than_two_bounds(self, mock_set): with self.assertRaisesRegexp( ValueError, 'Expected time coordinate with two bounds, ' 'got 3 bounds'): - _product_definition_template_8_and_11(self.cube, - mock.sentinel.grib) + _product_definition_template_8_10_and_11(self.cube, + mock.sentinel.grib) -class TestEndOfOverallTimeInterval(tests.IrisTest): +class TestEndOfOverallTimeInterval(tests.IrisGribTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. @@ -147,7 +149,7 @@ def test_default_calendar(self, mock_set): cube.add_aux_coord(coord) grib = mock.sentinel.grib - _product_definition_template_8_and_11(cube, grib) + _product_definition_template_8_10_and_11(cube, grib) mock_set.assert_any_call( grib, "yearOfEndOfOverallTimeInterval", 1972) @@ -171,7 +173,7 @@ def test_360_day_calendar(self, mock_set): cube.add_aux_coord(coord) grib = mock.sentinel.grib - _product_definition_template_8_and_11(cube, grib) + _product_definition_template_8_10_and_11(cube, grib) mock_set.assert_any_call( grib, "yearOfEndOfOverallTimeInterval", 1972) @@ -187,7 +189,7 @@ def test_360_day_calendar(self, mock_set): grib, "secondOfEndOfOverallTimeInterval", 7) -class TestNumberOfTimeRange(tests.IrisTest): +class TestNumberOfTimeRange(tests.IrisGribTest): @mock.patch.object(gribapi, 'grib_set') def test_other_cell_methods(self, mock_set): cube = stock.lat_lon_cube() @@ -202,7 +204,7 @@ def test_other_cell_methods(self, mock_set): cell_method = CellMethod(method='sum', coords=['time']) cube.add_cell_method(cell_method) - _product_definition_template_8_and_11(cube, mock.sentinel.grib) + _product_definition_template_8_10_and_11(cube, mock.sentinel.grib) mock_set.assert_any_call(mock.sentinel.grib, 'numberOfTimeRange', 1) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py index 88999fd4d5..bea61d0f62 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -15,31 +15,30 @@ # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . """ -Unit tests for -:func:`iris.fileformats.grib._save_rules.data_section`. +Unit tests for :func:`iris.fileformats.grib._save_rules.data_section`. """ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa -# import iris tests first so that some things can be initialised before +# import iris.tests first so that some things can be initialised before # importing anything else import iris.tests as tests +import mock import numpy as np import iris.cube from iris.fileformats.grib._save_rules import data_section -from iris.tests import mock GRIB_API = 'iris.fileformats.grib._save_rules.gribapi' GRIB_MESSAGE = mock.sentinel.GRIB_MESSAGE -class TestMDI(tests.IrisTest): +class TestMDI(tests.IrisGribTest): def assertBitmapOff(self, grib_api): # Check the use of a mask has been turned off via: # gribapi.grib_set(grib_message, 'bitmapPresent', 0) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_float32_as_int32.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_float32_as_int32.py index 0721ecaaa7..988ca565ca 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_float32_as_int32.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_float32_as_int32.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -29,7 +29,7 @@ from iris.fileformats.grib._save_rules import fixup_float32_as_int32 -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test_positive_zero(self): result = fixup_float32_as_int32(0.0) self.assertEqual(result, 0) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_int32_as_uint32.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_int32_as_uint32.py index 01e5d7dea9..daef795e65 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_int32_as_uint32.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_int32_as_uint32.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -29,7 +29,7 @@ from iris.fileformats.grib._save_rules import fixup_int32_as_uint32 -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test_very_negative(self): with self.assertRaises(ValueError): fixup_int32_as_uint32(-0x80000000) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py index 38225d030b..f18e76b8e3 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -29,12 +29,12 @@ from iris.coord_systems import LambertConformal from iris.exceptions import TranslationError -from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin from iris.fileformats.grib._save_rules import grid_definition_section +from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin -class Test(tests.IrisTest, GdtTestMixin): +class Test(tests.IrisGribTest, GdtTestMixin): def setUp(self): GdtTestMixin.setUp(self) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py index d73e9b72ce..dddf6ccb8e 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -30,12 +30,12 @@ import numpy as np from iris.coord_systems import GeogCS -from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin from iris.fileformats.grib._save_rules import grid_definition_template_0 +from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin -class Test(tests.IrisTest, GdtTestMixin): +class Test(tests.IrisGribTest, GdtTestMixin): def setUp(self): GdtTestMixin.setUp(self) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py index 419cd20d77..f13c968e9b 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -32,12 +32,12 @@ from iris.coord_systems import GeogCS, RotatedGeogCS from iris.exceptions import TranslationError from iris.fileformats.pp import EARTH_RADIUS as PP_DEFAULT_EARTH_RADIUS -from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin from iris.fileformats.grib._save_rules import grid_definition_template_1 +from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin -class Test(tests.IrisTest, GdtTestMixin): +class Test(tests.IrisGribTest, GdtTestMixin): def setUp(self): self.default_ellipsoid = GeogCS(PP_DEFAULT_EARTH_RADIUS) GdtTestMixin.setUp(self) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py index 8e27fe71cb..bf58924704 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -32,16 +32,16 @@ import iris.coords from iris.coord_systems import GeogCS, TransverseMercator from iris.exceptions import TranslationError -from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin from iris.fileformats.grib._save_rules import grid_definition_template_12 +from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin class FakeGribError(Exception): pass -class Test(tests.IrisTest, GdtTestMixin): +class Test(tests.IrisGribTest, GdtTestMixin): def setUp(self): self.default_ellipsoid = GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py index e65ad41107..1849f6207a 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -32,12 +32,12 @@ from iris.coord_systems import GeogCS, RotatedGeogCS from iris.exceptions import TranslationError from iris.fileformats.pp import EARTH_RADIUS as PP_DEFAULT_EARTH_RADIUS -from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin from iris.fileformats.grib._save_rules import grid_definition_template_5 +from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin -class Test(tests.IrisTest, GdtTestMixin): +class Test(tests.IrisGribTest, GdtTestMixin): def setUp(self): GdtTestMixin.setUp(self) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_identification.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_identification.py index 728c82d157..ec4f967c05 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_identification.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_identification.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2016, Met Office +# (C) British Crown Copyright 2016 - 2017, Met Office # # This file is part of Iris. # @@ -24,12 +24,13 @@ import iris.tests as tests import gribapi +import mock -import iris.fileformats.grib -from iris.fileformats.grib._save_rules import identification -from iris.tests import mock +import iris import iris.tests.stock as stock -from iris.tests.test_grib_load_translations import TestGribSimple + +from iris.fileformats.grib._save_rules import identification +from iris.tests.unit.fileformats.grib import TestGribSimple GRIB_API = 'iris.fileformats.grib._save_rules.gribapi' diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py new file mode 100644 index 0000000000..f6300ec735 --- /dev/null +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py @@ -0,0 +1,77 @@ +# (C) British Crown Copyright 2016 - 2017, Met Office +# +# This file is part of Iris. +# +# Iris is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Iris. If not, see . +""" +Unit tests for +:func:`iris.fileformats.grib._save_rules.product_definition_template_1` + +""" + +from __future__ import (absolute_import, division, print_function) +from six.moves import (filter, input, map, range, zip) # noqa + +# Import iris.tests first so that some things can be initialised before +# importing anything else. +import iris.tests as tests + +from cf_units import Unit +import gribapi +import mock + +from iris.coords import CellMethod, DimCoord +import iris.tests.stock as stock + +from iris.fileformats.grib._save_rules import product_definition_template_1 + + +class TestRealizationIdentifier(tests.IrisGribTest): + def setUp(self): + self.cube = stock.lat_lon_cube() + # Rename cube to avoid warning about unknown discipline/parameter. + self.cube.rename('air_temperature') + coord = DimCoord([45], 'time', + units=Unit('days since epoch', calendar='standard')) + self.cube.add_aux_coord(coord) + + @mock.patch.object(gribapi, 'grib_set') + def test_realization(self, mock_set): + cube = self.cube + coord = DimCoord(10, 'realization', units='1') + cube.add_aux_coord(coord) + + product_definition_template_1(cube, mock.sentinel.grib) + mock_set.assert_any_call(mock.sentinel.grib, + "productDefinitionTemplateNumber", 1) + mock_set.assert_any_call(mock.sentinel.grib, + "perturbationNumber", 10) + mock_set.assert_any_call(mock.sentinel.grib, + "numberOfForecastsInEnsemble", 255) + mock_set.assert_any_call(mock.sentinel.grib, + "typeOfEnsembleForecast", 255) + + @mock.patch.object(gribapi, 'grib_set') + def test_multiple_realization_values(self, mock_set): + cube = self.cube + coord = DimCoord([8, 9, 10], 'realization', units='1') + cube.add_aux_coord(coord, 0) + + msg = "'realization' coordinate with one point is required" + with self.assertRaisesRegexp(ValueError, msg): + product_definition_template_1(cube, mock.sentinel.grib) + + +if __name__ == "__main__": + tests.main() diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py new file mode 100644 index 0000000000..6e70207605 --- /dev/null +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py @@ -0,0 +1,75 @@ +# (C) British Crown Copyright 2013 - 2017, Met Office +# +# This file is part of Iris. +# +# Iris is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Iris. If not, see . +""" +Unit tests for +:func:`iris.fileformats.grib._save_rules.product_definition_template_10` + +""" + +from __future__ import (absolute_import, division, print_function) +from six.moves import (filter, input, map, range, zip) # noqa + +# Import iris.tests first so that some things can be initialised before +# importing anything else. +import iris.tests as tests + +from cf_units import Unit +import gribapi +import mock + +from iris.coords import DimCoord +import iris.tests.stock as stock + +from iris.fileformats.grib._save_rules import product_definition_template_10 + + +class TestPercentileValueIdentifier(tests.IrisGribTest): + def setUp(self): + self.cube = stock.lat_lon_cube() + # Rename cube to avoid warning about unknown discipline/parameter. + self.cube.rename('y_wind') + time_coord = DimCoord( + 20, 'time', bounds=[0, 40], + units=Unit('days since epoch', calendar='julian')) + self.cube.add_aux_coord(time_coord) + + @mock.patch.object(gribapi, 'grib_set') + def test_percentile_value(self, mock_set): + cube = self.cube + percentile_coord = DimCoord(95, long_name='percentile_over_time') + cube.add_aux_coord(percentile_coord) + + product_definition_template_10(cube, mock.sentinel.grib) + mock_set.assert_any_call(mock.sentinel.grib, + "productDefinitionTemplateNumber", 10) + mock_set.assert_any_call(mock.sentinel.grib, + "percentileValue", 95) + + @mock.patch.object(gribapi, 'grib_set') + def test_multiple_percentile_value(self, mock_set): + cube = self.cube + percentile_coord = DimCoord([5, 10, 15], + long_name='percentile_over_time') + cube.add_aux_coord(percentile_coord, 0) + err_msg = "A cube 'percentile_over_time' coordinate with one point "\ + "is required" + with self.assertRaisesRegexp(ValueError, err_msg): + product_definition_template_10(cube, mock.sentinel.grib) + + +if __name__ == "__main__": + tests.main() diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py index f390504207..fabe20564b 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -29,14 +29,15 @@ from cf_units import Unit import gribapi +import mock from iris.coords import CellMethod, DimCoord -from iris.tests import mock import iris.tests.stock as stock + from iris.fileformats.grib._save_rules import product_definition_template_11 -class TestRealizationIdentifier(tests.IrisTest): +class TestRealizationIdentifier(tests.IrisGribTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py index c039f67c21..4971fa53c3 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2016, Met Office +# (C) British Crown Copyright 2016 - 2017, Met Office # # This file is part of Iris. # @@ -29,14 +29,15 @@ from cf_units import Unit import gribapi +import mock from iris.coords import DimCoord -from iris.tests import mock import iris.tests.stock as stock + from iris.fileformats.grib._save_rules import product_definition_template_40 -class TestChemicalConstituentIdentifier(tests.IrisTest): +class TestChemicalConstituentIdentifier(tests.IrisGribTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. @@ -52,10 +53,10 @@ def test_constituent_type(self, mock_set): product_definition_template_40(cube, mock.sentinel.grib) mock_set.assert_any_call(mock.sentinel.grib, - "productDefinitionTemplateNumber", 40) + 'productDefinitionTemplateNumber', 40) mock_set.assert_any_call(mock.sentinel.grib, - "constituentType", 0) + 'constituentType', 0) -if __name__ == "__main__": +if __name__ == '__main__': tests.main() diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py index 24c8a82b13..25f9e623d9 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -29,14 +29,15 @@ from cf_units import Unit import gribapi +import mock from iris.coords import CellMethod, DimCoord -from iris.tests import mock import iris.tests.stock as stock + from iris.fileformats.grib._save_rules import product_definition_template_8 -class TestProductDefinitionIdentifier(tests.IrisTest): +class TestProductDefinitionIdentifier(tests.IrisGribTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py index b9a334a241..a5d71daf44 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2016, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -24,27 +24,18 @@ import iris.tests as tests import gribapi +import mock -import iris.fileformats.grib +from iris.fileformats.grib import load_cubes from iris.fileformats.grib._save_rules import reference_time -from iris.tests import mock -import iris.tests.stock as stock -from iris.tests.test_grib_load_translations import TestGribSimple -GRIB_API = 'iris.fileformats.grib._save_rules.gribapi' - - -class Test(TestGribSimple): - @tests.skip_data - def test_forecast_period(self): - # The stock cube has a non-compliant forecast_period. - iris.fileformats.grib.hindcast_workaround = True - cube = stock.global_grib2() - +class Test(tests.IrisGribTest): + def _test(self, cube): grib = mock.Mock() mock_gribapi = mock.Mock(spec=gribapi) - with mock.patch(GRIB_API, mock_gribapi): + with mock.patch('iris.fileformats.grib._save_rules.gribapi', + mock_gribapi): reference_time(cube, grib) mock_gribapi.assert_has_calls( @@ -52,25 +43,20 @@ def test_forecast_period(self): mock.call.grib_set_long(grib, "dataDate", '19980306'), mock.call.grib_set_long(grib, "dataTime", '0300')]) + @tests.skip_data + def test_forecast_period(self): + # The stock cube has a non-compliant forecast_period. + fname = tests.get_data_path(('GRIB', 'global_t', 'global.grib2')) + [cube] = load_cubes(fname) + self._test(cube) + @tests.skip_data def test_no_forecast_period(self): # The stock cube has a non-compliant forecast_period. - iris.fileformats.grib.hindcast_workaround = True - cube = stock.global_grib2() + fname = tests.get_data_path(('GRIB', 'global_t', 'global.grib2')) + [cube] = load_cubes(fname) cube.remove_coord("forecast_period") - frt_coords = cube.coords('forecast_reference_time') - if frt_coords: - cube.remove_coord(frt_coords[0]) - - grib = mock.Mock() - mock_gribapi = mock.Mock(spec=gribapi) - with mock.patch(GRIB_API, mock_gribapi): - reference_time(cube, grib) - - mock_gribapi.assert_has_calls( - [mock.call.grib_set_long(grib, "significanceOfReferenceTime", 3), - mock.call.grib_set_long(grib, "dataDate", '19941201'), - mock.call.grib_set_long(grib, "dataTime", '0000')]) + self._test(cube) if __name__ == "__main__": diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py index 9489be5455..04903d3dfe 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # @@ -15,8 +15,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . """ -Unit tests for -:func:`iris.fileformats.grib._save_rules.set_fixed_surfaces`. +Unit tests for :func:`iris.fileformats.grib._save_rules.set_fixed_surfaces`. """ @@ -32,10 +31,11 @@ import iris.cube import iris.coords + from iris.fileformats.grib._save_rules import set_fixed_surfaces -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def test_bounded_altitude_feet(self): cube = iris.cube.Cube([0]) cube.add_aux_coord(iris.coords.AuxCoord( diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py index a94df1572a..ad72c23a78 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -15,8 +15,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . """ -Unit tests for -:func:`iris.fileformats.grib._save_rules.set_time_increment` +Unit tests for :func:`iris.fileformats.grib._save_rules.set_time_increment` """ @@ -28,13 +27,14 @@ import iris.tests as tests import gribapi +import mock from iris.coords import CellMethod + from iris.fileformats.grib._save_rules import set_time_increment -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): @mock.patch.object(gribapi, 'grib_set') def test_no_intervals(self, mock_set): cell_method = CellMethod('sum', 'time') diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py index e1a9d8f1a5..e771f4885d 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -15,8 +15,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . """ -Unit tests for -:func:`iris.fileformats.grib._save_rules.set_time_range` +Unit tests for :func:`iris.fileformats.grib._save_rules.set_time_range` """ @@ -32,13 +31,14 @@ from cf_units import Unit import gribapi +import mock from iris.coords import DimCoord + from iris.fileformats.grib._save_rules import set_time_range -from iris.tests import mock -class Test(tests.IrisTest): +class Test(tests.IrisGribTest): def setUp(self): self.coord = DimCoord(0, 'time', units=Unit('hours since epoch', diff --git a/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py b/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py index 6f741a7372..dc0621fa9f 100644 --- a/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py +++ b/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2015, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -27,10 +27,13 @@ import iris.tests as tests from biggus import NumpyArrayAdapter +import mock import numpy as np +from iris.exceptions import TranslationError + from iris.fileformats.grib import GribWrapper, GribDataProxy -from iris.tests import mock + _message_length = 1000 @@ -40,7 +43,8 @@ def _mock_grib_get_long(grib_message, key): numberOfValues=200, jPointsAreConsecutive=0, Ni=20, - Nj=10) + Nj=10, + edition=1) try: result = lookup[key] except KeyError: @@ -60,7 +64,32 @@ def _mock_grib_get_native_type(grib_message, key): return result -class Test_deferred(tests.IrisTest): +class Test_edition(tests.IrisGribTest): + def setUp(self): + self.patch('iris.fileformats.grib.GribWrapper._confirm_in_scope') + self.patch('iris.fileformats.grib.GribWrapper._compute_extra_keys') + self.patch('gribapi.grib_get_long', _mock_grib_get_long) + self.patch('gribapi.grib_get_string', _mock_grib_get_string) + self.patch('gribapi.grib_get_native_type', _mock_grib_get_native_type) + self.tell = mock.Mock(side_effect=[_message_length]) + + def test_not_edition_1(self): + def func(grib_message, key): + return 2 + + emsg = "GRIB edition 2 is not supported by 'GribWrapper'" + with mock.patch('gribapi.grib_get_long', func): + with self.assertRaisesRegexp(TranslationError, emsg): + GribWrapper(None) + + def test_edition_1(self): + grib_message = 'regular_ll' + grib_fh = mock.Mock(tell=self.tell) + wrapper = GribWrapper(grib_message, grib_fh) + self.assertEqual(wrapper.grib_message, grib_message) + + +class Test_deferred(tests.IrisGribTest): def setUp(self): confirm_patch = mock.patch( 'iris.fileformats.grib.GribWrapper._confirm_in_scope') @@ -85,10 +114,9 @@ def setUp(self): def test_regular_sequential(self): tell_tale = np.arange(1, 5) * _message_length grib_fh = mock.Mock(tell=mock.Mock(side_effect=tell_tale)) - auto_regularise = False grib_message = 'regular_ll' for i, _ in enumerate(tell_tale): - gw = GribWrapper(grib_message, grib_fh, auto_regularise) + gw = GribWrapper(grib_message, grib_fh) self.assertIsInstance(gw._data, NumpyArrayAdapter) proxy = gw._data.concrete self.assertIsInstance(proxy, GribDataProxy) @@ -97,16 +125,14 @@ def test_regular_sequential(self): self.assertIs(proxy.fill_value, np.nan) self.assertEqual(proxy.path, grib_fh.name) self.assertEqual(proxy.offset, _message_length * i) - self.assertEqual(proxy.regularise, auto_regularise) def test_regular_mixed(self): tell_tale = np.arange(1, 5) * _message_length expected = tell_tale - _message_length grib_fh = mock.Mock(tell=mock.Mock(side_effect=tell_tale)) - auto_regularise = False grib_message = 'regular_ll' for offset in expected: - gw = GribWrapper(grib_message, grib_fh, auto_regularise) + gw = GribWrapper(grib_message, grib_fh) self.assertIsInstance(gw._data, NumpyArrayAdapter) proxy = gw._data.concrete self.assertIsInstance(proxy, GribDataProxy) @@ -115,15 +141,13 @@ def test_regular_mixed(self): self.assertIs(proxy.fill_value, np.nan) self.assertEqual(proxy.path, grib_fh.name) self.assertEqual(proxy.offset, offset) - self.assertEqual(proxy.regularise, auto_regularise) def test_reduced_sequential(self): tell_tale = np.arange(1, 5) * _message_length grib_fh = mock.Mock(tell=mock.Mock(side_effect=tell_tale)) - auto_regularise = False grib_message = 'reduced_gg' for i, _ in enumerate(tell_tale): - gw = GribWrapper(grib_message, grib_fh, auto_regularise) + gw = GribWrapper(grib_message, grib_fh) self.assertIsInstance(gw._data, NumpyArrayAdapter) proxy = gw._data.concrete self.assertIsInstance(proxy, GribDataProxy) @@ -132,16 +156,14 @@ def test_reduced_sequential(self): self.assertIs(proxy.fill_value, np.nan) self.assertEqual(proxy.path, grib_fh.name) self.assertEqual(proxy.offset, _message_length * i) - self.assertEqual(proxy.regularise, auto_regularise) def test_reduced_mixed(self): tell_tale = np.arange(1, 5) * _message_length expected = tell_tale - _message_length grib_fh = mock.Mock(tell=mock.Mock(side_effect=tell_tale)) - auto_regularise = False grib_message = 'reduced_gg' for offset in expected: - gw = GribWrapper(grib_message, grib_fh, auto_regularise) + gw = GribWrapper(grib_message, grib_fh) self.assertIsInstance(gw._data, NumpyArrayAdapter) proxy = gw._data.concrete self.assertIsInstance(proxy, GribDataProxy) @@ -150,7 +172,6 @@ def test_reduced_mixed(self): self.assertIs(proxy.fill_value, np.nan) self.assertEqual(proxy.path, grib_fh.name) self.assertEqual(proxy.offset, offset) - self.assertEqual(proxy.regularise, auto_regularise) if __name__ == '__main__': diff --git a/lib/iris/tests/unit/fileformats/grib/test__load_generate.py b/lib/iris/tests/unit/fileformats/grib/test__load_generate.py new file mode 100644 index 0000000000..cca422ba3d --- /dev/null +++ b/lib/iris/tests/unit/fileformats/grib/test__load_generate.py @@ -0,0 +1,80 @@ +# (C) British Crown Copyright 2016 - 2017, Met Office +# +# This file is part of Iris. +# +# Iris is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Iris. If not, see . +"""Unit tests for the `iris.fileformats.grib._load_generate` function.""" + +from __future__ import (absolute_import, division, print_function) +from six.moves import (filter, input, map, range, zip) # noqa + +import iris.tests as tests + +import mock + +import iris +from iris.exceptions import TranslationError +from iris.fileformats.rules import Loader + +import iris.fileformats.grib +from iris.fileformats.grib import GribWrapper +from iris.fileformats.grib import _load_generate +from iris.fileformats.grib.message import GribMessage + + +class Test(tests.IrisGribTest): + def setUp(self): + self.fname = mock.sentinel.fname + self.message_id = mock.sentinel.message_id + self.grib_fh = mock.sentinel.grib_fh + + def _make_test_message(self, sections): + raw_message = mock.Mock(sections=sections, _message_id=self.message_id) + file_ref = mock.Mock(open_file=self.grib_fh) + return GribMessage(raw_message, None, file_ref=file_ref) + + def test_grib1(self): + sections = [{'editionNumber': 1}] + message = self._make_test_message(sections) + mfunc = 'iris.fileformats.grib.GribMessage.messages_from_filename' + mclass = 'iris.fileformats.grib.GribWrapper' + with mock.patch(mfunc, return_value=[message]) as mock_func: + with mock.patch(mclass, spec=GribWrapper) as mock_wrapper: + field = next(_load_generate(self.fname)) + mock_func.assert_called_once_with(self.fname) + self.assertIsInstance(field, GribWrapper) + mock_wrapper.assert_called_once_with(self.message_id, + grib_fh=self.grib_fh) + + def test_grib2(self): + sections = [{'editionNumber': 2}] + message = self._make_test_message(sections) + mfunc = 'iris.fileformats.grib.GribMessage.messages_from_filename' + with mock.patch(mfunc, return_value=[message]) as mock_func: + field = next(_load_generate(self.fname)) + mock_func.assert_called_once_with(self.fname) + self.assertEqual(field, message) + + def test_grib_unknown(self): + sections = [{'editionNumber': 0}] + message = self._make_test_message(sections) + mfunc = 'iris.fileformats.grib.GribMessage.messages_from_filename' + emsg = 'GRIB edition 0 is not supported' + with mock.patch(mfunc, return_value=[message]): + with self.assertRaisesRegexp(TranslationError, emsg): + next(_load_generate(self.fname)) + + +if __name__ == '__main__': + tests.main() diff --git a/lib/iris/tests/unit/fileformats/grib/test_as_messages.py b/lib/iris/tests/unit/fileformats/grib/test_as_messages.py deleted file mode 100644 index bc2fc7a7ac..0000000000 --- a/lib/iris/tests/unit/fileformats/grib/test_as_messages.py +++ /dev/null @@ -1,50 +0,0 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office -# -# This file is part of Iris. -# -# Iris is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Iris is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Iris. If not, see . -"""Unit tests for the `iris.fileformats.grib.as_messages` function.""" - -from __future__ import (absolute_import, division, print_function) -from six.moves import (filter, input, map, range, zip) # noqa - -import iris.tests as tests - -import gribapi - -import iris -from iris.coords import DimCoord -import iris.fileformats.grib as grib -from iris.tests import mock -import iris.tests.stock as stock - - -class TestAsMessages(tests.IrisTest): - def setUp(self): - self.cube = stock.realistic_3d() - - def test_as_messages(self): - realization = 2 - type_of_process = 4 - coord = DimCoord(realization, standard_name='realization', units='1') - self.cube.add_aux_coord(coord) - messages = grib.as_messages(self.cube) - for message in messages: - self.assertEqual(gribapi.grib_get_long(message, - 'typeOfProcessedData'), - type_of_process) - - -if __name__ == "__main__": - tests.main() diff --git a/lib/iris/tests/unit/fileformats/grib/test_as_pairs.py b/lib/iris/tests/unit/fileformats/grib/test_as_pairs.py deleted file mode 100644 index 3aa76097ba..0000000000 --- a/lib/iris/tests/unit/fileformats/grib/test_as_pairs.py +++ /dev/null @@ -1,51 +0,0 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office -# -# This file is part of Iris. -# -# Iris is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Iris is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Iris. If not, see . -"""Unit tests for the `iris.fileformats.grib.as_pairs` function.""" - -from __future__ import (absolute_import, division, print_function) -from six.moves import (filter, input, map, range, zip) # noqa - -import iris.tests as tests - -import gribapi - -import iris -from iris.coords import DimCoord -import iris.fileformats.grib as grib -from iris.tests import mock -import iris.tests.stock as stock - - -class TestAsPairs(tests.IrisTest): - def setUp(self): - self.cube = stock.realistic_3d() - - def test_as_pairs(self): - realization = 2 - type_of_process = 4 - coord = DimCoord(realization, standard_name='realization', units='1') - self.cube.add_aux_coord(coord) - slices_and_messages = grib.as_pairs(self.cube) - for aslice, message in slices_and_messages: - self.assertEqual(aslice.shape, (9, 11)) - self.assertEqual(gribapi.grib_get_long(message, - 'typeOfProcessedData'), - type_of_process) - - -if __name__ == "__main__": - tests.main() diff --git a/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py b/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py index f3559a1676..c734e9de27 100644 --- a/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py +++ b/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2014 - 2016, Met Office +# (C) British Crown Copyright 2014 - 2017, Met Office # # This file is part of Iris. # @@ -21,64 +21,40 @@ import iris.tests as tests +import mock + import iris -import iris.fileformats.grib -import iris.fileformats.grib.load_rules -import iris.fileformats.rules +from iris.fileformats.rules import Loader +import iris.fileformats.grib from iris.fileformats.grib import load_cubes -from iris.tests import mock - - -class TestToggle(tests.IrisTest): - def _test(self, mode, generator, converter): - # Ensure that `load_cubes` defers to - # `iris.fileformats.rules.load_cubes`, passing a correctly - # configured `Loader` instance. - with iris.FUTURE.context(strict_grib_load=mode): - with mock.patch('iris.fileformats.rules.load_cubes') as rules_load: - rules_load.return_value = mock.sentinel.RESULT - result = load_cubes(mock.sentinel.FILES, - mock.sentinel.CALLBACK, - mock.sentinel.REGULARISE) - if mode: - kw_args = {} - else: - kw_args = {'auto_regularise': mock.sentinel.REGULARISE} - loader = iris.fileformats.rules.Loader( - generator, kw_args, - converter, None) - rules_load.assert_called_once_with(mock.sentinel.FILES, - mock.sentinel.CALLBACK, - loader) - self.assertIs(result, mock.sentinel.RESULT) - def test_sloppy_mode(self): - # Ensure that `load_cubes` uses: - # iris.fileformats.grib.grib_generator - # iris.fileformats.grib.load_rules.convert - self._test(False, iris.fileformats.grib.grib_generator, - iris.fileformats.grib.load_rules.convert) - def test_strict_mode(self): - # Ensure that `load_cubes` uses: - # iris.fileformats.grib.message.GribMessage.messages_from_filename - # iris.fileformats.grib._load_convert.convert - self._test( - True, - iris.fileformats.grib.message.GribMessage.messages_from_filename, - iris.fileformats.grib._load_convert.convert) +class Test(tests.IrisGribTest): + def test(self): + generator = iris.fileformats.grib._load_generate + converter = iris.fileformats.grib._load_convert.convert + files = mock.sentinel.FILES + callback = mock.sentinel.CALLBACK + expected_result = mock.sentinel.RESULT + with mock.patch('iris.fileformats.rules.load_cubes') as rules_load: + rules_load.return_value = expected_result + result = load_cubes(files, callback) + kwargs = {} + loader = Loader(generator, kwargs, converter, None) + rules_load.assert_called_once_with(files, callback, loader) + self.assertIs(result, expected_result) @tests.skip_data -class Test_load_cubes(tests.IrisTest): +class Test_load_cubes(tests.IrisGribTest): def test_reduced_raw(self): # Loading a GRIB message defined on a reduced grid without # interpolating to a regular grid. gribfile = tests.get_data_path( ("GRIB", "reduced", "reduced_gg.grib2")) - grib_generator = load_cubes(gribfile, auto_regularise=False) + grib_generator = load_cubes(gribfile) self.assertCML(next(grib_generator)) diff --git a/lib/iris/tests/unit/fileformats/grib/test_save_grib2.py b/lib/iris/tests/unit/fileformats/grib/test_save_grib2.py new file mode 100644 index 0000000000..7cd42117cf --- /dev/null +++ b/lib/iris/tests/unit/fileformats/grib/test_save_grib2.py @@ -0,0 +1,62 @@ +# (C) British Crown Copyright 2016 - 2017, Met Office +# +# This file is part of Iris. +# +# Iris is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Iris. If not, see . +"""Unit tests for the `iris.fileformats.grib.save_grib2` function.""" + +from __future__ import (absolute_import, division, print_function) +from six.moves import (filter, input, map, range, zip) # noqa +import six + +# Import iris.tests first so that some things can be initialised before +# importing anything else. +import iris.tests as tests + +import mock + +import iris.fileformats.grib + + +class TestSaveGrib2(tests.IrisGribTest): + def setUp(self): + self.cube = mock.sentinel.cube + self.target = mock.sentinel.target + func = 'iris.fileformats.grib.save_pairs_from_cube' + self.messages = list(range(10)) + slices = self.messages + side_effect = [zip(slices, self.messages)] + self.save_pairs_from_cube = self.patch(func, side_effect=side_effect) + func = 'iris.fileformats.grib.save_messages' + self.save_messages = self.patch(func) + + def _check(self, append=False): + iris.fileformats.grib.save_grib2(self.cube, self.target, append=append) + self.save_pairs_from_cube.called_once_with(self.cube) + args, kwargs = self.save_messages.call_args + self.assertEqual(len(args), 2) + messages, target = args + self.assertEqual(list(messages), self.messages) + self.assertEqual(target, self.target) + self.assertEqual(kwargs, dict(append=append)) + + def test_save_no_append(self): + self._check() + + def test_save_append(self): + self._check(append=True) + + +if __name__ == "__main__": + tests.main() diff --git a/lib/iris/tests/unit/fileformats/grib/test_save_messages.py b/lib/iris/tests/unit/fileformats/grib/test_save_messages.py index 33694e2b6d..2f9b1f6e07 100644 --- a/lib/iris/tests/unit/fileformats/grib/test_save_messages.py +++ b/lib/iris/tests/unit/fileformats/grib/test_save_messages.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2015, Met Office +# (C) British Crown Copyright 2016 - 2017, Met Office # # This file is part of Iris. # @@ -25,13 +25,13 @@ import iris.tests as tests import gribapi +import mock import numpy as np -import iris.fileformats.grib as grib -from iris.tests import mock +import iris.fileformats.grib -class TestSaveMessages(tests.IrisTest): +class TestSaveMessages(tests.IrisGribTest): def setUp(self): # Create a test object to stand in for a real PPField. self.grib_message = gribapi.grib_new_from_samples("GRIB2") @@ -47,7 +47,8 @@ def test_save(self): # as the gribapi code does a type check # this is deemed acceptable within the scope of this unit test with self.assertRaises((AssertionError, TypeError)): - grib.save_messages([self.grib_message], 'foo.grib2') + iris.fileformats.grib.save_messages([self.grib_message], + 'foo.grib2') self.assertTrue(mock.call('foo.grib2', 'wb') in m.mock_calls) def test_save_append(self): @@ -61,8 +62,9 @@ def test_save_append(self): # as the gribapi code does a type check # this is deemed acceptable within the scope of this unit test with self.assertRaises((AssertionError, TypeError)): - grib.save_messages([self.grib_message], 'foo.grib2', - append=True) + iris.fileformats.grib.save_messages([self.grib_message], + 'foo.grib2', + append=True) self.assertTrue(mock.call('foo.grib2', 'ab') in m.mock_calls) From 8de4c5a1061d09c52eb6b58206e2915b416095ec Mon Sep 17 00:00:00 2001 From: marqh Date: Tue, 4 Apr 2017 12:39:17 +0000 Subject: [PATCH 03/11] minimal GRIB integration test alterations --- lib/iris/tests/test_grib_load_translations.py | 106 ++++-------------- .../test_grib_phenomenon_translations.py | 2 +- .../grib/message/test_GribMessage.py | 14 +-- 3 files changed, 28 insertions(+), 94 deletions(-) diff --git a/lib/iris/tests/test_grib_load_translations.py b/lib/iris/tests/test_grib_load_translations.py index f868d8d70d..5fc7334c4f 100644 --- a/lib/iris/tests/test_grib_load_translations.py +++ b/lib/iris/tests/test_grib_load_translations.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2016, Met Office +# (C) British Crown Copyright 2010 - 2017, Met Office # # This file is part of Iris. # @@ -42,6 +42,7 @@ from iris.tests import mock import iris.tests.stock import iris.util +import iris.fileformats.grib.grib_phenom_translation as gpt # Run tests in no graphics mode if matplotlib is not available. if tests.MPL_AVAILABLE: @@ -302,15 +303,6 @@ def test_timeunits_grib1_specific(self): ) TestGribTimecodes._run_timetests(self, tests) - def test_timeunits_grib2_specific(self): - tests = ( - (2, 13, None, 1.0, 'seconds'), - # check the extra grib1 keys FAIL - (2, 14, TestGribTimecodes._err_bad_timeunit(14), 0.0, '??'), - (2, 254, TestGribTimecodes._err_bad_timeunit(254), 0.0, '??'), - ) - TestGribTimecodes._run_timetests(self, tests) - def test_timeunits_calendar(self): tests = ( (1, 3, TestGribTimecodes._err_bad_timeunit(3), 0.0, 'months'), @@ -324,55 +316,9 @@ def test_timeunits_calendar(self): def test_timeunits_invalid(self): tests = ( (1, 111, TestGribTimecodes._err_bad_timeunit(111), 1.0, '??'), - (2, 27, TestGribTimecodes._err_bad_timeunit(27), 1.0, '??'), ) TestGribTimecodes._run_timetests(self, tests) - def test_load_probability_forecast(self): - # Test GribWrapper interpretation of PDT 4.9 data. - # NOTE: - # Currently Iris has only partial support for PDT 4.9. - # Though it can load the data, key metadata (thresholds) is lost. - # At present, we are not testing for this. - - # Make a testing grib message in memory, with gribapi. - grib_message = gribapi.grib_new_from_samples('GRIB2') - gribapi.grib_set_long(grib_message, 'productDefinitionTemplateNumber', - 9) - gribapi.grib_set_string(grib_message, 'stepRange', '10-55') - grib_wrapper = iris.fileformats.grib.GribWrapper(grib_message) - - # Define two expected datetimes for _periodEndDateTime as - # gribapi v1.9.16 mis-calculates this. - # See https://software.ecmwf.int/wiki/display/GRIB/\ - # GRIB+API+version+1.9.18+released - try: - # gribapi v1.9.16 has no __version__ attribute. - gribapi_ver = gribapi.__version__ - except AttributeError: - gribapi_ver = gribapi.grib_get_api_version() - - if StrictVersion(gribapi_ver) < StrictVersion('1.9.18'): - exp_end_date = datetime.datetime(year=2007, month=3, day=25, - hour=12, minute=0, second=0) - else: - exp_end_date = datetime.datetime(year=2007, month=3, day=25, - hour=19, minute=0, second=0) - - # Check that it captures the statistics time period info. - # (And for now, nothing else) - self.assertEqual( - grib_wrapper._referenceDateTime, - datetime.datetime(year=2007, month=3, day=23, - hour=12, minute=0, second=0) - ) - self.assertEqual( - grib_wrapper._periodStartDateTime, - datetime.datetime(year=2007, month=3, day=23, - hour=22, minute=0, second=0) - ) - self.assertEqual(grib_wrapper._periodEndDateTime, exp_end_date) - def test_warn_unknown_pdts(self): # Test loading of an unrecognised GRIB Product Definition Template. @@ -389,13 +335,10 @@ def test_warn_unknown_pdts(self): # Load the message from the file as a cube. cube_generator = iris.fileformats.grib.load_cubes( temp_gribfile_path) - cube = next(cube_generator) - - # Check the cube has an extra "warning" attribute. - self.assertEqual( - cube.attributes['GRIB_LOAD_WARNING'], - 'unsupported GRIB2 ProductDefinitionTemplate: #4.5' - ) + with self.assertRaises(iris.exceptions.TranslationError) as te: + cube = next(cube_generator) + self.assertEqual('Product definition template [5]' + ' is not supported', str(te.exception)) @tests.skip_grib @@ -421,7 +364,8 @@ def cube_from_message(self, grib): grib_message = FakeGribMessage(**grib.__dict__) wrapped_msg = iris.fileformats.grib.GribWrapper(grib_message) cube, _, _ = iris.fileformats.rules._make_cube( - wrapped_msg, iris.fileformats.grib.load_rules.convert) + wrapped_msg, + iris.fileformats.grib._grib1_load_rules.grib1_convert) return cube @@ -487,30 +431,15 @@ def mock_grib(self): grib.phenomenon_points = lambda unit: [0.0] return grib - def known_grib2(self, discipline, category, param, - standard_name, long_name, units_str): - grib = self.mock_grib() - grib.discipline = discipline - grib.parameterCategory = category - grib.parameterNumber = param - cube = self.cube_from_message(grib) - try: - _cf_units = cf_units.Unit(units_str) - except ValueError: - _cf_units = cf_units.Unit('???') - self.assertEqual(cube.standard_name, standard_name) - self.assertEqual(cube.long_name, long_name) - self.assertEqual(cube.units, _cf_units) - def test_grib2_unknownparam(self): grib = self.mock_grib() grib.discipline = 999 grib.parameterCategory = 999 grib.parameterNumber = 9999 - cube = self.cube_from_message(grib) - self.assertEqual(cube.standard_name, None) - self.assertEqual(cube.long_name, None) - self.assertEqual(cube.units, cf_units.Unit("???")) + result = gpt.grib2_phenom_to_cf_info(grib.discipline, + grib.parameterCategory, + grib.parameterNumber) + self.assertIsNone(result) def test_grib2_known_standard_params(self): # check we know how to translate at least these params @@ -533,7 +462,8 @@ def test_grib2_known_standard_params(self): (0, 3, 3, None, "icao_standard_atmosphere_reference_height", "m"), (0, 3, 5, "geopotential_height", None, "m"), (0, 3, 9, "geopotential_height_anomaly", None, "m"), - (0, 6, 1, "cloud_area_fraction", None, "%"), + (0, 6, 1, None, + "cloud_area_fraction_assuming_maximum_random_overlap", "1"), (0, 6, 6, "atmosphere_mass_content_of_cloud_liquid_water", None, "kg m-2"), (0, 7, 6, @@ -547,8 +477,12 @@ def test_grib2_known_standard_params(self): for (discipline, category, number, standard_name, long_name, units) in full_set: - self.known_grib2(discipline, category, number, - standard_name, long_name, units) + result = gpt.grib2_phenom_to_cf_info(discipline, + category, + number) + self.assertEqual(result.standard_name, standard_name) + self.assertEqual(result.long_name, long_name) + self.assertEqual(result.units, units) if __name__ == "__main__": diff --git a/lib/iris/tests/test_grib_phenomenon_translations.py b/lib/iris/tests/test_grib_phenomenon_translations.py index d10e145387..e85b93cfa7 100644 --- a/lib/iris/tests/test_grib_phenomenon_translations.py +++ b/lib/iris/tests/test_grib_phenomenon_translations.py @@ -37,7 +37,7 @@ @tests.skip_grib class TestGribLookupTableType(tests.IrisTest): def test_lookuptable_type(self): - ll = gptx.LookupTable([('a', 1), ('b', 2)]) + ll = gptx._LookupTable([('a', 1), ('b', 2)]) assert ll['a'] == 1 assert ll['q'] is None ll['q'] = 15 diff --git a/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py b/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py index 0c986f354a..7d976ca258 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py @@ -212,31 +212,31 @@ def _example_section_3(grib_definition_template_number, scanning_mode): 'Ni': 4} -class Test_data__grid_template_0(tests.IrisGribTest, +class Test_data__grid_template_0(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(0, scanning_mode) -class Test_data__grid_template_1(tests.IrisGribTest, +class Test_data__grid_template_1(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(1, scanning_mode) -class Test_data__grid_template_5(tests.IrisGribTest, +class Test_data__grid_template_5(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(5, scanning_mode) -class Test_data__grid_template_12(tests.IrisGribTest, +class Test_data__grid_template_12(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(12, scanning_mode) -class Test_data__grid_template_30(tests.IrisGribTest, +class Test_data__grid_template_30(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): section_3 = _example_section_3(30, scanning_mode) @@ -248,13 +248,13 @@ def section_3(self, scanning_mode): return section_3 -class Test_data__grid_template_40_regular(tests.IrisGribTest, +class Test_data__grid_template_40_regular(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(40, scanning_mode) -class Test_data__grid_template_90(tests.IrisGribTest, +class Test_data__grid_template_90(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): section_3 = _example_section_3(90, scanning_mode) From d66ee0f098900cbae53cdd4151f68091e71cfb87 Mon Sep 17 00:00:00 2001 From: marqh Date: Tue, 4 Apr 2017 12:42:23 +0000 Subject: [PATCH 04/11] remove IrisGribTest --- lib/iris/tests/__init__.py | 2 -- lib/iris/tests/test_grib_phenomenon_translations.py | 2 +- lib/iris/tests/unit/fileformats/grib/__init__.py | 4 ++-- .../grib/grib1_load_rules/test_grib1_convert.py | 2 +- .../unit/fileformats/grib/load_convert/__init__.py | 2 +- .../grib/load_convert/test__hindcast_fix.py | 2 +- .../grib/load_convert/test_bitmap_section.py | 2 +- .../unit/fileformats/grib/load_convert/test_convert.py | 4 ++-- .../fileformats/grib/load_convert/test_data_cutoff.py | 2 +- .../fileformats/grib/load_convert/test_ellipsoid.py | 2 +- .../grib/load_convert/test_ellipsoid_geometry.py | 2 +- .../grib/load_convert/test_ensemble_identifier.py | 2 +- .../grib/load_convert/test_fixup_float32_from_int32.py | 2 +- .../grib/load_convert/test_fixup_int32_from_uint32.py | 2 +- .../grib/load_convert/test_forecast_period_coord.py | 2 +- .../grib/load_convert/test_generating_process.py | 2 +- .../grib/load_convert/test_grib2_convert.py | 2 +- .../test_grid_definition_template_0_and_1.py | 2 +- .../load_convert/test_grid_definition_template_12.py | 2 +- .../load_convert/test_grid_definition_template_20.py | 2 +- .../load_convert/test_grid_definition_template_30.py | 2 +- .../load_convert/test_grid_definition_template_40.py | 4 ++-- .../test_grid_definition_template_4_and_5.py | 2 +- .../load_convert/test_grid_definition_template_5.py | 2 +- .../load_convert/test_grid_definition_template_90.py | 2 +- .../grib/load_convert/test_other_time_coord.py | 4 ++-- .../load_convert/test_product_definition_template_1.py | 2 +- .../test_product_definition_template_10.py | 2 +- .../test_product_definition_template_11.py | 2 +- .../test_product_definition_template_31.py | 2 +- .../test_product_definition_template_40.py | 2 +- .../load_convert/test_product_definition_template_8.py | 2 +- .../load_convert/test_product_definition_template_9.py | 2 +- .../grib/load_convert/test_projection_centre.py | 2 +- .../grib/load_convert/test_reference_time_coord.py | 2 +- .../grib/load_convert/test_resolution_flags.py | 2 +- .../grib/load_convert/test_scanning_mode.py | 2 +- .../grib/load_convert/test_statistical_cell_method.py | 2 +- .../test_statistical_forecast_period_coord.py | 2 +- .../grib/load_convert/test_time_range_unit.py | 2 +- .../grib/load_convert/test_translate_phenomenon.py | 2 +- .../unit/fileformats/grib/load_convert/test_unscale.py | 2 +- .../grib/load_convert/test_validity_time_coord.py | 2 +- .../grib/load_convert/test_vertical_coords.py | 2 +- .../unit/fileformats/grib/message/test_GribMessage.py | 10 +++++----- .../unit/fileformats/grib/message/test_Section.py | 6 +++--- .../unit/fileformats/grib/message/test__DataProxy.py | 2 +- .../fileformats/grib/message/test__MessageLocation.py | 2 +- .../fileformats/grib/message/test__RawGribMessage.py | 2 +- .../grib/save_rules/test__missing_forecast_period.py | 4 ++-- .../save_rules/test__non_missing_forecast_period.py | 2 +- .../test__product_definition_template_8_10_and_11.py | 8 ++++---- .../fileformats/grib/save_rules/test_data_section.py | 2 +- .../grib/save_rules/test_fixup_float32_as_int32.py | 2 +- .../grib/save_rules/test_fixup_int32_as_uint32.py | 2 +- .../grib/save_rules/test_grid_definition_section.py | 2 +- .../grib/save_rules/test_grid_definition_template_0.py | 2 +- .../grib/save_rules/test_grid_definition_template_1.py | 2 +- .../save_rules/test_grid_definition_template_12.py | 2 +- .../grib/save_rules/test_grid_definition_template_5.py | 2 +- .../save_rules/test_product_definition_template_1.py | 2 +- .../save_rules/test_product_definition_template_10.py | 2 +- .../save_rules/test_product_definition_template_11.py | 2 +- .../save_rules/test_product_definition_template_40.py | 2 +- .../save_rules/test_product_definition_template_8.py | 2 +- .../fileformats/grib/save_rules/test_reference_time.py | 2 +- .../grib/save_rules/test_set_fixed_surfaces.py | 2 +- .../grib/save_rules/test_set_time_increment.py | 2 +- .../fileformats/grib/save_rules/test_set_time_range.py | 2 +- .../tests/unit/fileformats/grib/test_GribWrapper.py | 4 ++-- .../tests/unit/fileformats/grib/test__load_generate.py | 2 +- .../tests/unit/fileformats/grib/test_load_cubes.py | 4 ++-- .../tests/unit/fileformats/grib/test_save_grib2.py | 2 +- .../tests/unit/fileformats/grib/test_save_messages.py | 2 +- 74 files changed, 89 insertions(+), 91 deletions(-) diff --git a/lib/iris/tests/__init__.py b/lib/iris/tests/__init__.py index cb06a1a17b..e7cd49c4b6 100644 --- a/lib/iris/tests/__init__.py +++ b/lib/iris/tests/__init__.py @@ -932,8 +932,6 @@ class IrisTest(six.with_metaclass(_TestTimingsMetaclass, IrisTest_nometa)): # @iristest_timing_decorator explicitly to your new testclass. pass -class IrisGribTest(IrisTest): - pass get_result_path = IrisTest.get_result_path diff --git a/lib/iris/tests/test_grib_phenomenon_translations.py b/lib/iris/tests/test_grib_phenomenon_translations.py index e85b93cfa7..5d7f40a266 100644 --- a/lib/iris/tests/test_grib_phenomenon_translations.py +++ b/lib/iris/tests/test_grib_phenomenon_translations.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2013 - 2015, Met Office +# (C) British Crown Copyright 2013 - 2017, Met Office # # This file is part of Iris. # diff --git a/lib/iris/tests/unit/fileformats/grib/__init__.py b/lib/iris/tests/unit/fileformats/grib/__init__.py index 5e04304b8b..c3e0e5a857 100644 --- a/lib/iris/tests/unit/fileformats/grib/__init__.py +++ b/lib/iris/tests/unit/fileformats/grib/__init__.py @@ -161,7 +161,7 @@ def set_timeunit_code(self, timecode): self['unitOfTime'] = timecode -class TestField(tests.IrisGribTest): +class TestField(tests.IrisTest): def _test_for_coord(self, field, convert, coord_predicate, expected_points, expected_bounds): (factories, references, standard_name, long_name, units, @@ -208,7 +208,7 @@ def sorted_by_coordname(list): [type(coord) for coord, dims in coords_and_dims_expected]) -class TestGribSimple(tests.IrisGribTest): +class TestGribSimple(tests.IrisTest): # A testing class that does not need the test data. def mock_grib(self): # A mock grib message, with attributes that can't be Mocks themselves. diff --git a/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/test_grib1_convert.py b/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/test_grib1_convert.py index a22f84cafa..4ef951c6eb 100644 --- a/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/test_grib1_convert.py +++ b/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/test_grib1_convert.py @@ -38,7 +38,7 @@ from iris.tests.unit.fileformats.grib import TestField -class TestBadEdition(tests.IrisGribTest): +class TestBadEdition(tests.IrisTest): def test(self): message = mock.Mock(edition=2) emsg = 'GRIB edition 2 is not supported' diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/__init__.py b/lib/iris/tests/unit/fileformats/grib/load_convert/__init__.py index b7bcee25c3..60b499cd66 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/__init__.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/__init__.py @@ -40,7 +40,7 @@ def empty_metadata(): return metadata -class LoadConvertTest(tests.IrisGribTest): +class LoadConvertTest(tests.IrisTest): def assertMetadataEqual(self, result, expected): # Compare two metadata dictionaries. Gives slightly more # helpful error message than: self.assertEqual(result, expected) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test__hindcast_fix.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test__hindcast_fix.py index 55b897aa01..69dcc9259e 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test__hindcast_fix.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test__hindcast_fix.py @@ -31,7 +31,7 @@ from iris.fileformats.grib._load_convert import _hindcast_fix as hindcast_fix -class TestHindcastFix(tests.IrisGribTest): +class TestHindcastFix(tests.IrisTest): # setup tests : provided value, fix-applies, expected-fixed FixTest = namedtuple('FixTest', ('given', 'fixable', 'fixed')) test_values = [ diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py index 57b0168fba..a725c7ee6c 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py @@ -32,7 +32,7 @@ from iris.tests.unit.fileformats.grib import _make_test_message -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_bitmap_unsupported(self): # bitMapIndicator in range 1-254. # Note that bitMapIndicator = 1-253 and bitMapIndicator = 254 mean two diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py index 631e405d52..948a3fddb0 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py @@ -31,7 +31,7 @@ from iris.tests.unit.fileformats.grib import _make_test_message -class TestGribMessage(tests.IrisGribTest): +class TestGribMessage(tests.IrisTest): def test_edition_2(self): def func(field, metadata): return metadata['factories'].append(factory) @@ -55,7 +55,7 @@ def test_edition_1_bad(self): convert(field) -class TestGribWrapper(tests.IrisGribTest): +class TestGribWrapper(tests.IrisTest): def test_edition_2_bad(self): # Test object with no '.sections', and '.edition' ==2. field = mock.Mock(edition=2, spec=('edition')) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py index e263391834..23d928a485 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py @@ -32,7 +32,7 @@ from iris.fileformats.grib._load_convert import data_cutoff -class TestDataCutoff(tests.IrisGribTest): +class TestDataCutoff(tests.IrisTest): def _check(self, hours, minutes, request_warning, expect_warning=False): # Setup the environment. patch_target = 'iris.fileformats.grib._load_convert.options' diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py index 8cf724458f..b1addd985b 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py @@ -40,7 +40,7 @@ MDI = ma.masked -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_shape_unsupported(self): unsupported = [2, 4, 5, 8, 9, 10, MDI] emsg = 'unsupported shape of the earth' diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid_geometry.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid_geometry.py index f0bbbb6565..a14fa3d90b 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid_geometry.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid_geometry.py @@ -29,7 +29,7 @@ from iris.fileformats.grib._load_convert import ellipsoid_geometry -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): self.section = {'scaledValueOfEarthMajorAxis': 10, 'scaleFactorOfEarthMajorAxis': 1, diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py index c42ff985fc..06b3a01204 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py @@ -36,7 +36,7 @@ from iris.fileformats.grib._load_convert import ensemble_identifier -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): module = 'iris.fileformats.grib._load_convert' self.patch('warnings.warn') diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_float32_from_int32.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_float32_from_int32.py index d8597804d8..ee84fd007a 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_float32_from_int32.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_float32_from_int32.py @@ -29,7 +29,7 @@ from iris.fileformats.grib._load_convert import fixup_float32_from_int32 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_negative(self): result = fixup_float32_from_int32(-0x3f000000) self.assertEqual(result, -0.5) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_int32_from_uint32.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_int32_from_uint32.py index 6bc28305a6..91ab71cab6 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_int32_from_uint32.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_fixup_int32_from_uint32.py @@ -29,7 +29,7 @@ from iris.fileformats.grib._load_convert import fixup_int32_from_uint32 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_negative(self): result = fixup_int32_from_uint32(0x80000005) self.assertEqual(result, -5) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py index 1eeeb693ba..54b850ec58 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py @@ -31,7 +31,7 @@ from iris.fileformats.grib._load_convert import forecast_period_coord -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test(self): # (indicatorOfUnitOfTimeRange, forecastTime, expected-hours) times = [(0, 60, 1), # minutes diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_generating_process.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_generating_process.py index b57a62aa7a..ee6ab0972f 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_generating_process.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_generating_process.py @@ -30,7 +30,7 @@ from iris.fileformats.grib._load_convert import generating_process -class TestGeneratingProcess(tests.IrisGribTest): +class TestGeneratingProcess(tests.IrisTest): def setUp(self): self.warn_patch = self.patch('warnings.warn') diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py index 579910a085..1194e98056 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py @@ -31,7 +31,7 @@ from iris.tests.unit.fileformats.grib import _make_test_message -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): this = 'iris.fileformats.grib._load_convert' self.patch('{}.reference_time_coord'.format(this), return_value=None) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py index ff6364f619..88c1840999 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py @@ -33,7 +33,7 @@ import grid_definition_template_0_and_1 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_unsupported_quasi_regular__number_of_octets(self): section = {'numberOfOctectsForNumberOfPoints': 1} diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py index e07d24f3dd..1842df412d 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py @@ -40,7 +40,7 @@ MDI = 2 ** 32 - 1 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def section_3(self): section = { 'shapeOfTheEarth': 7, diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py index 6dabcc6cfc..55ff28eb1b 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py @@ -39,7 +39,7 @@ MDI = 2 ** 32 - 1 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def section_3(self): section = { diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py index b4808c7fb9..38b755c06d 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py @@ -39,7 +39,7 @@ MDI = 2 ** 32 - 1 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def section_3(self): section = { diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py index dbb4aa7456..2142237577 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py @@ -44,7 +44,7 @@ def get_computed_key(self, key): return self.get(key) -class Test_regular(tests.IrisGribTest): +class Test_regular(tests.IrisTest): def section_3(self): section = _Section({ @@ -118,7 +118,7 @@ def test_reverse_latitude(self): self.assertEqual(metadata, expected) -class Test_reduced(tests.IrisGribTest): +class Test_reduced(tests.IrisTest): def section_3(self): section = _Section({ diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py index 8fb604b0e2..867706ed86 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py @@ -41,7 +41,7 @@ RESOLUTION = 1e6 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): self.patch('warnings.warn') self.patch('iris.fileformats.grib._load_convert._is_circular', diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py index 48e23adac3..488f998019 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py @@ -33,7 +33,7 @@ from iris.fileformats.grib._load_convert import grid_definition_template_5 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): def func(s, m, y, x, c): return m['dim_coords_and_dims'].append(item) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py index 1bee9c600c..518312f044 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py @@ -41,7 +41,7 @@ MDI = 2 ** 32 - 1 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def uk(self): section = { 'shapeOfTheEarth': 3, diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py index e602d054f6..fdef3b53b4 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py @@ -31,7 +31,7 @@ from iris.fileformats.grib._load_convert import other_time_coord -class TestValid(tests.IrisGribTest): +class TestValid(tests.IrisTest): def test_t(self): rt = iris.coords.DimCoord(48, 'time', units='hours since epoch') fp = iris.coords.DimCoord(6, 'forecast_period', units='hours') @@ -49,7 +49,7 @@ def test_frt(self): self.assertEqual(result, expected) -class TestInvalid(tests.IrisGribTest): +class TestInvalid(tests.IrisTest): def test_t_with_bounds(self): rt = iris.coords.DimCoord(48, 'time', units='hours since epoch', bounds=[36, 60]) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py index cb6b157e6d..da2ddea7da 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py @@ -36,7 +36,7 @@ from iris.fileformats.grib._load_convert import product_definition_template_1 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): def func(s, m, f): return m['cell_methods'].append(self.cell_method) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py index 914e5cd01e..8127d86f5f 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py @@ -35,7 +35,7 @@ from iris.tests.unit.fileformats.grib.load_convert import empty_metadata -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): module = 'iris.fileformats.grib._load_convert' this_module = '{}.product_definition_template_10'.format(module) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py index 542555a793..3afa932c7f 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py @@ -36,7 +36,7 @@ from iris.fileformats.grib._load_convert import product_definition_template_11 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): def func(s, m, f): return m['cell_methods'].append(self.cell_method) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py index 7283984527..acd2d81392 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py @@ -36,7 +36,7 @@ from iris.fileformats.grib._load_convert import product_definition_template_31 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): self.patch('warnings.warn') self.metadata = {'factories': [], 'references': [], diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py index 715e9becd1..4b88679a90 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py @@ -34,7 +34,7 @@ from iris.tests.unit.fileformats.grib.load_convert import empty_metadata -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): self.section_4 = {'hoursAfterDataCutoff': _MDI, 'minutesAfterDataCutoff': _MDI, diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py index 092a050973..7cb726d363 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py @@ -33,7 +33,7 @@ from iris.fileformats.grib._load_convert import product_definition_template_8 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): module = 'iris.fileformats.grib._load_convert' self.module = module diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py index b41cad4f20..5ce01df967 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py @@ -35,7 +35,7 @@ from iris.fileformats.grib._load_convert import Probability, _MDI -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): # Create patches for called routines module = 'iris.fileformats.grib._load_convert' diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_projection_centre.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_projection_centre.py index 5b70f714a5..780ffe8868 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_projection_centre.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_projection_centre.py @@ -30,7 +30,7 @@ ProjectionCentre) -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_unset(self): expected = ProjectionCentre(False, False) self.assertEqual(projection_centre(0x0), expected) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py index 57c073892b..b76afb2ada 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py @@ -39,7 +39,7 @@ from iris.fileformats.grib._load_convert import reference_time_coord -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): self.section = {'year': 2007, 'month': 1, diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_resolution_flags.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_resolution_flags.py index e274461815..22681a24e1 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_resolution_flags.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_resolution_flags.py @@ -30,7 +30,7 @@ ResolutionFlags) -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_unset(self): expected = ResolutionFlags(False, False, False) self.assertEqual(resolution_flags(0x0), expected) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py index 1cdfb94da2..c0abec6b07 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py @@ -31,7 +31,7 @@ from iris.fileformats.grib._load_convert import scanning_mode, ScanningMode -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_unset(self): expected = ScanningMode(False, False, False, False) self.assertEqual(scanning_mode(0x0), expected) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py index c300abad10..47af476437 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py @@ -32,7 +32,7 @@ from iris.fileformats.grib._load_convert import statistical_cell_method -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): self.section = {} self.section['numberOfTimeRange'] = 1 diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py index e5150c71f5..9a64557ec9 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py @@ -34,7 +34,7 @@ statistical_forecast_period_coord -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): module = 'iris.fileformats.grib._load_convert' self.module = module diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py index 7cc6eaeb23..f35168d1e3 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py @@ -32,7 +32,7 @@ from iris.fileformats.grib._load_convert import time_range_unit -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): self.unit_by_indicator = {0: Unit('minutes'), 1: Unit('hours'), diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py index 8f66f59ee9..2a932b0488 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py @@ -37,7 +37,7 @@ from iris.fileformats.grib.grib_phenom_translation import _GribToCfDataClass -class Test_probability(tests.IrisGribTest): +class Test_probability(tests.IrisTest): def setUp(self): # Patch inner call to return a given phenomenon type. target_module = 'iris.fileformats.grib._load_convert' diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py index 9e26446d56..18a514ead9 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py @@ -34,7 +34,7 @@ # Reference GRIB2 Regulation 92.1.12. -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_single(self): self.assertEqual(unscale(123, 1), 12.3) self.assertEqual(unscale(123, -1), 1230.0) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py index 4845389d56..920e8876f8 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py @@ -35,7 +35,7 @@ from iris.fileformats.grib._load_convert import validity_time_coord -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): self.fp = DimCoord(5, standard_name='forecast_period', units='hours') self.fp_test_bounds = np.array([[1.0, 9.0]]) diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py index de65b0df54..ddda21d9b1 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py @@ -38,7 +38,7 @@ _MDI as MISSING_LEVEL -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): self.metadata = {'factories': [], 'references': [], 'standard_name': None, diff --git a/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py b/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py index 7d976ca258..66d2ba1cc9 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py @@ -43,7 +43,7 @@ @tests.skip_data -class Test_messages_from_filename(tests.IrisGribTest): +class Test_messages_from_filename(tests.IrisTest): def test(self): filename = tests.get_data_path(('GRIB', '3_layer_viz', '3_layer.grib2')) @@ -61,7 +61,7 @@ def test_release_file(self): self.assertTrue(my_file.closed) -class Test_sections(tests.IrisGribTest): +class Test_sections(tests.IrisTest): def test(self): # Check that the `sections` attribute defers to the `sections` # attribute on the underlying _RawGribMessage. @@ -69,7 +69,7 @@ def test(self): self.assertIs(message.sections, mock.sentinel.SECTIONS) -class Test_data__masked(tests.IrisGribTest): +class Test_data__masked(tests.IrisTest): def setUp(self): self.bitmap = np.array([0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1]) self.shape = (3, 4) @@ -127,7 +127,7 @@ def test_bitmap__invalid_indicator(self): message.data.ndarray() -class Test_data__unsupported(tests.IrisGribTest): +class Test_data__unsupported(tests.IrisTest): def test_unsupported_grid_definition(self): message = _make_test_message({3: {'sourceOfGridDefinition': 1}, 6: SECTION_6_NO_BITMAP}) @@ -266,7 +266,7 @@ def section_3(self, scanning_mode): return section_3 -class Test_data__unknown_grid_template(tests.IrisGribTest): +class Test_data__unknown_grid_template(tests.IrisTest): def test(self): message = _make_test_message( {3: _example_section_3(999, 0), diff --git a/lib/iris/tests/unit/fileformats/grib/message/test_Section.py b/lib/iris/tests/unit/fileformats/grib/message/test_Section.py index 7a4b581784..0de759f61b 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test_Section.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test_Section.py @@ -33,7 +33,7 @@ @tests.skip_data -class Test___getitem__(tests.IrisGribTest): +class Test___getitem__(tests.IrisTest): def setUp(self): filename = tests.get_data_path(('GRIB', 'uk_t', 'uk_t.grib2')) with open(filename, 'rb') as grib_fh: @@ -66,7 +66,7 @@ def test_invalid(self): @tests.skip_data -class Test__getitem___pdt_31(tests.IrisGribTest): +class Test__getitem___pdt_31(tests.IrisTest): def setUp(self): filename = tests.get_data_path(('GRIB', 'umukv', 'ukv_chan9.grib2')) with open(filename, 'rb') as grib_fh: @@ -84,7 +84,7 @@ def test_array(self): @tests.skip_data -class Test_get_computed_key(tests.IrisGribTest): +class Test_get_computed_key(tests.IrisTest): def test_gdt40_computed(self): fname = tests.get_data_path(('GRIB', 'gaussian', 'regular_gg.grib2')) with open(fname, 'rb') as grib_fh: diff --git a/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py b/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py index 1231f4ec35..11dee72796 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py @@ -34,7 +34,7 @@ from iris.fileformats.grib.message import _DataProxy -class Test__bitmap(tests.IrisGribTest): +class Test__bitmap(tests.IrisTest): def test_no_bitmap(self): section_6 = {'bitMapIndicator': 255, 'bitmap': None} data_proxy = _DataProxy(0, 0, 0, 0) diff --git a/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py b/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py index 13fd819300..d1a71aeccd 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py @@ -31,7 +31,7 @@ from iris.fileformats.grib.message import _MessageLocation -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test(self): message_location = _MessageLocation(mock.sentinel.filename, mock.sentinel.location) diff --git a/lib/iris/tests/unit/fileformats/grib/message/test__RawGribMessage.py b/lib/iris/tests/unit/fileformats/grib/message/test__RawGribMessage.py index 45c78ce813..7cfae668c1 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test__RawGribMessage.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test__RawGribMessage.py @@ -32,7 +32,7 @@ @tests.skip_data -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): filename = tests.get_data_path(('GRIB', 'uk_t', 'uk_t.grib2')) with open(filename, 'rb') as grib_fh: diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py index b952ddda94..fca6c7a21a 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py @@ -36,7 +36,7 @@ from iris.fileformats.grib._save_rules import _missing_forecast_period -class TestNoForecastReferenceTime(tests.IrisGribTest): +class TestNoForecastReferenceTime(tests.IrisTest): def test_no_bounds(self): t_coord = DimCoord(15, 'time', units='hours since epoch') cube = Cube(23) @@ -71,7 +71,7 @@ def test_with_bounds(self): self.assertEqual(res, expected) -class TestWithForecastReferenceTime(tests.IrisGribTest): +class TestWithForecastReferenceTime(tests.IrisTest): def test_no_bounds(self): t_coord = DimCoord(3, 'time', units='days since epoch') frt_coord = DimCoord(8, 'forecast_reference_time', diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py index c386a6e623..abcc1fee46 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py @@ -30,7 +30,7 @@ from iris.fileformats.grib._save_rules import _non_missing_forecast_period -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def _cube(self, t_bounds=False): time_coord = iris.coords.DimCoord(15, standard_name='time', units='hours since epoch') diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_10_and_11.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_10_and_11.py index 48bffbbd02..c90dd0f4fe 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_10_and_11.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_10_and_11.py @@ -39,7 +39,7 @@ _product_definition_template_8_10_and_11 -class TestTypeOfStatisticalProcessing(tests.IrisGribTest): +class TestTypeOfStatisticalProcessing(tests.IrisTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. @@ -89,7 +89,7 @@ def test_cell_method_coord_name_fail(self, mock_set): _product_definition_template_8_10_and_11(cube, mock.sentinel.grib) -class TestTimeCoordPrerequisites(tests.IrisGribTest): +class TestTimeCoordPrerequisites(tests.IrisTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. @@ -132,7 +132,7 @@ def test_more_than_two_bounds(self, mock_set): mock.sentinel.grib) -class TestEndOfOverallTimeInterval(tests.IrisGribTest): +class TestEndOfOverallTimeInterval(tests.IrisTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. @@ -189,7 +189,7 @@ def test_360_day_calendar(self, mock_set): grib, "secondOfEndOfOverallTimeInterval", 7) -class TestNumberOfTimeRange(tests.IrisGribTest): +class TestNumberOfTimeRange(tests.IrisTest): @mock.patch.object(gribapi, 'grib_set') def test_other_cell_methods(self, mock_set): cube = stock.lat_lon_cube() diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py index bea61d0f62..7a178167dc 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py @@ -38,7 +38,7 @@ GRIB_MESSAGE = mock.sentinel.GRIB_MESSAGE -class TestMDI(tests.IrisGribTest): +class TestMDI(tests.IrisTest): def assertBitmapOff(self, grib_api): # Check the use of a mask has been turned off via: # gribapi.grib_set(grib_message, 'bitmapPresent', 0) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_float32_as_int32.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_float32_as_int32.py index 988ca565ca..ff816ae7e9 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_float32_as_int32.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_float32_as_int32.py @@ -29,7 +29,7 @@ from iris.fileformats.grib._save_rules import fixup_float32_as_int32 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_positive_zero(self): result = fixup_float32_as_int32(0.0) self.assertEqual(result, 0) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_int32_as_uint32.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_int32_as_uint32.py index daef795e65..6941b43284 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_int32_as_uint32.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_fixup_int32_as_uint32.py @@ -29,7 +29,7 @@ from iris.fileformats.grib._save_rules import fixup_int32_as_uint32 -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_very_negative(self): with self.assertRaises(ValueError): fixup_int32_as_uint32(-0x80000000) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py index f18e76b8e3..ab08e32daf 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py @@ -34,7 +34,7 @@ from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin -class Test(tests.IrisGribTest, GdtTestMixin): +class Test(tests.IrisTest, GdtTestMixin): def setUp(self): GdtTestMixin.setUp(self) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py index dddf6ccb8e..a5cd47beaf 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py @@ -35,7 +35,7 @@ from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin -class Test(tests.IrisGribTest, GdtTestMixin): +class Test(tests.IrisTest, GdtTestMixin): def setUp(self): GdtTestMixin.setUp(self) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py index f13c968e9b..2c482ec29d 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py @@ -37,7 +37,7 @@ from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin -class Test(tests.IrisGribTest, GdtTestMixin): +class Test(tests.IrisTest, GdtTestMixin): def setUp(self): self.default_ellipsoid = GeogCS(PP_DEFAULT_EARTH_RADIUS) GdtTestMixin.setUp(self) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py index bf58924704..756b00d5e2 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py @@ -41,7 +41,7 @@ class FakeGribError(Exception): pass -class Test(tests.IrisGribTest, GdtTestMixin): +class Test(tests.IrisTest, GdtTestMixin): def setUp(self): self.default_ellipsoid = GeogCS(semi_major_axis=6377563.396, semi_minor_axis=6356256.909) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py index 1849f6207a..3e973386ea 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py @@ -37,7 +37,7 @@ from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin -class Test(tests.IrisGribTest, GdtTestMixin): +class Test(tests.IrisTest, GdtTestMixin): def setUp(self): GdtTestMixin.setUp(self) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py index f6300ec735..603d2a7928 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py @@ -37,7 +37,7 @@ from iris.fileformats.grib._save_rules import product_definition_template_1 -class TestRealizationIdentifier(tests.IrisGribTest): +class TestRealizationIdentifier(tests.IrisTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py index 6e70207605..a5636f174f 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py @@ -37,7 +37,7 @@ from iris.fileformats.grib._save_rules import product_definition_template_10 -class TestPercentileValueIdentifier(tests.IrisGribTest): +class TestPercentileValueIdentifier(tests.IrisTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py index fabe20564b..bbaa56ff7d 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py @@ -37,7 +37,7 @@ from iris.fileformats.grib._save_rules import product_definition_template_11 -class TestRealizationIdentifier(tests.IrisGribTest): +class TestRealizationIdentifier(tests.IrisTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py index 4971fa53c3..927e6a95dc 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py @@ -37,7 +37,7 @@ from iris.fileformats.grib._save_rules import product_definition_template_40 -class TestChemicalConstituentIdentifier(tests.IrisGribTest): +class TestChemicalConstituentIdentifier(tests.IrisTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py index 25f9e623d9..f1d139be79 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py @@ -37,7 +37,7 @@ from iris.fileformats.grib._save_rules import product_definition_template_8 -class TestProductDefinitionIdentifier(tests.IrisGribTest): +class TestProductDefinitionIdentifier(tests.IrisTest): def setUp(self): self.cube = stock.lat_lon_cube() # Rename cube to avoid warning about unknown discipline/parameter. diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py index a5d71daf44..afeebcecac 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py @@ -30,7 +30,7 @@ from iris.fileformats.grib._save_rules import reference_time -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def _test(self, cube): grib = mock.Mock() mock_gribapi = mock.Mock(spec=gribapi) diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py index 04903d3dfe..759c14543d 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py @@ -35,7 +35,7 @@ from iris.fileformats.grib._save_rules import set_fixed_surfaces -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test_bounded_altitude_feet(self): cube = iris.cube.Cube([0]) cube.add_aux_coord(iris.coords.AuxCoord( diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py index ad72c23a78..ae04e71755 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py @@ -34,7 +34,7 @@ from iris.fileformats.grib._save_rules import set_time_increment -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): @mock.patch.object(gribapi, 'grib_set') def test_no_intervals(self, mock_set): cell_method = CellMethod('sum', 'time') diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py index e771f4885d..5856951339 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py @@ -38,7 +38,7 @@ from iris.fileformats.grib._save_rules import set_time_range -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): self.coord = DimCoord(0, 'time', units=Unit('hours since epoch', diff --git a/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py b/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py index dc0621fa9f..04a7fb5c89 100644 --- a/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py +++ b/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py @@ -64,7 +64,7 @@ def _mock_grib_get_native_type(grib_message, key): return result -class Test_edition(tests.IrisGribTest): +class Test_edition(tests.IrisTest): def setUp(self): self.patch('iris.fileformats.grib.GribWrapper._confirm_in_scope') self.patch('iris.fileformats.grib.GribWrapper._compute_extra_keys') @@ -89,7 +89,7 @@ def test_edition_1(self): self.assertEqual(wrapper.grib_message, grib_message) -class Test_deferred(tests.IrisGribTest): +class Test_deferred(tests.IrisTest): def setUp(self): confirm_patch = mock.patch( 'iris.fileformats.grib.GribWrapper._confirm_in_scope') diff --git a/lib/iris/tests/unit/fileformats/grib/test__load_generate.py b/lib/iris/tests/unit/fileformats/grib/test__load_generate.py index cca422ba3d..7552960ef9 100644 --- a/lib/iris/tests/unit/fileformats/grib/test__load_generate.py +++ b/lib/iris/tests/unit/fileformats/grib/test__load_generate.py @@ -33,7 +33,7 @@ from iris.fileformats.grib.message import GribMessage -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def setUp(self): self.fname = mock.sentinel.fname self.message_id = mock.sentinel.message_id diff --git a/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py b/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py index c734e9de27..9fd60f718c 100644 --- a/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py +++ b/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py @@ -30,7 +30,7 @@ from iris.fileformats.grib import load_cubes -class Test(tests.IrisGribTest): +class Test(tests.IrisTest): def test(self): generator = iris.fileformats.grib._load_generate converter = iris.fileformats.grib._load_convert.convert @@ -47,7 +47,7 @@ def test(self): @tests.skip_data -class Test_load_cubes(tests.IrisGribTest): +class Test_load_cubes(tests.IrisTest): def test_reduced_raw(self): # Loading a GRIB message defined on a reduced grid without diff --git a/lib/iris/tests/unit/fileformats/grib/test_save_grib2.py b/lib/iris/tests/unit/fileformats/grib/test_save_grib2.py index 7cd42117cf..a887f495cb 100644 --- a/lib/iris/tests/unit/fileformats/grib/test_save_grib2.py +++ b/lib/iris/tests/unit/fileformats/grib/test_save_grib2.py @@ -29,7 +29,7 @@ import iris.fileformats.grib -class TestSaveGrib2(tests.IrisGribTest): +class TestSaveGrib2(tests.IrisTest): def setUp(self): self.cube = mock.sentinel.cube self.target = mock.sentinel.target diff --git a/lib/iris/tests/unit/fileformats/grib/test_save_messages.py b/lib/iris/tests/unit/fileformats/grib/test_save_messages.py index 2f9b1f6e07..a149f82fe2 100644 --- a/lib/iris/tests/unit/fileformats/grib/test_save_messages.py +++ b/lib/iris/tests/unit/fileformats/grib/test_save_messages.py @@ -31,7 +31,7 @@ import iris.fileformats.grib -class TestSaveMessages(tests.IrisGribTest): +class TestSaveMessages(tests.IrisTest): def setUp(self): # Create a test object to stand in for a real PPField. self.grib_message = gribapi.grib_new_from_samples("GRIB2") From a59d10b5c2b93201c10f72ed76301594cdacbca8 Mon Sep 17 00:00:00 2001 From: marqh Date: Tue, 4 Apr 2017 12:46:47 +0000 Subject: [PATCH 05/11] remove grib version string --- lib/iris/fileformats/grib/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/iris/fileformats/grib/__init__.py b/lib/iris/fileformats/grib/__init__.py index 8898ed0506..bd58b8e017 100644 --- a/lib/iris/fileformats/grib/__init__.py +++ b/lib/iris/fileformats/grib/__init__.py @@ -47,8 +47,6 @@ from .message import GribMessage -__version__ = '0.10.0-dev' - __all__ = ['load_cubes', 'save_grib2', 'load_pairs_from_fields', 'save_pairs_from_cube', 'save_messages'] From 9a9f20250b5bd36828f4b3b4d164e02e045afd7d Mon Sep 17 00:00:00 2001 From: marqh Date: Tue, 4 Apr 2017 14:14:22 +0000 Subject: [PATCH 06/11] manage conditional imports --- lib/iris/tests/test_grib_load_translations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/iris/tests/test_grib_load_translations.py b/lib/iris/tests/test_grib_load_translations.py index 5fc7334c4f..25f508dbd6 100644 --- a/lib/iris/tests/test_grib_load_translations.py +++ b/lib/iris/tests/test_grib_load_translations.py @@ -42,7 +42,6 @@ from iris.tests import mock import iris.tests.stock import iris.util -import iris.fileformats.grib.grib_phenom_translation as gpt # Run tests in no graphics mode if matplotlib is not available. if tests.MPL_AVAILABLE: @@ -54,6 +53,7 @@ if tests.GRIB_AVAILABLE: import gribapi import iris.fileformats.grib + import iris.fileformats.grib.grib_phenom_translation as gpt def _mock_gribapi_fetch(message, key): From ae8bfeff4b02f85b020c49276214eba782f3a74e Mon Sep 17 00:00:00 2001 From: marqh Date: Wed, 5 Apr 2017 07:53:53 +0000 Subject: [PATCH 07/11] review points --- lib/iris/fileformats/grib/__init__.py | 14 +- lib/iris/fileformats/grib/_grib_cf_map.py | 8 +- lib/iris/fileformats/grib/_load_convert.py | 7 +- lib/iris/fileformats/grib/_save_rules.py | 5 +- .../grib/grib_phenom_translation.py | 2 +- .../test_grib_phenomenon_translations.py | 171 ------------------ .../tests/unit/fileformats/grib/__init__.py | 30 +-- .../grib1_load_rules/test_grib1_convert.py | 5 +- .../test_grib_phenom_translation.py | 3 - .../grib/load_convert/test_bitmap_section.py | 1 - .../grib/load_convert/test_convert.py | 4 +- .../grib/load_convert/test_data_cutoff.py | 3 +- .../grib/load_convert/test_ellipsoid.py | 1 - .../load_convert/test_ensemble_identifier.py | 3 +- .../test_forecast_period_coord.py | 1 - .../grib/load_convert/test_grib2_convert.py | 2 +- .../test_grid_definition_template_0_and_1.py | 3 +- .../test_grid_definition_template_12.py | 3 +- .../test_grid_definition_template_20.py | 3 +- .../test_grid_definition_template_30.py | 3 +- .../test_grid_definition_template_40.py | 3 +- .../test_grid_definition_template_4_and_5.py | 6 +- .../test_grid_definition_template_5.py | 2 +- .../test_grid_definition_template_90.py | 3 +- .../load_convert/test_other_time_coord.py | 1 - .../test_product_definition_template_0.py | 6 +- .../test_product_definition_template_1.py | 3 +- .../test_product_definition_template_10.py | 2 +- .../test_product_definition_template_11.py | 3 +- .../test_product_definition_template_31.py | 6 +- .../test_product_definition_template_40.py | 1 - .../test_product_definition_template_8.py | 3 +- .../test_product_definition_template_9.py | 4 +- .../load_convert/test_reference_time_coord.py | 5 +- .../grib/load_convert/test_scanning_mode.py | 1 - .../test_statistical_cell_method.py | 1 - .../test_statistical_forecast_period_coord.py | 2 +- .../grib/load_convert/test_time_range_unit.py | 2 +- .../load_convert/test_translate_phenomenon.py | 2 +- .../load_convert/test_validity_time_coord.py | 3 +- .../grib/load_convert/test_vertical_coords.py | 3 +- .../grib/message/test_GribMessage.py | 10 +- .../grib/message/test__DataProxy.py | 3 +- .../grib/message/test__MessageLocation.py | 5 +- .../fileformats/grib/save_rules/__init__.py | 2 +- .../test__missing_forecast_period.py | 4 - .../test__non_missing_forecast_period.py | 1 - ...product_definition_template_8_10_and_11.py | 3 +- .../grib/save_rules/test_data_section.py | 3 +- .../test_grid_definition_section.py | 1 - .../test_grid_definition_template_0.py | 1 - .../test_grid_definition_template_1.py | 3 +- .../test_grid_definition_template_12.py | 3 +- .../test_grid_definition_template_5.py | 3 +- .../grib/save_rules/test_identification.py | 8 +- .../test_product_definition_template_1.py | 5 +- .../test_product_definition_template_10.py | 5 +- .../test_product_definition_template_11.py | 5 +- .../test_product_definition_template_40.py | 5 +- .../test_product_definition_template_8.py | 5 +- .../grib/save_rules/test_reference_time.py | 2 +- .../save_rules/test_set_fixed_surfaces.py | 1 - .../save_rules/test_set_time_increment.py | 3 +- .../grib/save_rules/test_set_time_range.py | 3 +- .../unit/fileformats/grib/test_GribWrapper.py | 3 +- .../fileformats/grib/test__load_generate.py | 8 +- .../unit/fileformats/grib/test_load_cubes.py | 8 +- .../unit/fileformats/grib/test_save_grib2.py | 3 +- .../fileformats/grib/test_save_messages.py | 4 +- 69 files changed, 96 insertions(+), 348 deletions(-) delete mode 100644 lib/iris/tests/test_grib_phenomenon_translations.py diff --git a/lib/iris/fileformats/grib/__init__.py b/lib/iris/fileformats/grib/__init__.py index bd58b8e017..c52fdc61c1 100644 --- a/lib/iris/fileformats/grib/__init__.py +++ b/lib/iris/fileformats/grib/__init__.py @@ -38,13 +38,11 @@ import iris import iris.coord_systems as coord_systems -from iris.exceptions import TranslationError, NotYetImplementedError - -# NOTE: careful here, to avoid circular imports (as iris imports grib) -from . import grib_phenom_translation as gptx -from . import _save_rules -from ._load_convert import convert as load_convert -from .message import GribMessage +from iris.exceptions import TranslationError, NotYetImplementedError, IrisError +from iris.fileformats.grib import grib_phenom_translation as gptx +from iris.fileformats.grib import _save_rules +from iris.fileformats.grib._load_convert import convert as load_convert +from iris.fileformats.grib.message import GribMessage __all__ = ['load_cubes', 'save_grib2', 'load_pairs_from_fields', @@ -195,7 +193,7 @@ def _confirm_in_scope(self): # forbid alternate row scanning # (uncommon entry from GRIB2 flag table 3.4, also in GRIB1) if self.alternativeRowScanning == 1: - raise ValueError("alternativeRowScanning == 1 not handled.") + raise IrisError("alternativeRowScanning == 1 not handled.") def __getattr__(self, key): """Return a grib key, or one of our extra keys.""" diff --git a/lib/iris/fileformats/grib/_grib_cf_map.py b/lib/iris/fileformats/grib/_grib_cf_map.py index 8f9418e55c..63c597c9ba 100644 --- a/lib/iris/fileformats/grib/_grib_cf_map.py +++ b/lib/iris/fileformats/grib/_grib_cf_map.py @@ -1,19 +1,19 @@ # (C) British Crown Copyright 2013 - 2017, Met Office # -# This file is part of iris. +# This file is part of Iris. # -# iris is free software: you can redistribute it and/or modify it under +# Iris is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the # Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # -# iris is distributed in the hope that it will be useful, +# Iris is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License -# along with iris. If not, see . +# along with Iris. If not, see . # # DO NOT EDIT: AUTO-GENERATED # Created on 14 October 2016 15:10 from diff --git a/lib/iris/fileformats/grib/_load_convert.py b/lib/iris/fileformats/grib/_load_convert.py index ce553e60c8..892de22b22 100644 --- a/lib/iris/fileformats/grib/_load_convert.py +++ b/lib/iris/fileformats/grib/_load_convert.py @@ -38,13 +38,12 @@ import iris.coord_systems as icoord_systems from iris.coords import AuxCoord, DimCoord, CellMethod from iris.exceptions import TranslationError -from . import grib_phenom_translation as itranslation +from iris.fileformats.grib._grib1_load_rules import grib1_convert +from iris.fileformats.grib import grib_phenom_translation as itranslation +from iris.fileformats.grib.message import GribMessage from iris.fileformats.rules import ConversionMetadata, Factory, Reference from iris.util import _is_circular -from ._grib1_load_rules import grib1_convert -from .message import GribMessage - # Restrict the names imported from this namespace. __all__ = ['convert'] diff --git a/lib/iris/fileformats/grib/_save_rules.py b/lib/iris/fileformats/grib/_save_rules.py index 522d97dcd0..d9e914f7fb 100644 --- a/lib/iris/fileformats/grib/_save_rules.py +++ b/lib/iris/fileformats/grib/_save_rules.py @@ -36,8 +36,9 @@ import iris import iris.exceptions from iris.coord_systems import GeogCS, RotatedGeogCS, TransverseMercator -from . import grib_phenom_translation as gptx -from ._load_convert import (_STATISTIC_TYPE_NAMES, _TIME_RANGE_UNITS) +from iris.fileformats.grib import grib_phenom_translation as gptx +from iris.fileformats.grib._load_convert import (_STATISTIC_TYPE_NAMES, + _TIME_RANGE_UNITS) from iris.util import is_regular, regular_step diff --git a/lib/iris/fileformats/grib/grib_phenom_translation.py b/lib/iris/fileformats/grib/grib_phenom_translation.py index 0fce03a8fb..68c533666b 100644 --- a/lib/iris/fileformats/grib/grib_phenom_translation.py +++ b/lib/iris/fileformats/grib/grib_phenom_translation.py @@ -37,7 +37,7 @@ import cf_units -from . import _grib_cf_map as grcf +from iris.fileformats.grib import _grib_cf_map as grcf import iris.std_names diff --git a/lib/iris/tests/test_grib_phenomenon_translations.py b/lib/iris/tests/test_grib_phenomenon_translations.py deleted file mode 100644 index 5d7f40a266..0000000000 --- a/lib/iris/tests/test_grib_phenomenon_translations.py +++ /dev/null @@ -1,171 +0,0 @@ -# (C) British Crown Copyright 2013 - 2017, Met Office -# -# This file is part of Iris. -# -# Iris is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Iris is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Iris. If not, see . -''' -Created on Apr 26, 2013 - -@author: itpp -''' - -from __future__ import (absolute_import, division, print_function) -from six.moves import (filter, input, map, range, zip) # noqa - -# Import iris tests first so that some things can be initialised before -# importing anything else -import iris.tests as tests - -import cf_units - -if tests.GRIB_AVAILABLE: - import gribapi - import iris.fileformats.grib.grib_phenom_translation as gptx - - -@tests.skip_grib -class TestGribLookupTableType(tests.IrisTest): - def test_lookuptable_type(self): - ll = gptx._LookupTable([('a', 1), ('b', 2)]) - assert ll['a'] == 1 - assert ll['q'] is None - ll['q'] = 15 - assert ll['q'] == 15 - ll['q'] = 15 - assert ll['q'] == 15 - with self.assertRaises(KeyError): - ll['q'] = 7 - del ll['q'] - ll['q'] = 7 - assert ll['q'] == 7 - - -@tests.skip_grib -class TestGribPhenomenonLookup(tests.IrisTest): - def test_grib1_cf_lookup(self): - def check_grib1_cf(param, - standard_name, long_name, units, - height=None, - t2version=128, centre=98, expect_none=False): - a_cf_unit = cf_units.Unit(units) - cfdata = gptx.grib1_phenom_to_cf_info(param_number=param, - table2_version=t2version, - centre_number=centre) - if expect_none: - self.assertIsNone(cfdata) - else: - self.assertEqual(cfdata.standard_name, standard_name) - self.assertEqual(cfdata.long_name, long_name) - self.assertEqual(cfdata.units, a_cf_unit) - if height is None: - self.assertIsNone(cfdata.set_height) - else: - self.assertEqual(cfdata.set_height, float(height)) - - check_grib1_cf(165, 'x_wind', None, 'm s-1', 10.0) - check_grib1_cf(168, 'dew_point_temperature', None, 'K', 2) - check_grib1_cf(130, 'air_temperature', None, 'K') - check_grib1_cf(235, None, "grib_skin_temperature", "K") - check_grib1_cf(235, None, "grib_skin_temperature", "K", - t2version=9999, expect_none=True) - check_grib1_cf(235, None, "grib_skin_temperature", "K", - centre=9999, expect_none=True) - check_grib1_cf(9999, None, "grib_skin_temperature", "K", - expect_none=True) - - def test_grib2_cf_lookup(self): - def check_grib2_cf(discipline, category, number, - standard_name, long_name, units, - expect_none=False): - a_cf_unit = cf_units.Unit(units) - cfdata = gptx.grib2_phenom_to_cf_info(param_discipline=discipline, - param_category=category, - param_number=number) - if expect_none: - self.assertIsNone(cfdata) - else: - self.assertEqual(cfdata.standard_name, standard_name) - self.assertEqual(cfdata.long_name, long_name) - self.assertEqual(cfdata.units, a_cf_unit) - - # These should work - check_grib2_cf(0, 0, 2, "air_potential_temperature", None, "K") - check_grib2_cf(0, 19, 1, None, "grib_physical_atmosphere_albedo", "%") - check_grib2_cf(2, 0, 2, "soil_temperature", None, "K") - check_grib2_cf(10, 2, 0, "sea_ice_area_fraction", None, 1) - check_grib2_cf(2, 0, 0, "land_area_fraction", None, 1) - check_grib2_cf(0, 19, 1, None, "grib_physical_atmosphere_albedo", "%") - check_grib2_cf(0, 1, 64, - "atmosphere_mass_content_of_water_vapor", None, - "kg m-2") - check_grib2_cf(2, 0, 7, "surface_altitude", None, "m") - - # These should fail - check_grib2_cf(9999, 2, 0, "sea_ice_area_fraction", None, 1, - expect_none=True) - check_grib2_cf(10, 9999, 0, "sea_ice_area_fraction", None, 1, - expect_none=True) - check_grib2_cf(10, 2, 9999, "sea_ice_area_fraction", None, 1, - expect_none=True) - - def test_cf_grib2_lookup(self): - def check_cf_grib2(standard_name, long_name, - discipline, category, number, units, - expect_none=False): - a_cf_unit = cf_units.Unit(units) - gribdata = gptx.cf_phenom_to_grib2_info(standard_name, long_name) - if expect_none: - self.assertIsNone(gribdata) - else: - self.assertEqual(gribdata.discipline, discipline) - self.assertEqual(gribdata.category, category) - self.assertEqual(gribdata.number, number) - self.assertEqual(gribdata.units, a_cf_unit) - - # These should work - check_cf_grib2("sea_surface_temperature", None, - 10, 3, 0, 'K') - check_cf_grib2("air_temperature", None, - 0, 0, 0, 'K') - check_cf_grib2("soil_temperature", None, - 2, 0, 2, "K") - check_cf_grib2("land_area_fraction", None, - 2, 0, 0, '1') - check_cf_grib2("land_binary_mask", None, - 2, 0, 0, '1') - check_cf_grib2("atmosphere_mass_content_of_water_vapor", None, - 0, 1, 64, "kg m-2") - check_cf_grib2("surface_altitude", None, - 2, 0, 7, "m") - - # These should fail - check_cf_grib2("air_temperature", "user_long_UNRECOGNISED", - 0, 0, 0, 'K') - check_cf_grib2("air_temperature_UNRECOGNISED", None, - 0, 0, 0, 'K', - expect_none=True) - check_cf_grib2(None, "user_long_UNRECOGNISED", - 0, 0, 0, 'K', - expect_none=True) - check_cf_grib2(None, "precipitable_water", - 0, 1, 3, 'kg m-2') - check_cf_grib2("invalid_unknown", "precipitable_water", - 0, 1, 3, 'kg m-2', - expect_none=True) - check_cf_grib2(None, None, 0, 0, 0, '', - expect_none=True) - - -if __name__ == '__main__': - tests.main() diff --git a/lib/iris/tests/unit/fileformats/grib/__init__.py b/lib/iris/tests/unit/fileformats/grib/__init__.py index c3e0e5a857..6e0dcadb8e 100644 --- a/lib/iris/tests/unit/fileformats/grib/__init__.py +++ b/lib/iris/tests/unit/fileformats/grib/__init__.py @@ -24,13 +24,12 @@ import iris.tests as tests import gribapi -import mock import numpy as np import iris - import iris.fileformats.grib from iris.fileformats.grib.message import GribMessage +from iris.tests import mock def _make_test_message(sections): @@ -206,30 +205,3 @@ def sorted_by_coordname(list): self.assertEqual( [type(coord) for coord, dims in coords_and_dims_got], [type(coord) for coord, dims in coords_and_dims_expected]) - - -class TestGribSimple(tests.IrisTest): - # A testing class that does not need the test data. - def mock_grib(self): - # A mock grib message, with attributes that can't be Mocks themselves. - grib = mock.Mock() - grib.startStep = 0 - grib.phenomenon_points = lambda unit: 3 - grib._forecastTimeUnit = "hours" - grib.productDefinitionTemplateNumber = 0 - # define a level type (NB these 2 are effectively the same) - grib.levelType = 1 - grib.typeOfFirstFixedSurface = 1 - grib.typeOfSecondFixedSurface = 1 - return grib - - def cube_from_message(self, grib): - # Parameter translation now uses the GribWrapper, so we must convert - # the Mock-based fake message to a FakeGribMessage. - with mock.patch('iris.fileformats.grib.gribapi', _mock_gribapi): - grib_message = FakeGribMessage(**grib.__dict__) - wrapped_msg = iris.fileformats.grib.GribWrapper(grib_message) - cube, _, _ = iris.fileformats.rules._make_cube( - wrapped_msg, - iris.fileformats.grib._grib1_load_rules.grib1_convert) - return cube diff --git a/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/test_grib1_convert.py b/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/test_grib1_convert.py index 4ef951c6eb..19ea6ab80e 100644 --- a/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/test_grib1_convert.py +++ b/lib/iris/tests/unit/fileformats/grib/grib1_load_rules/test_grib1_convert.py @@ -27,15 +27,14 @@ import cf_units import gribapi -import mock import iris from iris.exceptions import TranslationError -from iris.fileformats.rules import Reference - from iris.fileformats.grib import GribWrapper from iris.fileformats.grib._grib1_load_rules import grib1_convert +from iris.fileformats.rules import Reference from iris.tests.unit.fileformats.grib import TestField +from iris.tests import mock class TestBadEdition(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/test_grib_phenom_translation.py b/lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/test_grib_phenom_translation.py index 8ebdb21ca7..c4b9570e2d 100644 --- a/lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/test_grib_phenom_translation.py +++ b/lib/iris/tests/unit/fileformats/grib/grib_phenom_translation/test_grib_phenom_translation.py @@ -17,9 +17,6 @@ ''' Unit tests for the mod:`iris.fileformats.grib.grib_phenom_translation` module. -Carried over from old iris/tests/test_grib_phenom_translation.py. -Code is out of step with current test conventions and standards. - ''' from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py index a725c7ee6c..f7fa1b2896 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_bitmap_section.py @@ -27,7 +27,6 @@ import iris.tests as tests from iris.exceptions import TranslationError - from iris.fileformats.grib._load_convert import bitmap_section from iris.tests.unit.fileformats.grib import _make_test_message diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py index 948a3fddb0..a5cbc5e248 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_convert.py @@ -23,11 +23,9 @@ # before importing anything else. import iris.tests as tests -import mock - from iris.exceptions import TranslationError - from iris.fileformats.grib._load_convert import convert +from iris.tests import mock from iris.tests.unit.fileformats.grib import _make_test_message diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py index 23d928a485..e03624fa97 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py @@ -26,10 +26,9 @@ # before importing anything else. import iris.tests as tests -import mock - from iris.fileformats.grib._load_convert import _MDI as MDI from iris.fileformats.grib._load_convert import data_cutoff +from iris.tests import mock class TestDataCutoff(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py index b1addd985b..dde4be0f22 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ellipsoid.py @@ -30,7 +30,6 @@ import iris.coord_systems as icoord_systems from iris.exceptions import TranslationError - from iris.fileformats.grib._load_convert import ellipsoid diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py index 06b3a01204..cc31ccd638 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_ensemble_identifier.py @@ -28,12 +28,11 @@ import iris.tests as tests from copy import deepcopy -import mock import warnings from iris.coords import DimCoord - from iris.fileformats.grib._load_convert import ensemble_identifier +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py index 54b850ec58..866c03dbb4 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_forecast_period_coord.py @@ -27,7 +27,6 @@ import iris.tests as tests from iris.coords import DimCoord - from iris.fileformats.grib._load_convert import forecast_period_coord diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py index 1194e98056..d1590abe31 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grib2_convert.py @@ -24,10 +24,10 @@ import iris.tests as tests import copy -import mock import iris.fileformats.grib from iris.fileformats.grib._load_convert import grib2_convert +from iris.tests import mock from iris.tests.unit.fileformats.grib import _make_test_message diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py index 88c1840999..c22bc8d5b1 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_0_and_1.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2016 - 2017, Met Office +# (C) British Crown Copyright 2015 - 2017, Met Office # # This file is part of Iris. # @@ -28,7 +28,6 @@ import iris.tests as tests from iris.exceptions import TranslationError - from iris.fileformats.grib._load_convert \ import grid_definition_template_0_and_1 diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py index 1842df412d..4b59e3de44 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_12.py @@ -32,9 +32,8 @@ import iris.coord_systems import iris.coords import iris.exceptions - -from iris.tests.unit.fileformats.grib.load_convert import empty_metadata from iris.fileformats.grib._load_convert import grid_definition_template_12 +from iris.tests.unit.fileformats.grib.load_convert import empty_metadata MDI = 2 ** 32 - 1 diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py index 55ff28eb1b..1389a42ce3 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_20.py @@ -31,9 +31,8 @@ import iris.coord_systems import iris.coords - -from iris.tests.unit.fileformats.grib.load_convert import empty_metadata from iris.fileformats.grib._load_convert import grid_definition_template_20 +from iris.tests.unit.fileformats.grib.load_convert import empty_metadata MDI = 2 ** 32 - 1 diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py index 38b755c06d..8114fa8f68 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_30.py @@ -31,9 +31,8 @@ import iris.coord_systems import iris.coords - -from iris.tests.unit.fileformats.grib.load_convert import empty_metadata from iris.fileformats.grib._load_convert import grid_definition_template_30 +from iris.tests.unit.fileformats.grib.load_convert import empty_metadata MDI = 2 ** 32 - 1 diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py index 2142237577..c34756ed2c 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_40.py @@ -31,9 +31,8 @@ import iris.coord_systems import iris.coords - -from iris.tests.unit.fileformats.grib.load_convert import empty_metadata from iris.fileformats.grib._load_convert import grid_definition_template_40 +from iris.tests.unit.fileformats.grib.load_convert import empty_metadata MDI = 2 ** 32 - 1 diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py index 867706ed86..942ad07935 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py @@ -28,14 +28,14 @@ import iris.tests as tests from copy import deepcopy -import mock -import numpy as np import warnings -from iris.coords import DimCoord +import numpy as np +from iris.coords import DimCoord from iris.fileformats.grib._load_convert import \ grid_definition_template_4_and_5, _MDI as MDI +from iris.tests import mock RESOLUTION = 1e6 diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py index 488f998019..ae63540cbd 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_5.py @@ -28,9 +28,9 @@ import iris.tests as tests from copy import deepcopy -import mock from iris.fileformats.grib._load_convert import grid_definition_template_5 +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py index 518312f044..4501cd028b 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_90.py @@ -33,9 +33,8 @@ import iris.coord_systems import iris.coords import iris.exceptions - -from iris.tests.unit.fileformats.grib.load_convert import empty_metadata from iris.fileformats.grib._load_convert import grid_definition_template_90 +from iris.tests.unit.fileformats.grib.load_convert import empty_metadata MDI = 2 ** 32 - 1 diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py index fdef3b53b4..1ae013c410 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_other_time_coord.py @@ -27,7 +27,6 @@ import iris.tests as tests import iris.coords - from iris.fileformats.grib._load_convert import other_time_coord diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_0.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_0.py index a4de342aa5..ae40e215a6 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_0.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_0.py @@ -27,14 +27,12 @@ # before importing anything else. import iris.tests as tests -import mock - import iris.coords - import iris.fileformats.grib +from iris.fileformats.grib._load_convert import product_definition_template_0 +from iris.tests import mock from iris.tests.unit.fileformats.grib.load_convert import (LoadConvertTest, empty_metadata) -from iris.fileformats.grib._load_convert import product_definition_template_0 MDI = 0xffffffff diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py index da2ddea7da..012107f146 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_1.py @@ -28,12 +28,11 @@ import iris.tests as tests from copy import deepcopy -import mock import warnings from iris.coords import DimCoord - from iris.fileformats.grib._load_convert import product_definition_template_1 +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py index 8127d86f5f..95ce05d461 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_10.py @@ -28,10 +28,10 @@ import iris.tests as tests from copy import deepcopy -import mock from iris.coords import DimCoord from iris.fileformats.grib._load_convert import product_definition_template_10 +from iris.tests import mock from iris.tests.unit.fileformats.grib.load_convert import empty_metadata diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py index 3afa932c7f..beebb9632d 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_11.py @@ -28,12 +28,11 @@ import iris.tests as tests from copy import deepcopy -import mock import warnings from iris.coords import DimCoord, CellMethod - from iris.fileformats.grib._load_convert import product_definition_template_11 +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py index acd2d81392..fc0c117bfd 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_31.py @@ -27,13 +27,13 @@ import iris.tests as tests from copy import deepcopy -import mock -import numpy as np import warnings -from iris.coords import AuxCoord +import numpy as np +from iris.coords import AuxCoord from iris.fileformats.grib._load_convert import product_definition_template_31 +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py index 4b88679a90..c23b608649 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py @@ -28,7 +28,6 @@ import iris.tests as tests import iris.coords - from iris.fileformats.grib._load_convert import \ product_definition_template_40, _MDI from iris.tests.unit.fileformats.grib.load_convert import empty_metadata diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py index 7cb726d363..eeb203aed9 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_8.py @@ -28,9 +28,8 @@ # before importing anything else. import iris.tests as tests -import mock - from iris.fileformats.grib._load_convert import product_definition_template_8 +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py index 5ce01df967..a5ff6d8d8d 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py @@ -27,12 +27,10 @@ # before importing anything else. import iris.tests as tests -import mock - from iris.exceptions import TranslationError - from iris.fileformats.grib._load_convert import product_definition_template_9 from iris.fileformats.grib._load_convert import Probability, _MDI +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py index b76afb2ada..05e30c2a36 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_reference_time_coord.py @@ -35,7 +35,6 @@ from iris.coords import DimCoord from iris.exceptions import TranslationError - from iris.fileformats.grib._load_convert import reference_time_coord @@ -60,12 +59,12 @@ def _check(self, section, standard_name=None): coord = reference_time_coord(section) self.assertEqual(coord, expected) - def test_start_of_forecast(self): + def test_start_of_forecast0(self): section = deepcopy(self.section) section['significanceOfReferenceTime'] = 0 self._check(section, 'forecast_reference_time') - def test_start_of_forecast(self): + def test_start_of_forecast1(self): section = deepcopy(self.section) section['significanceOfReferenceTime'] = 1 self._check(section, 'forecast_reference_time') diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py index c0abec6b07..54724adb19 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_scanning_mode.py @@ -27,7 +27,6 @@ import iris.tests as tests from iris.exceptions import TranslationError - from iris.fileformats.grib._load_convert import scanning_mode, ScanningMode diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py index 47af476437..8ed2a26fc4 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_cell_method.py @@ -28,7 +28,6 @@ import iris.tests as tests from iris.exceptions import TranslationError - from iris.fileformats.grib._load_convert import statistical_cell_method diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py index 9a64557ec9..bc9e71a265 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_statistical_forecast_period_coord.py @@ -28,10 +28,10 @@ import iris.tests as tests import datetime -import mock from iris.fileformats.grib._load_convert import \ statistical_forecast_period_coord +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py index f35168d1e3..a708e68460 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_time_range_unit.py @@ -27,8 +27,8 @@ import iris.tests as tests from cf_units import Unit -from iris.exceptions import TranslationError +from iris.exceptions import TranslationError from iris.fileformats.grib._load_convert import time_range_unit diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py index 2a932b0488..8fd2e32911 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_translate_phenomenon.py @@ -30,8 +30,8 @@ from copy import deepcopy from cf_units import Unit -from iris.coords import DimCoord +from iris.coords import DimCoord from iris.fileformats.grib._load_convert import (Probability, translate_phenomenon) from iris.fileformats.grib.grib_phenom_translation import _GribToCfDataClass diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py index 920e8876f8..1e2de6af88 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_validity_time_coord.py @@ -27,12 +27,11 @@ import iris.tests as tests from cf_units import Unit -import mock import numpy as np from iris.coords import DimCoord - from iris.fileformats.grib._load_convert import validity_time_coord +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py index ddda21d9b1..0ed7fea41e 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py @@ -27,15 +27,14 @@ import iris.tests as tests from copy import deepcopy -import mock from iris.coords import DimCoord from iris.exceptions import TranslationError - from iris.fileformats.grib._load_convert import vertical_coords from iris.fileformats.grib._load_convert import \ _TYPE_OF_FIXED_SURFACE_MISSING as MISSING_SURFACE, \ _MDI as MISSING_LEVEL +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py b/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py index 66d2ba1cc9..d0dd419ff6 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test_GribMessage.py @@ -30,12 +30,11 @@ from abc import ABCMeta, abstractmethod import biggus -import mock import numpy as np from iris.exceptions import TranslationError - from iris.fileformats.grib.message import GribMessage +from iris.tests import mock from iris.tests.unit.fileformats.grib import _make_test_message @@ -212,30 +211,35 @@ def _example_section_3(grib_definition_template_number, scanning_mode): 'Ni': 4} +@tests.iristest_timing_decorator class Test_data__grid_template_0(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(0, scanning_mode) +@tests.iristest_timing_decorator class Test_data__grid_template_1(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(1, scanning_mode) +@tests.iristest_timing_decorator class Test_data__grid_template_5(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(5, scanning_mode) +@tests.iristest_timing_decorator class Test_data__grid_template_12(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(12, scanning_mode) +@tests.iristest_timing_decorator class Test_data__grid_template_30(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): @@ -248,12 +252,14 @@ def section_3(self, scanning_mode): return section_3 +@tests.iristest_timing_decorator class Test_data__grid_template_40_regular(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): return _example_section_3(40, scanning_mode) +@tests.iristest_timing_decorator class Test_data__grid_template_90(tests.IrisTest_nometa, Mixin_data__grid_template): def section_3(self, scanning_mode): diff --git a/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py b/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py index 11dee72796..4181dfc3fd 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test__DataProxy.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . """ -Unit tests for the `iris.message._DataProxy` class. +Unit tests for the `iris.fileformats.grib.message._DataProxy` class. """ @@ -30,7 +30,6 @@ from numpy.random import randint from iris.exceptions import TranslationError - from iris.fileformats.grib.message import _DataProxy diff --git a/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py b/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py index d1a71aeccd..05aff8c43d 100644 --- a/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py +++ b/lib/iris/tests/unit/fileformats/grib/message/test__MessageLocation.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . """ -Unit tests for the `iris.message._MessageLocation` class. +Unit tests for the `iris.fileformats.grib.message._MessageLocation` class. """ @@ -26,9 +26,8 @@ # importing anything else. import iris.tests as tests -import mock - from iris.fileformats.grib.message import _MessageLocation +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/__init__.py b/lib/iris/tests/unit/fileformats/grib/save_rules/__init__.py index 7457e0019e..5ede464eac 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/__init__.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/__init__.py @@ -23,11 +23,11 @@ # importing anything else. import iris.tests as tests -import mock import numpy as np import iris from iris.fileformats.pp import EARTH_RADIUS as PP_DEFAULT_EARTH_RADIUS +from iris.tests import mock class GdtTestMixin(object): diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py index fca6c7a21a..9bc193d26b 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test__missing_forecast_period.py @@ -27,12 +27,8 @@ # importing anything else. import iris.tests as tests -import datetime -import numpy as np - from iris.cube import Cube from iris.coords import DimCoord - from iris.fileformats.grib._save_rules import _missing_forecast_period diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py index abcc1fee46..4dd3d88b79 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test__non_missing_forecast_period.py @@ -26,7 +26,6 @@ import cf_units import iris - from iris.fileformats.grib._save_rules import _non_missing_forecast_period diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_10_and_11.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_10_and_11.py index c90dd0f4fe..c4a7b70466 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_10_and_11.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test__product_definition_template_8_10_and_11.py @@ -30,11 +30,10 @@ from cf_units import Unit import gribapi -import mock from iris.coords import CellMethod, DimCoord +from iris.tests import mock import iris.tests.stock as stock - from iris.fileformats.grib._save_rules import \ _product_definition_template_8_10_and_11 diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py index 7a178167dc..ff40af8a2a 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_data_section.py @@ -26,12 +26,11 @@ # importing anything else import iris.tests as tests -import mock import numpy as np import iris.cube - from iris.fileformats.grib._save_rules import data_section +from iris.tests import mock GRIB_API = 'iris.fileformats.grib._save_rules.gribapi' diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py index ab08e32daf..087dd897bd 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_section.py @@ -29,7 +29,6 @@ from iris.coord_systems import LambertConformal from iris.exceptions import TranslationError - from iris.fileformats.grib._save_rules import grid_definition_section from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py index a5cd47beaf..a4f581b78e 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_0.py @@ -30,7 +30,6 @@ import numpy as np from iris.coord_systems import GeogCS - from iris.fileformats.grib._save_rules import grid_definition_template_0 from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py index 2c482ec29d..f8cdf181d4 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_1.py @@ -31,9 +31,8 @@ from iris.coord_systems import GeogCS, RotatedGeogCS from iris.exceptions import TranslationError -from iris.fileformats.pp import EARTH_RADIUS as PP_DEFAULT_EARTH_RADIUS - from iris.fileformats.grib._save_rules import grid_definition_template_1 +from iris.fileformats.pp import EARTH_RADIUS as PP_DEFAULT_EARTH_RADIUS from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py index 756b00d5e2..fb59a1f9e0 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_12.py @@ -31,9 +31,8 @@ import iris.coords from iris.coord_systems import GeogCS, TransverseMercator -from iris.exceptions import TranslationError - from iris.fileformats.grib._save_rules import grid_definition_template_12 +from iris.exceptions import TranslationError from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py index 3e973386ea..5b55f1f4d7 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_grid_definition_template_5.py @@ -31,9 +31,8 @@ from iris.coord_systems import GeogCS, RotatedGeogCS from iris.exceptions import TranslationError -from iris.fileformats.pp import EARTH_RADIUS as PP_DEFAULT_EARTH_RADIUS - from iris.fileformats.grib._save_rules import grid_definition_template_5 +from iris.fileformats.pp import EARTH_RADIUS as PP_DEFAULT_EARTH_RADIUS from iris.tests.unit.fileformats.grib.save_rules import GdtTestMixin diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_identification.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_identification.py index ec4f967c05..695ec45639 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_identification.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_identification.py @@ -24,14 +24,12 @@ import iris.tests as tests import gribapi -import mock import iris -import iris.tests.stock as stock - from iris.fileformats.grib._save_rules import identification -from iris.tests.unit.fileformats.grib import TestGribSimple - +from iris.tests import mock +import iris.tests.stock as stock +from iris.tests.test_grib_load_translations import TestGribSimple GRIB_API = 'iris.fileformats.grib._save_rules.gribapi' diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py index 603d2a7928..114305d56d 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_1.py @@ -29,12 +29,11 @@ from cf_units import Unit import gribapi -import mock from iris.coords import CellMethod, DimCoord -import iris.tests.stock as stock - from iris.fileformats.grib._save_rules import product_definition_template_1 +from iris.tests import mock +import iris.tests.stock as stock class TestRealizationIdentifier(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py index a5636f174f..f2d0c2b84c 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_10.py @@ -29,12 +29,11 @@ from cf_units import Unit import gribapi -import mock from iris.coords import DimCoord -import iris.tests.stock as stock - from iris.fileformats.grib._save_rules import product_definition_template_10 +from iris.tests import mock +import iris.tests.stock as stock class TestPercentileValueIdentifier(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py index bbaa56ff7d..d40eeb9676 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_11.py @@ -29,12 +29,11 @@ from cf_units import Unit import gribapi -import mock from iris.coords import CellMethod, DimCoord -import iris.tests.stock as stock - from iris.fileformats.grib._save_rules import product_definition_template_11 +from iris.tests import mock +import iris.tests.stock as stock class TestRealizationIdentifier(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py index 927e6a95dc..9eb56b4133 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_40.py @@ -29,12 +29,11 @@ from cf_units import Unit import gribapi -import mock from iris.coords import DimCoord -import iris.tests.stock as stock - from iris.fileformats.grib._save_rules import product_definition_template_40 +from iris.tests import mock +import iris.tests.stock as stock class TestChemicalConstituentIdentifier(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py index f1d139be79..3fbe619489 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_product_definition_template_8.py @@ -29,12 +29,11 @@ from cf_units import Unit import gribapi -import mock from iris.coords import CellMethod, DimCoord -import iris.tests.stock as stock - from iris.fileformats.grib._save_rules import product_definition_template_8 +from iris.tests import mock +import iris.tests.stock as stock class TestProductDefinitionIdentifier(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py index afeebcecac..4b97a337bd 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_reference_time.py @@ -24,10 +24,10 @@ import iris.tests as tests import gribapi -import mock from iris.fileformats.grib import load_cubes from iris.fileformats.grib._save_rules import reference_time +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py index 759c14543d..f4f50b27a9 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_fixed_surfaces.py @@ -31,7 +31,6 @@ import iris.cube import iris.coords - from iris.fileformats.grib._save_rules import set_fixed_surfaces diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py index ae04e71755..bddf9930b6 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_increment.py @@ -27,11 +27,10 @@ import iris.tests as tests import gribapi -import mock from iris.coords import CellMethod - from iris.fileformats.grib._save_rules import set_time_increment +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py index 5856951339..d6ff83351e 100644 --- a/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py +++ b/lib/iris/tests/unit/fileformats/grib/save_rules/test_set_time_range.py @@ -31,11 +31,10 @@ from cf_units import Unit import gribapi -import mock from iris.coords import DimCoord - from iris.fileformats.grib._save_rules import set_time_range +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py b/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py index 04a7fb5c89..6f620e7d8f 100644 --- a/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py +++ b/lib/iris/tests/unit/fileformats/grib/test_GribWrapper.py @@ -27,12 +27,11 @@ import iris.tests as tests from biggus import NumpyArrayAdapter -import mock import numpy as np from iris.exceptions import TranslationError - from iris.fileformats.grib import GribWrapper, GribDataProxy +from iris.tests import mock _message_length = 1000 diff --git a/lib/iris/tests/unit/fileformats/grib/test__load_generate.py b/lib/iris/tests/unit/fileformats/grib/test__load_generate.py index 7552960ef9..dc0cdabb16 100644 --- a/lib/iris/tests/unit/fileformats/grib/test__load_generate.py +++ b/lib/iris/tests/unit/fileformats/grib/test__load_generate.py @@ -19,18 +19,18 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa +# Import iris.tests first so that some things can be initialised before +# importing anything else. import iris.tests as tests -import mock - import iris from iris.exceptions import TranslationError -from iris.fileformats.rules import Loader - import iris.fileformats.grib from iris.fileformats.grib import GribWrapper from iris.fileformats.grib import _load_generate from iris.fileformats.grib.message import GribMessage +from iris.fileformats.rules import Loader +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py b/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py index 9fd60f718c..659b57371d 100644 --- a/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py +++ b/lib/iris/tests/unit/fileformats/grib/test_load_cubes.py @@ -19,15 +19,15 @@ from __future__ import (absolute_import, division, print_function) from six.moves import (filter, input, map, range, zip) # noqa +# Import iris.tests first so that some things can be initialised before +# importing anything else. import iris.tests as tests -import mock - import iris -from iris.fileformats.rules import Loader - import iris.fileformats.grib from iris.fileformats.grib import load_cubes +from iris.fileformats.rules import Loader +from iris.tests import mock class Test(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/test_save_grib2.py b/lib/iris/tests/unit/fileformats/grib/test_save_grib2.py index a887f495cb..7572dbdd61 100644 --- a/lib/iris/tests/unit/fileformats/grib/test_save_grib2.py +++ b/lib/iris/tests/unit/fileformats/grib/test_save_grib2.py @@ -24,9 +24,8 @@ # importing anything else. import iris.tests as tests -import mock - import iris.fileformats.grib +from iris.tests import mock class TestSaveGrib2(tests.IrisTest): diff --git a/lib/iris/tests/unit/fileformats/grib/test_save_messages.py b/lib/iris/tests/unit/fileformats/grib/test_save_messages.py index a149f82fe2..0968b3fb3b 100644 --- a/lib/iris/tests/unit/fileformats/grib/test_save_messages.py +++ b/lib/iris/tests/unit/fileformats/grib/test_save_messages.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2016 - 2017, Met Office +# (C) British Crown Copyright 2015 - 2017, Met Office # # This file is part of Iris. # @@ -25,10 +25,10 @@ import iris.tests as tests import gribapi -import mock import numpy as np import iris.fileformats.grib +from iris.tests import mock class TestSaveMessages(tests.IrisTest): From ac617475eb354ce797ac52faf88d1317f723dafe Mon Sep 17 00:00:00 2001 From: marqh Date: Thu, 6 Apr 2017 08:33:17 +0000 Subject: [PATCH 08/11] adopt eccodes --- .travis.yml | 2 +- conda-requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 85a7cacca4..13f0622c3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,7 @@ install: conda install --quiet --file minimal-conda-requirements.txt; else if [[ "$TRAVIS_PYTHON_VERSION" == 3* ]]; then - sed -e '/ecmwf_grib/d' -e '/esmpy/d' -e '/iris-grib/d' -e 's/#.\+$//' conda-requirements.txt | xargs conda install --quiet; + sed -e '/esmpy/d' -e 's/#.\+$//' conda-requirements.txt | xargs conda install --quiet; else conda install --quiet --file conda-requirements.txt; fi diff --git a/conda-requirements.txt b/conda-requirements.txt index 30869af920..aa80d181bf 100644 --- a/conda-requirements.txt +++ b/conda-requirements.txt @@ -26,7 +26,7 @@ requests # Optional iris dependencies nc_time_axis -iris-grib +python-eccodes esmpy>=7.0 gdal libmo_unpack From a5ee0bc7446964e2ffde3c9ad8dc88beb66ba473 Mon Sep 17 00:00:00 2001 From: marqh Date: Thu, 6 Apr 2017 09:06:45 +0000 Subject: [PATCH 09/11] alter _MDI value --- lib/iris/fileformats/grib/_load_convert.py | 28 ++++++++++--------- lib/iris/tests/test_grib_save.py | 17 +++++------ .../grib/load_convert/test_data_cutoff.py | 4 ++- .../test_grid_definition_template_4_and_5.py | 3 +- .../test_product_definition_template_40.py | 8 ++++-- .../test_product_definition_template_9.py | 8 ++++-- .../grib/load_convert/test_unscale.py | 3 +- .../grib/load_convert/test_vertical_coords.py | 3 +- 8 files changed, 43 insertions(+), 31 deletions(-) diff --git a/lib/iris/fileformats/grib/_load_convert.py b/lib/iris/fileformats/grib/_load_convert.py index 892de22b22..d12e6dd168 100644 --- a/lib/iris/fileformats/grib/_load_convert.py +++ b/lib/iris/fileformats/grib/_load_convert.py @@ -159,7 +159,9 @@ def _unscale(v, f): if isinstance(value, Iterable) or isinstance(factor, Iterable): def _masker(item): - result = ma.masked_equal(item, _MDI) + mdis = [mdi for mdi in _MDIs] + result = ma.masked_equal(item, mdis[0]) + result = ma.masked_equal(result, mdis[1]) if ma.count_masked(result): # Circumvent downstream NumPy "RuntimeWarning" # of "overflow encountered in power" in _unscale @@ -173,13 +175,13 @@ def _masker(item): result = result.data else: result = ma.masked - if value != _MDI and factor != _MDI: + if value not in _MDIs and factor not in _MDIs: result = _unscale(value, factor) return result # Regulations 92.1.4 and 92.1.5. -_MDI = 2 ** 32 - 1 +_MDIs = set((2 ** 31 - 1, 2 ** 32 - 1)) # Note: # 1. Integer "on-disk" values (aka. coded keys) in GRIB messages: # - Are 8-, 16-, or 32-bit. @@ -624,10 +626,10 @@ def grid_definition_template_4_and_5(section, metadata, y_name, x_name, cs): basicAngleOfTheInitialProductDomain = section[key] subdivisionsOfBasicAngle = section['subdivisionsOfBasicAngle'] - if basicAngleOfTheInitialProductDomain in [0, _MDI]: + if basicAngleOfTheInitialProductDomain in [0] + [m for m in _MDIs]: basicAngleOfTheInitialProductDomain = 1. - if subdivisionsOfBasicAngle in [0, _MDI]: + if subdivisionsOfBasicAngle in [0] + [m for m in _MDIs]: subdivisionsOfBasicAngle = 1. / _GRID_ACCURACY_IN_DEGREES resolution = np.float64(basicAngleOfTheInitialProductDomain) @@ -1068,7 +1070,7 @@ def grid_definition_template_90(section, metadata): :class:`collections.OrderedDict` of metadata. """ - if section['Nr'] == _MDI: + if section['Nr'] in _MDIs: raise TranslationError('Unsupported orthographic grid.') elif section['Nr'] == 0: raise TranslationError('Unsupported zero height for space-view.') @@ -1350,7 +1352,7 @@ def hybrid_factories(section, metadata): units='Pa') metadata['aux_coords_and_dims'].append((coord, None)) # Create the sigma scalar coordinate. - offset += NV / 2 + offset += int(NV / 2) coord = AuxCoord(pv[offset], long_name='sigma') metadata['aux_coords_and_dims'].append((coord, None)) # Create the associated factory reference. @@ -1393,7 +1395,7 @@ def vertical_coords(section, metadata): if fixed_surface is None: if typeOfFirstFixedSurface != _TYPE_OF_FIXED_SURFACE_MISSING: - if scaledValueOfFirstFixedSurface == _MDI: + if scaledValueOfFirstFixedSurface in _MDIs: if options.warn_on_unsupported: msg = 'Unable to translate type of first fixed ' \ 'surface with missing scaled value.' @@ -1417,7 +1419,7 @@ def vertical_coords(section, metadata): key = 'scaledValueOfSecondFixedSurface' scaledValueOfSecondFixedSurface = section[key] - if scaledValueOfSecondFixedSurface == _MDI: + if scaledValueOfSecondFixedSurface in _MDIs: msg = 'Product definition section 4 has missing ' \ 'scaled value of second fixed surface' raise TranslationError(msg) @@ -1652,8 +1654,8 @@ def data_cutoff(hoursAfterDataCutoff, minutesAfterDataCutoff): Message section 4, octet 17. """ - if (hoursAfterDataCutoff != _MDI or - minutesAfterDataCutoff != _MDI): + if (hoursAfterDataCutoff not in _MDIs or + minutesAfterDataCutoff not in _MDIs): if options.warn_on_unsupported: warnings.warn('Unable to translate "hours and/or minutes ' 'after data cutoff".') @@ -1905,12 +1907,12 @@ def product_definition_template_9(section, metadata, frt_coord): if probability_typecode == 1: # Type is "above upper level". threshold_value = section['scaledValueOfUpperLimit'] - if threshold_value == _MDI: + if threshold_value in _MDIs: msg = 'Product definition section 4 has missing ' \ 'scaled value of upper limit' raise TranslationError(msg) threshold_scaling = section['scaleFactorOfUpperLimit'] - if threshold_scaling == _MDI: + if threshold_scaling in _MDIs: msg = 'Product definition section 4 has missing ' \ 'scale factor of upper limit' raise TranslationError(msg) diff --git a/lib/iris/tests/test_grib_save.py b/lib/iris/tests/test_grib_save.py index 53c14a169c..95df9c0dfc 100644 --- a/lib/iris/tests/test_grib_save.py +++ b/lib/iris/tests/test_grib_save.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2016, Met Office +# (C) British Crown Copyright 2010 - 2017, Met Office # # This file is part of Iris. # @@ -34,7 +34,8 @@ if tests.GRIB_AVAILABLE: import gribapi - + from iris.fileformats.grib._load_convert import _MDIs + _mdis = [mdi for mdi in _MDIs] @tests.skip_data @tests.skip_grib @@ -50,10 +51,10 @@ def test_latlon_forecast_plev(self): iris.save(cubes, temp_file_path) expect_diffs = {'totalLength': (4837, 4832), 'productionStatusOfProcessedData': (0, 255), - 'scaleFactorOfRadiusOfSphericalEarth': (4294967295, + 'scaleFactorOfRadiusOfSphericalEarth': (_mdis[1], 0), 'shapeOfTheEarth': (0, 1), - 'scaledValueOfRadiusOfSphericalEarth': (4294967295, + 'scaledValueOfRadiusOfSphericalEarth': (_mdis[1], 6367470), 'typeOfGeneratingProcess': (0, 255), 'generatingProcessIdentifier': (128, 255), @@ -70,10 +71,10 @@ def test_rotated_latlon(self): iris.save(cubes, temp_file_path) expect_diffs = {'totalLength': (648196, 648191), 'productionStatusOfProcessedData': (0, 255), - 'scaleFactorOfRadiusOfSphericalEarth': (4294967295, + 'scaleFactorOfRadiusOfSphericalEarth': (_mdis[1], 0), 'shapeOfTheEarth': (0, 1), - 'scaledValueOfRadiusOfSphericalEarth': (4294967295, + 'scaledValueOfRadiusOfSphericalEarth': (_mdis[1], 6367470), 'longitudeOfLastGridPoint': (392109982, 32106370), 'latitudeOfLastGridPoint': (19419996, 19419285), @@ -91,10 +92,10 @@ def test_time_mean(self): cubes = iris.load(source_grib) expect_diffs = {'totalLength': (21232, 21227), 'productionStatusOfProcessedData': (0, 255), - 'scaleFactorOfRadiusOfSphericalEarth': (4294967295, + 'scaleFactorOfRadiusOfSphericalEarth': (_mdis[1], 0), 'shapeOfTheEarth': (0, 1), - 'scaledValueOfRadiusOfSphericalEarth': (4294967295, + 'scaledValueOfRadiusOfSphericalEarth': (_mdis[1], 6367470), 'longitudeOfLastGridPoint': (356249908, 356249809), 'latitudeOfLastGridPoint': (-89999938, -89999944), diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py index e03624fa97..4090a12535 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_data_cutoff.py @@ -26,10 +26,12 @@ # before importing anything else. import iris.tests as tests -from iris.fileformats.grib._load_convert import _MDI as MDI +from iris.fileformats.grib._load_convert import _MDIs from iris.fileformats.grib._load_convert import data_cutoff from iris.tests import mock +MDI = [mdi for mdi in _MDIs][1] + class TestDataCutoff(tests.IrisTest): def _check(self, hours, minutes, request_warning, expect_warning=False): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py index 942ad07935..ac263721b9 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_grid_definition_template_4_and_5.py @@ -34,9 +34,10 @@ from iris.coords import DimCoord from iris.fileformats.grib._load_convert import \ - grid_definition_template_4_and_5, _MDI as MDI + grid_definition_template_4_and_5, _MDIs from iris.tests import mock +MDI = [mdi for mdi in _MDIs][1] RESOLUTION = 1e6 diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py index c23b608649..fa9805ac4a 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_40.py @@ -29,14 +29,16 @@ import iris.coords from iris.fileformats.grib._load_convert import \ - product_definition_template_40, _MDI + product_definition_template_40, _MDIs from iris.tests.unit.fileformats.grib.load_convert import empty_metadata +MDI = [mdi for mdi in _MDIs][1] + class Test(tests.IrisTest): def setUp(self): - self.section_4 = {'hoursAfterDataCutoff': _MDI, - 'minutesAfterDataCutoff': _MDI, + self.section_4 = {'hoursAfterDataCutoff': MDI, + 'minutesAfterDataCutoff': MDI, 'constituentType': 1, 'indicatorOfUnitOfTimeRange': 0, # minutes 'startStep': 360, diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py index a5ff6d8d8d..c00e527334 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_product_definition_template_9.py @@ -29,9 +29,11 @@ from iris.exceptions import TranslationError from iris.fileformats.grib._load_convert import product_definition_template_9 -from iris.fileformats.grib._load_convert import Probability, _MDI +from iris.fileformats.grib._load_convert import Probability, _MDIs from iris.tests import mock +MDI = [mdi for mdi in _MDIs][1] + class Test(tests.IrisTest): def setUp(self): @@ -69,14 +71,14 @@ def test_fail_bad_probability_type(self): self.section, self.metadata, self.frt_coord) def test_fail_bad_threshold_value(self): - self.section['scaledValueOfUpperLimit'] = _MDI + self.section['scaledValueOfUpperLimit'] = MDI with self.assertRaisesRegexp(TranslationError, 'missing scaled value'): product_definition_template_9( self.section, self.metadata, self.frt_coord) def test_fail_bad_threshold_scalefactor(self): - self.section['scaleFactorOfUpperLimit'] = _MDI + self.section['scaleFactorOfUpperLimit'] = MDI with self.assertRaisesRegexp(TranslationError, 'missing scale factor'): product_definition_template_9( diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py index 18a514ead9..c91bd5454d 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py @@ -29,10 +29,11 @@ import numpy as np import numpy.ma as ma -from iris.fileformats.grib._load_convert import unscale, _MDI as MDI +from iris.fileformats.grib._load_convert import unscale, _MDIs # Reference GRIB2 Regulation 92.1.12. +MDI = [mdi for mdi in _MDIs][1] class Test(tests.IrisTest): def test_single(self): diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py index 0ed7fea41e..060011a245 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py @@ -33,9 +33,10 @@ from iris.fileformats.grib._load_convert import vertical_coords from iris.fileformats.grib._load_convert import \ _TYPE_OF_FIXED_SURFACE_MISSING as MISSING_SURFACE, \ - _MDI as MISSING_LEVEL + _MDIs from iris.tests import mock +MISSING_LEVEL = [mdi for mdi in _MDIs][1] class Test(tests.IrisTest): def setUp(self): From 8aa78d72c1983c0b66361cfd6f669f2dc91d0602 Mon Sep 17 00:00:00 2001 From: marqh Date: Thu, 6 Apr 2017 11:53:29 +0000 Subject: [PATCH 10/11] re-establish grib skipper --- lib/iris/tests/integration/test_grib_load.py | 2 ++ .../unit/fileformats/grib/load_convert/test_vertical_coords.py | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/iris/tests/integration/test_grib_load.py b/lib/iris/tests/integration/test_grib_load.py index a66c54a762..e5b6ac2d50 100644 --- a/lib/iris/tests/integration/test_grib_load.py +++ b/lib/iris/tests/integration/test_grib_load.py @@ -56,6 +56,8 @@ skip_irisgrib_fails = skipIf(iris_internal_grib_module is None, 'Test(s) are not currently ussable with ' '"iris_grib".') +skip_irisgrib_fails = skipIf(True, 'Test(s) are not currently ussable with ' + '"iris_grib".') @tests.skip_data diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py index 060011a245..dd82dbfd65 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_vertical_coords.py @@ -38,6 +38,7 @@ MISSING_LEVEL = [mdi for mdi in _MDIs][1] + class Test(tests.IrisTest): def setUp(self): self.metadata = {'factories': [], 'references': [], From 8c2a6274417e6a3f3532d6b5d9161d6d3aba9a30 Mon Sep 17 00:00:00 2001 From: marqh Date: Thu, 6 Apr 2017 12:10:32 +0000 Subject: [PATCH 11/11] code standard --- lib/iris/tests/integration/test_grib_load.py | 2 +- .../tests/unit/fileformats/grib/load_convert/test_unscale.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/iris/tests/integration/test_grib_load.py b/lib/iris/tests/integration/test_grib_load.py index e5b6ac2d50..bae290cde4 100644 --- a/lib/iris/tests/integration/test_grib_load.py +++ b/lib/iris/tests/integration/test_grib_load.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2016, Met Office +# (C) British Crown Copyright 2010 - 2017, Met Office # # This file is part of Iris. # diff --git a/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py b/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py index c91bd5454d..c0005a2410 100644 --- a/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py +++ b/lib/iris/tests/unit/fileformats/grib/load_convert/test_unscale.py @@ -32,9 +32,9 @@ from iris.fileformats.grib._load_convert import unscale, _MDIs # Reference GRIB2 Regulation 92.1.12. - MDI = [mdi for mdi in _MDIs][1] + class Test(tests.IrisTest): def test_single(self): self.assertEqual(unscale(123, 1), 12.3)