Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-zstandard for openSUSE:Factory checked in at 2023-11-05 12:19:12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-zstandard (Old) and /work/SRC/openSUSE:Factory/.python-zstandard.new.17445 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-zstandard" Sun Nov 5 12:19:12 2023 rev:12 rq:1123361 version:0.22.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-zstandard/python-zstandard.changes 2023-04-25 16:53:05.577950104 +0200 +++ /work/SRC/openSUSE:Factory/.python-zstandard.new.17445/python-zstandard.changes 2023-11-05 12:19:29.886687035 +0100 @@ -1,0 +2,10 @@ +Sat Nov 4 07:46:54 UTC 2023 - Bernhard Wiedemann <bwiedem...@suse.de> + +- Update to version 0.22.0 + * Official support for CPython 3.12 + * ZstdDecompressor.decompressobj() now accepts a read_across_frames + boolean named argument to control whether to transparently read across + multiple zstd frames. It still defaults to False to preserve existing + behavior + +------------------------------------------------------------------- Old: ---- zstandard-0.21.0.tar.gz New: ---- zstandard-0.22.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-zstandard.spec ++++++ --- /var/tmp/diff_new_pack.UkmBwK/_old 2023-11-05 12:19:30.602713301 +0100 +++ /var/tmp/diff_new_pack.UkmBwK/_new 2023-11-05 12:19:30.602713301 +0100 @@ -18,7 +18,7 @@ %define skip_python2 1 Name: python-zstandard -Version: 0.21.0 +Version: 0.22.0 Release: 0 Summary: Zstandard bindings for Python License: BSD-3-Clause ++++++ zstandard-0.21.0.tar.gz -> zstandard-0.22.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/Cargo.lock new/zstandard-0.22.0/Cargo.lock --- old/zstandard-0.21.0/Cargo.lock 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/Cargo.lock 2023-11-01 07:04:15.000000000 +0100 @@ -243,7 +243,7 @@ [[package]] name = "python-zstandard" -version = "0.21.0" +version = "0.22.0" dependencies = [ "libc", "num_cpus", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/Cargo.toml new/zstandard-0.22.0/Cargo.toml --- old/zstandard-0.21.0/Cargo.toml 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/Cargo.toml 2023-11-01 07:04:15.000000000 +0100 @@ -1,6 +1,6 @@ [package] name = "python-zstandard" -version = "0.21.0" +version = "0.22.0" authors = ["Gregory Szorc <gregory.sz...@gmail.com>"] edition = "2021" license = "BSD-3-Clause" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/PKG-INFO new/zstandard-0.22.0/PKG-INFO --- old/zstandard-0.21.0/PKG-INFO 2023-04-17 03:35:38.049346400 +0200 +++ new/zstandard-0.22.0/PKG-INFO 2023-11-01 07:04:17.186340000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: zstandard -Version: 0.21.0 +Version: 0.22.0 Summary: Zstandard bindings for Python Home-page: https://github.com/indygreg/python-zstandard Author: Gregory Szorc @@ -12,12 +12,12 @@ Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: C -Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 -Requires-Python: >=3.7 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=3.8 Provides-Extra: cffi License-File: LICENSE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/c-ext/decompressobj.c new/zstandard-0.22.0/c-ext/decompressobj.c --- old/zstandard-0.21.0/c-ext/decompressobj.c 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/c-ext/decompressobj.c 2023-11-01 07:04:15.000000000 +0100 @@ -89,7 +89,7 @@ } } - if (0 == zresult) { + if (0 == zresult && !self->readAcrossFrames) { self->finished = 1; /* We should only get here at most once. */ @@ -98,6 +98,13 @@ break; } + else if (0 == zresult && self->readAcrossFrames) { + if (input.pos == input.size) { + break; + } else { + output.pos = 0; + } + } else if (input.pos == input.size && output.pos == 0) { break; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/c-ext/decompressor.c new/zstandard-0.22.0/c-ext/decompressor.c --- old/zstandard-0.21.0/c-ext/decompressor.c 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/c-ext/decompressor.c 2023-11-01 07:04:15.000000000 +0100 @@ -397,13 +397,14 @@ static ZstdDecompressionObj *Decompressor_decompressobj(ZstdDecompressor *self, PyObject *args, PyObject *kwargs) { - static char *kwlist[] = {"write_size", NULL}; + static char *kwlist[] = {"write_size", "read_across_frames", NULL}; ZstdDecompressionObj *result = NULL; size_t outSize = ZSTD_DStreamOutSize(); + PyObject *readAcrossFrames = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|k:decompressobj", kwlist, - &outSize)) { + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|kO:decompressobj", kwlist, + &outSize, &readAcrossFrames)) { return NULL; } @@ -426,6 +427,8 @@ result->decompressor = self; Py_INCREF(result->decompressor); result->outSize = outSize; + result->readAcrossFrames = + readAcrossFrames ? PyObject_IsTrue(readAcrossFrames) : 0; return result; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/c-ext/python-zstandard.h new/zstandard-0.22.0/c-ext/python-zstandard.h --- old/zstandard-0.21.0/c-ext/python-zstandard.h 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/c-ext/python-zstandard.h 2023-11-01 07:04:15.000000000 +0100 @@ -31,7 +31,7 @@ /* Remember to change the string in zstandard/__init__.py, rust-ext/src/lib.rs, and debian/changelog as well */ -#define PYTHON_ZSTANDARD_VERSION "0.21.0" +#define PYTHON_ZSTANDARD_VERSION "0.22.0" typedef enum { compressorobj_flush_finish, @@ -220,6 +220,7 @@ ZstdDecompressor *decompressor; size_t outSize; + int readAcrossFrames; int finished; PyObject *unused_data; } ZstdDecompressionObj; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/debian/changelog new/zstandard-0.22.0/debian/changelog --- old/zstandard-0.21.0/debian/changelog 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/debian/changelog 2023-11-01 07:04:15.000000000 +0100 @@ -1,3 +1,9 @@ +python-zstandard (0.22.0) unstable; urgency=low + + * New version. + + -- Gregory Szorc <gregory.sz...@gmail.com> Mon, 16 Apr 2023 18:00:00 -0700 + python-zstandard (0.21.0) unstable; urgency=low * New version. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/debian/control new/zstandard-0.22.0/debian/control --- old/zstandard-0.21.0/debian/control 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/debian/control 2023-11-01 07:04:15.000000000 +0100 @@ -10,7 +10,7 @@ python3-pytest, python3-setuptools Standards-Version: 3.9.1 -X-Python3-Version: >= 3.7 +X-Python3-Version: >= 3.8 Homepage: https://github.com/indygreg/python-zstandard Vcs-Browser: https://github.com/indygreg/python-zstandard.git Vcs-Git: https://github.com/indygreg/python-zstandard.git diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/pyproject.toml new/zstandard-0.22.0/pyproject.toml --- old/zstandard-0.21.0/pyproject.toml 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/pyproject.toml 2023-11-01 07:04:15.000000000 +0100 @@ -1,2 +1,11 @@ +[build-system] +requires = [ + "cffi==1.16.0", + "setuptools==68.2.2", + "wheel==0.41.2", +] +# Need to use legacy backend because setup_zstd.py breaks build isolation. +build-backend = "setuptools.build_meta:__legacy__" + [tool.black] line-length = 80 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/rust-ext/src/decompressionobj.rs new/zstandard-0.22.0/rust-ext/src/decompressionobj.rs --- old/zstandard-0.21.0/rust-ext/src/decompressionobj.rs 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/rust-ext/src/decompressionobj.rs 2023-11-01 07:04:15.000000000 +0100 @@ -18,15 +18,21 @@ pub struct ZstdDecompressionObj { dctx: Arc<DCtx<'static>>, write_size: usize, + read_across_frames: bool, finished: bool, unused_data: Vec<u8>, } impl ZstdDecompressionObj { - pub fn new(dctx: Arc<DCtx<'static>>, write_size: usize) -> PyResult<Self> { + pub fn new( + dctx: Arc<DCtx<'static>>, + write_size: usize, + read_across_frames: bool, + ) -> PyResult<Self> { Ok(ZstdDecompressionObj { dctx, write_size, + read_across_frames, finished: false, unused_data: vec![], }) @@ -68,7 +74,7 @@ chunks.append(chunk)?; } - if zresult == 0 { + if zresult == 0 && !self.read_across_frames { self.finished = true; // TODO clear out decompressor? @@ -78,6 +84,12 @@ } break; + } else if zresult == 0 && self.read_across_frames { + if in_buffer.pos == in_buffer.size { + break; + } else { + dest_buffer.clear(); + } } else if in_buffer.pos == in_buffer.size && dest_buffer.len() < dest_buffer.capacity() { break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/rust-ext/src/decompressor.rs new/zstandard-0.22.0/rust-ext/src/decompressor.rs --- old/zstandard-0.21.0/rust-ext/src/decompressor.rs 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/rust-ext/src/decompressor.rs 2023-11-01 07:04:15.000000000 +0100 @@ -371,11 +371,12 @@ Ok(PyBytes::new(py, &last_buffer)) } - #[pyo3(signature = (write_size=None))] + #[pyo3(signature = (write_size=None, read_across_frames=false))] fn decompressobj( &self, py: Python, write_size: Option<usize>, + read_across_frames: bool, ) -> PyResult<ZstdDecompressionObj> { if let Some(write_size) = write_size { if write_size < 1 { @@ -387,7 +388,7 @@ self.setup_dctx(py, true)?; - ZstdDecompressionObj::new(self.dctx.clone(), write_size) + ZstdDecompressionObj::new(self.dctx.clone(), write_size, read_across_frames) } fn memory_size(&self) -> usize { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/rust-ext/src/lib.rs new/zstandard-0.22.0/rust-ext/src/lib.rs --- old/zstandard-0.21.0/rust-ext/src/lib.rs 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/rust-ext/src/lib.rs 2023-11-01 07:04:15.000000000 +0100 @@ -32,7 +32,7 @@ // Remember to change the string in c-ext/python-zstandard.h, zstandard/__init__.py, // and debian/changelog as well. -const VERSION: &'static str = "0.21.0"; +const VERSION: &'static str = "0.22.0"; #[pymodule] fn backend_rust(py: Python, module: &PyModule) -> PyResult<()> { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/setup.py new/zstandard-0.22.0/setup.py --- old/zstandard-0.21.0/setup.py 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/setup.py 2023-11-01 07:04:15.000000000 +0100 @@ -7,15 +7,20 @@ from __future__ import print_function -from distutils.version import LooseVersion import platform import os import sys from setuptools import setup +# Python 3.12 dropped distutils from the stdlib. Try to access it via +# setuptools. +try: + from setuptools._distutils.version import LooseVersion +except ImportError: + from distutils.version import LooseVersion -if sys.version_info[0:2] < (3, 7): - print("Python 3.7+ is required", file=sys.stderr) +if sys.version_info[0:2] < (3, 8): + print("Python 3.8+ is required", file=sys.stderr) sys.exit(1) # Need change in 1.10 for ffi.from_buffer() to handle all buffer types @@ -126,17 +131,17 @@ author="Gregory Szorc", author_email="gregory.sz...@gmail.com", license="BSD", - python_requires=">=3.7", + python_requires=">=3.8", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Programming Language :: C", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ], keywords=["zstandard", "zstd", "compression"], packages=["zstandard"], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/tests/common.py new/zstandard-0.22.0/tests/common.py --- old/zstandard-0.21.0/tests/common.py 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/tests/common.py 2023-11-01 07:04:15.000000000 +0100 @@ -3,11 +3,6 @@ from typing import List -try: - import hypothesis # type: ignore -except ImportError: - hypothesis = None # type: ignore - class NonClosingBytesIO(io.BytesIO): """BytesIO that saves the underlying buffer on close(). @@ -121,18 +116,3 @@ samples.append(inputs[-(i % 5)] * (i + 2)) return samples - - -if hypothesis: - default_settings = hypothesis.settings(deadline=10000) - hypothesis.settings.register_profile("default", default_settings) - - ci_settings = hypothesis.settings(deadline=20000, max_examples=1000) - hypothesis.settings.register_profile("ci", ci_settings) - - expensive_settings = hypothesis.settings(deadline=None, max_examples=10000) - hypothesis.settings.register_profile("expensive", expensive_settings) - - hypothesis.settings.load_profile( - os.environ.get("HYPOTHESIS_PROFILE", "default") - ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/tests/conftest.py new/zstandard-0.22.0/tests/conftest.py --- old/zstandard-0.21.0/tests/conftest.py 1970-01-01 01:00:00.000000000 +0100 +++ new/zstandard-0.22.0/tests/conftest.py 2023-11-01 07:04:15.000000000 +0100 @@ -0,0 +1,21 @@ +import os + +try: + import hypothesis # type: ignore +except ImportError: + hypothesis = None # type: ignore + + +if hypothesis: + default_settings = hypothesis.settings(deadline=10000) + hypothesis.settings.register_profile("default", default_settings) + + ci_settings = hypothesis.settings(deadline=20000, max_examples=1000) + hypothesis.settings.register_profile("ci", ci_settings) + + expensive_settings = hypothesis.settings(deadline=None, max_examples=10000) + hypothesis.settings.register_profile("expensive", expensive_settings) + + hypothesis.settings.load_profile( + os.environ.get("HYPOTHESIS_PROFILE", "default") + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/tests/test_compressor_fuzzing.py new/zstandard-0.22.0/tests/test_compressor_fuzzing.py --- old/zstandard-0.21.0/tests/test_compressor_fuzzing.py 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/tests/test_compressor_fuzzing.py 2023-11-01 07:04:15.000000000 +0100 @@ -131,7 +131,6 @@ def test_buffer_source_read_variance( self, original, level, source_read_size, read_sizes ): - refctx = zstd.ZstdCompressor(level=level) ref_frame = refctx.compress(original) @@ -197,7 +196,6 @@ def test_buffer_source_readinto( self, original, level, source_read_size, read_size ): - refctx = zstd.ZstdCompressor(level=level) ref_frame = refctx.compress(original) @@ -267,7 +265,6 @@ def test_buffer_source_readinto_variance( self, original, level, source_read_size, read_sizes ): - refctx = zstd.ZstdCompressor(level=level) ref_frame = refctx.compress(original) @@ -404,7 +401,6 @@ def test_buffer_source_read1_variance( self, original, level, source_read_size, read_sizes ): - refctx = zstd.ZstdCompressor(level=level) ref_frame = refctx.compress(original) @@ -545,7 +541,6 @@ def test_buffer_source_readinto1_variance( self, original, level, source_read_size, read_sizes ): - refctx = zstd.ZstdCompressor(level=level) ref_frame = refctx.compress(original) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/tests/test_decompressor_decompress.py new/zstandard-0.22.0/tests/test_decompressor_decompress.py --- old/zstandard-0.21.0/tests/test_decompressor_decompress.py 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/tests/test_decompressor_decompress.py 2023-11-01 07:04:15.000000000 +0100 @@ -194,7 +194,8 @@ dctx.decompress(foo + bar, read_across_frames=True) with self.assertRaisesRegex( - zstd.ZstdError, "%d bytes of unused data, which is disallowed" % len(bar) + zstd.ZstdError, + "%d bytes of unused data, which is disallowed" % len(bar), ): dctx.decompress(foo + bar, allow_extra_data=False) @@ -205,7 +206,9 @@ dctx = zstd.ZstdDecompressor() self.assertEqual(dctx.decompress(frame + b"junk"), b"foo") - self.assertEqual(dctx.decompress(frame + b"junk", allow_extra_data=True), b"foo") + self.assertEqual( + dctx.decompress(frame + b"junk", allow_extra_data=True), b"foo" + ) with self.assertRaisesRegex( zstd.ZstdError, "4 bytes of unused data, which is disallowed" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/tests/test_decompressor_decompressobj.py new/zstandard-0.22.0/tests/test_decompressor_decompressobj.py --- old/zstandard-0.21.0/tests/test_decompressor_decompressobj.py 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/tests/test_decompressor_decompressobj.py 2023-11-01 07:04:15.000000000 +0100 @@ -109,3 +109,39 @@ for i in range(128): dobj = dctx.decompressobj(write_size=i + 1) self.assertEqual(dobj.decompress(data), source) + + def test_multiple_frames_default(self): + cctx = zstd.ZstdCompressor() + foo = cctx.compress(b"foo") + bar = cctx.compress(b"bar") + + dctx = zstd.ZstdDecompressor() + dobj = dctx.decompressobj() + + self.assertEqual(dobj.decompress(foo + bar), b"foo") + self.assertEqual(dobj.unused_data, bar) + self.assertEqual(dobj.unconsumed_tail, b"") + + def test_read_across_frames_false(self): + cctx = zstd.ZstdCompressor() + foo = cctx.compress(b"foo") + bar = cctx.compress(b"bar") + + dctx = zstd.ZstdDecompressor() + dobj = dctx.decompressobj(read_across_frames=False) + + self.assertEqual(dobj.decompress(foo + bar), b"foo") + self.assertEqual(dobj.unused_data, bar) + self.assertEqual(dobj.unconsumed_tail, b"") + + def test_read_across_frames_true(self): + cctx = zstd.ZstdCompressor() + foo = cctx.compress(b"foo") + bar = cctx.compress(b"bar") + + dctx = zstd.ZstdDecompressor() + dobj = dctx.decompressobj(read_across_frames=True) + + self.assertEqual(dobj.decompress(foo + bar), b"foobar") + self.assertEqual(dobj.unused_data, b"") + self.assertEqual(dobj.unconsumed_tail, b"") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/tests/test_decompressor_fuzzing.py new/zstandard-0.22.0/tests/test_decompressor_fuzzing.py --- old/zstandard-0.21.0/tests/test_decompressor_fuzzing.py 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/tests/test_decompressor_fuzzing.py 2023-11-01 07:04:15.000000000 +0100 @@ -298,6 +298,7 @@ @hypothesis.settings( suppress_health_check=[ + hypothesis.HealthCheck.data_too_large, hypothesis.HealthCheck.large_base_example, hypothesis.HealthCheck.too_slow, ] @@ -348,7 +349,6 @@ read_sizes=strategies.data(), ) def test_multiple_frames(self, chunks, level, source_read_size, read_sizes): - cctx = zstd.ZstdCompressor(level=level) source = io.BytesIO() buffer = io.BytesIO() @@ -518,6 +518,109 @@ self.assertEqual(b"".join(chunks), original) + @hypothesis.given( + chunks=strategies.lists( + strategies.sampled_from(random_input_data()), + min_size=2, + max_size=10, + ), + level=strategies.integers(min_value=1, max_value=5), + write_size=strategies.integers( + min_value=1, + max_value=4 * zstd.DECOMPRESSION_RECOMMENDED_OUTPUT_SIZE, + ), + read_sizes=strategies.data(), + ) + def test_read_across_frames_false( + self, chunks, level, write_size, read_sizes + ): + cctx = zstd.ZstdCompressor(level=level) + + source = io.BytesIO() + source_chunks = [] + compressed = io.BytesIO() + + for chunk in chunks: + source.write(chunk) + source_chunks.append(chunk) + compressed.write(cctx.compress(chunk)) + + compressed.seek(0) + + dctx = zstd.ZstdDecompressor() + dobj = dctx.decompressobj( + write_size=write_size, read_across_frames=False + ) + + decompressed = io.BytesIO() + + while True: + read_size = read_sizes.draw(strategies.integers(1, 4096)) + chunk = compressed.read(read_size) + if not chunk: + break + + try: + decompressed.write(dobj.decompress(chunk)) + except zstd.ZstdError as e: + if e.args[0] == "cannot use a decompressobj multiple times": + break + else: + raise + + self.assertEqual(decompressed.getvalue(), source_chunks[0]) + + @hypothesis.settings( + suppress_health_check=[ + hypothesis.HealthCheck.large_base_example, + ] + ) + @hypothesis.given( + chunks=strategies.lists( + strategies.sampled_from(random_input_data()), + min_size=2, + max_size=10, + ), + level=strategies.integers(min_value=1, max_value=5), + write_size=strategies.integers( + min_value=1, + max_value=4 * zstd.DECOMPRESSION_RECOMMENDED_OUTPUT_SIZE, + ), + read_sizes=strategies.data(), + ) + def test_read_across_frames_true( + self, chunks, level, write_size, read_sizes + ): + cctx = zstd.ZstdCompressor(level=level) + + source = io.BytesIO() + source_chunks = [] + compressed = io.BytesIO() + + for chunk in chunks: + source.write(chunk) + source_chunks.append(chunk) + compressed.write(cctx.compress(chunk)) + + compressed.seek(0) + + dctx = zstd.ZstdDecompressor() + dobj = dctx.decompressobj( + write_size=write_size, read_across_frames=True + ) + + decompressed = io.BytesIO() + + while True: + read_size = read_sizes.draw(strategies.integers(1, 4096)) + chunk = compressed.read(read_size) + if not chunk: + break + + decompressed.write(dobj.decompress(chunk)) + + self.assertEqual(decompressed.getvalue(), source.getvalue()) + @unittest.skipUnless("ZSTD_SLOW_TESTS" in os.environ, "ZSTD_SLOW_TESTS not set") class TestDecompressor_read_to_iter_fuzzing(unittest.TestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/tests/test_module_attributes.py new/zstandard-0.22.0/tests/test_module_attributes.py --- old/zstandard-0.21.0/tests/test_module_attributes.py 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/tests/test_module_attributes.py 2023-11-01 07:04:15.000000000 +0100 @@ -7,7 +7,7 @@ def test_version(self): self.assertEqual(zstd.ZSTD_VERSION, (1, 5, 5)) - self.assertEqual(zstd.__version__, "0.21.0") + self.assertEqual(zstd.__version__, "0.22.0") def test_features(self): self.assertIsInstance(zstd.backend_features, set) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/zstandard/__init__.py new/zstandard-0.22.0/zstandard/__init__.py --- old/zstandard-0.21.0/zstandard/__init__.py 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/zstandard/__init__.py 2023-11-01 07:04:15.000000000 +0100 @@ -80,7 +80,7 @@ ) # Keep this in sync with python-zstandard.h, rust-ext/src/lib.rs, and debian/changelog. -__version__ = "0.21.0" +__version__ = "0.22.0" _MODE_CLOSED = 0 _MODE_READ = 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/zstandard/__init__.pyi new/zstandard-0.22.0/zstandard/__init__.pyi --- old/zstandard-0.21.0/zstandard/__init__.pyi 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/zstandard/__init__.pyi 2023-11-01 07:04:15.000000000 +0100 @@ -403,7 +403,9 @@ *, closefd=False, ) -> ZstdDecompressionReader: ... - def decompressobj(self, write_size: int = ...) -> ZstdDecompressionObj: ... + def decompressobj( + self, write_size: int = ..., read_across_frames: bool = False + ) -> ZstdDecompressionObj: ... def read_to_iter( self, reader: Union[IO[bytes], ByteString], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/zstandard/backend_cffi.py new/zstandard-0.22.0/zstandard/backend_cffi.py --- old/zstandard-0.21.0/zstandard/backend_cffi.py 2023-04-17 03:35:34.000000000 +0200 +++ new/zstandard-0.22.0/zstandard/backend_cffi.py 2023-11-01 07:04:15.000000000 +0100 @@ -424,7 +424,6 @@ ldm_hash_rate_log=-1, threads=0, ): - params = lib.ZSTD_createCCtxParams() if params == ffi.NULL: raise MemoryError() @@ -2919,8 +2918,9 @@ subsequent calls needs to be concatenated to reassemble the full decompressed byte sequence. - Each instance is single use: once an input frame is decoded, - ``decompress()`` can no longer be called. + If ``read_across_frames=False``, each instance is single use: once an + input frame is decoded, ``decompress()`` will raise an exception. If + ``read_across_frames=True``, instances can decode multiple frames. >>> dctx = zstandard.ZstdDecompressor() >>> dobj = dctx.decompressobj() @@ -2942,10 +2942,11 @@ efficient as other APIs. """ - def __init__(self, decompressor, write_size): + def __init__(self, decompressor, write_size, read_across_frames): self._decompressor = decompressor self._write_size = write_size self._finished = False + self._read_across_frames = read_across_frames self._unused_input = b"" def decompress(self, data): @@ -2992,13 +2993,22 @@ chunks.append(ffi.buffer(out_buffer.dst, out_buffer.pos)[:]) # 0 is only seen when a frame is fully decoded *and* fully flushed. - # But there may be extra input data: make that available to - # `unused_input`. - if zresult == 0: + # Behavior depends on whether we're in single or multiple frame + # mode. + if zresult == 0 and not self._read_across_frames: + # Mark the instance as done and make any unconsumed input available + # for retrieval. self._finished = True self._decompressor = None self._unused_input = data[in_buffer.pos : in_buffer.size] break + elif zresult == 0 and self._read_across_frames: + # We're at the end of a fully flushed frame and we can read more. + # Try to read more if there's any more input. + if in_buffer.pos == in_buffer.size: + break + else: + out_buffer.pos = 0 # We're not at the end of the frame *or* we're not fully flushed. @@ -3900,13 +3910,21 @@ self, source, read_size, read_across_frames, closefd=closefd ) - def decompressobj(self, write_size=DECOMPRESSION_RECOMMENDED_OUTPUT_SIZE): + def decompressobj( + self, + write_size=DECOMPRESSION_RECOMMENDED_OUTPUT_SIZE, + read_across_frames=False, + ): """Obtain a standard library compatible incremental decompressor. See :py:class:`ZstdDecompressionObj` for more documentation and usage examples. - :param write_size: + :param write_size: size of internal output buffer to collect decompressed + chunks in. + :param read_across_frames: whether to read across multiple zstd frames. + If False, reading stops after 1 frame and subsequent decompress + attempts will raise an exception. :return: :py:class:`zstandard.ZstdDecompressionObj` """ @@ -3914,7 +3932,9 @@ raise ValueError("write_size must be positive") self._ensure_dctx() - return ZstdDecompressionObj(self, write_size=write_size) + return ZstdDecompressionObj( + self, write_size=write_size, read_across_frames=read_across_frames + ) def read_to_iter( self, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/zstandard.egg-info/PKG-INFO new/zstandard-0.22.0/zstandard.egg-info/PKG-INFO --- old/zstandard-0.21.0/zstandard.egg-info/PKG-INFO 2023-04-17 03:35:38.000000000 +0200 +++ new/zstandard-0.22.0/zstandard.egg-info/PKG-INFO 2023-11-01 07:04:17.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: zstandard -Version: 0.21.0 +Version: 0.22.0 Summary: Zstandard bindings for Python Home-page: https://github.com/indygreg/python-zstandard Author: Gregory Szorc @@ -12,12 +12,12 @@ Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: C -Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 -Requires-Python: >=3.7 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=3.8 Provides-Extra: cffi License-File: LICENSE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zstandard-0.21.0/zstandard.egg-info/SOURCES.txt new/zstandard-0.22.0/zstandard.egg-info/SOURCES.txt --- old/zstandard-0.21.0/zstandard.egg-info/SOURCES.txt 2023-04-17 03:35:38.000000000 +0200 +++ new/zstandard-0.22.0/zstandard.egg-info/SOURCES.txt 2023-11-01 07:04:17.000000000 +0100 @@ -56,6 +56,7 @@ rust-ext/src/zstd_safe.rs tests/__init__.py tests/common.py +tests/conftest.py tests/test_buffer_util.py tests/test_compressor.py tests/test_compressor_chunker.py