Repository: pandas-dev/pandas

Issue:
TypeError when reading a Series with read_hdf(mode='r') from fixed format
#### Code Sample, a copy-pastable example if possible

```python
import tempfile
import pandas as pd

series = pd.Series([1, 2, 3])
with tempfile.NamedTemporaryFile() as tf:
    series.to_hdf(tf.name, 'data', format='fixed')
    result = pd.read_hdf(tf.name, key='data', mode='r')
assert (result == series).all()
```

```
[...]/site-packages/pandas/io/pytables.py in read_index(self, key, **kwargs)
   2453             return self.read_sparse_intindex(key, **kwargs)
   2454         elif variety == u('regular'):
-> 2455             _, index = self.read_index_node(getattr(self.group, key), **kwargs)
   2456             return index
   2457         else:  # pragma: no cover

TypeError: read_index_node() got an unexpected keyword argument 'mode'
```
#### Problem description

For some inexplicable reason, `pd.read_hdf` opens files in append mode rather than read-only, so I pass `mode='r'`. For 'table' format, and also for `DataFrame` data in 'fixed' format, this works fine, but for `Series` data in 'fixed' format there is a `TypeError`

#### Output of ``pd.show_versions()``

<details>
INSTALLED VERSIONS
------------------
commit: None
python: 3.6.1.final.0
python-bits: 64
OS: Linux
OS-release: 4.8.0-53-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_GB.UTF-8
LOCALE: en_GB.UTF-8

pandas: 0.20.1
pytest: 3.1.1
pip: 9.0.1
setuptools: 27.2.0
Cython: None
numpy: 1.11.3
scipy: None
xarray: None
IPython: 6.0.0
sphinx: None
patsy: None
dateutil: 2.6.0
pytz: 2017.2
blosc: None
bottleneck: None
tables: 3.3.0
numexpr: 2.6.2
feather: None
matplotlib: None
openpyxl: 2.4.7
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: 0.999
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: 2.9.6
s3fs: None
pandas_gbq: None
pandas_datareader: None

</details>

TypeError when reading a Series with read_hdf(mode='r') from fixed format
#### Code Sample, a copy-pastable example if possible

```python
import tempfile
import pandas as pd

series = pd.Series([1, 2, 3])
with tempfile.NamedTemporaryFile() as tf:
    series.to_hdf(tf.name, 'data', format='fixed')
    result = pd.read_hdf(tf.name, key='data', mode='r')
assert (result == series).all()
```

```
[...]/site-packages/pandas/io/pytables.py in read_index(self, key, **kwargs)
   2453             return self.read_sparse_intindex(key, **kwargs)
   2454         elif variety == u('regular'):
-> 2455             _, index = self.read_index_node(getattr(self.group, key), **kwargs)
   2456             return index
   2457         else:  # pragma: no cover

TypeError: read_index_node() got an unexpected keyword argument 'mode'
```
#### Problem description

For some inexplicable reason, `pd.read_hdf` opens files in append mode rather than read-only, so I pass `mode='r'`. For 'table' format, and also for `DataFrame` data in 'fixed' format, this works fine, but for `Series` data in 'fixed' format there is a `TypeError`

#### Output of ``pd.show_versions()``

<details>
INSTALLED VERSIONS
------------------
commit: None
python: 3.6.1.final.0
python-bits: 64
OS: Linux
OS-release: 4.8.0-53-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_GB.UTF-8
LOCALE: en_GB.UTF-8

pandas: 0.20.1
pytest: 3.1.1
pip: 9.0.1
setuptools: 27.2.0
Cython: None
numpy: 1.11.3
scipy: None
xarray: None
IPython: 6.0.0
sphinx: None
patsy: None
dateutil: 2.6.0
pytz: 2017.2
blosc: None
bottleneck: None
tables: 3.3.0
numexpr: 2.6.2
feather: None
matplotlib: None
openpyxl: 2.4.7
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: 0.999
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: 2.9.6
s3fs: None
pandas_gbq: None
pandas_datareader: None

</details>



Discussion:
> For some inexplicable reason, pd.read_hdf opens files in append mode rather than read-only, so I pass 

this is the default for ``PyTables``. append allows you to read/write files, but won't erase the file if it exists (``w``).
Does `pd.read_csv` open the CSV in append mode? Perhaps `pd.read_hdf` should not use the PyTables default.
> Does pd.read_csv open the CSV in append mode? Perhaps pd.read_hdf should not use the PyTables default.

no, that is read-only.  (though you can open the file yourself in append mode if you wanted)

The difference of course is that PyTables acts more like a database, where you generally both read and write, while a file you generally don't.


we had this discussion here FYI: https://github.com/pandas-dev/pandas/issues/13623
OK, that discussion is off-topic here. If you want to make a method called 'read_X' open a file for writing, that's your right, but please at least make it possible for me to open it read-only.
well if you submit a PR to fix that would be helpful 
> For some inexplicable reason, pd.read_hdf opens files in append mode rather than read-only, so I pass 

this is the default for ``PyTables``. append allows you to read/write files, but won't erase the file if it exists (``w``).
Does `pd.read_csv` open the CSV in append mode? Perhaps `pd.read_hdf` should not use the PyTables default.
> Does pd.read_csv open the CSV in append mode? Perhaps pd.read_hdf should not use the PyTables default.

no, that is read-only.  (though you can open the file yourself in append mode if you wanted)

The difference of course is that PyTables acts more like a database, where you generally both read and write, while a file you generally don't.


we had this discussion here FYI: https://github.com/pandas-dev/pandas/issues/13623
OK, that discussion is off-topic here. If you want to make a method called 'read_X' open a file for writing, that's your right, but please at least make it possible for me to open it read-only.
well if you submit a PR to fix that would be helpful 

Resolving patch:
diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py
--- a/pandas/io/pytables.py
+++ b/pandas/io/pytables.py
@@ -282,7 +282,7 @@ def to_hdf(path_or_buf, key, value, mode=None, complevel=None, complib=None,
         f(path_or_buf)
 
 
-def read_hdf(path_or_buf, key=None, **kwargs):
+def read_hdf(path_or_buf, key=None, mode='r', **kwargs):
     """ read from the store, close it if we opened it
 
         Retrieve pandas object stored in file, optionally based on where
@@ -290,13 +290,16 @@ def read_hdf(path_or_buf, key=None, **kwargs):
 
         Parameters
         ----------
-        path_or_buf : path (string), buffer, or path object (pathlib.Path or
-            py._path.local.LocalPath) to read from
+        path_or_buf : path (string), buffer or path object (pathlib.Path or
+            py._path.local.LocalPath) designating the file to open, or an
+            already opened pd.HDFStore object
 
             .. versionadded:: 0.19.0 support for pathlib, py.path.
 
         key : group identifier in the store. Can be omitted if the HDF file
             contains a single pandas object.
+        mode : string, {'r', 'r+', 'a'}, default 'r'. Mode to use when opening
+            the file. Ignored if path_or_buf is a pd.HDFStore.
         where : list of Term (or convertable) objects, optional
         start : optional, integer (defaults to None), row number to start
             selection
@@ -313,10 +316,9 @@ def read_hdf(path_or_buf, key=None, **kwargs):
 
         """
 
-    if kwargs.get('mode', 'a') not in ['r', 'r+', 'a']:
+    if mode not in ['r', 'r+', 'a']:
         raise ValueError('mode {0} is not allowed while performing a read. '
-                         'Allowed modes are r, r+ and a.'
-                         .format(kwargs.get('mode')))
+                         'Allowed modes are r, r+ and a.'.format(mode))
     # grab the scope
     if 'where' in kwargs:
         kwargs['where'] = _ensure_term(kwargs['where'], scope_level=1)
@@ -343,7 +345,7 @@ def read_hdf(path_or_buf, key=None, **kwargs):
             raise compat.FileNotFoundError(
                 'File %s does not exist' % path_or_buf)
 
-        store = HDFStore(path_or_buf, **kwargs)
+        store = HDFStore(path_or_buf, mode=mode, **kwargs)
         # can't auto open/close if we are using an iterator
         # so delegate to the iterator
         auto_close = True


Test patch:
diff --git a/pandas/tests/io/test_pytables.py b/pandas/tests/io/test_pytables.py
--- a/pandas/tests/io/test_pytables.py
+++ b/pandas/tests/io/test_pytables.py
@@ -5247,6 +5247,17 @@ def test_query_compare_column_type(self):
                         expected = df.loc[[], :]
                     tm.assert_frame_equal(expected, result)
 
+    @pytest.mark.parametrize('format', ['fixed', 'table'])
+    def test_read_hdf_series_mode_r(self, format):
+        # GH 16583
+        # Tests that reading a Series saved to an HDF file
+        # still works if a mode='r' argument is supplied
+        series = tm.makeFloatSeries()
+        with ensure_clean_path(self.path) as path:
+            series.to_hdf(path, key='data', format=format)
+            result = pd.read_hdf(path, key='data', mode='r')
+        tm.assert_series_equal(result, series)
+
     @pytest.mark.skipif(sys.version_info < (3, 6), reason="Need python 3.6")
     def test_fspath(self):
         with tm.ensure_clean('foo.h5') as path: