diff options
author | Ma Lin <animalize@users.noreply.github.com> | 2021-06-22 21:57:41 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-22 16:57:41 +0300 |
commit | 8bc26d8c9d092840054f57f9b4620de0d40d8423 (patch) | |
tree | 3dc513f095270c7efc8d44fcb02e4dc0349f23df | |
parent | 0ff487b8abe70f091285acf367b795861eed8049 (diff) | |
download | cpython-git-8bc26d8c9d092840054f57f9b4620de0d40d8423.tar.gz |
bpo-44439: BZ2File.write()/LZMAFile.write() handle length correctly (GH-26846)
No longer use len() to get the length of the input data. For some buffer protocol objects, the length obtained by using len() is wrong.
Co-authored-by: Marco Ribeiro <marcoffee@users.noreply.github.com>
-rw-r--r-- | Lib/bz2.py | 16 | ||||
-rw-r--r-- | Lib/lzma.py | 16 | ||||
-rw-r--r-- | Lib/test/test_bz2.py | 9 | ||||
-rw-r--r-- | Lib/test/test_lzma.py | 9 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2021-06-17-15-01-51.bpo-44439.1S7QhT.rst | 3 |
5 files changed, 45 insertions, 8 deletions
diff --git a/Lib/bz2.py b/Lib/bz2.py index ce07ebeb14..7447d12fc4 100644 --- a/Lib/bz2.py +++ b/Lib/bz2.py @@ -226,15 +226,23 @@ class BZ2File(_compression.BaseStream): """Write a byte string to the file. Returns the number of uncompressed bytes written, which is - always len(data). Note that due to buffering, the file on disk - may not reflect the data written until close() is called. + always the length of data in bytes. Note that due to buffering, + the file on disk may not reflect the data written until close() + is called. """ with self._lock: self._check_can_write() + if isinstance(data, (bytes, bytearray)): + length = len(data) + else: + # accept any data that supports the buffer protocol + data = memoryview(data) + length = data.nbytes + compressed = self._compressor.compress(data) self._fp.write(compressed) - self._pos += len(data) - return len(data) + self._pos += length + return length def writelines(self, seq): """Write a sequence of byte strings to the file. diff --git a/Lib/lzma.py b/Lib/lzma.py index 0817b872d2..0aa30fe87f 100644 --- a/Lib/lzma.py +++ b/Lib/lzma.py @@ -225,14 +225,22 @@ class LZMAFile(_compression.BaseStream): """Write a bytes object to the file. Returns the number of uncompressed bytes written, which is - always len(data). Note that due to buffering, the file on disk - may not reflect the data written until close() is called. + always the length of data in bytes. Note that due to buffering, + the file on disk may not reflect the data written until close() + is called. """ self._check_can_write() + if isinstance(data, (bytes, bytearray)): + length = len(data) + else: + # accept any data that supports the buffer protocol + data = memoryview(data) + length = data.nbytes + compressed = self._compressor.compress(data) self._fp.write(compressed) - self._pos += len(data) - return len(data) + self._pos += length + return length def seek(self, offset, whence=io.SEEK_SET): """Change the file position. diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index 1fce9d82d2..c84f70ebb0 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -1,6 +1,7 @@ from test import support from test.support import bigmemtest, _4G +import array import unittest from io import BytesIO, DEFAULT_BUFFER_SIZE import os @@ -618,6 +619,14 @@ class BZ2FileTest(BaseTest): with BZ2File(BytesIO(truncated[:i])) as f: self.assertRaises(EOFError, f.read, 1) + def test_issue44439(self): + q = array.array('Q', [1, 2, 3, 4, 5]) + LENGTH = len(q) * q.itemsize + + with BZ2File(BytesIO(), 'w') as f: + self.assertEqual(f.write(q), LENGTH) + self.assertEqual(f.tell(), LENGTH) + class BZ2CompressorTest(BaseTest): def testCompress(self): diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index 0f3af27efa..ef7dd6325d 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -1,4 +1,5 @@ import _compression +import array from io import BytesIO, UnsupportedOperation, DEFAULT_BUFFER_SIZE import os import pathlib @@ -1227,6 +1228,14 @@ class FileTestCase(unittest.TestCase): self.assertTrue(d2.eof) self.assertEqual(out1 + out2, entire) + def test_issue44439(self): + q = array.array('Q', [1, 2, 3, 4, 5]) + LENGTH = len(q) * q.itemsize + + with LZMAFile(BytesIO(), 'w') as f: + self.assertEqual(f.write(q), LENGTH) + self.assertEqual(f.tell(), LENGTH) + class OpenTestCase(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2021-06-17-15-01-51.bpo-44439.1S7QhT.rst b/Misc/NEWS.d/next/Library/2021-06-17-15-01-51.bpo-44439.1S7QhT.rst new file mode 100644 index 0000000000..2739668370 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-06-17-15-01-51.bpo-44439.1S7QhT.rst @@ -0,0 +1,3 @@ +Fix in :meth:`bz2.BZ2File.write` / :meth:`lzma.LZMAFile.write` methods, when +the input data is an object that supports the buffer protocol, the file length +may be wrong. |