Skip to content

Commit 1d896ed

Browse files
authored
[3.6] bpo-32228: Reset raw_pos after unwinding the raw stream (GH-4858) (#5389)
Ensure that ``truncate()`` preserves the file position (as reported by ``tell()``) after writes longer than the buffer size.. (cherry picked from commit 059f58c)
1 parent 33febfb commit 1d896ed

File tree

3 files changed

+28
-4
lines changed

3 files changed

+28
-4
lines changed

‎Lib/test/test_io.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,23 @@ def test_truncate(self):
17031703
with self.open(support.TESTFN, "rb", buffering=0) as f:
17041704
self.assertEqual(f.read(), b"abc")
17051705

1706+
def test_truncate_after_write(self):
1707+
# Ensure that truncate preserves the file position after
1708+
# writes longer than the buffer size.
1709+
# Issue: https://bugs.python.org/issue32228
1710+
with self.open(support.TESTFN, "wb") as f:
1711+
# Fill with some buffer
1712+
f.write(b'\x00' * 10000)
1713+
buffer_sizes = [8192, 4096, 200]
1714+
for buffer_size in buffer_sizes:
1715+
with self.open(support.TESTFN, "r+b", buffering=buffer_size) as f:
1716+
f.write(b'\x00' * (buffer_size + 1))
1717+
# After write write_pos and write_end are set to 0
1718+
f.read(1)
1719+
# read operation makes sure that pos != raw_pos
1720+
f.truncate()
1721+
self.assertEqual(f.tell(), buffer_size + 2)
1722+
17061723
@unittest.skipUnless(threading, 'Threading required for this test.')
17071724
@support.requires_resource('cpu')
17081725
def test_threads(self):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ensure that ``truncate()`` preserves the file position (as reported by ``tell()``) after writes longer than the buffer size.

‎Modules/_io/bufferedio.c‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,7 +1311,6 @@ _io__Buffered_seek_impl(buffered *self, PyObject *targetobj, int whence)
13111311
if (res == NULL)
13121312
goto end;
13131313
Py_CLEAR(res);
1314-
_bufferedwriter_reset_buf(self);
13151314
}
13161315

13171316
/* TODO: align on block boundary and read buffer if needed? */
@@ -1878,8 +1877,6 @@ _bufferedwriter_raw_write(buffered *self, char *start, Py_ssize_t len)
18781877
return n;
18791878
}
18801879

1881-
/* `restore_pos` is 1 if we need to restore the raw stream position at
1882-
the end, 0 otherwise. */
18831880
static PyObject *
18841881
_bufferedwriter_flush_unlocked(buffered *self)
18851882
{
@@ -1920,9 +1917,18 @@ _bufferedwriter_flush_unlocked(buffered *self)
19201917
goto error;
19211918
}
19221919

1923-
_bufferedwriter_reset_buf(self);
19241920

19251921
end:
1922+
/* This ensures that after return from this function,
1923+
VALID_WRITE_BUFFER(self) returns false.
1924+
1925+
This is a required condition because when a tell() is called
1926+
after flushing and if VALID_READ_BUFFER(self) is false, we need
1927+
VALID_WRITE_BUFFER(self) to be false to have
1928+
RAW_OFFSET(self) == 0.
1929+
1930+
Issue: https://bugs.python.org/issue32228 */
1931+
_bufferedwriter_reset_buf(self);
19261932
Py_RETURN_NONE;
19271933

19281934
error:

0 commit comments

Comments
 (0)