Repository: pandas-dev/pandas

Issue:
ExcelFile.parse() and pd.read_xlsx() have different behavior for "sheetname" argument
#### Code Sample, a copy-pastable example if possible

* `pd.read_excel()`
```python
>>> import pandas as pd
>>> pd.read_excel('sampledata.xlsx', sheet_name='Sheet2')
      a   b       c
0  this  is  sheet2
>>> pd.read_excel('sampledata.xlsx', sheetname='Sheet2')
/Users/<myname>/.pyenv/versions/miniconda3-latest/envs/py36/envs/py36/lib/python3.6/site-packages/pandas/util/_decorators.py:118: FutureWarning: The `sheetname` keyword is deprecated, use `sheet_name` instead
  return func(*args, **kwargs)
      a   b       c
0  this  is  sheet2
```

* `ExcelFile.parse()`
```python
>>> import pandas as pd
>>> xlsx_file=pd.ExcelFile('sampledata.xlsx')
>>> xlsx_file.sheet_names
['Sheet1', 'Sheet2', 'Sheet3']
>>> xlsx_file.parse(sheet_name='Sheet2')
      a   b       c
0  this  is  sheet2
>>> xlsx_file.parse(sheetname='Sheet2')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/<myname>/.pyenv/versions/miniconda3-latest/envs/py36/envs/py36/lib/python3.6/site-packages/pandas/io/excel.py", line 327, in parse
    **kwds)
TypeError: _parse_excel() got multiple values for keyword argument 'sheetname'
```

#### Problem description

* The document says ExcelFile.parse() is "Equivalent to read_excel(ExcelFile, ...)", but when using argument `sheetname`,which is deprecated, these two gives different results.
  * pd.read_excel() works with `FutureWarning`, but ExcelFile.parse() gives `TypeError` instead.

#### Expected Output

 ExcelFile.parse() should raise `FutureWarning` and use the value of `sheetname` as that of `sheet_name`

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.6.4.final.0
python-bits: 64
OS: Darwin
OS-release: 17.5.0
machine: x86_64
processor: i386
byteorder: little
LC_ALL: None
LANG: ja_JP.UTF-8
LOCALE: ja_JP.UTF-8

pandas: 0.22.0
pytest: 3.3.2
pip: 9.0.1
setuptools: 38.4.0
Cython: 0.27.3
numpy: 1.14.0
scipy: 1.0.0
pyarrow: None
xarray: None
IPython: 6.2.1
sphinx: 1.6.6
patsy: 0.5.0
dateutil: 2.6.1
pytz: 2017.3
blosc: None
bottleneck: 1.2.1
tables: 3.4.2
numexpr: 2.6.4
feather: None
matplotlib: 2.1.2
openpyxl: 2.4.10
xlrd: 1.1.0
xlwt: 1.2.0
xlsxwriter: 1.0.2
lxml: 4.1.1
bs4: 4.6.0
html5lib: 1.0.1
sqlalchemy: 1.2.1
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None
</details>



Discussion:
you are welcome to submit a fix to this, though ``sheetname`` is going to be removed in the next version.

Resolving patch:
diff --git a/pandas/io/excel.py b/pandas/io/excel.py
--- a/pandas/io/excel.py
+++ b/pandas/io/excel.py
@@ -303,20 +303,11 @@ def read_excel(io,
                convert_float=True,
                **kwds):
 
-    # Can't use _deprecate_kwarg since sheetname=None has a special meaning
-    if is_integer(sheet_name) and sheet_name == 0 and 'sheetname' in kwds:
-        warnings.warn("The `sheetname` keyword is deprecated, use "
-                      "`sheet_name` instead", FutureWarning, stacklevel=2)
-        sheet_name = kwds.pop("sheetname")
-    elif 'sheetname' in kwds:
-        raise TypeError("Cannot specify both `sheet_name` and `sheetname`. "
-                        "Use just `sheet_name`")
-
     if not isinstance(io, ExcelFile):
         io = ExcelFile(io, engine=engine)
 
-    return io._parse_excel(
-        sheetname=sheet_name,
+    return io.parse(
+        sheet_name=sheet_name,
         header=header,
         names=names,
         index_col=index_col,
@@ -435,7 +426,16 @@ def parse(self,
         docstring for more info on accepted parameters
         """
 
-        return self._parse_excel(sheetname=sheet_name,
+        # Can't use _deprecate_kwarg since sheetname=None has a special meaning
+        if is_integer(sheet_name) and sheet_name == 0 and 'sheetname' in kwds:
+            warnings.warn("The `sheetname` keyword is deprecated, use "
+                          "`sheet_name` instead", FutureWarning, stacklevel=2)
+            sheet_name = kwds.pop("sheetname")
+        elif 'sheetname' in kwds:
+            raise TypeError("Cannot specify both `sheet_name` "
+                            "and `sheetname`. Use just `sheet_name`")
+
+        return self._parse_excel(sheet_name=sheet_name,
                                  header=header,
                                  names=names,
                                  index_col=index_col,
@@ -489,7 +489,7 @@ def _excel2num(x):
             return i in usecols
 
     def _parse_excel(self,
-                     sheetname=0,
+                     sheet_name=0,
                      header=0,
                      names=None,
                      index_col=None,
@@ -585,14 +585,14 @@ def _parse_cell(cell_contents, cell_typ):
         ret_dict = False
 
         # Keep sheetname to maintain backwards compatibility.
-        if isinstance(sheetname, list):
-            sheets = sheetname
+        if isinstance(sheet_name, list):
+            sheets = sheet_name
             ret_dict = True
-        elif sheetname is None:
+        elif sheet_name is None:
             sheets = self.sheet_names
             ret_dict = True
         else:
-            sheets = [sheetname]
+            sheets = [sheet_name]
 
         # handle same-type duplicates.
         sheets = list(OrderedDict.fromkeys(sheets).keys())


Test patch:
diff --git a/pandas/tests/io/test_excel.py b/pandas/tests/io/test_excel.py
--- a/pandas/tests/io/test_excel.py
+++ b/pandas/tests/io/test_excel.py
@@ -503,20 +503,35 @@ def test_sheet_name_and_sheetname(self, ext):
         # GH10559: Minor improvement: Change "sheet_name" to "sheetname"
         # GH10969: DOC: Consistent var names (sheetname vs sheet_name)
         # GH12604: CLN GH10559 Rename sheetname variable to sheet_name
+        # GH20920: ExcelFile.parse() and pd.read_xlsx() have different
+        #          behavior for "sheetname" argument
         dfref = self.get_csv_refdf('test1')
-        df1 = self.get_exceldf('test1', ext, sheet_name='Sheet1')    # doc
+        df1 = self.get_exceldf('test1', ext,
+                               sheet_name='Sheet1')  # doc
         with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
             df2 = self.get_exceldf('test1', ext,
                                    sheetname='Sheet1')  # bkwrd compat
 
+        excel = self.get_excelfile('test1', ext)
+        df1_parse = excel.parse(sheet_name='Sheet1')    # doc
+        with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
+            df2_parse = excel.parse(sheetname='Sheet1')  # bkwrd compat
+
         tm.assert_frame_equal(df1, dfref, check_names=False)
         tm.assert_frame_equal(df2, dfref, check_names=False)
+        tm.assert_frame_equal(df1_parse, dfref, check_names=False)
+        tm.assert_frame_equal(df2_parse, dfref, check_names=False)
 
     def test_sheet_name_both_raises(self, ext):
         with tm.assert_raises_regex(TypeError, "Cannot specify both"):
             self.get_exceldf('test1', ext, sheetname='Sheet1',
                              sheet_name='Sheet1')
 
+        excel = self.get_excelfile('test1', ext)
+        with tm.assert_raises_regex(TypeError, "Cannot specify both"):
+            excel.parse(sheetname='Sheet1',
+                        sheet_name='Sheet1')
+
 
 @pytest.mark.parametrize("ext", ['.xls', '.xlsx', '.xlsm'])
 class TestXlrdReader(ReadingTestsBase):