@@ -1272,6 +1272,80 @@ def test_excel_read_tables_with_and_without_blank_row(self, tmp_path):
1272
1272
def test_excel_read_tables_with_and_without_blank_row (self , engine_and_read_ext , tmp_path ):
1273
1273
"""
1274
1274
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
1275
1349
Test that nrows parameter correctly handles adjacent tables with and without blank rows.
1276
1350
"""
1277
1351
engine , read_ext = engine_and_read_ext
0 commit comments