Skip to content

Commit 548d00d

Browse files
authored
Merge pull request #173 from pycompression/fixclosingbugs
Fix a bug where a filehandle would remain unclosed
2 parents 5871c6f + 27f3f7b commit 548d00d

File tree

3 files changed

+26
-20
lines changed

3 files changed

+26
-20
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Changelog
99
1010
version 1.6.0-dev
1111
-----------------
12+
+ Fix a bug where a filehandle remained opened when ``igzip_threaded.open``
13+
was used for writing with a wrong compression level.
1214
+ Fix a memory leak that occurred when an error was thrown for a gzip header
1315
with the wrong magic numbers.
1416
+ Fix a memory leak that occurred when isal_zlib.decompressobj was given a

src/isal/igzip_threaded.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import queue
1313
import struct
1414
import threading
15-
from typing import BinaryIO, List, Optional, Tuple
15+
from typing import List, Optional, Tuple
1616

1717
from . import igzip, isal_zlib
1818

@@ -56,20 +56,13 @@ def open(filename, mode="rb", compresslevel=igzip._COMPRESS_LEVEL_TRADEOFF,
5656
threads = multiprocessing.cpu_count()
5757
except: # noqa: E722
5858
threads = 1
59-
open_mode = mode.replace("t", "b")
60-
if isinstance(filename, (str, bytes)) or hasattr(filename, "__fspath__"):
61-
binary_file = builtins.open(filename, open_mode)
62-
elif hasattr(filename, "read") or hasattr(filename, "write"):
63-
binary_file = filename
64-
else:
65-
raise TypeError("filename must be a str or bytes object, or a file")
6659
if "r" in mode:
6760
gzip_file = io.BufferedReader(
68-
_ThreadedGzipReader(binary_file, block_size=block_size))
61+
_ThreadedGzipReader(filename, block_size=block_size))
6962
else:
7063
gzip_file = io.BufferedWriter(
7164
_ThreadedGzipWriter(
72-
fp=binary_file,
65+
filename,
7366
block_size=block_size,
7467
level=compresslevel,
7568
threads=threads
@@ -81,10 +74,20 @@ def open(filename, mode="rb", compresslevel=igzip._COMPRESS_LEVEL_TRADEOFF,
8174
return gzip_file
8275

8376

77+
def open_as_binary_stream(filename, open_mode):
78+
if isinstance(filename, (str, bytes)) or hasattr(filename, "__fspath__"):
79+
binary_file = builtins.open(filename, open_mode)
80+
elif hasattr(filename, "read") or hasattr(filename, "write"):
81+
binary_file = filename
82+
else:
83+
raise TypeError("filename must be a str or bytes object, or a file")
84+
return binary_file
85+
86+
8487
class _ThreadedGzipReader(io.RawIOBase):
85-
def __init__(self, fp, queue_size=2, block_size=1024 * 1024):
86-
self.raw = fp
87-
self.fileobj = igzip._IGzipReader(fp, buffersize=8 * block_size)
88+
def __init__(self, filename, queue_size=2, block_size=1024 * 1024):
89+
self.raw = open_as_binary_stream(filename, "rb")
90+
self.fileobj = igzip._IGzipReader(self.raw, buffersize=8 * block_size)
8891
self.pos = 0
8992
self.read_file = False
9093
self.queue = queue.Queue(queue_size)
@@ -193,15 +196,14 @@ class _ThreadedGzipWriter(io.RawIOBase):
193196
compressing and output is handled in one thread.
194197
"""
195198
def __init__(self,
196-
fp: BinaryIO,
199+
filename,
197200
level: int = isal_zlib.ISAL_DEFAULT_COMPRESSION,
198201
threads: int = 1,
199202
queue_size: int = 1,
200203
block_size: int = 1024 * 1024,
201204
):
202205
self.lock = threading.Lock()
203206
self.exception: Optional[Exception] = None
204-
self.raw = fp
205207
self.level = level
206208
self.previous_block = b""
207209
# Deflating random data results in an output a little larger than the
@@ -236,6 +238,7 @@ def __init__(self,
236238
self.running = False
237239
self._size = 0
238240
self._closed = False
241+
self.raw = open_as_binary_stream(filename, "wb")
239242
self._write_gzip_header()
240243
self.start()
241244

tests/test_igzip_threaded.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def test_threaded_write_oversized_block_no_error(threads):
9999
@pytest.mark.parametrize("threads", [1, 3])
100100
def test_threaded_write_error(threads):
101101
f = igzip_threaded._ThreadedGzipWriter(
102-
fp=io.BytesIO(), level=3,
102+
io.BytesIO(), level=3,
103103
threads=threads, block_size=8 * 1024)
104104
# Bypass the write method which should not allow blocks larger than
105105
# block_size.
@@ -139,10 +139,11 @@ def test_writer_not_readable():
139139

140140

141141
def test_writer_wrong_level():
142-
with pytest.raises(ValueError) as error:
143-
igzip_threaded._ThreadedGzipWriter(io.BytesIO(), level=42)
144-
error.match("Invalid compression level")
145-
error.match("42")
142+
with tempfile.NamedTemporaryFile("wb") as tmp:
143+
with pytest.raises(ValueError) as error:
144+
igzip_threaded.open(tmp.name, mode="wb", compresslevel=42)
145+
error.match("Invalid compression level")
146+
error.match("42")
146147

147148

148149
def test_writer_too_low_threads():

0 commit comments

Comments
 (0)