@@ -1272,6 +1272,80 @@ def test_excel_read_tables_with_and_without_blank_row(self, tmp_path):
12721272 def test_excel_read_tables_with_and_without_blank_row (self , engine_and_read_ext , tmp_path ):
12731273 """
12741274 GH-61123
1275+ """
1276+ def test_excel_read_tables_with_and_without_blank_row (self , engine_and_read_ext , tmp_path ):
1277+ engine , read_ext = engine_and_read_ext
1278+
1279+ # Skip incompatible engine/extension combinations
1280+ if engine == 'xlrd' and read_ext != '.xls' :
1281+ pytest .skip (f"Engine { engine } not compatible with { read_ext } " )
1282+ if engine == 'odf' and read_ext != '.ods' :
1283+ pytest .skip (f"Engine { engine } not compatible with { read_ext } " )
1284+ if engine == 'pyxlsb' and read_ext != '.xlsb' :
1285+ pytest .skip (f"Engine { engine } not compatible with { read_ext } " )
1286+
1287+ # Map reader engines to appropriate writer engines
1288+ writer_engine = None
1289+ if read_ext == '.xlsx' or read_ext == '.xlsm' :
1290+ writer_engine = 'openpyxl'
1291+ elif read_ext == '.xls' :
1292+ writer_engine = 'xlwt'
1293+ elif read_ext == '.xlsb' :
1294+ writer_engine = 'xlsxwriter' # Use xlsxwriter for xlsb files
1295+ elif read_ext == '.ods' :
1296+ writer_engine = 'odf'
1297+
1298+ if writer_engine is None :
1299+ pytest .skip (f"No writer engine available for { read_ext } " )
1300+
1301+ try :
1302+ # Create test files with tables with and without blank rows between them
1303+ # File 1: Two tables with a blank row between
1304+ file1 = tmp_path / f"test1{ read_ext } "
1305+ df_upper = pd .DataFrame ({"A" : [1 , 2 , 3 ], "B" : [4 , 5 , 6 ]})
1306+ df_lower = pd .DataFrame ({"A" : [7 , 8 , 9 ], "B" : [10 , 11 , 12 ]})
1307+
1308+ with pd .ExcelWriter (file1 , engine = writer_engine ) as writer :
1309+ df_upper .to_excel (writer , sheet_name = "Sheet1" , index = False )
1310+ # Add blank row by starting lower table at row 5 (0-based index + header)
1311+ df_lower .to_excel (writer , sheet_name = "Sheet1" , startrow = 5 , index = False )
1312+
1313+ # File 2: Two tables with no blank row
1314+ file2 = tmp_path / f"test2{ read_ext } "
1315+ with pd .ExcelWriter (file2 , engine = writer_engine ) as writer :
1316+ df_upper .to_excel (writer , sheet_name = "Sheet1" , index = False )
1317+ # No blank row, lower table starts right after (row 4 = header of second table)
1318+ df_lower .to_excel (writer , sheet_name = "Sheet1" , startrow = 4 , index = False )
1319+
1320+ # Read first 3 rows (header + 3 data rows)
1321+ # Using nrows=3 to get exactly the upper table without blank rows
1322+ df1 = pd .read_excel (file1 , header = 0 , nrows = 3 , engine = engine )
1323+ df2 = pd .read_excel (file2 , header = 0 , nrows = 3 , engine = engine )
1324+
1325+ # Expected data - just the upper table
1326+ expected = pd .DataFrame ({"A" : [1 , 2 , 3 ], "B" : [4 , 5 , 6 ]})
1327+
1328+ # Check content
1329+ tm .assert_frame_equal (df1 , expected )
1330+ tm .assert_frame_equal (df2 , expected )
1331+
1332+ # Verify we didn't read the header of the next table in df2
1333+ # If we did, the last row would contain column headers from the second table
1334+ assert df1 .shape == (3 , 2 ), f"Expected (3, 2) but got { df1 .shape } "
1335+ assert df2 .shape == (3 , 2 ), f"Expected (3, 2) but got { df2 .shape } "
1336+
1337+ # Fix the comparison warning by checking specific values instead
1338+ assert df2 .iloc [- 1 , 0 ] == 3 , f"Expected 3 but got { df2 .iloc [- 1 , 0 ]} "
1339+ assert df2 .iloc [- 1 , 1 ] == 6 , f"Expected 6 but got { df2 .iloc [- 1 , 1 ]} "
1340+ except ImportError :
1341+ pytest .skip (f"Required writer engine { writer_engine } not available" )
1342+ except ValueError as e :
1343+ if "No Excel writer" in str (e ):
1344+ pytest .skip (f"Excel writer { writer_engine } not available" )
1345+ else :
1346+ raise
1347+ """
1348+ GH-61123
12751349 Test that nrows parameter correctly handles adjacent tables with and without blank rows.
12761350 """
12771351 engine , read_ext = engine_and_read_ext
0 commit comments