diff options
author | Inada Naoki <songofacandy@gmail.com> | 2019-11-27 13:22:06 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-11-27 13:22:06 (GMT) |
commit | ea9835c5d154ab6a54eed627958473b6768b28cc (patch) | |
tree | e65f74eab9ca75f936121582111232d970aa159c /Lib | |
parent | 1bddf890e595a865414645c6041733043c4081f8 (diff) | |
download | cpython-ea9835c5d154ab6a54eed627958473b6768b28cc.zip cpython-ea9835c5d154ab6a54eed627958473b6768b28cc.tar.gz cpython-ea9835c5d154ab6a54eed627958473b6768b28cc.tar.bz2 |
bpo-26730: Fix SpooledTemporaryFile data corruption (GH-17400)
SpooledTemporaryFile.rollback() might cause data corruption
when it is in text mode.
Co-Authored-By: Serhiy Storchaka <storchaka@gmail.com>
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/tempfile.py | 15 | ||||
-rw-r--r-- | Lib/test/test_tempfile.py | 25 |
2 files changed, 23 insertions, 17 deletions
diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 45709cb..6287554 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -633,10 +633,9 @@ class SpooledTemporaryFile: if 'b' in mode: self._file = _io.BytesIO() else: - # Setting newline="\n" avoids newline translation; - # this is important because otherwise on Windows we'd - # get double newline translation upon rollover(). - self._file = _io.StringIO(newline="\n") + self._file = _io.TextIOWrapper(_io.BytesIO(), + encoding=encoding, errors=errors, + newline=newline) self._max_size = max_size self._rolled = False self._TemporaryFileArgs = {'mode': mode, 'buffering': buffering, @@ -656,8 +655,12 @@ class SpooledTemporaryFile: newfile = self._file = TemporaryFile(**self._TemporaryFileArgs) del self._TemporaryFileArgs - newfile.write(file.getvalue()) - newfile.seek(file.tell(), 0) + pos = file.tell() + if hasattr(newfile, 'buffer'): + newfile.buffer.write(file.detach().getvalue()) + else: + newfile.write(file.getvalue()) + newfile.seek(pos, 0) self._rolled = True diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index f995f6c..232c5da 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1114,7 +1114,8 @@ class TestSpooledTemporaryFile(BaseTestCase): def test_text_mode(self): # Creating a SpooledTemporaryFile with a text mode should produce # a file object reading and writing (Unicode) text strings. - f = tempfile.SpooledTemporaryFile(mode='w+', max_size=10) + f = tempfile.SpooledTemporaryFile(mode='w+', max_size=10, + encoding="utf-8") f.write("abc\n") f.seek(0) self.assertEqual(f.read(), "abc\n") @@ -1124,9 +1125,9 @@ class TestSpooledTemporaryFile(BaseTestCase): self.assertFalse(f._rolled) self.assertEqual(f.mode, 'w+') self.assertIsNone(f.name) - self.assertIsNone(f.newlines) - self.assertIsNone(f.encoding) - self.assertIsNone(f.errors) + self.assertEqual(f.newlines, os.linesep) + self.assertEqual(f.encoding, "utf-8") + self.assertEqual(f.errors, "strict") f.write("xyzzy\n") f.seek(0) @@ -1139,8 +1140,8 @@ class TestSpooledTemporaryFile(BaseTestCase): self.assertEqual(f.mode, 'w+') self.assertIsNotNone(f.name) self.assertEqual(f.newlines, os.linesep) - self.assertIsNotNone(f.encoding) - self.assertIsNotNone(f.errors) + self.assertEqual(f.encoding, "utf-8") + self.assertEqual(f.errors, "strict") def test_text_newline_and_encoding(self): f = tempfile.SpooledTemporaryFile(mode='w+', max_size=10, @@ -1152,13 +1153,15 @@ class TestSpooledTemporaryFile(BaseTestCase): self.assertFalse(f._rolled) self.assertEqual(f.mode, 'w+') self.assertIsNone(f.name) - self.assertIsNone(f.newlines) - self.assertIsNone(f.encoding) - self.assertIsNone(f.errors) + self.assertIsNotNone(f.newlines) + self.assertEqual(f.encoding, "utf-8") + self.assertEqual(f.errors, "ignore") - f.write("\u039B" * 20 + "\r\n") + f.write("\u039C" * 10 + "\r\n") + f.write("\u039D" * 20) f.seek(0) - self.assertEqual(f.read(), "\u039B\r\n" + ("\u039B" * 20) + "\r\n") + self.assertEqual(f.read(), + "\u039B\r\n" + ("\u039C" * 10) + "\r\n" + ("\u039D" * 20)) self.assertTrue(f._rolled) self.assertEqual(f.mode, 'w+') self.assertIsNotNone(f.name) |