Repository: pandas-dev/pandas

Issue:
ERR: in python >= 3.5 use FileNotFoundError instead of OSError
Simple to reproduce:

``` python
import pandas as pd
try:
    pd.read_csv('this_file_does_not_exist.csv')
except FileNotFoundError:
    print('This should execute.')
```

Instead, I get an OSError, which tells me in text that `OSError: File b'this_file_does_not_exist.csv' does not exist`.

https://docs.python.org/3.5/library/exceptions.html seems to suggest that OSError now has various subclasses, e.g. FileNotFoundError, for specific errors such as this one.

Output of `pd.show_versions()`:

```
INSTALLED VERSIONS
------------------
commit: None
python: 3.5.2.final.0
python-bits: 64
OS: Linux
OS-release: 4.4.0-31-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8

pandas: 0.18.1
nose: None
pip: 8.1.2
setuptools: 23.0.0
Cython: None
numpy: 1.11.1
scipy: 0.18.0
statsmodels: None
xarray: None
IPython: 5.0.0
sphinx: None
patsy: None
dateutil: 2.5.3
pytz: 2016.6.1
blosc: None
bottleneck: None
tables: None
numexpr: None
matplotlib: 1.5.1
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: None
httplib2: None
apiclient: None
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: 2.8
boto: None
pandas_datareader: None
```



Discussion:
FYI your syntax is non-python

`try/except` is the idiom

sure, this could be enable for >= 3.5. would have to be compat for < 3.5 (continue to raise `IOError`). These both subclass `OSError` so would be ok. pull-requests are welcome.

compat code should go in `pandas/compat/__init__.py`

This change would also should be make across the entire io suite. E.g. `json, hdf, excel`, anything that can read from files.

Edited original comment to fix typo you pointed out. Switching between languages is a pain. I agree with you proposed solution. Thanks for the pointers!

@brunobeltran hah, no problem. easy solution to switching. just always use python! lol

@jreback : `IOError` does not sub-class `OSError` in Python 2.x FYI, but the compat point still stands.  In Python 3.x, `IOError` is aliased to `OSError`.


Resolving patch:
diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py
--- a/pandas/compat/__init__.py
+++ b/pandas/compat/__init__.py
@@ -107,6 +107,10 @@ def signature(f):
     long = int
     unichr = chr
 
+    # This was introduced in Python 3.3, but we don't support
+    # Python 3.x < 3.4, so checking PY3 is safe.
+    FileNotFoundError = FileNotFoundError
+
     # list-producing versions of the major Python iterating functions
     def lrange(*args, **kwargs):
         return list(range(*args, **kwargs))
@@ -125,6 +129,8 @@ def lfilter(*args, **kwargs):
     import re
     _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
 
+    FileNotFoundError = IOError
+
     def isidentifier(s, dotted=False):
         return bool(_name_re.match(s))
 
diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py
--- a/pandas/io/pytables.py
+++ b/pandas/io/pytables.py
@@ -322,7 +322,8 @@ def read_hdf(path_or_buf, key=None, **kwargs):
             exists = False
 
         if not exists:
-            raise IOError('File %s does not exist' % path_or_buf)
+            raise compat.FileNotFoundError(
+                'File %s does not exist' % path_or_buf)
 
         # can't auto open/close if we are using an iterator
         # so delegate to the iterator


Test patch:
diff --git a/pandas/io/tests/parser/common.py b/pandas/io/tests/parser/common.py
--- a/pandas/io/tests/parser/common.py
+++ b/pandas/io/tests/parser/common.py
@@ -654,9 +654,10 @@ def test_file(self):
         tm.assert_frame_equal(url_table, local_table)
 
     def test_nonexistent_path(self):
-        # don't segfault pls #2428
+        # gh-2428: pls no segfault
+        # gh-14086: raise more helpful FileNotFoundError
         path = '%s.csv' % tm.rands(10)
-        self.assertRaises(IOError, self.read_csv, path)
+        self.assertRaises(compat.FileNotFoundError, self.read_csv, path)
 
     def test_missing_trailing_delimiters(self):
         data = """A,B,C,D
diff --git a/pandas/io/tests/test_pytables.py b/pandas/io/tests/test_pytables.py
--- a/pandas/io/tests/test_pytables.py
+++ b/pandas/io/tests/test_pytables.py
@@ -339,7 +339,8 @@ def test_api(self):
 
         # File path doesn't exist
         path = ""
-        self.assertRaises(IOError, read_hdf, path, 'df')
+        self.assertRaises(compat.FileNotFoundError,
+                          read_hdf, path, 'df')
 
     def test_api_default_format(self):