Skip to content

Commit 9c33e5e

Browse files
authored
ERR: Unify error message for bad excel sheetnames (#39482)
1 parent ddbf377 commit 9c33e5e

File tree

10 files changed

+47
-17
lines changed

10 files changed

+47
-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
@@ -434,6 +434,17 @@ def get_sheet_by_index(self, index):
434434
def get_sheet_data(self, sheet, convert_float):
435435
pass
436436

437+
def raise_if_bad_sheet_by_index(self, index: int) -> None:
438+
n_sheets = len(self.sheet_names)
439+
if index >= n_sheets:
440+
raise ValueError(
441+
f"Worksheet index {index} is invalid, {n_sheets} worksheets found"
442+
)
443+
444+
def raise_if_bad_sheet_by_name(self, name: str) -> None:
445+
if name not in self.sheet_names:
446+
raise ValueError(f"Worksheet named '{name}' not found")
447+
437448
def parse(
438449
self,
439450
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
@@ -494,9 +494,11 @@ def sheet_names(self) -> List[str]:
494494
return self.book.sheetnames
495495

496496
def get_sheet_by_name(self, name: str):
497+
self.raise_if_bad_sheet_by_name(name)
497498
return self.book[name]
498499

499500
def get_sheet_by_index(self, index: int):
501+
self.raise_if_bad_sheet_by_index(index)
500502
return self.book.worksheets[index]
501503

502504
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
@@ -665,6 +665,16 @@ def test_bad_engine_raises(self, read_ext):
665665
with pytest.raises(ValueError, match="Unknown engine: foo"):
666666
pd.read_excel("", engine=bad_engine)
667667

668+
@pytest.mark.parametrize(
669+
"sheet_name",
670+
[3, [0, 3], [3, 0], "Sheet4", ["Sheet1", "Sheet4"], ["Sheet4", "Sheet1"]],
671+
)
672+
def test_bad_sheetname_raises(self, read_ext, sheet_name):
673+
# GH 39250
674+
msg = "Worksheet index 3 is invalid|Worksheet named 'Sheet4' not found"
675+
with pytest.raises(ValueError, match=msg):
676+
pd.read_excel("blank" + read_ext, sheet_name=sheet_name)
677+
668678
def test_missing_file_raises(self, read_ext):
669679
bad_file = f"foo{read_ext}"
670680
# CI tests with zh_CN.utf8, translates to "No such file or directory"
@@ -1263,6 +1273,17 @@ def test_sheet_name(self, request, read_ext, df_ref):
12631273
tm.assert_frame_equal(df1_parse, df_ref, check_names=False)
12641274
tm.assert_frame_equal(df2_parse, df_ref, check_names=False)
12651275

1276+
@pytest.mark.parametrize(
1277+
"sheet_name",
1278+
[3, [0, 3], [3, 0], "Sheet4", ["Sheet1", "Sheet4"], ["Sheet4", "Sheet1"]],
1279+
)
1280+
def test_bad_sheetname_raises(self, read_ext, sheet_name):
1281+
# GH 39250
1282+
msg = "Worksheet index 3 is invalid|Worksheet named 'Sheet4' not found"
1283+
with pytest.raises(ValueError, match=msg):
1284+
with pd.ExcelFile("blank" + read_ext) as excel:
1285+
excel.parse(sheet_name=sheet_name)
1286+
12661287
def test_excel_read_buffer(self, engine, read_ext):
12671288
pth = "test1" + read_ext
12681289
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

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ 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 = "No sheet named <'invalid_sheet_name'>"
46+
msg = "Worksheet named 'invalid_sheet_name' not found"
4747
with ExcelFile(path, engine="xlrd") as excel:
48-
with pytest.raises(xlrd.XLRDError, match=msg):
48+
with pytest.raises(ValueError, match=msg):
4949
pd.read_excel(excel, sheet_name="invalid_sheet_name")
5050

5151

0 commit comments

Comments
 (0)