Repository: opendatacube/datacube-core

Issue:
Extra files in source distribution packaging
`python setup.py sdist` builds source distribution with docs and tests and bunch of random files in it, that should not be included. Strangely `bdist_wheel` does not include those and generates proper release file.

https://github.com/conda-forge/datacube-feedstock/issues/25


Discussion:

Resolving patch:
diff --git a/datacube/utils/xarray_geoextensions.py b/datacube/utils/xarray_geoextensions.py
--- a/datacube/utils/xarray_geoextensions.py
+++ b/datacube/utils/xarray_geoextensions.py
@@ -9,9 +9,8 @@
 `.geobox`, `.affine` and `.extent` respectively.
 
 """
-
+import warnings
 import xarray
-
 from datacube.utils import geometry, spatial_dims
 from datacube.utils.math import affine_from_axis
 
@@ -27,34 +26,48 @@ def _norm_crs(crs):
 
 def _get_crs_from_attrs(obj, sdims):
     """ Looks for attribute named `crs` containing CRS string
-        1. Checks spatials coords attrs
-        2. Checks data variable attrs
-        3. Checks dataset attrs
+        - Checks spatials coords attrs
+        - Checks data variable attrs
+        - Checks dataset attrs
 
         Returns
         =======
         Content for `.attrs[crs]` usually it's a string
         None if not present in any of the places listed above
     """
-    if isinstance(obj, xarray.Dataset):
-        if len(obj.data_vars) > 0:
-            data_array = next(iter(obj.data_vars.values()))
+    crs_set = set()
+
+    def _add_candidate(crs):
+        if crs is None:
+            return
+        if isinstance(crs, str):
+            crs_set.add(crs)
         else:
-            # fall back option
-            return obj.attrs.get('crs', None)
+            warnings.warn(f"Ignoring crs attribute of type: {type(crs)}")
+
+    def process_attrs(attrs):
+        _add_candidate(attrs.get('crs', None))
+        _add_candidate(attrs.get('crs_wkt', None))
+
+    def process_datavar(x):
+        process_attrs(x.attrs)
+        for dim in sdims:
+            if dim in x.coords:
+                process_attrs(x.coords[dim].attrs)
+
+    if isinstance(obj, xarray.Dataset):
+        process_attrs(obj.attrs)
+        for dv in obj.data_vars.values():
+            process_datavar(dv)
     else:
-        data_array = obj
+        process_datavar(obj)
 
-    crs_set = set(data_array[d].attrs.get('crs', None) for d in sdims)
     crs = None
     if len(crs_set) > 1:
         raise ValueError('Spatial dimensions have different crs.')
     elif len(crs_set) == 1:
         crs = crs_set.pop()
 
-    if crs is None:
-        # fall back option
-        crs = data_array.attrs.get('crs', None) or obj.attrs.get('crs', None)
     return crs
 
 
@@ -148,7 +161,8 @@ def _xarray_geobox(obj):
 
     try:
         crs = _norm_crs(crs)
-    except ValueError:
+    except (ValueError, geometry.CRSError):
+        warnings.warn(f"Encountered malformed CRS: {crs}")
         return None
 
     h, w = (obj.coords[dim].size for dim in sdims)


Test patch:
diff --git a/tests/test_xarray_extension.py b/tests/test_xarray_extension.py
--- a/tests/test_xarray_extension.py
+++ b/tests/test_xarray_extension.py
@@ -75,10 +75,15 @@ def test_xr_geobox_unhappy():
     xx.y.attrs.update(crs='EPSG:3857')
     assert xx.band.geobox is None
 
-    # test _norm_crs exception is caught
+    # test crs not being a string
     xx = mk_sample_xr_dataset(crs=None)
     xx.attrs['crs'] = ['this will not parse']
-    assert xx.geobox is None
+    with pytest.warns(UserWarning):
+        assert xx.geobox is None
+
+    xx.attrs['crs'] = 'this will fail CRS() constructor'
+    with pytest.warns(UserWarning):
+        assert xx.geobox is None
 
 
 def test_crs_from_coord():
@@ -165,3 +170,8 @@ def test_assign_crs(odc_style_xr_dataset):
 
     with pytest.raises(ValueError):
         assign_crs(xx_nocrs)
+
+
+def test_xr_opendataset(example_netcdf_path):
+    xx = xr.open_dataset(example_netcdf_path)
+    assert xx.geobox is None