From 170ba354822e575e9b43de6582853eab9b344cf1 Mon Sep 17 00:00:00 2001 From: Eduardo Gonzalez Date: Wed, 13 Nov 2019 22:42:56 +0000 Subject: [PATCH] Allow nested dictionaries in the Zarr backend (#3517) --- doc/whats-new.rst | 2 ++ xarray/backends/api.py | 7 +++++-- xarray/tests/test_backends.py | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index ea3b012cc98..4a3d3ba2afc 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -96,6 +96,8 @@ Bug fixes (:issue:`3402`). By `Deepak Cherian `_ - Allow appending datetime and bool data variables to zarr stores. (:issue:`3480`). By `Akihiro Matsukawa `_. +- Allow nested dictionaries in zarr attributes. + (:issue:`3517`). By `Eduardo Gonzalez `_. Documentation ~~~~~~~~~~~~~ diff --git a/xarray/backends/api.py b/xarray/backends/api.py index 945b3937c43..b4e0444dbe8 100644 --- a/xarray/backends/api.py +++ b/xarray/backends/api.py @@ -179,7 +179,7 @@ def check_name(name): check_name(k) -def _validate_attrs(dataset): +def _validate_attrs(dataset, backend="netCDF"): """`attrs` must have a string key and a value which is either: a number, a string, an ndarray or a list/tuple of numbers/strings. """ @@ -198,6 +198,9 @@ def check_attr(name, value): "serialization to netCDF files".format(name) ) + if isinstance(value, dict) and ("zarr" in backend): + value = [value] + if not isinstance(value, (str, Number, np.ndarray, np.number, list, tuple)): raise TypeError( "Invalid value for attr: {} must be a number, " @@ -1299,7 +1302,7 @@ def to_zarr( # validate Dataset keys, DataArray names, and attr keys/values _validate_dataset_names(dataset) - _validate_attrs(dataset) + _validate_attrs(dataset, backend='zarr') if mode == "a": _validate_datatypes_for_zarr_append(dataset) diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index de3a7eadab0..8029297a5c4 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -1754,6 +1754,13 @@ def test_encoding_kwarg_fixed_width_string(self): def test_dataset_caching(self): super().test_dataset_caching() + def test_nested_dictionary(self): + original = create_test_data() + original.attrs["foo"] = {"bar": 1} + with self.create_zarr_target() as store_target: + original.to_zarr(store_target, mode="w") + assert_identical(original, xr.open_zarr(store_target)) + def test_append_write(self): ds, ds_to_append, _ = create_append_test_data() with self.create_zarr_target() as store_target: