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

Reply via email to