Repository: ecmwf/earthkit-data (python)

Issue:
Crash when trying to parse netcdf file for fields
### What happened?

When we get the following data from cds:

```python 
import earthkit.data
lc_data = earthkit.data.from_source(
    "cds",
    'satellite-land-cover',
   {
     'year': '2022',
     'version': 'v2.1.1',
     'variable': 'all',
     'format': 'zip',
   }
)
```
earthkit-data crashes during parsing the fields in the NetCDF file the zip file contains.

```
File ~/git/earthkit-data/earthkit/data/readers/netcdf.py:169, in get_fields_from_ds(ds, array_backend, field_type, check_only)
    167     v = ds[name]
    168     skip.update(getattr(v, "coordinates", "").split(" "))
--> 169     skip.update(getattr(v, "bounds", "").split(" "))
    170     skip.update(getattr(v, "grid_mapping", "").split(" "))
    172 for name in ds.data_vars:

File /opt/homebrew/Caskroom/miniforge/base/envs/dev_ecc/lib/python3.10/site-packages/xarray/core/common.py:278, in AttrAccessMixin.__getattr__(self, name)
    276         with suppress(KeyError):
    277             return source[name]
--> 278 raise AttributeError(
    279     f"{type(self).__name__!r} object has no attribute {name!r}"
    280 )

AttributeError: 'DataArray' object has no attribute 'split'
```

### What are the steps to reproduce the bug?

See above.

### Version

<-0.5.5

### Platform (OS and architecture)

all

### Relevant log output

_No response_

### Accompanying data

_No response_

### Organisation

ECMWF

Pull request:
Avoid crash in netcdf field parsing when bounds is not a str
Fixes #337 

Patch:
diff --git a/earthkit/data/readers/netcdf.py b/earthkit/data/readers/netcdf.py
index d847d68..64f18be 100644
--- a/earthkit/data/readers/netcdf.py
+++ b/earthkit/data/readers/netcdf.py
@@ -162,11 +162,16 @@ def get_fields_from_ds(
 
     skip = set()
 
+    def _skip_attr(v, attr_name):
+        attr_val = getattr(v, attr_name, "")
+        if isinstance(attr_val, str):
+            skip.update(attr_val.split(" "))
+
     for name in ds.data_vars:
         v = ds[name]
-        skip.update(getattr(v, "coordinates", "").split(" "))
-        skip.update(getattr(v, "bounds", "").split(" "))
-        skip.update(getattr(v, "grid_mapping", "").split(" "))
+        _skip_attr(v, "coordinates")
+        _skip_attr(v, "bounds")
+        _skip_attr(v, "grid_mapping")
 
     for name in ds.data_vars:
         if name in skip:


Test patch:
diff --git a/tests/netcdf/test_netcdf_fieldlist.py b/tests/netcdf/test_netcdf_fieldlist.py
index fe15119..f26e1a0 100644
--- a/tests/netcdf/test_netcdf_fieldlist.py
+++ b/tests/netcdf/test_netcdf_fieldlist.py
@@ -38,6 +38,25 @@ def test_netcdf_fieldlist_string_coord():
     assert np.allclose(ds[0].to_points()["y"], y_ref)
 
 
+def test_netcdf_fieldlist_bounds():
+    """Check if having non string values in bounds does not cause a crash"""
+    import xarray as xr
+
+    a = xr.DataArray(
+        [
+            [[11, 12, 13], [21, 22, 23], [31, 32, 33]],
+            [[14, 15, 16], [24, 25, 26], [34, 35, 36]],
+        ],
+        coords={"level": [0, 0], "x": [1, 2, 3], "y": [4, 5, 6]},
+        attrs={"bounds": [1, 2, 3]},
+        name="dummyvar",
+    )
+
+    ds = earthkit.data.from_object(a)
+    assert ds
+    assert len(ds) == 2
+
+
 if __name__ == "__main__":
     from earthkit.data.testing import main