Repository: h5py/h5py (python)

Issue:
dataset.read_direct fails with ZeroDivisionError when '0 in shape'
Potential regression of #281, or simply related. H5py allows storing of empty arrays where any one of the dimensions can be 0 (e.g., `(0,)`, `(1, 0)`, `(1, 0, 3)`). If this dataset is read using `read_direct`, a `ZeroDivisionError` is thrown.

<details>
    <summary>System version details (click to expand)</summary>
 * Operating System (e.g. Windows 10, MacOS 10.11, Ubuntu 16.04.2 LTS, CentOS 7)
Arch Linux

 * Python version (e.g. 2.7, 3.5)
3.6.0

 * Where Python was acquired (e.g. system Python on MacOS or Linux, Anaconda on
   Windows)
System repositories

 * h5py version (e.g. 2.6)
2.7.0

 * HDF5 version (e.g. 1.8.17)
1.10
</details>

Minimal script:
```python
import h5py
import numpy as np


print(h5py.version.info)

shape = (0,)  # also try (1, 0, 2) or anything with 0 in it
hfile = h5py.File("/tmp/zerodata.h5", "w")
ds = hfile.create_dataset("ds", data=np.zeros(shape))

print("---------------------------------")
print(ds)
print(hfile["ds"])
print(hfile["ds"][:])

dsread = np.zeros(shape)
hfile["ds"].read_direct(dsread)  # division by zero
hfile.close()
```

Output
```
Summary of the h5py configuration
---------------------------------

h5py    2.7.0
HDF5    1.10.0
Python  3.6.0 (default, Jan 16 2017, 12:12:55)
[GCC 6.3.1 20170109]
sys.platform    linux
sys.maxsize     9223372036854775807
numpy   1.12.1

---------------------------------
<HDF5 dataset "ds": shape (0,), type "<f8">
<HDF5 dataset "ds": shape (0,), type "<f8">
[]
Traceback (most recent call last):
  File "zero.py", line 17, in <module>
    hfile["ds"].read_direct(dsread)  # division by zero
  File "/usr/lib/python3.6/site-packages/h5py/_hl/dataset.py", line 654, in read_direct
    for mspace in dest_sel.broadcast(source_sel.mshape):
  File "/usr/lib/python3.6/site-packages/h5py/_hl/selections.py", line 303, in broadcast
    chunks = tuple(x//y for x, y in zip(count, tshape))
  File "/usr/lib/python3.6/site-packages/h5py/_hl/selections.py", line 303, in <genexpr>
    chunks = tuple(x//y for x, y in zip(count, tshape))
ZeroDivisionError: integer division or modulo by zero
```

Pull request description:
Fix .read_direct() when one selected dimension is 0
Datasets and selections with zero length on one or more dimensions are allowed and usually work (similar to numpy arrays with a zero dimension), but there was a bug causing a ZeroDivisionError in `.read_direct()`. This should fix it.

Closes #870

Patch:
diff --git a/h5py/_hl/selections.py b/h5py/_hl/selections.py
index c3d9359f..b3df7c24 100644
--- a/h5py/_hl/selections.py
+++ b/h5py/_hl/selections.py
@@ -290,6 +290,10 @@ class SimpleSelection(Selection):
         rank = len(count)
         tshape = self.expand_shape(source_shape)
 
+        # Avoid ZeroDivisionError below (after the shape checks in expand_source)
+        if any(d == 0 for d in count):
+            return
+
         chunks = tuple(x//y for x, y in zip(count, tshape))
         nchunks = product(chunks)
 
diff --git a/news/read-direct-zero.rst b/news/read-direct-zero.rst
new file mode 100644
index 00000000..c1bf15b2
--- /dev/null
+++ b/news/read-direct-zero.rst
@@ -0,0 +1,29 @@
+New features
+------------
+
+* <news item>
+
+Deprecations
+------------
+
+* <news item>
+
+Exposing HDF5 functions
+-----------------------
+
+* <news item>
+
+Bug fixes
+---------
+
+* Fixed an error using :meth:`.Dataset.read_direct` with a zero-size selection.
+
+Building h5py
+-------------
+
+* <news item>
+
+Development
+-----------
+
+* <news item>


Test patch:
diff --git a/h5py/tests/test_dataset.py b/h5py/tests/test_dataset.py
index 950d35c4..1cd20c71 100644
--- a/h5py/tests/test_dataset.py
+++ b/h5py/tests/test_dataset.py
@@ -264,6 +264,18 @@ class TestReadDirectly:
         with pytest.raises(TypeError):
             dset.read_direct(arr)
 
+    def test_zero_length(self, writable_file):
+        shape = (0, 20)
+        dset = writable_file.create_dataset("dset", shape, dtype=np.int64)
+        arr = np.zeros(shape, dtype=np.int64)
+        dset.read_direct(arr)
+
+        # We should still get an error if the shape is wrong
+        arr2 = np.zeros((0, 25), dtype=np.int64)
+        with pytest.raises(TypeError):
+            dset.read_direct(arr2)
+
+
 class TestWriteDirectly:
 
     """