Skip to content

Commit

Permalink
GH-105808: Fix a regression introduced in GH-101251 (#105910)
Browse files Browse the repository at this point in the history
Fix a regression introduced in GH-101251, causing GzipFile.flush() to
not flush the compressor (nor pass along the zip_mode argument).
  • Loading branch information
Yhg1s authored Jun 19, 2023
1 parent 7a56a41 commit 1858db7
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Lib/gzip.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,9 @@ def close(self):
def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
self._check_not_closed()
if self.mode == WRITE:
# Ensure the compressor's buffer is flushed
self._buffer.flush()
# Ensure the compressor's buffer is flushed
self.fileobj.write(self.compress.flush(zlib_mode))
self.fileobj.flush()

def fileno(self):
Expand Down
49 changes: 49 additions & 0 deletions Lib/test/test_gzip.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import struct
import sys
import unittest
import zlib
from subprocess import PIPE, Popen
from test.support import import_helper
from test.support import os_helper
Expand Down Expand Up @@ -616,6 +617,54 @@ def test_issue44439(self):
self.assertEqual(f.write(q), LENGTH)
self.assertEqual(f.tell(), LENGTH)

def test_flush_flushes_compressor(self):
# See issue GH-105808.
b = io.BytesIO()
message = b"important message here."
with gzip.GzipFile(fileobj=b, mode='w') as f:
f.write(message)
f.flush()
partial_data = b.getvalue()
full_data = b.getvalue()
self.assertEqual(gzip.decompress(full_data), message)
# The partial data should contain the gzip header and the complete
# message, but not the end-of-stream markers (so we can't just
# decompress it directly).
with self.assertRaises(EOFError):
gzip.decompress(partial_data)
d = zlib.decompressobj(wbits=-zlib.MAX_WBITS)
f = io.BytesIO(partial_data)
gzip._read_gzip_header(f)
read_message = d.decompress(f.read())
self.assertEqual(read_message, message)

def test_flush_modes(self):
# Make sure the argument to flush is properly passed to the
# zlib.compressobj; see issue GH-105808.
class FakeCompressor:
def __init__(self):
self.modes = []
def compress(self, data):
return b''
def flush(self, mode=-1):
self.modes.append(mode)
return b''
b = io.BytesIO()
fc = FakeCompressor()
with gzip.GzipFile(fileobj=b, mode='w') as f:
f.compress = fc
f.flush()
f.flush(50)
f.flush(zlib_mode=100)
# The implicit close will also flush the compressor.
expected_modes = [
zlib.Z_SYNC_FLUSH,
50,
100,
-1,
]
self.assertEqual(fc.modes, expected_modes)


class TestOpen(BaseTest):
def test_binary_modes(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +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).

2 comments on commit 1858db7

@brettcannon
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this broke WASI because it assumes zlib is always available (which is not a promise we make; see #91246 for the issue to start making that promise).

@brettcannon
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #106160 to track the zlib issue.

Please sign in to comment.