Skip to content

Commit 225cc4c

Browse files
[3.12] GH-105808: Fix a regression introduced in GH-101251 (GH-105910) (#105920)
GH-105808: Fix a regression introduced in GH-101251 (GH-105910) Fix a regression introduced in GH-101251, causing GzipFile.flush() to not flush the compressor (nor pass along the zip_mode argument). (cherry picked from commit 1858db7) Co-authored-by: T. Wouters <[email protected]>
1 parent 5e524ef commit 225cc4c

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-1
lines changed

Lib/gzip.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,9 @@ def close(self):
370370
def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
371371
self._check_not_closed()
372372
if self.mode == WRITE:
373-
# Ensure the compressor's buffer is flushed
374373
self._buffer.flush()
374+
# Ensure the compressor's buffer is flushed
375+
self.fileobj.write(self.compress.flush(zlib_mode))
375376
self.fileobj.flush()
376377

377378
def fileno(self):

Lib/test/test_gzip.py

+49
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import struct
1010
import sys
1111
import unittest
12+
import zlib
1213
from subprocess import PIPE, Popen
1314
from test.support import import_helper
1415
from test.support import os_helper
@@ -616,6 +617,54 @@ def test_issue44439(self):
616617
self.assertEqual(f.write(q), LENGTH)
617618
self.assertEqual(f.tell(), LENGTH)
618619

620+
def test_flush_flushes_compressor(self):
621+
# See issue GH-105808.
622+
b = io.BytesIO()
623+
message = b"important message here."
624+
with gzip.GzipFile(fileobj=b, mode='w') as f:
625+
f.write(message)
626+
f.flush()
627+
partial_data = b.getvalue()
628+
full_data = b.getvalue()
629+
self.assertEqual(gzip.decompress(full_data), message)
630+
# The partial data should contain the gzip header and the complete
631+
# message, but not the end-of-stream markers (so we can't just
632+
# decompress it directly).
633+
with self.assertRaises(EOFError):
634+
gzip.decompress(partial_data)
635+
d = zlib.decompressobj(wbits=-zlib.MAX_WBITS)
636+
f = io.BytesIO(partial_data)
637+
gzip._read_gzip_header(f)
638+
read_message = d.decompress(f.read())
639+
self.assertEqual(read_message, message)
640+
641+
def test_flush_modes(self):
642+
# Make sure the argument to flush is properly passed to the
643+
# zlib.compressobj; see issue GH-105808.
644+
class FakeCompressor:
645+
def __init__(self):
646+
self.modes = []
647+
def compress(self, data):
648+
return b''
649+
def flush(self, mode=-1):
650+
self.modes.append(mode)
651+
return b''
652+
b = io.BytesIO()
653+
fc = FakeCompressor()
654+
with gzip.GzipFile(fileobj=b, mode='w') as f:
655+
f.compress = fc
656+
f.flush()
657+
f.flush(50)
658+
f.flush(zlib_mode=100)
659+
# The implicit close will also flush the compressor.
660+
expected_modes = [
661+
zlib.Z_SYNC_FLUSH,
662+
50,
663+
100,
664+
-1,
665+
]
666+
self.assertEqual(fc.modes, expected_modes)
667+
619668

620669
class TestOpen(BaseTest):
621670
def test_binary_modes(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a regression introduced in GH-101251 for 3.12, causing :meth:`gzip.GzipFile.flush` to not flush the compressor (nor pass along the ``zip_mode`` argument).

0 commit comments

Comments
 (0)