Repository: pydata/xarray

Issue:
⚠️ Nightly upstream-dev CI failed ⚠️
[Workflow Run URL](https://github.com/pydata/xarray/actions/runs/1764208897)
<details><summary>Python 3.10 Test Summary Info</summary>

```
FAILED xarray/tests/test_backends.py::TestH5NetCDFData::test_append_write - V...
FAILED xarray/tests/test_backends.py::TestH5NetCDFData::test_append_overwrite_values
FAILED xarray/tests/test_backends.py::TestH5NetCDFData::test_encoding_unlimited_dims
FAILED xarray/tests/test_backends.py::TestH5NetCDFFileObject::test_append_write
FAILED xarray/tests/test_backends.py::TestH5NetCDFFileObject::test_append_overwrite_values
FAILED xarray/tests/test_backends.py::TestH5NetCDFFileObject::test_encoding_unlimited_dims
FAILED xarray/tests/test_backends.py::TestH5NetCDFViaDaskData::test_append_write
FAILED xarray/tests/test_backends.py::TestH5NetCDFViaDaskData::test_append_overwrite_values
FAILED xarray/tests/test_backends.py::TestH5NetCDFViaDaskData::test_encoding_unlimited_dims
FAILED xarray/tests/test_variable.py::TestVariableWithDask::test_multiindex
```

</details>

Discussion:
kmuehlbauer: @mathause Although I'm pretty sure that this fixes the upstream-dev run, it would be good to verify. Is it possible to run the upstream-dev with this PR?
kmuehlbauer: I'd also like to add fixes to silence new FutureWarnings emitted by `h5netcdf` (hence the WIP). 
mathause: Great! Thanks for the fix. Yes the string `[test-upstream]` has to be part of the commit message (if you have nothing new to commit you can do `git commit --allow-empty -m "[test-upstream]"`)
kmuehlbauer: @dionhaefner 

Could you please have a look at the chunksizes FutureWarnings (eg. here https://github.com/pydata/xarray/runs/4965336696?check_suite_focus=true#step:8:344). 

Are the chunksizes via `h5py` and via `h5netcdf` heuristics correct?
kmuehlbauer: @mathause The fix is applied and works. We have some issues with `chunking` over at `h5netcdf`. This will be fixed after sorting this out.
mathause: Looks good the remaining upstream failure does not seem to be related.

Patch:
diff --git a/HOW_TO_RELEASE.md b/HOW_TO_RELEASE.md
index 16dc3b94..33a4fb9b 100644
--- a/HOW_TO_RELEASE.md
+++ b/HOW_TO_RELEASE.md
@@ -25,11 +25,11 @@ upstream        https://github.com/pydata/xarray (push)
      ```
  3. Add a list of contributors with:
     ```sh
-    git log "$(git tag --sort="v:refname" | tail -1).." --format=%aN | sort -u | perl -pe 's/\n/$1, /'
+    git log "$(git tag --sort=v:refname | tail -1).." --format=%aN | sort -u | perl -pe 's/\n/$1, /'
     ```
     This will return the number of contributors:
     ```sh
-    git log $(git tag --sort="v:refname" | tail -1).. --format=%aN | sort -u | wc -l
+    git log "$(git tag --sort=v:refname | tail -1).." --format=%aN | sort -u | wc -l
     ```
  4. Write a release summary: ~50 words describing the high level features. This
     will be used in the release emails, tweets, GitHub release notes, etc.
diff --git a/doc/user-guide/computation.rst b/doc/user-guide/computation.rst
index b0cc93ce..cb6eadc8 100644
--- a/doc/user-guide/computation.rst
+++ b/doc/user-guide/computation.rst
@@ -364,7 +364,7 @@ methods. This supports the block aggregation along multiple dimensions,
 .. ipython:: python
 
     x = np.linspace(0, 10, 300)
-    t = pd.date_range("15/12/1999", periods=364)
+    t = pd.date_range("1999-12-15", periods=364)
     da = xr.DataArray(
         np.sin(x) * np.cos(np.linspace(0, 1, 364)[:, np.newaxis]),
         dims=["time", "x"],
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 8896dd62..a33f557a 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -14,10 +14,48 @@ What's New
 
     np.random.seed(123456)
 
-.. _whats-new.0.X.Y+1:
+.. _whats-new.0.21.1:
+
+v0.21.1 (unreleased)
+--------------------
+
+New Features
+~~~~~~~~~~~~
+
+
+Breaking changes
+~~~~~~~~~~~~~~~~
+
+
+Deprecations
+~~~~~~~~~~~~
+
+
+Bug fixes
+~~~~~~~~~
+
+
+Documentation
+~~~~~~~~~~~~~
+
+
+Internal Changes
+~~~~~~~~~~~~~~~~
+
+
+.. _whats-new.0.21.0:
+
+v0.21.0 (27 January 2022)
+-------------------------
+
+Many thanks to the 20 contributors to the v0.21.0 release!
+
+Abel Aoun, Anderson Banihirwe, Ant Gib, Chris Roat, Cindy Chiao,
+Deepak Cherian, Dominik Stańczak, Fabian Hofmann, Illviljan, Jody Klymak, Joseph
+K Aicher, Mark Harfouche, Mathias Hauser, Matthew Roeschke, Maximilian Roos,
+Michael Delgado, Pascal Bourgault, Pierre, Ray Bell, Romain Caneill, Tim Heap,
+Tom Nicholas, Zeb Nicholls, joseph nowak, keewis.
 
-v0.21.0 (unreleased)
----------------------
 
 New Features
 ~~~~~~~~~~~~
@@ -63,11 +101,7 @@ Bug fixes
   By `Michael Delgado <https://github.com/delgadom>`_.
 - `dt.season <https://xarray.pydata.org/en/stable/generated/xarray.DataArray.dt.season.html>`_  can now handle NaN and NaT.  (:pull:`5876`).
   By `Pierre Loicq <https://github.com/pierreloicq>`_.
-
-
-Documentation
-~~~~~~~~~~~~~
-
+- Determination of zarr chunks handles empty lists for encoding chunks or variable chunks that occurs in certain cirumstances (:pull:`5526`). By `Chris Roat <https://github.com/chrisroat>`_.
 
 Internal Changes
 ~~~~~~~~~~~~~~~~
diff --git a/xarray/backends/h5netcdf_.py b/xarray/backends/h5netcdf_.py
index 671ea617..735aa5fc 100644
--- a/xarray/backends/h5netcdf_.py
+++ b/xarray/backends/h5netcdf_.py
@@ -81,7 +81,11 @@ def _read_attributes(h5netcdf_var):
 
 
 _extract_h5nc_encoding = functools.partial(
-    _extract_nc4_variable_encoding, lsd_okay=False, h5py_okay=True, backend="h5netcdf"
+    _extract_nc4_variable_encoding,
+    lsd_okay=False,
+    h5py_okay=True,
+    backend="h5netcdf",
+    unlimited_dims=None,
 )
 
 
@@ -231,12 +235,24 @@ class H5NetCDFStore(WritableCFDataStore):
         return FrozenDict(_read_attributes(self.ds))
 
     def get_dimensions(self):
-        return self.ds.dimensions
+        if Version(h5netcdf.__version__) >= Version("0.14.0.dev0"):
+            return FrozenDict((k, len(v)) for k, v in self.ds.dimensions.items())
+        else:
+            return self.ds.dimensions
 
     def get_encoding(self):
-        return {
-            "unlimited_dims": {k for k, v in self.ds.dimensions.items() if v is None}
-        }
+        if Version(h5netcdf.__version__) >= Version("0.14.0.dev0"):
+            return {
+                "unlimited_dims": {
+                    k for k, v in self.ds.dimensions.items() if v.isunlimited()
+                }
+            }
+        else:
+            return {
+                "unlimited_dims": {
+                    k for k, v in self.ds.dimensions.items() if v is None
+                }
+            }
 
     def set_dimension(self, name, length, is_unlimited=False):
         if is_unlimited:
diff --git a/xarray/backends/zarr.py b/xarray/backends/zarr.py
index 8bd34386..efb22bef 100644
--- a/xarray/backends/zarr.py
+++ b/xarray/backends/zarr.py
@@ -84,7 +84,8 @@ class ZarrArrayWrapper(BackendArray):
 
 def _determine_zarr_chunks(enc_chunks, var_chunks, ndim, name, safe_chunks):
     """
-    Given encoding chunks (possibly None) and variable chunks (possibly None)
+    Given encoding chunks (possibly None or []) and variable chunks
+    (possibly None or []).
     """
 
     # zarr chunk spec:
@@ -93,7 +94,7 @@ def _determine_zarr_chunks(enc_chunks, var_chunks, ndim, name, safe_chunks):
 
     # if there are no chunks in encoding and the variable data is a numpy
     # array, then we let zarr use its own heuristics to pick the chunks
-    if var_chunks is None and enc_chunks is None:
+    if not var_chunks and not enc_chunks:
         return None
 
     # if there are no chunks in encoding but there are dask chunks, we try to
@@ -102,7 +103,7 @@ def _determine_zarr_chunks(enc_chunks, var_chunks, ndim, name, safe_chunks):
     # http://zarr.readthedocs.io/en/latest/spec/v1.html#chunks
     # while dask chunks can be variable sized
     # http://dask.pydata.org/en/latest/array-design.html#chunks
-    if var_chunks and enc_chunks is None:
+    if var_chunks and not enc_chunks:
         if any(len(set(chunks[:-1])) > 1 for chunks in var_chunks):
             raise ValueError(
                 "Zarr requires uniform chunk sizes except for final chunk. "
@@ -145,7 +146,7 @@ def _determine_zarr_chunks(enc_chunks, var_chunks, ndim, name, safe_chunks):
 
     # if there are chunks in encoding and the variable data is a numpy array,
     # we use the specified chunks
-    if var_chunks is None:
+    if not var_chunks:
         return enc_chunks_tuple
 
     # the hard case
diff --git a/xarray/core/indexes.py b/xarray/core/indexes.py
index 844751f2..b66fbdf6 100644
--- a/xarray/core/indexes.py
+++ b/xarray/core/indexes.py
@@ -231,9 +231,14 @@ class PandasIndex(Index):
                         )
                     indexer = self.index.get_loc(label_value)
                 else:
-                    indexer = self.index.get_loc(
-                        label_value, method=method, tolerance=tolerance
-                    )
+                    if method is not None:
+                        indexer = get_indexer_nd(self.index, label, method, tolerance)
+                        if np.any(indexer < 0):
+                            raise KeyError(
+                                f"not all values found in index {coord_name!r}"
+                            )
+                    else:
+                        indexer = self.index.get_loc(label_value)
             elif label.dtype.kind == "b":
                 indexer = label
             else:
diff --git a/xarray/core/missing.py b/xarray/core/missing.py
index 2525272f..f3bb5351 100644
--- a/xarray/core/missing.py
+++ b/xarray/core/missing.py
@@ -564,9 +564,8 @@ def _localize(var, indexes_coords):
         minval = np.nanmin(new_x.values)
         maxval = np.nanmax(new_x.values)
         index = x.to_index()
-        imin = index.get_loc(minval, method="nearest")
-        imax = index.get_loc(maxval, method="nearest")
-
+        imin = index.get_indexer([minval], method="nearest").item()
+        imax = index.get_indexer([maxval], method="nearest").item()
         indexes[dim] = slice(max(imin - 2, 0), imax + 2)
         indexes_coords[dim] = (x[indexes[dim]], new_x)
     return var.isel(**indexes), indexes_coords


Test patch:
diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py
index 356335f4..bea90e1f 100644
--- a/xarray/tests/test_backends.py
+++ b/xarray/tests/test_backends.py
@@ -2383,6 +2383,20 @@ class ZarrBase(CFEncodedBase):
             ds_b = xr.open_zarr(store_target, use_cftime=True)
             assert xr.coding.times.contains_cftime_datetimes(ds_b.time)
 
+    def test_write_read_select_write(self):
+        # Test for https://github.com/pydata/xarray/issues/4084
+        ds = create_test_data()
+
+        # NOTE: using self.roundtrip, which uses open_dataset, will not trigger the bug.
+        with self.create_zarr_target() as initial_store:
+            ds.to_zarr(initial_store, mode="w")
+            ds1 = xr.open_zarr(initial_store)
+
+        # Combination of where+squeeze triggers error on write.
+        ds_sel = ds1.where(ds1.coords["dim3"] == "a", drop=True).squeeze("dim3")
+        with self.create_zarr_target() as final_store:
+            ds_sel.to_zarr(final_store, mode="w")
+
 
 @requires_zarr
 class TestZarrDictStore(ZarrBase):