From 9994e3c7e82c7cd8516844be9a089ed97822c9f6 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Fri, 28 Jun 2019 17:50:37 -0500 Subject: [PATCH 1/6] Decoupled xlrd tests from openpyxl --- pandas/tests/io/excel/test_readers.py | 65 ++++++++++++++------------- pandas/tests/io/excel/test_xlrd.py | 17 +++++++ 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/pandas/tests/io/excel/test_readers.py b/pandas/tests/io/excel/test_readers.py index 579f39e21d3c1..31fd5a2be74c9 100644 --- a/pandas/tests/io/excel/test_readers.py +++ b/pandas/tests/io/excel/test_readers.py @@ -15,7 +15,6 @@ import pandas.util.testing as tm from pandas.io.common import URLError -from pandas.io.excel import ExcelFile @contextlib.contextmanager @@ -736,17 +735,20 @@ class TestExcelFileRead: pytest.param(None, marks=pytest.mark.skipif( not td.safe_import("xlrd"), reason="no xlrd")), ]) - def cd_and_set_engine(self, request, datapath, monkeypatch): + def cd_and_set_engine(self, request, datapath, monkeypatch, read_ext): """ Change directory and set engine for ExcelFile objects. """ + if request.param == 'openpyxl' and read_ext == '.xls': + pytest.skip() + func = partial(pd.ExcelFile, engine=request.param) monkeypatch.chdir(datapath("io", "data")) monkeypatch.setattr(pd, 'ExcelFile', func) def test_excel_passes_na(self, read_ext): - excel = ExcelFile('test4' + read_ext) + excel = pd.ExcelFile('test4' + read_ext) parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=False, na_values=['apple']) @@ -754,6 +756,7 @@ def test_excel_passes_na(self, read_ext): columns=['Test']) tm.assert_frame_equal(parsed, expected) + excel = pd.ExcelFile('test4' + read_ext) parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=True, na_values=['apple']) expected = DataFrame([[np.nan], [1], [np.nan], [np.nan], ['rabbit']], @@ -761,7 +764,7 @@ def test_excel_passes_na(self, read_ext): tm.assert_frame_equal(parsed, expected) # 13967 - excel = ExcelFile('test5' + read_ext) + excel = pd.ExcelFile('test5' + read_ext) parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=False, na_values=['apple']) @@ -769,6 +772,7 @@ def test_excel_passes_na(self, read_ext): columns=['Test']) tm.assert_frame_equal(parsed, expected) + excel = pd.ExcelFile('test5' + read_ext) parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=True, na_values=['apple']) expected = DataFrame([[np.nan], [1], [np.nan], [np.nan], ['rabbit']], @@ -778,7 +782,7 @@ def test_excel_passes_na(self, read_ext): @pytest.mark.parametrize('arg', ['sheet', 'sheetname', 'parse_cols']) def test_unexpected_kwargs_raises(self, read_ext, arg): # gh-17964 - excel = ExcelFile('test1' + read_ext) + excel = pd.ExcelFile('test1' + read_ext) kwarg = {arg: 'Sheet1'} msg = "unexpected keyword argument `{}`".format(arg) @@ -787,38 +791,39 @@ def test_unexpected_kwargs_raises(self, read_ext, arg): def test_excel_table_sheet_by_index(self, read_ext, df_ref): - excel = ExcelFile('test1' + read_ext) + excel = pd.ExcelFile('test1' + read_ext) df1 = pd.read_excel(excel, 0, index_col=0) df2 = pd.read_excel(excel, 1, skiprows=[1], index_col=0) tm.assert_frame_equal(df1, df_ref, check_names=False) tm.assert_frame_equal(df2, df_ref, check_names=False) + excel = pd.ExcelFile('test1' + read_ext) df1 = excel.parse(0, index_col=0) df2 = excel.parse(1, skiprows=[1], index_col=0) tm.assert_frame_equal(df1, df_ref, check_names=False) tm.assert_frame_equal(df2, df_ref, check_names=False) + excel = pd.ExcelFile('test1' + read_ext) df3 = pd.read_excel(excel, 0, index_col=0, skipfooter=1) tm.assert_frame_equal(df3, df1.iloc[:-1]) + excel = pd.ExcelFile('test1' + read_ext) with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): df4 = pd.read_excel(excel, 0, index_col=0, skip_footer=1) tm.assert_frame_equal(df3, df4) + excel = pd.ExcelFile('test1' + read_ext) df3 = excel.parse(0, index_col=0, skipfooter=1) tm.assert_frame_equal(df3, df1.iloc[:-1]) - import xlrd # will move to engine-specific tests as new ones are added - with pytest.raises(xlrd.XLRDError): - pd.read_excel(excel, 'asdf') - def test_sheet_name(self, read_ext, df_ref): filename = "test1" sheet_name = "Sheet1" - excel = ExcelFile(filename + read_ext) + excel = pd.ExcelFile(filename + read_ext) df1_parse = excel.parse(sheet_name=sheet_name, index_col=0) # doc + excel = pd.ExcelFile(filename + read_ext) df2_parse = excel.parse(index_col=0, sheet_name=sheet_name) @@ -826,31 +831,27 @@ def test_sheet_name(self, read_ext, df_ref): tm.assert_frame_equal(df2_parse, df_ref, check_names=False) def test_excel_read_buffer(self, read_ext): - pth = 'test1' + read_ext - expected = pd.read_excel(pth, 'Sheet1', index_col=0) + engine = pd.ExcelFile.keywords['engine'] # TODO: fixturize + expected = pd.read_excel(pth, 'Sheet1', index_col=0, engine=engine) with open(pth, 'rb') as f: - xls = ExcelFile(f) + xls = pd.ExcelFile(f) actual = pd.read_excel(xls, 'Sheet1', index_col=0) tm.assert_frame_equal(expected, actual) - def test_reader_closes_file(self, read_ext): - - f = open('test1' + read_ext, 'rb') - with ExcelFile(f) as xlsx: - # parses okay - pd.read_excel(xlsx, 'Sheet1', index_col=0) - - assert f.closed - @pytest.mark.parametrize('excel_engine', [ - 'xlrd', - None - ]) - def test_read_excel_engine_value(self, read_ext, excel_engine): - # GH 26566 - xl = ExcelFile("test1" + read_ext, engine=excel_engine) - msg = "Engine should not be specified when passing an ExcelFile" - with pytest.raises(ValueError, match=msg): - pd.read_excel(xl, engine='openpyxl') +@td.skip_if_no("openpyxl") +@pytest.mark.parametrize('excel_engine', [ + pytest.param('xlrd', marks=pytest.mark.skipif( + not td.safe_import("xlrd"), reason="no xlrd")), + pytest.param(None, marks=pytest.mark.skipif( + not td.safe_import("xlrd"), reason="no xlrd")), +]) +def test_conflicting_excel_engines(read_ext, excel_engine, datapath): + # GH 26566 + path = datapath("io", "data", 'test1{}'.format(read_ext)) + xl = pd.ExcelFile(path, engine=excel_engine) + msg = "Engine should not be specified when passing an ExcelFile" + with pytest.raises(ValueError, match=msg): + pd.read_excel(xl, engine='openpyxl') diff --git a/pandas/tests/io/excel/test_xlrd.py b/pandas/tests/io/excel/test_xlrd.py index b9fc9305a4033..a46e458da4ab8 100644 --- a/pandas/tests/io/excel/test_xlrd.py +++ b/pandas/tests/io/excel/test_xlrd.py @@ -27,3 +27,20 @@ def test_read_xlrd_book(read_ext, frame): result = pd.read_excel(book, sheet_name=sheet_name, engine=engine, index_col=0) tm.assert_frame_equal(df, result) + + +# TODO: test for openpyxl as well +def test_excel_table_sheet_by_index(datapath, read_ext): + excel = pd.ExcelFile(datapath("io", "data", 'test1{}'.format(read_ext))) + with pytest.raises(xlrd.XLRDError): + pd.read_excel(excel, 'asdf') + + +# TODO: test for openpyxl as well +def test_reader_closes_file(datapath, read_ext): + f = open(datapath("io", "data", 'test1{}'.format(read_ext)), 'rb') + with pd.ExcelFile(f, engine='xlrd') as xlsx: + # parses okay + pd.read_excel(xlsx, 'Sheet1', index_col=0, engine='xlrd') + + assert f.closed From 66363b4bb26a3f19bc306d4a8fab0352f072f429 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Sat, 29 Jun 2019 09:39:02 -0500 Subject: [PATCH 2/6] Converted all ExcelFile reads to CM --- pandas/tests/io/excel/test_readers.py | 84 ++++++++++++++------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/pandas/tests/io/excel/test_readers.py b/pandas/tests/io/excel/test_readers.py index 31fd5a2be74c9..9f9f67e2e3c29 100644 --- a/pandas/tests/io/excel/test_readers.py +++ b/pandas/tests/io/excel/test_readers.py @@ -748,33 +748,31 @@ def cd_and_set_engine(self, request, datapath, monkeypatch, read_ext): def test_excel_passes_na(self, read_ext): - excel = pd.ExcelFile('test4' + read_ext) - - parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=False, - na_values=['apple']) + with pd.ExcelFile('test4' + read_ext) as excel: + parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=False, + na_values=['apple']) expected = DataFrame([['NA'], [1], ['NA'], [np.nan], ['rabbit']], columns=['Test']) tm.assert_frame_equal(parsed, expected) - excel = pd.ExcelFile('test4' + read_ext) - parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=True, - na_values=['apple']) + with pd.ExcelFile('test4' + read_ext) as excel: + parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=True, + na_values=['apple']) expected = DataFrame([[np.nan], [1], [np.nan], [np.nan], ['rabbit']], columns=['Test']) tm.assert_frame_equal(parsed, expected) # 13967 - excel = pd.ExcelFile('test5' + read_ext) - - parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=False, - na_values=['apple']) + with pd.ExcelFile('test5' + read_ext) as excel: + parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=False, + na_values=['apple']) expected = DataFrame([['1.#QNAN'], [1], ['nan'], [np.nan], ['rabbit']], columns=['Test']) tm.assert_frame_equal(parsed, expected) - excel = pd.ExcelFile('test5' + read_ext) - parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=True, - na_values=['apple']) + with pd.ExcelFile('test5' + read_ext) as excel: + parsed = pd.read_excel(excel, 'Sheet1', keep_default_na=True, + na_values=['apple']) expected = DataFrame([[np.nan], [1], [np.nan], [np.nan], ['rabbit']], columns=['Test']) tm.assert_frame_equal(parsed, expected) @@ -782,50 +780,52 @@ def test_excel_passes_na(self, read_ext): @pytest.mark.parametrize('arg', ['sheet', 'sheetname', 'parse_cols']) def test_unexpected_kwargs_raises(self, read_ext, arg): # gh-17964 - excel = pd.ExcelFile('test1' + read_ext) - kwarg = {arg: 'Sheet1'} msg = "unexpected keyword argument `{}`".format(arg) - with pytest.raises(TypeError, match=msg): - pd.read_excel(excel, **kwarg) + + with pd.ExcelFile('test1' + read_ext) as excel: + with pytest.raises(TypeError, match=msg): + pd.read_excel(excel, **kwarg) def test_excel_table_sheet_by_index(self, read_ext, df_ref): - excel = pd.ExcelFile('test1' + read_ext) - - df1 = pd.read_excel(excel, 0, index_col=0) - df2 = pd.read_excel(excel, 1, skiprows=[1], index_col=0) + with pd.ExcelFile('test1' + read_ext) as excel: + df1 = pd.read_excel(excel, 0, index_col=0) + df2 = pd.read_excel(excel, 1, skiprows=[1], index_col=0) tm.assert_frame_equal(df1, df_ref, check_names=False) tm.assert_frame_equal(df2, df_ref, check_names=False) - excel = pd.ExcelFile('test1' + read_ext) - df1 = excel.parse(0, index_col=0) - df2 = excel.parse(1, skiprows=[1], index_col=0) + with pd.ExcelFile('test1' + read_ext) as excel: + df1 = excel.parse(0, index_col=0) + df2 = excel.parse(1, skiprows=[1], index_col=0) tm.assert_frame_equal(df1, df_ref, check_names=False) tm.assert_frame_equal(df2, df_ref, check_names=False) - excel = pd.ExcelFile('test1' + read_ext) - df3 = pd.read_excel(excel, 0, index_col=0, skipfooter=1) + with pd.ExcelFile('test1' + read_ext) as excel: + df3 = pd.read_excel(excel, 0, index_col=0, skipfooter=1) tm.assert_frame_equal(df3, df1.iloc[:-1]) - excel = pd.ExcelFile('test1' + read_ext) with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - df4 = pd.read_excel(excel, 0, index_col=0, skip_footer=1) + with pd.ExcelFile('test1' + read_ext) as excel: + df4 = pd.read_excel(excel, 0, index_col=0, skip_footer=1) + tm.assert_frame_equal(df3, df4) - excel = pd.ExcelFile('test1' + read_ext) - df3 = excel.parse(0, index_col=0, skipfooter=1) + with pd.ExcelFile('test1' + read_ext) as excel: + df3 = excel.parse(0, index_col=0, skipfooter=1) + tm.assert_frame_equal(df3, df1.iloc[:-1]) def test_sheet_name(self, read_ext, df_ref): filename = "test1" sheet_name = "Sheet1" - excel = pd.ExcelFile(filename + read_ext) - df1_parse = excel.parse(sheet_name=sheet_name, index_col=0) # doc - excel = pd.ExcelFile(filename + read_ext) - df2_parse = excel.parse(index_col=0, - sheet_name=sheet_name) + with pd.ExcelFile(filename + read_ext) as excel: + df1_parse = excel.parse(sheet_name=sheet_name, index_col=0) # doc + + with pd.ExcelFile(filename + read_ext) as excel: + df2_parse = excel.parse(index_col=0, + sheet_name=sheet_name) tm.assert_frame_equal(df1_parse, df_ref, check_names=False) tm.assert_frame_equal(df2_parse, df_ref, check_names=False) @@ -836,8 +836,9 @@ def test_excel_read_buffer(self, read_ext): expected = pd.read_excel(pth, 'Sheet1', index_col=0, engine=engine) with open(pth, 'rb') as f: - xls = pd.ExcelFile(f) - actual = pd.read_excel(xls, 'Sheet1', index_col=0) + with pd.ExcelFile(f) as xls: + actual = pd.read_excel(xls, 'Sheet1', index_col=0) + tm.assert_frame_equal(expected, actual) @@ -851,7 +852,8 @@ def test_excel_read_buffer(self, read_ext): def test_conflicting_excel_engines(read_ext, excel_engine, datapath): # GH 26566 path = datapath("io", "data", 'test1{}'.format(read_ext)) - xl = pd.ExcelFile(path, engine=excel_engine) msg = "Engine should not be specified when passing an ExcelFile" - with pytest.raises(ValueError, match=msg): - pd.read_excel(xl, engine='openpyxl') + + with pd.ExcelFile(path, engine=excel_engine) as xl: + with pytest.raises(ValueError, match=msg): + pd.read_excel(xl, engine='openpyxl') From 559f13c794472c4af303ba98c8edae521a1dce16 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Sat, 29 Jun 2019 09:46:06 -0500 Subject: [PATCH 3/6] Cleaned up non-xlrd failures --- pandas/tests/io/excel/test_readers.py | 9 +++++++++ pandas/tests/io/excel/test_writers.py | 2 ++ pandas/tests/io/excel/test_xlrd.py | 10 ---------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pandas/tests/io/excel/test_readers.py b/pandas/tests/io/excel/test_readers.py index 9f9f67e2e3c29..de2deafa10e20 100644 --- a/pandas/tests/io/excel/test_readers.py +++ b/pandas/tests/io/excel/test_readers.py @@ -841,6 +841,15 @@ def test_excel_read_buffer(self, read_ext): tm.assert_frame_equal(expected, actual) + def test_reader_closes_file(self, read_ext): + f = open('test1' + read_ext, 'rb') + engine = pd.ExcelFile.keywords['engine'] # TODO: fixturize + with pd.ExcelFile(f) as xlsx: + # parses okay + pd.read_excel(xlsx, 'Sheet1', index_col=0, engine=engine) + + assert f.closed + @td.skip_if_no("openpyxl") @pytest.mark.parametrize('excel_engine', [ diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index a4fdcdf70a3ea..45f7ebb22a0be 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -250,6 +250,7 @@ class and any subclasses, on account of the `autouse=True` set_option(option_name, prev_engine) # Roll back option change +@td.skip_if_no('xlrd') @pytest.mark.parametrize("engine,ext", [ pytest.param('openpyxl', '.xlsx', marks=pytest.mark.skipif( not td.safe_import('openpyxl'), reason='No openpyxl')), @@ -1237,6 +1238,7 @@ def check_called(func): 'something.xls', engine='dummy')) +@td.skip_if_no('xlrd') @td.skip_if_no('openpyxl') @pytest.mark.skipif(not PY36, reason='requires fspath') class TestFSPath: diff --git a/pandas/tests/io/excel/test_xlrd.py b/pandas/tests/io/excel/test_xlrd.py index a46e458da4ab8..1132bd5d17c3a 100644 --- a/pandas/tests/io/excel/test_xlrd.py +++ b/pandas/tests/io/excel/test_xlrd.py @@ -34,13 +34,3 @@ def test_excel_table_sheet_by_index(datapath, read_ext): excel = pd.ExcelFile(datapath("io", "data", 'test1{}'.format(read_ext))) with pytest.raises(xlrd.XLRDError): pd.read_excel(excel, 'asdf') - - -# TODO: test for openpyxl as well -def test_reader_closes_file(datapath, read_ext): - f = open(datapath("io", "data", 'test1{}'.format(read_ext)), 'rb') - with pd.ExcelFile(f, engine='xlrd') as xlsx: - # parses okay - pd.read_excel(xlsx, 'Sheet1', index_col=0, engine='xlrd') - - assert f.closed From d32ac00f12a212a6d843c025c11824d654f34a53 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Sat, 29 Jun 2019 09:48:12 -0500 Subject: [PATCH 4/6] lint fixup --- pandas/tests/io/excel/test_readers.py | 4 ++-- pandas/tests/io/excel/test_writers.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/tests/io/excel/test_readers.py b/pandas/tests/io/excel/test_readers.py index de2deafa10e20..73d4d4cf2b0a4 100644 --- a/pandas/tests/io/excel/test_readers.py +++ b/pandas/tests/io/excel/test_readers.py @@ -782,7 +782,7 @@ def test_unexpected_kwargs_raises(self, read_ext, arg): # gh-17964 kwarg = {arg: 'Sheet1'} msg = "unexpected keyword argument `{}`".format(arg) - + with pd.ExcelFile('test1' + read_ext) as excel: with pytest.raises(TypeError, match=msg): pd.read_excel(excel, **kwarg) @@ -806,7 +806,7 @@ def test_excel_table_sheet_by_index(self, read_ext, df_ref): tm.assert_frame_equal(df3, df1.iloc[:-1]) with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - with pd.ExcelFile('test1' + read_ext) as excel: + with pd.ExcelFile('test1' + read_ext) as excel: df4 = pd.read_excel(excel, 0, index_col=0, skip_footer=1) tm.assert_frame_equal(df3, df4) diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index 45f7ebb22a0be..a0f0314b04e64 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -250,7 +250,7 @@ class and any subclasses, on account of the `autouse=True` set_option(option_name, prev_engine) # Roll back option change -@td.skip_if_no('xlrd') +@td.skip_if_no('xlrd') @pytest.mark.parametrize("engine,ext", [ pytest.param('openpyxl', '.xlsx', marks=pytest.mark.skipif( not td.safe_import('openpyxl'), reason='No openpyxl')), @@ -1238,7 +1238,7 @@ def check_called(func): 'something.xls', engine='dummy')) -@td.skip_if_no('xlrd') +@td.skip_if_no('xlrd') @td.skip_if_no('openpyxl') @pytest.mark.skipif(not PY36, reason='requires fspath') class TestFSPath: From cf7eaf068b358e3076856ecfe7f065057a1a742d Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Sat, 29 Jun 2019 09:56:29 -0500 Subject: [PATCH 5/6] Context mgr in test_xlrd --- pandas/tests/io/excel/test_xlrd.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pandas/tests/io/excel/test_xlrd.py b/pandas/tests/io/excel/test_xlrd.py index 1132bd5d17c3a..94e1435d4dfab 100644 --- a/pandas/tests/io/excel/test_xlrd.py +++ b/pandas/tests/io/excel/test_xlrd.py @@ -31,6 +31,7 @@ def test_read_xlrd_book(read_ext, frame): # TODO: test for openpyxl as well def test_excel_table_sheet_by_index(datapath, read_ext): - excel = pd.ExcelFile(datapath("io", "data", 'test1{}'.format(read_ext))) - with pytest.raises(xlrd.XLRDError): - pd.read_excel(excel, 'asdf') + path = datapath("io", "data", 'test1{}'.format(read_ext)) + with pd.ExcelFile(path) as excel: + with pytest.raises(xlrd.XLRDError): + pd.read_excel(excel, 'asdf') From cb52c3a513828850316e3625cd85a108dd0d44d5 Mon Sep 17 00:00:00 2001 From: Will Ayd Date: Sat, 29 Jun 2019 10:02:43 -0500 Subject: [PATCH 6/6] Simplified test_conflicting_excel_engines --- pandas/tests/io/excel/test_readers.py | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/pandas/tests/io/excel/test_readers.py b/pandas/tests/io/excel/test_readers.py index 73d4d4cf2b0a4..40a6970aa7f04 100644 --- a/pandas/tests/io/excel/test_readers.py +++ b/pandas/tests/io/excel/test_readers.py @@ -850,19 +850,10 @@ def test_reader_closes_file(self, read_ext): assert f.closed + def test_conflicting_excel_engines(self, read_ext): + # GH 26566 + msg = "Engine should not be specified when passing an ExcelFile" -@td.skip_if_no("openpyxl") -@pytest.mark.parametrize('excel_engine', [ - pytest.param('xlrd', marks=pytest.mark.skipif( - not td.safe_import("xlrd"), reason="no xlrd")), - pytest.param(None, marks=pytest.mark.skipif( - not td.safe_import("xlrd"), reason="no xlrd")), -]) -def test_conflicting_excel_engines(read_ext, excel_engine, datapath): - # GH 26566 - path = datapath("io", "data", 'test1{}'.format(read_ext)) - msg = "Engine should not be specified when passing an ExcelFile" - - with pd.ExcelFile(path, engine=excel_engine) as xl: - with pytest.raises(ValueError, match=msg): - pd.read_excel(xl, engine='openpyxl') + with pd.ExcelFile("test1" + read_ext) as xl: + with pytest.raises(ValueError, match=msg): + pd.read_excel(xl, engine='foo')