From 059f58ce938d9c3f0286412a4efb1b9131339421 Mon Sep 17 00:00:00 2001 From: Nitish Chandra Date: Sun, 28 Jan 2018 21:30:09 +0530 Subject: bpo-32228: Reset raw_pos after unwinding the raw stream (#4858) Ensure that ``truncate()`` preserves the file position (as reported by ``tell()``) after writes longer than the buffer size. --- Lib/test/test_io.py | 17 +++++++++++++++++ .../Library/2017-12-22-16-47-41.bpo-32228.waPx3q.rst | 1 + Modules/_io/bufferedio.c | 14 ++++++++++---- 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2017-12-22-16-47-41.bpo-32228.waPx3q.rst diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index c6345e9..4be5631 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1723,6 +1723,23 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests): with self.open(support.TESTFN, "rb", buffering=0) as f: self.assertEqual(f.read(), b"abc") + def test_truncate_after_write(self): + # Ensure that truncate preserves the file position after + # writes longer than the buffer size. + # Issue: https://bugs.python.org/issue32228 + with self.open(support.TESTFN, "wb") as f: + # Fill with some buffer + f.write(b'\x00' * 10000) + buffer_sizes = [8192, 4096, 200] + for buffer_size in buffer_sizes: + with self.open(support.TESTFN, "r+b", buffering=buffer_size) as f: + f.write(b'\x00' * (buffer_size + 1)) + # After write write_pos and write_end are set to 0 + f.read(1) + # read operation makes sure that pos != raw_pos + f.truncate() + self.assertEqual(f.tell(), buffer_size + 2) + @support.requires_resource('cpu') def test_threads(self): try: diff --git a/Misc/NEWS.d/next/Library/2017-12-22-16-47-41.bpo-32228.waPx3q.rst b/Misc/NEWS.d/next/Library/2017-12-22-16-47-41.bpo-32228.waPx3q.rst new file mode 100644 index 0000000..3bbe7c4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-12-22-16-47-41.bpo-32228.waPx3q.rst @@ -0,0 +1 @@ +Ensure that ``truncate()`` preserves the file position (as reported by ``tell()``) after writes longer than the buffer size. diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 2b7aaaf..358a654 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -1292,7 +1292,6 @@ _io__Buffered_seek_impl(buffered *self, PyObject *targetobj, int whence) if (res == NULL) goto end; Py_CLEAR(res); - _bufferedwriter_reset_buf(self); } /* TODO: align on block boundary and read buffer if needed? */ @@ -1852,8 +1851,6 @@ _bufferedwriter_raw_write(buffered *self, char *start, Py_ssize_t len) return n; } -/* `restore_pos` is 1 if we need to restore the raw stream position at - the end, 0 otherwise. */ static PyObject * _bufferedwriter_flush_unlocked(buffered *self) { @@ -1894,9 +1891,18 @@ _bufferedwriter_flush_unlocked(buffered *self) goto error; } - _bufferedwriter_reset_buf(self); end: + /* This ensures that after return from this function, + VALID_WRITE_BUFFER(self) returns false. + + This is a required condition because when a tell() is called + after flushing and if VALID_READ_BUFFER(self) is false, we need + VALID_WRITE_BUFFER(self) to be false to have + RAW_OFFSET(self) == 0. + + Issue: https://bugs.python.org/issue32228 */ + _bufferedwriter_reset_buf(self); Py_RETURN_NONE; error: -- cgit v0.12