Skip to content

Commit 6a65fc7

Browse files
Backport PR pandas-dev#39482: ERR: Unify error message for bad excel sheetnames (pandas-dev#39536)
Co-authored-by: Richard Shadrach <[email protected]>
1 parent f3773a8 commit 6a65fc7

File tree

10 files changed

+48
-17
lines changed

10 files changed

+48
-17
lines changed

doc/source/whatsnew/v1.2.2.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Fixed regressions
2929
Bug fixes
3030
~~~~~~~~~
3131

32-
-
32+
- :func:`pandas.read_excel` error message when a specified ``sheetname`` does not exist is now uniform across engines (:issue:`39250`)
3333
-
3434

3535
.. ---------------------------------------------------------------------------

pandas/io/excel/_base.py

+11
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,17 @@ def get_sheet_by_index(self, index):
425425
def get_sheet_data(self, sheet, convert_float):
426426
pass
427427

428+
def raise_if_bad_sheet_by_index(self, index: int) -> None:
429+
n_sheets = len(self.sheet_names)
430+
if index >= n_sheets:
431+
raise ValueError(
432+
f"Worksheet index {index} is invalid, {n_sheets} worksheets found"
433+
)
434+
435+
def raise_if_bad_sheet_by_name(self, name: str) -> None:
436+
if name not in self.sheet_names:
437+
raise ValueError(f"Worksheet named '{name}' not found")
438+
428439
def parse(
429440
self,
430441
sheet_name=0,

pandas/io/excel/_odfreader.py

+2
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,14 @@ def sheet_names(self) -> List[str]:
5757
def get_sheet_by_index(self, index: int):
5858
from odf.table import Table
5959

60+
self.raise_if_bad_sheet_by_index(index)
6061
tables = self.book.getElementsByType(Table)
6162
return tables[index]
6263

6364
def get_sheet_by_name(self, name: str):
6465
from odf.table import Table
6566

67+
self.raise_if_bad_sheet_by_name(name)
6668
tables = self.book.getElementsByType(Table)
6769

6870
for table in tables:

pandas/io/excel/_openpyxl.py

+2
Original file line numberDiff line numberDiff line change
@@ -492,9 +492,11 @@ def sheet_names(self) -> List[str]:
492492
return self.book.sheetnames
493493

494494
def get_sheet_by_name(self, name: str):
495+
self.raise_if_bad_sheet_by_name(name)
495496
return self.book[name]
496497

497498
def get_sheet_by_index(self, index: int):
499+
self.raise_if_bad_sheet_by_index(index)
498500
return self.book.worksheets[index]
499501

500502
def _convert_cell(self, cell, convert_float: bool) -> Scalar:

pandas/io/excel/_pyxlsb.py

+2
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ def sheet_names(self) -> List[str]:
4747
return self.book.sheets
4848

4949
def get_sheet_by_name(self, name: str):
50+
self.raise_if_bad_sheet_by_name(name)
5051
return self.book.get_sheet(name)
5152

5253
def get_sheet_by_index(self, index: int):
54+
self.raise_if_bad_sheet_by_index(index)
5355
# pyxlsb sheets are indexed from 1 onwards
5456
# There's a fix for this in the source, but the pypi package doesn't have it
5557
return self.book.get_sheet(index + 1)

pandas/io/excel/_xlrd.py

+2
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ def sheet_names(self):
4444
return self.book.sheet_names()
4545

4646
def get_sheet_by_name(self, name):
47+
self.raise_if_bad_sheet_by_name(name)
4748
return self.book.sheet_by_name(name)
4849

4950
def get_sheet_by_index(self, index):
51+
self.raise_if_bad_sheet_by_index(index)
5052
return self.book.sheet_by_index(index)
5153

5254
def get_sheet_data(self, sheet, convert_float):

pandas/tests/io/excel/test_odf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,5 @@ def test_nonexistent_sheetname_raises(read_ext):
4242
# GH-27676
4343
# Specifying a non-existent sheet_name parameter should throw an error
4444
# with the sheet name.
45-
with pytest.raises(ValueError, match="sheet xyz not found"):
45+
with pytest.raises(ValueError, match="Worksheet named 'xyz' not found"):
4646
pd.read_excel("blank.ods", sheet_name="xyz")

pandas/tests/io/excel/test_readers.py

+21
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,16 @@ def test_bad_engine_raises(self, read_ext):
622622
with pytest.raises(ValueError, match="Unknown engine: foo"):
623623
pd.read_excel("", engine=bad_engine)
624624

625+
@pytest.mark.parametrize(
626+
"sheet_name",
627+
[3, [0, 3], [3, 0], "Sheet4", ["Sheet1", "Sheet4"], ["Sheet4", "Sheet1"]],
628+
)
629+
def test_bad_sheetname_raises(self, read_ext, sheet_name):
630+
# GH 39250
631+
msg = "Worksheet index 3 is invalid|Worksheet named 'Sheet4' not found"
632+
with pytest.raises(ValueError, match=msg):
633+
pd.read_excel("blank" + read_ext, sheet_name=sheet_name)
634+
625635
def test_missing_file_raises(self, read_ext):
626636
bad_file = f"foo{read_ext}"
627637
# CI tests with zh_CN.utf8, translates to "No such file or directory"
@@ -1159,6 +1169,17 @@ def test_sheet_name(self, read_ext, df_ref):
11591169
tm.assert_frame_equal(df1_parse, df_ref, check_names=False)
11601170
tm.assert_frame_equal(df2_parse, df_ref, check_names=False)
11611171

1172+
@pytest.mark.parametrize(
1173+
"sheet_name",
1174+
[3, [0, 3], [3, 0], "Sheet4", ["Sheet1", "Sheet4"], ["Sheet4", "Sheet1"]],
1175+
)
1176+
def test_bad_sheetname_raises(self, read_ext, sheet_name):
1177+
# GH 39250
1178+
msg = "Worksheet index 3 is invalid|Worksheet named 'Sheet4' not found"
1179+
with pytest.raises(ValueError, match=msg):
1180+
with pd.ExcelFile("blank" + read_ext) as excel:
1181+
excel.parse(sheet_name=sheet_name)
1182+
11621183
def test_excel_read_buffer(self, engine, read_ext):
11631184
pth = "test1" + read_ext
11641185
expected = pd.read_excel(pth, sheet_name="Sheet1", index_col=0, engine=engine)

pandas/tests/io/excel/test_writers.py

+3-13
Original file line numberDiff line numberDiff line change
@@ -347,19 +347,9 @@ def test_excel_sheet_by_name_raise(self, path, engine):
347347

348348
tm.assert_frame_equal(gt, df)
349349

350-
if engine == "odf":
351-
msg = "sheet 0 not found"
352-
with pytest.raises(ValueError, match=msg):
353-
pd.read_excel(xl, "0")
354-
elif engine == "xlwt":
355-
import xlrd
356-
357-
msg = "No sheet named <'0'>"
358-
with pytest.raises(xlrd.XLRDError, match=msg):
359-
pd.read_excel(xl, sheet_name="0")
360-
else:
361-
with pytest.raises(KeyError, match="Worksheet 0 does not exist."):
362-
pd.read_excel(xl, sheet_name="0")
350+
msg = "Worksheet named '0' not found"
351+
with pytest.raises(ValueError, match=msg):
352+
pd.read_excel(xl, "0")
363353

364354
def test_excel_writer_context_manager(self, frame, path):
365355
with ExcelWriter(path) as writer:

pandas/tests/io/excel/test_xlrd.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ def test_read_xlrd_book(read_ext, frame):
4343
# TODO: test for openpyxl as well
4444
def test_excel_table_sheet_by_index(datapath, read_ext):
4545
path = datapath("io", "data", "excel", f"test1{read_ext}")
46+
msg = "Worksheet named 'invalid_sheet_name' not found"
4647
with ExcelFile(path, engine="xlrd") as excel:
47-
with pytest.raises(xlrd.XLRDError):
48-
pd.read_excel(excel, sheet_name="asdf")
48+
with pytest.raises(ValueError, match=msg):
49+
pd.read_excel(excel, sheet_name="invalid_sheet_name")
4950

5051

5152
def test_excel_file_warning_with_xlsx_file(datapath):

0 commit comments

Comments
 (0)