Repository: ioos/compliance-checker
Pull request #655 (2019-05-27 07:53:08)

Small fix & update to CF check_duplicate_axes
This started out as a fix to a small bug, but also includes two other minor tweaks:
* ba051b7 is the bug fix - `check_duplicate_axis` was failing incorrectly when the name of one variable was  a substring of another (e.g. if a dataset has both "DEPTH" and "NOMINAL_DEPTH" with axis='Z', and "TEMP" defined only "NOMINAL_DEPTH" as an auxiliary coordinate variable, `get_axis_map` would still pick up both depth vars, leading to an apparent duplicate Z axis).
* 4aaa5b6 is a suggestion - I assume if a varaible actually has an axis attribute defined in a meaningful way, there's no need for `get_axis_map` to try and guess what the axis is??
* ba051b7 is also a suggestion - I think this makes the error message more helpful.

I hope this makes sense. Feel free to reject either of the last two commits, but I hope at least the first one can go in.


Patch:
diff --git a/compliance_checker/cf/cf.py b/compliance_checker/cf/cf.py
index efe2c62..5a2435f 100644
--- a/compliance_checker/cf/cf.py
+++ b/compliance_checker/cf/cf.py
@@ -2155,15 +2155,13 @@ class CFBaseCheck(BaseCheck):
             # For every coordinate associated with this variable, keep track of
             # which coordinates define an axis and assert that there are no
             # duplicate axis attributes defined in the set of associated
-            # coordinates.
-            for axis, coordinates in axis_map.items():
-                for coordinate in coordinates:
-                    axis_attr = getattr(ds.variables[coordinate], 'axis', None)
-                    no_duplicates.assert_true(axis_attr is None or axis_attr not in axes,
-                                              "'{}' has duplicate axis {} defined by {}".format(name, axis_attr, coordinate))
-
-                    if axis_attr and axis_attr not in axes:
-                        axes.append(axis_attr)
+            # coordinates. axis_map includes coordinates that don't actually have
+            # an axis attribute, so we need to ignore those here.
+            for axis, coords in axis_map.items():
+                coords = [c for c in coords if hasattr(ds.variables[c], 'axis')]
+                no_duplicates.assert_true(len(coords) <= 1,
+                                          "'{}' has duplicate axis {} defined by [{}]".format(name, axis, ', '.join(sorted(coords)))
+                                          )
 
             ret_val.append(no_duplicates.to_result())
 
diff --git a/compliance_checker/cfutil.py b/compliance_checker/cfutil.py
index 2420def..f4a23e5 100644
--- a/compliance_checker/cfutil.py
+++ b/compliance_checker/cfutil.py
@@ -733,24 +733,28 @@ def get_axis_map(ds, variable):
 
     coordinates = getattr(ds.variables[variable], "coordinates", None)
     if not isinstance(coordinates, basestring):
-        coordinates = ''
+        coordinates = []
+    else:
+        coordinates = coordinates.split(' ')
 
     # For example
     # {'x': ['longitude'], 'y': ['latitude'], 't': ['time']}
     axis_map = defaultdict(list)
     for coord_name in all_coords:
-        if is_compression_coordinate(ds, coord_name):
-            axis = 'C'
-        elif coord_name in times:
-            axis = 'T'
-        elif coord_name in longitudes:
-            axis = 'X'
-        elif coord_name in latitudes:
-            axis = 'Y'
-        elif coord_name in heights:
-            axis = 'Z'
-        else:
-            axis = 'U'
+        axis = getattr(ds.variables[coord_name], 'axis', None)
+        if not axis or axis not in ('X', 'Y', 'Z', 'T'):
+            if is_compression_coordinate(ds, coord_name):
+                axis = 'C'
+            elif coord_name in times:
+                axis = 'T'
+            elif coord_name in longitudes:
+                axis = 'X'
+            elif coord_name in latitudes:
+                axis = 'Y'
+            elif coord_name in heights:
+                axis = 'Z'
+            else:
+                axis = 'U'
 
         if coord_name in ds.variables[variable].dimensions:
             if coord_name not in axis_map[axis]:


Test patch:
diff --git a/compliance_checker/tests/test_cf.py b/compliance_checker/tests/test_cf.py
index 9864312..cdc4b25 100644
--- a/compliance_checker/tests/test_cf.py
+++ b/compliance_checker/tests/test_cf.py
@@ -962,7 +962,7 @@ class TestCF(BaseTestCase):
 
         # only one check run here, so we can directly compare all the values
         assert scored != out_of
-        assert messages[0] == u"'temp' has duplicate axis X defined by lon_rho"
+        assert messages[0] == u"'temp' has duplicate axis X defined by [lon_rho, lon_u]"
 
     def test_check_multi_dimensional_coords(self):
         '''