From c472f8a4c79f872edb9dcd7825f786ecb9aff5c0 Mon Sep 17 00:00:00 2001 From: ellesmith88 <40183561+ellesmith88@users.noreply.github.com> Date: Thu, 1 Jul 2021 16:50:33 +0100 Subject: [PATCH] Allow user to explicitly disable coordinates attribute (#5514) --- doc/whats-new.rst | 3 +++ xarray/conventions.py | 12 +++++++++++ xarray/tests/test_conventions.py | 34 ++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 57ec853915b..daddc91b73f 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -48,6 +48,9 @@ New Features By `Justus Magin `_. - Allow plotting categorical data (:pull:`5464`). By `Jimmy Westling `_. +- Allow removal of the coordinate attribute ``coordinates`` on variables by setting ``.attrs['coordinates']= None`` + (:issue:`5510`). + By `Elle Smith `_. Breaking changes ~~~~~~~~~~~~~~~~ diff --git a/xarray/conventions.py b/xarray/conventions.py index 901d19bd99b..c3a05e42f82 100644 --- a/xarray/conventions.py +++ b/xarray/conventions.py @@ -751,6 +751,18 @@ def _encode_coordinates(variables, attributes, non_dim_coord_names): f"'coordinates' found in both attrs and encoding for variable {name!r}." ) + # if coordinates set to None, don't write coordinates attribute + if ( + "coordinates" in attrs + and attrs.get("coordinates") is None + or "coordinates" in encoding + and encoding.get("coordinates") is None + ): + # make sure "coordinates" is removed from attrs/encoding + attrs.pop("coordinates", None) + encoding.pop("coordinates", None) + continue + # this will copy coordinates from encoding to attrs if "coordinates" in attrs # after the next line, "coordinates" is never in encoding # we get support for attrs["coordinates"] for free. diff --git a/xarray/tests/test_conventions.py b/xarray/tests/test_conventions.py index cd8e3419231..ceea167719f 100644 --- a/xarray/tests/test_conventions.py +++ b/xarray/tests/test_conventions.py @@ -142,6 +142,40 @@ def test_do_not_overwrite_user_coordinates(self): with pytest.raises(ValueError, match=r"'coordinates' found in both attrs"): conventions.encode_dataset_coordinates(orig) + def test_emit_coordinates_attribute_in_attrs(self): + orig = Dataset( + {"a": 1, "b": 1}, + coords={"t": np.array("2004-11-01T00:00:00", dtype=np.datetime64)}, + ) + + orig["a"].attrs["coordinates"] = None + enc, _ = conventions.encode_dataset_coordinates(orig) + + # check coordinate attribute emitted for 'a' + assert "coordinates" not in enc["a"].attrs + assert "coordinates" not in enc["a"].encoding + + # check coordinate attribute not emitted for 'b' + assert enc["b"].attrs.get("coordinates") == "t" + assert "coordinates" not in enc["b"].encoding + + def test_emit_coordinates_attribute_in_encoding(self): + orig = Dataset( + {"a": 1, "b": 1}, + coords={"t": np.array("2004-11-01T00:00:00", dtype=np.datetime64)}, + ) + + orig["a"].encoding["coordinates"] = None + enc, _ = conventions.encode_dataset_coordinates(orig) + + # check coordinate attribute emitted for 'a' + assert "coordinates" not in enc["a"].attrs + assert "coordinates" not in enc["a"].encoding + + # check coordinate attribute not emitted for 'b' + assert enc["b"].attrs.get("coordinates") == "t" + assert "coordinates" not in enc["b"].encoding + @requires_dask def test_string_object_warning(self): original = Variable(("x",), np.array(["foo", "bar"], dtype=object)).chunk()