Repository: dask/dask

Issue:
read hdf() throws misguided exception when file or key not found
For example:

```
import dask.dataframe as dd

ddf = dd.read_hdf('garbage_hdf_store', '/garbageKey')

ValueError: Input must be a list longer than 0
```

in pandas the exception is accurate

```
pd.read_hdf('garbage_hdf_store')
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-9-5999b52e4e3a> in <module>()
----> 1 pd.read_hdf('garbage_hdf_store')

C:\Anaconda3\lib\site-packages\pandas\io\pytables.py in read_hdf(path_or_buf, key, **kwargs)
    304 
    305         if not exists:
--> 306             raise IOError('File %s does not exist' % path_or_buf)
    307 
    308         # can't auto open/close if we are using an iterator

OSError: File garbage_hdf_store does not exist
```



Discussion:
Is there any reason that this cannot be resolved by adding the following checks to `read_hdf`?

```
@wraps(pd.read_hdf)
def read_hdf(pattern, key, start=0, stop=None, columns=None,
             chunksize=1000000, sorted_index=False, lock=True, mode='a'):
...
    if not paths:
        raise IOError('File does not exist')

    for path in paths:
        try:
            exists = os.path.exists(path)
            # if filepath is too long
        except (TypeError, ValueError):
            exists = False
        if not exists:
            raise IOError('File %s does not exist' % path)

```

I am not sure if the second check is actually necessary, as I (inelegantly) stole it from the pytables implementation. If no files match the pattern, then the list of paths should be empty. @mrocklin, can you comment?

An intelligent error when no files match would be welcome.  Sorry for the long delay.
Marking as a good first issue. Seems reasonably straightforward to add a useful error message here and likely not too difficult after a cursory look over things. Though feel free to adjust the labeling if I have misjudged things here.
I'm interested in picking this up as my first issue. I see there was an attempt at fixing it some time ago in the linked PR, has there been any progress since (without closing this issue) or am I good to go?
You should be good to go, thanks.

Resolving patch:
diff --git a/dask/dataframe/io/hdf.py b/dask/dataframe/io/hdf.py
--- a/dask/dataframe/io/hdf.py
+++ b/dask/dataframe/io/hdf.py
@@ -473,6 +473,20 @@ def read_hdf(
         paths = sorted(glob(pattern))
     else:
         paths = pattern
+
+    if not isinstance(pattern, str) and len(paths) == 0:
+        raise ValueError("No files provided")
+    if not paths or len(paths) == 0:
+        raise IOError("File(s) not found: {0}".format(pattern))
+    for path in paths:
+        try:
+            exists = os.path.exists(path)
+        except (ValueError, TypeError):
+            exists = False
+        if not exists:
+            raise IOError(
+                "File not found or insufficient permissions: {0}".format(path)
+            )
     if (start != 0 or stop is not None) and len(paths) > 1:
         raise NotImplementedError(read_hdf_error_msg)
     if chunksize <= 0:


Test patch:
diff --git a/dask/dataframe/io/tests/test_hdf.py b/dask/dataframe/io/tests/test_hdf.py
--- a/dask/dataframe/io/tests/test_hdf.py
+++ b/dask/dataframe/io/tests/test_hdf.py
@@ -829,3 +829,18 @@ def test_hdf_filenames():
     assert ddf.to_hdf("foo*.hdf5", "key") == ["foo0.hdf5", "foo1.hdf5"]
     os.remove("foo0.hdf5")
     os.remove("foo1.hdf5")
+
+
+def test_hdf_path_exceptions():
+
+    # single file doesn't exist
+    with pytest.raises(IOError):
+        dd.read_hdf("nonexistant_store_X34HJK", "/tmp")
+
+    # a file from a list of files doesn't exist
+    with pytest.raises(IOError):
+        dd.read_hdf(["nonexistant_store_X34HJK", "nonexistant_store_UY56YH"], "/tmp")
+
+    # list of files is empty
+    with pytest.raises(ValueError):
+        dd.read_hdf([], "/tmp")