Repository: zarr-developers/zarr-python

Issue:
FutureWarning from zarr.open
The following code snippet will raise a FutureWarning:
```python
import numpy
import zarr                                                                                                                                                                                                                                                                                                                                                        
group = zarr.open()                                                                                                                                                                                                                                                                                                                                                
group.create('foo', dtype=[('a', int), ('b', int)], fill_value=numpy.zeros((), dtype=[('a', int), ('b', int)])[()], shape=(10, 10))                                                                                                                                                                                                                                
```

#### Problem description

The current behavior raises a FutureWarning:
```python
zarr/util.py:238: FutureWarning: elementwise == comparison failed and returning scalar instead; this will raise an error or perform elementwise comparison in the future.
  elif fill_value == 0:
```
This happens because of the comparison of the utterable dtype to 0 (in line 258) above. I'm not sure if this error is a false positive from numpy or something that will actually break in a future numpy version.

#### Version and installation information

Please provide the following:

* Value of ``zarr.__version__``: '2.3.2'
* Value of ``numcodecs.__version__``: '0.6.4'
* Version of Python interpreter: 3.7.6
* Operating system (Linux/Windows/Mac): Mac and Linux
* How Zarr was installed: using conda (also from source)


Discussion:
joshmoore: Thanks for taking a look at this, @alanhdu! I've trigger the workflows.
codecov[bot]: ## [Codecov](https://codecov.io/gh/zarr-developers/zarr-python/pull/1397?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers) Report
> Merging [#1397](https://codecov.io/gh/zarr-developers/zarr-python/pull/1397?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers) (6463ece) into [main](https://codecov.io/gh/zarr-developers/zarr-python/commit/a2e864c14dbf2f7ab2ddba61f90fed8e38406b72?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers) (a2e864c) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff            @@
##              main     #1397   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           37        37           
  Lines        14856     14857    +1     
=========================================
+ Hits         14856     14857    +1     
```

| [Impacted Files](https://codecov.io/gh/zarr-developers/zarr-python/pull/1397?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers) | Coverage Δ | |
|---|---|---|
| [zarr/tests/test\_util.py](https://codecov.io/gh/zarr-developers/zarr-python/pull/1397?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers#diff-emFyci90ZXN0cy90ZXN0X3V0aWwucHk=) | `100.00% <100.00%> (ø)` | |
| [zarr/util.py](https://codecov.io/gh/zarr-developers/zarr-python/pull/1397?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zarr-developers#diff-emFyci91dGlsLnB5) | `100.00% <100.00%> (ø)` | |

joshmoore: Thanks again, @alanhdu. I've added a changelog entry if you'd like to review. Workflows are running again with an updated main branch. Unless anyone has an objection, I'd say let's get this into 2.15.x.

Patch:
diff --git a/docs/release.rst b/docs/release.rst
index 7442e519..83588bb3 100644
--- a/docs/release.rst
+++ b/docs/release.rst
@@ -52,6 +52,9 @@ Bug fixes
 * Fix ``ReadOnlyError`` when opening V3 store via fsspec reference file system.
   By :user:`Joe Hamman <jhamman>` :issue:`1383`.
 
+* Fix ``normalize_fill_value`` for structured arrays.
+  By :user:`Alan Du <alanhdu>` :issue:`1397`.
+
 .. _release_2.14.2:
 
 2.14.2
diff --git a/zarr/util.py b/zarr/util.py
index b661f5f6..6ba20b96 100644
--- a/zarr/util.py
+++ b/zarr/util.py
@@ -295,7 +295,7 @@ def normalize_fill_value(fill_value, dtype: np.dtype):
     if fill_value is None or dtype.hasobject:
         # no fill value
         pass
-    elif fill_value == 0:
+    elif not isinstance(fill_value, np.void) and fill_value == 0:
         # this should be compatible across numpy versions for any array type, including
         # structured arrays
         fill_value = np.zeros((), dtype=dtype)[()]


Test patch:
diff --git a/zarr/tests/test_util.py b/zarr/tests/test_util.py
index 0a717b8f..e01aa671 100644
--- a/zarr/tests/test_util.py
+++ b/zarr/tests/test_util.py
@@ -119,6 +119,7 @@ def test_normalize_fill_value():
     structured_dtype = np.dtype([('foo', 'S3'), ('bar', 'i4'), ('baz', 'f8')])
     expect = np.array((b'', 0, 0.), dtype=structured_dtype)[()]
     assert expect == normalize_fill_value(0, dtype=structured_dtype)
+    assert expect == normalize_fill_value(expect, dtype=structured_dtype)
     assert '' == normalize_fill_value(0, dtype=np.dtype('U1'))