By far most of the work on this release has been done by
@ptweezy. Almost all bug fixes and improvements
are their work.
Major changes:
- Updated included ISA-L library to 2.32.1.
- Python 3.10 and PyPy 3.10 are no longer supported. Added support for PyPy
3.12 and Python 3.15. - Restore PyPy wheel builds.
- Fix builds on macOS with Apple Silicon, which used the portable C code of
ISA-L rather than its optimized aarch64 assembly. On an Apple M4,
decompression is 1.6 to 3 times faster, CRC-32 is 50 times faster, and
Adler-32 is 7 times faster.
Optimizations and features:
isal_zlib.compressobjsupportsstrategy=Z_FIXEDat compression
level 0. Level 0 then uses fixed Huffman codes instead of starting each
block with a Huffman table of about 110 bytes. This shrinks small flushed
writes, such as websocket messages: JSON messages of about 440 bytes, each
followed byflush(Z_SYNC_FLUSH), compress to an average of 75 instead
of 188 bytes. Blocks of a few KiB or more can be larger: about 5% for JSON
and 10% for Python source code at 64 KiB. Other strategies, andZ_FIXED
at levels 1 to 3, still give a warning and use the default strategy.- Speed up
import isal.igzipandimport isal.igzip_threadedby
importingargparseandshutilonly when the command line interface
runs, and by dropping themultiprocessingimport. - Speed up reading lines from binary
igzipfiles through iteration,
readlines(), andreadline(). On an Apple M4, reading FASTQ data is
1.13 times faster, and reading lines of a few bytes is 1.5 times faster. - Speed up each
write()call to binaryigzipfiles on CPython. On an
AMD Ryzen 7 9800X3D with Python 3.14.8, writing FASTQ data or Python source
code one line at a time is 1.1 times faster. - Speed up many small writes to
igzip.IGzipFileon Python 3.12 and later.
IGzipFilecollects these writes in the write buffer of
gzip.GzipFileand compresses them in large chunks, which makes writing
lines 1.4 times faster.
Bug fixes:
- Fix zlib streams compressed with a preset dictionary so they can be decoded
by standard zlib, including builds linked against older ISA-L releases. - Fix backward seeking in igzip files by discarding buffered compressed data
when rewinding the underlying file. - Fix a crash when
igzip.decompressgets an object that is not
bytes-like, such as astr. It raises aTypeErrorinstead. - Fix a crash after creating
igzip_lib.IgzipDecompressorobjects with a
zdict. - Fix error handling when more than one interpreter imports
isal.isal_zliborisal.igzip_lib, for example, the main interpreter
and a legacy subinterpreter thatPy_NewInterpreter()creates. Each
import replaced the error classes that the C code raises, so
except isal_zlib.error,except igzip_lib.error, and
except gzip.BadGzipFilecaught errors only in the interpreter that
imported the modules last. - Fix a hang in
isal_zlib.Compress: afterflush()raised an error for
an unsupported mode, the next call on the same object never returned. - Fix a crash when you call
flush(Z_FINISH)twice on a level 0
isal_zlib.Compressobject. - Fix
unused_dataofisal_zlib.Decompressand
igzip_lib.IgzipDecompressor, which could contain bytes from outside the
input buffer whenmax_lengthsplit the end of a stream. At the end of a
stream,isal_zlib.Decompress.unconsumed_tailis empty, matching the
CPython fix for gh-158169. - Fix
igzip_lib.IgzipDecompressor.needs_input, which could beTrue
afterdecompress()returnedmax_lengthbytes while more output was
still available. Readers that stop at the end of the file when
needs_inputisTrue, such as CPython'sDecompressReader, raised
EOFErrorfor valid data. - Fix
isal_zlib.decompressobjwith awbitsof 40 to 47, which detects
zlib or gzip data. It failed on gzip data when the firstdecompress()
call got fewer than 2 bytes, for example, when the data arrived byte by
byte. - Fix
isal_zlib.decompressobjwith awbitsof 40 to 47 after a
flush()call that came before any data. It then failed on both zlib and
gzip data. igzip_threaded.openraisesValueErrorfor ablock_sizeof about
3.6 GiB or more andTypeErrorfor acompresslevelthat is not an
integer. Both raisedSystemErrorbefore.- Fix
igzip_threadedwriters at compression level 0, which raised
OverflowErrorfor data that does not compress, such as random bytes or
already compressed files. At compression level 0,block_sizemust now be
less than about 2 GiB. - Extraordinarily large gzip headers with either a very long comment or
filename will now cause anOverflowErrorrather than use more and more
memory. The size is limited to the internal buffer size which is currently
set at 512 KiB. - Fix
igzip_threadedreaders and writers that waited for a polling timeout
of up to 50 ms at the end of the stream or when closed. Writing and closing
a small file takes less than a millisecond instead of 60 ms. - Fix
igzip_threadedwriters that waited forever when writing to the
underlying file failed, for example, because the disk was full. A later call
towrite(),flush(), orclose()raises the error. After an error,
close()no longer closes a file object that you passed to
igzip_threaded.open. - Fix
igzip_threadedwriters with more than one thread that waited
forever when compressing a block failed, for example, because there was not
enough memory. A later call towrite(),flush(), orclose()
raises the error. - Fix a crash on the free-threaded build when one thread reads
unused_dataorunconsumed_tailof anisal_zlibdecompressor
while another thread decompresses with it. Reading the attributes of
isal_zlibdecompressors andigzip_lib.IgzipDecompressorfrom other
threads is now safe, and so is callingdecompress()from several
threads at the same time whenwbitsdetects the format. isal_zlib.crc32_combineand theread()andseek()methods of the
privateisal_zlib._GzipReaderclass raiseTypeErrorinstead of
SystemErrorfor arguments of the wrong type or number.
igzip.IGzipFilewas not affected.- Fix a crash on PyPy when you create an
isal_zlib.Compressor
isal_zlib.Decompressobject by calling the type, for example,
isal_zlib.Compress(), and then use it. Calling the type raises
TypeErrornow, as it does on CPython. Usecompressobj()and
decompressobj()instead. - Fix
readinto()of the privateisal_zlib._GzipReaderclass, which
wrote into read-only objects such asbytes. It raisesTypeErrornow.
igzip.IGzipFilewas not affected. - Fix the
mtimeattribute ofigzip.IGzipFileobjects in read mode,
which wasNonefor gzip files with a modification time of 0. It is 0 now,
as in thegzipmodule.gzip -n,python -m isal.igzip -n, and
igzip_threadedwriters make such files, and so doesgzip.compressby
default on Python 3.14. - Fix a crash when there is not enough memory to create an
igzip_lib.IgzipDecompressor. It raisesMemoryErrornow. - Fix a crash when there is not enough memory to raise
BadGzipFilefor
data that is not in the gzip format, for example, inigzip.decompress.
It raisesMemoryErrornow. - Fix a crash in
igzip_lib.IgzipDecompressor.decompress()after a call on
the same object raisedMemoryError. The next call used the input of the
failed call after the caller had released it. This matches the CPython fix
for gh-148395. - Fix a crash on macOS and PyPy when there is not enough memory to create the
internal lock of anisal_zlib.Compress,isal_zlib.Decompress, or
igzip_lib.IgzipDecompressorobject. It raisesMemoryErrornow. - Fix the
-0and--fastoptions ofpython -m isal.igzip, which
compressed at level 2 instead of level 0. - Fix crashes and wrong data when one thread calls
seek()of the
privateisal_zlib._GzipReaderclass while another thread reads from the
same object.igzip.IGzipFilewas not affected.