Skip to content

Commit b1e1885

Browse files
committed
Refactor code structure and add test for multiple CR cases
1 parent cdb9283 commit b1e1885

File tree

2 files changed

+43
-4
lines changed

2 files changed

+43
-4
lines changed

Lib/test/test_io/test_textio.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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\rline2\r', ['line1\n', '\n', 'line2\n']),
712+
(b'line1\r\r\rline2\r', ['line1\n', '\n', '\n', 'line2\n']),
713+
(b'line1\rline2\rline3\r', ['line1\n', 'line2\n', 'line3\n']),
714+
(b'\r\rdata\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")

Modules/_io/textio.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,12 +2847,12 @@ _io_TextIOWrapper_tell_impl(textio *self)
28472847
/* Skip the optimization if next_input is empty */
28482848
if (PyBytes_GET_SIZE(next_input) == 0) {
28492849
skip_bytes = 0;
2850-
}
2851-
else {
2850+
} else {
28522851
skip_back = 1;
28532852
assert(skip_back <= PyBytes_GET_SIZE(next_input));
28542853
input = PyBytes_AS_STRING(next_input);
2855-
while (skip_bytes > 0) {
2854+
}
2855+
while (skip_bytes > 0) {
28562856
/* Decode up to temptative start point */
28572857
if (_textiowrapper_decoder_setstate(self, &cookie) < 0)
28582858
goto fail;
@@ -2875,7 +2875,6 @@ _io_TextIOWrapper_tell_impl(textio *self)
28752875
skip_back *= 2;
28762876
}
28772877
}
2878-
}
28792878
if (skip_bytes <= 0) {
28802879
skip_bytes = 0;
28812880
if (_textiowrapper_decoder_setstate(self, &cookie) < 0)

0 commit comments

Comments
 (0)