@@ -704,6 +704,46 @@ def test_tell_after_readline_with_cr(self):
704704 remaining = f .read ()
705705 self .assertEqual (remaining , "" )
706706
707+ def test_tell_after_readline_with_multiple_cr (self ):
708+ # Test for gh-141314: TextIOWrapper.tell() assertion failure
709+ # when dealing with multiple standalone carriage returns
710+ test_cases = [
711+ (b'line1\r \r line2\r ' , ['line1\n ' , '\n ' , 'line2\n ' ]),
712+ (b'line1\r \r \r line2\r ' , ['line1\n ' , '\n ' , '\n ' , 'line2\n ' ]),
713+ (b'line1\r line2\r line3\r ' , ['line1\n ' , 'line2\n ' , 'line3\n ' ]),
714+ (b'\r \r data\r ' , ['\n ' , '\n ' , 'data\n ' ]),
715+ ]
716+
717+ for data , expected_lines in test_cases :
718+ with self .subTest (data = data ):
719+ with self .open (os_helper .TESTFN , "wb" ) as f :
720+ f .write (data )
721+
722+ with self .open (os_helper .TESTFN , "r" ) as f :
723+ # Read all lines and call tell() after each
724+ lines_read = []
725+ positions = []
726+ while True :
727+ pos_before = f .tell ()
728+ line = f .readline ()
729+ if not line :
730+ break
731+ lines_read .append (line )
732+ # This should not cause an assertion failure
733+ pos_after = f .tell ()
734+ positions .append ((pos_before , pos_after ))
735+
736+ # Verify lines read correctly
737+ self .assertEqual (lines_read , expected_lines )
738+
739+ # Verify we can seek back to each position
740+ f .seek (0 )
741+ for i , (pos_before , pos_after ) in enumerate (positions ):
742+ f .seek (pos_before )
743+ line = f .readline ()
744+ self .assertEqual (line , expected_lines [i ])
745+ self .assertEqual (f .tell (), pos_after )
746+
707747 def test_seek_with_encoder_state (self ):
708748 f = self .open (os_helper .TESTFN , "w" , encoding = "euc_jis_2004" )
709749 f .write ("\u00e6 \u0300 " )
0 commit comments