Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions lib/iris/fileformats/_pyke_rules/fc_rules_cf.krb
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@ fc_extras
import iris.coord_systems
import iris.fileformats.cf as cf
import iris.fileformats.netcdf
from iris.fileformats.netcdf import parse_cell_methods, UnknownCellMethodWarning
import iris.fileformats.pp as pp
import iris.exceptions
import iris.std_names
Expand Down Expand Up @@ -1024,8 +1025,18 @@ fc_extras

# Incorporate cell methods
nc_att_cell_methods = getattr(cf_var, CF_ATTR_CELL_METHODS, None)
cube.cell_methods = iris.fileformats.netcdf.parse_cell_methods(
cf_var.cf_name, nc_att_cell_methods)
with warnings.catch_warnings(record=True) as warning_records:
cube.cell_methods = parse_cell_methods(nc_att_cell_methods)
# Filter to get the warning we are interested in.
warning_records = [record for record in warning_records
if issubclass(record.category, UnknownCellMethodWarning)]
if len(warning_records) > 0:
# Output an enhanced warning message.
warn_record = warning_records[0]
name = '{}'.format(cf_var.cf_name)
msg = warn_record.message.args[0]
msg = msg.replace('variable', 'variable {!r}'.format(name))
warnings.warn(message=msg, category=UnknownCellMethodWarning)

# Set the cube global attributes.
for attr_name, attr_value in six.iteritems(cf_var.cf_group.global_attributes):
Expand Down
26 changes: 17 additions & 9 deletions lib/iris/fileformats/netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,20 +158,29 @@
''', re.VERBOSE)


def parse_cell_methods(cf_var_name, nc_cell_methods):
class UnknownCellMethodWarning(Warning):
pass


def parse_cell_methods(nc_cell_methods):
"""
Parse a CF cell_methods attribute string into a tuple of zero or
more CellMethod instances.

Args:

* cf_var_name (str):
The name of the netCDF variable that contains this cell methods
attribute.

* nc_cell_methods (str):
The value of the cell methods attribute to be parsed.

Returns:

* cell_methods
An iterable of :class:`iris.coords.CellMethod`.

Multiple coordinates, intervals and comments are supported.
If a method has a non-standard name a warning will be issued, but the
results are not affected.

"""

cell_methods = []
Expand All @@ -184,10 +193,9 @@ def parse_cell_methods(cf_var_name, nc_cell_methods):
# e.g. mean over years.
method_words = method.split()
if method_words[0].lower() not in _CM_KNOWN_METHODS:
msg = 'NetCDF variable {!r} contains unknown cell ' \
'method {!r}'
warnings.warn(msg.format('{}'.format(cf_var_name),
'{}'.format(method_words[0])))
msg = 'NetCDF variable contains unknown cell method {!r}'
warnings.warn(msg.format('{}'.format(method_words[0])),
UnknownCellMethodWarning)
d[_CM_METHOD] = method
name = d[_CM_NAME]
name = name.replace(' ', '')
Expand Down
31 changes: 31 additions & 0 deletions lib/iris/tests/integration/test_netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,19 @@
import iris.tests as tests

from contextlib import contextmanager
import os.path
import shutil
import tempfile
import warnings

import numpy as np

import iris
from iris.coords import CellMethod
from iris.cube import Cube, CubeList
from iris.fileformats.netcdf import CF_CONVENTIONS_VERSION
from iris.fileformats.netcdf import Saver
from iris.fileformats.netcdf import UnknownCellMethodWarning
from iris.tests import mock
import iris.tests.stock as stock

Expand Down Expand Up @@ -270,5 +277,29 @@ def test_print(self):
' - - '
' x x') in printed)


class TestCellMethod_unknown(tests.IrisTest):
def test_unknown_method(self):
cube = Cube([1, 2], long_name='odd_phenomenon')
cube.add_cell_method(CellMethod(method='oddity', coords=('x',)))
temp_dirpath = tempfile.mkdtemp()
try:
temp_filepath = os.path.join(temp_dirpath, 'tmp.nc')
iris.save(cube, temp_filepath)
with warnings.catch_warnings(record=True) as warning_records:
iris.load(temp_filepath)
# Filter to get the warning we are interested in.
warning_messages = [record.message for record in warning_records]
warning_messages = [warn for warn in warning_messages
if isinstance(warn, UnknownCellMethodWarning)]
self.assertEqual(len(warning_messages), 1)
message = warning_messages[0].args[0]
msg = ("NetCDF variable 'odd_phenomenon' contains unknown cell "
"method 'oddity'")
self.assertIn(msg, message)
finally:
shutil.rmtree(temp_dirpath)


if __name__ == "__main__":
tests.main()
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_simple(self):
]
expected = (CellMethod(method='mean', coords='time'),)
for cell_method_str in cell_method_strings:
res = parse_cell_methods('test_var', cell_method_str)
res = parse_cell_methods(cell_method_str)
self.assertEqual(res, expected)

def test_with_interval(self):
Expand All @@ -50,7 +50,7 @@ def test_with_interval(self):
expected = (CellMethod(method='variance', coords='time',
intervals='1 hr'),)
for cell_method_str in cell_method_strings:
res = parse_cell_methods('test_var', cell_method_str)
res = parse_cell_methods(cell_method_str)
self.assertEqual(res, expected)

def test_multiple(self):
Expand All @@ -65,7 +65,7 @@ def test_multiple(self):
CellMethod(method='mean', coords='time',
intervals='1 day'))
for cell_method_str in cell_method_strings:
res = parse_cell_methods('test_var', cell_method_str)
res = parse_cell_methods(cell_method_str)
self.assertEqual(res, expected)

def test_comment(self):
Expand All @@ -84,7 +84,7 @@ def test_comment(self):
CellMethod(method='mean', coords='time',
intervals='1 day', comments='second bit'))
for cell_method_str in cell_method_strings:
res = parse_cell_methods('test_var', cell_method_str)
res = parse_cell_methods(cell_method_str)
self.assertEqual(res, expected)

def test_portions_of_cells(self):
Expand All @@ -95,7 +95,7 @@ def test_portions_of_cells(self):
expected = (CellMethod(method='mean where sea_ice over sea',
coords='area'),)
for cell_method_str in cell_method_strings:
res = parse_cell_methods('test_var', cell_method_str)
res = parse_cell_methods(cell_method_str)
self.assertEqual(res, expected)

def test_climatology(self):
Expand All @@ -108,7 +108,7 @@ def test_climatology(self):
expected = (CellMethod(method='minimum within days', coords='time'),
CellMethod(method='mean over days', coords='time'))
for cell_method_str in cell_method_strings:
res = parse_cell_methods('test_var', cell_method_str)
res = parse_cell_methods(cell_method_str)
self.assertEqual(res, expected)

def test_climatology_with_unknown_method(self):
Expand All @@ -122,9 +122,8 @@ def test_climatology_with_unknown_method(self):
CellMethod(method='mean over days', coords='time'))
for cell_method_str in cell_method_strings:
with mock.patch('warnings.warn') as warn:
res = parse_cell_methods('test_var', cell_method_str)
self.assertIn("NetCDF variable 'test_var' contains unknown "
"cell method 'min'",
res = parse_cell_methods(cell_method_str)
self.assertIn("NetCDF variable contains unknown cell method 'min'",
warn.call_args[0][0])
self.assertEqual(res, expected)

Expand Down