Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-zipstream-ng for 
openSUSE:Factory checked in at 2026-03-14 22:22:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-zipstream-ng (Old)
 and      /work/SRC/openSUSE:Factory/.python-zipstream-ng.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-zipstream-ng"

Sat Mar 14 22:22:21 2026 rev:4 rq:1338807 version:1.9.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-zipstream-ng/python-zipstream-ng.changes  
2025-06-06 22:43:28.729853178 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-zipstream-ng.new.8177/python-zipstream-ng.changes
        2026-03-14 22:23:33.412017922 +0100
@@ -1,0 +2,6 @@
+Fri Mar 13 20:34:02 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 1.9.0:
+  * Add support for Zstandard compression where available (Python 3.14+)
+
+-------------------------------------------------------------------

Old:
----
  zipstream_ng-1.8.0.tar.gz

New:
----
  zipstream_ng-1.9.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-zipstream-ng.spec ++++++
--- /var/tmp/diff_new_pack.9iJjgd/_old  2026-03-14 22:23:33.972041122 +0100
+++ /var/tmp/diff_new_pack.9iJjgd/_new  2026-03-14 22:23:33.976041287 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-zipstream-ng
 #
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           python-zipstream-ng
-Version:        1.8.0
+Version:        1.9.0
 Release:        0
 Summary:        Modern and easy to use streamable zip file generator
 License:        LGPL-3.0-only

++++++ zipstream_ng-1.8.0.tar.gz -> zipstream_ng-1.9.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/zipstream_ng-1.8.0/PKG-INFO 
new/zipstream_ng-1.9.0/PKG-INFO
--- old/zipstream_ng-1.8.0/PKG-INFO     2024-10-10 07:22:18.014251700 +0200
+++ new/zipstream_ng-1.9.0/PKG-INFO     2025-08-29 03:02:57.751773400 +0200
@@ -1,9 +1,9 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: zipstream-ng
-Version: 1.8.0
+Version: 1.9.0
 Summary: A modern and easy to use streamable zip file generator
 Home-page: https://github.com/pR0Ps/zipstream-ng
-License: LGPLv3
+License: LGPL-3.0-only
 Project-URL: Source, https://github.com/pR0Ps/zipstream-ng
 Project-URL: Changelog, 
https://github.com/pR0Ps/zipstream-ng/blob/master/CHANGELOG.md
 Classifier: Programming Language :: Python :: 3
@@ -15,19 +15,30 @@
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
 Classifier: Operating System :: OS Independent
 Classifier: Topic :: System :: Archiving :: Compression
-Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 
(LGPLv3)
 Requires-Python: >=3.5.0
 Description-Content-Type: text/markdown
 License-File: LICENSE
 Provides-Extra: tests
 Requires-Dist: pytest; extra == "tests"
 Requires-Dist: pytest-cov; extra == "tests"
+Dynamic: classifier
+Dynamic: description
+Dynamic: description-content-type
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: project-url
+Dynamic: provides-extra
+Dynamic: requires-python
+Dynamic: summary
 
 zipstream-ng
 ============
-[![Status](https://github.com/pR0Ps/zipstream-ng/workflows/tests/badge.svg)](https://github.com/pR0Ps/zipstream-ng/actions/workflows/tests.yml)
+[![Status](https://github.com/pR0Ps/zipstream-ng/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/pR0Ps/zipstream-ng/actions/workflows/tests.yml?query=branch%3Amaster)
 
[![Version](https://img.shields.io/pypi/v/zipstream-ng.svg)](https://pypi.org/project/zipstream-ng/)
 ![Python](https://img.shields.io/pypi/pyversions/zipstream-ng.svg)
 
@@ -41,6 +52,7 @@
  - Can calculate the total size of the resulting zip file before generation 
even begins.
  - Low memory usage: Since the zip is generated as it's requested, very little 
has to be kept in
    memory (peak usage of less than 20MB is typical, even for TBs of files).
+ - Performant: On-par or faster than using the standard library to create 
non-streamed zip files.
  - Flexible API: Typical use cases are simple, complicated ones are possible.
  - Supports zipping data from files, bytes, strings, and any other iterable 
objects.
  - Keeps track of the date of the most recently modified file added to the zip 
file.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/zipstream_ng-1.8.0/README.md 
new/zipstream_ng-1.9.0/README.md
--- old/zipstream_ng-1.8.0/README.md    2024-10-10 07:04:36.000000000 +0200
+++ new/zipstream_ng-1.9.0/README.md    2025-08-29 02:34:51.000000000 +0200
@@ -1,6 +1,6 @@
 zipstream-ng
 ============
-[![Status](https://github.com/pR0Ps/zipstream-ng/workflows/tests/badge.svg)](https://github.com/pR0Ps/zipstream-ng/actions/workflows/tests.yml)
+[![Status](https://github.com/pR0Ps/zipstream-ng/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/pR0Ps/zipstream-ng/actions/workflows/tests.yml?query=branch%3Amaster)
 
[![Version](https://img.shields.io/pypi/v/zipstream-ng.svg)](https://pypi.org/project/zipstream-ng/)
 ![Python](https://img.shields.io/pypi/pyversions/zipstream-ng.svg)
 
@@ -14,6 +14,7 @@
  - Can calculate the total size of the resulting zip file before generation 
even begins.
  - Low memory usage: Since the zip is generated as it's requested, very little 
has to be kept in
    memory (peak usage of less than 20MB is typical, even for TBs of files).
+ - Performant: On-par or faster than using the standard library to create 
non-streamed zip files.
  - Flexible API: Typical use cases are simple, complicated ones are possible.
  - Supports zipping data from files, bytes, strings, and any other iterable 
objects.
  - Keeps track of the date of the most recently modified file added to the zip 
file.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/zipstream_ng-1.8.0/setup.py 
new/zipstream_ng-1.9.0/setup.py
--- old/zipstream_ng-1.8.0/setup.py     2024-10-10 07:13:26.000000000 +0200
+++ new/zipstream_ng-1.9.0/setup.py     2025-08-29 03:01:42.000000000 +0200
@@ -14,7 +14,7 @@
 
 setup(
     name="zipstream-ng",
-    version="1.8.0",
+    version="1.9.0",
     description="A modern and easy to use streamable zip file generator",
     long_description=long_description,
     long_description_content_type="text/markdown",
@@ -23,7 +23,7 @@
         "Source": "https://github.com/pR0Ps/zipstream-ng";,
         "Changelog": 
"https://github.com/pR0Ps/zipstream-ng/blob/master/CHANGELOG.md";,
     },
-    license="LGPLv3",
+    license="LGPL-3.0-only",
     classifiers=[
         "Programming Language :: Python :: 3",
         "Programming Language :: Python :: 3.5",
@@ -34,9 +34,10 @@
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
         "Programming Language :: Python :: 3.12",
+        "Programming Language :: Python :: 3.13",
+        "Programming Language :: Python :: 3.14",
         "Operating System :: OS Independent",
         "Topic :: System :: Archiving :: Compression",
-        "License :: OSI Approved :: GNU Lesser General Public License v3 
(LGPLv3)"
     ],
     packages=["zipstream"],
     entry_points={
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/zipstream_ng-1.8.0/tests/test_zipstream.py 
new/zipstream_ng-1.9.0/tests/test_zipstream.py
--- old/zipstream_ng-1.8.0/tests/test_zipstream.py      2024-10-10 
07:04:36.000000000 +0200
+++ new/zipstream_ng-1.9.0/tests/test_zipstream.py      2025-08-29 
02:46:34.000000000 +0200
@@ -20,6 +20,7 @@
 from zipstream import ZipStream
 
 
+PY313 = sys.version_info < (3, 14)
 PY36 = sys.version_info < (3, 7)
 PY35 = sys.version_info < (3, 6)
 
@@ -30,6 +31,14 @@
     ("mbyte", 1024 * 1024),
 ]
 
+COMPRESS_TYPES = [
+    zipfile.ZIP_STORED,
+    zipfile.ZIP_LZMA,
+    zipfile.ZIP_DEFLATED,
+    zipfile.ZIP_BZIP2,
+]
+if not PY313:
+    COMPRESS_TYPES.append(zipfile.ZIP_ZSTANDARD)
 
 # Patch is_dir onto ZipInfo objects in 3.5 to make testing easier
 @pytest.fixture(autouse=PY35)
@@ -107,12 +116,7 @@
 # Tests start
 ################################
 
[email protected]("ct", [
-    zipfile.ZIP_STORED,
-    zipfile.ZIP_LZMA,
-    zipfile.ZIP_DEFLATED,
-    zipfile.ZIP_BZIP2
-])
[email protected]("ct", COMPRESS_TYPES)
 def test_zipstream_compression(caplog, files, ct):
     """Test that all types of compression properly compress and extract"""
     caplog.set_level(logging.WARNING)
@@ -135,12 +139,7 @@
         _verify_zip_contains(zf, f)
 
 
[email protected]("ct", [
-    zipfile.ZIP_STORED,
-    zipfile.ZIP_LZMA,
-    zipfile.ZIP_DEFLATED,
-    zipfile.ZIP_BZIP2
-])
[email protected]("ct", COMPRESS_TYPES)
 @pytest.mark.parametrize("cl", [None, 2])
 def test_mixed_compression_and_getinfo(ct, cl):
     """Test that files are compressed using the correct method and level and
@@ -159,11 +158,14 @@
     zs.add(b"3c", arcname="3c", compress_type=zipfile.ZIP_DEFLATED, 
compress_level=TEST_CL)
     zs.add(b"4", arcname="4", compress_type=zipfile.ZIP_BZIP2)
     zs.add(b"4c", arcname="4c", compress_type=zipfile.ZIP_BZIP2, 
compress_level=TEST_CL)
+    if not PY313:
+        zs.add(b"5", arcname="5", compress_type=zipfile.ZIP_ZSTANDARD)
+        zs.add(b"5c", arcname="5c", compress_type=zipfile.ZIP_ZSTANDARD, 
compress_level=TEST_CL)
 
     zf = _get_zip(zs)
     zinfos = zf.infolist()
     fullinfos = zs.info_list()
-    assert len(zinfos) == len(fullinfos) == 9
+    assert len(zinfos) == len(fullinfos) == 9 + (0 if PY313 else 2)
 
     def assert_zinfo(idx, name, compress_type, compress_level):
         zi = zinfos[idx]
@@ -189,6 +191,9 @@
     assert_zinfo(6, "3c", zipfile.ZIP_DEFLATED, TEST_CL)
     assert_zinfo(7, "4", zipfile.ZIP_BZIP2, cl)
     assert_zinfo(8, "4c", zipfile.ZIP_BZIP2, TEST_CL)
+    if not PY313:
+        assert_zinfo(9, "5", zipfile.ZIP_ZSTANDARD, cl)
+        assert_zinfo(10, "5c", zipfile.ZIP_ZSTANDARD, TEST_CL)
 
 
 @pytest.mark.parametrize("zip64", [False, True])
@@ -368,6 +373,34 @@
             zs.add(".", arcname=".", compress_type=ct)
 
 
[email protected](PY313, reason="Tests zstd compress_level (Python 3.14+ 
only)")
+def test_invalid_zstd_compression():
+    """Test zstd values outside of valid ones cause an error"""
+    ZipStream(compress_type=zipfile.ZIP_ZSTANDARD)
+
+    from compression.zstd import CompressionParameter
+    lower, upper = CompressionParameter.compression_level.bounds()
+
+    for x in (lower, lower+1, 0, upper-1, upper):
+        ZipStream(compress_type=zipfile.ZIP_ZSTANDARD, compress_level=x)
+
+    for x in (lower-1, upper+1):
+        with pytest.raises(ValueError):
+            ZipStream(compress_type=zipfile.ZIP_ZSTANDARD, compress_level=x)
+        with pytest.raises(ValueError):
+            ZipStream().add_path(".", compress_type=zipfile.ZIP_ZSTANDARD, 
compress_level=x)
+        with pytest.raises(ValueError):
+            ZipStream().add(".", arcname=".", 
compress_type=zipfile.ZIP_ZSTANDARD, compress_level=x)
+
+        zs = ZipStream(compress_type=zipfile.ZIP_ZSTANDARD)
+        with pytest.raises(ValueError):
+            zs.add(".", arcname=".", compress_level=x)
+
+        zs = ZipStream(compress_level=x)
+        with pytest.raises(ValueError):
+            zs.add(".", arcname=".", compress_type=zipfile.ZIP_ZSTANDARD)
+
+
 def test_multibyte_and_non_ascii_characters_in_filenames():
     zs = ZipStream(sized=True)
     zs.add(None, "☆/")
@@ -734,12 +767,7 @@
     [b"a", b"list", b"of", b"bytes"],
     _gen_rand()
 ])
[email protected]("ct", [
-    zipfile.ZIP_STORED,
-    zipfile.ZIP_LZMA,
-    zipfile.ZIP_DEFLATED,
-    zipfile.ZIP_BZIP2
-])
[email protected]("ct", COMPRESS_TYPES)
 def test_adding_data(caplog, data, ct):
     """Test adding non-files with different compression methods"""
     caplog.set_level(logging.WARNING)
@@ -1173,6 +1201,36 @@
         assert zinfos[0].date_time == (2107, 12, 31, 23, 59, 58)
 
 
[email protected](PY313, reason="Tests zstd compress_level (Python 3.14+ 
only)")
+def test_zstd_uses_compression_level():
+    """Test that the zstd compression level is applied"""
+    zs = ZipStream(compress_type=zipfile.ZIP_ZSTANDARD)
+    test = b"a"*1024
+    zs.add(test, "-7.txt", compress_level=-7)
+    zs.add(test, "default.txt")
+    zs.add(test, "22.txt", compress_level=22)
+
+    data = bytes(zs)
+    info = list(zs.info_list())
+    assert len(info) == zs.num_streamed() == 3
+
+    for x in info:
+        assert x["size"] == 1024
+        assert x["compress_type"] == zipfile.ZIP_ZSTANDARD
+        assert x["CRC"] == 2085984185
+
+    assert info[0]["name"] == "-7.txt"
+    assert info[1]["name"] == "default.txt"
+    assert info[2]["name"] == "22.txt"
+
+    # check compress level set
+    assert info[0]["compress_level"] == -7
+    assert info[1]["compress_level"] == None
+    assert info[2]["compress_level"] == 22
+
+    # check different compressed sizes for each level (in decreasing order as 
level increases)
+    assert info[0]["compressed_size"] > info[1]["compressed_size"] > 
info[2]["compressed_size"]
+
 def test_info_list(monkeypatch):
     faketime = (1980, 1, 1, 0, 0, 0)
 
@@ -1228,8 +1286,8 @@
     assert len([x for x in info2 if not x["streamed"]]) == zs.num_queued() == 0
     assert len([x for x in info2 if x["streamed"]]) == zs.num_streamed() == 3
 
-    # Make sure any information that ws provided up-front hasn't changed
-    # (except for the "streamed" key which mush got False -> True)
+    # Make sure any information that was provided up-front hasn't changed
+    # (except for the "streamed" key which must go False -> True)
     for pre, post in zip(info, info2):
         for k, v in pre.items():
             if k == "streamed":
@@ -1525,6 +1583,9 @@
         ZipStream(sized=True, compress_type=zipfile.ZIP_LZMA)
     with pytest.raises(ValueError):
         ZipStream(sized=True, compress_type=zipfile.ZIP_BZIP2)
+    if not PY313:
+        with pytest.raises(ValueError):
+            ZipStream(sized=True, compress_type=zipfile.ZIP_ZSTANDARD)
 
     with pytest.raises(ValueError):
         ZipStream.from_path(".", sized=True, 
compress_type=zipfile.ZIP_DEFLATED)
@@ -1546,6 +1607,9 @@
         szs.add("invalid", "invalid", compress_type=zipfile.ZIP_LZMA)
     with pytest.raises(ValueError):
         szs.add("invalid", "invalid", compress_type=zipfile.ZIP_BZIP2)
+    if not PY313:
+        with pytest.raises(ValueError):
+            szs.add("invalid", "invalid", compress_type=zipfile.ZIP_ZSTANDARD)
 
     assert szs.sized
     calculated = len(szs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/zipstream_ng-1.8.0/zipstream/_async.py 
new/zipstream_ng-1.9.0/zipstream/_async.py
--- old/zipstream_ng-1.8.0/zipstream/_async.py  1970-01-01 01:00:00.000000000 
+0100
+++ new/zipstream_ng-1.9.0/zipstream/_async.py  2025-05-23 03:51:24.000000000 
+0200
@@ -0,0 +1,96 @@
+
+
+__all__ += ["AsyncZipStream"]
+
+import asyncio
+try:
+    from asyncio import to_thread
+except ImportError:
+    # backport asyncio.to_thread from Python 3.9
+    from contextvars import copy_context
+    async def to_thread(func, /, *args, **kwargs):
+        loop = asyncio.get_running_loop()
+        return await loop.run_in_executor(
+            None,
+            functools.partial(copy_context().run, func, *args, **kwargs)
+        )
+
+async def _to_async_iter(it):
+    SENTINEL = object()
+    i = iter(it)
+    while True:
+        x = await to_thread(next, i, SENTINEL)
+        if x is SENTINEL:
+            break
+        yield x
+
+def _make_delegate_call(name):
+    @functools.wraps(getattr(ZipStream, name))
+    def method(self, *args, **kwargs):
+        return getattr(self._zip, name)(*args, **kwargs)
+    return method
+
+def _delegate(*fcns):
+    def cls_builder(cls):
+        for name in fcns:
+            setattr(cls, name, _make_delegate_call(name))
+        return cls
+    return cls_builder
+
+def _make_delegate_property(name):
+    return property(
+        fget=functools.wraps(getattr(ZipStream, name))(lambda s: 
getattr(s._zip, name)),
+        fset=lambda s, v: setattr(s._zip, name, v)
+    )
+
+def _delegate_property(*fcns):
+    def cls_builder(cls):
+        for name in fcns:
+            setattr(cls, name, _make_delegate_property(name))
+        return cls
+    return cls_builder
+
+
+@_delegate("__len__", "__bool__", "__bytes__", "is_empty", "num_queued", 
"num_streamed", "mkdir", "info_list")
+@_delegate_property("sized", "last_modified", "comment")
+class AsyncZipStream:
+    """An asynchronous write-only zip that is generated from source files/data
+    as it's asynchronously iterated over.
+
+    Ideal for situations where a zip file needs to be dynamically generated
+    without using temporary files (ie: web applications).
+
+    Implementation note: This class is an implementation of the synchronous
+    ZipStream class that delegates the work to a threadpool.
+    """
+
+    @functools.wraps(ZipStream.__init__)
+    def __init__(self, *args, **kwargs):
+        self._zip = ZipStream(*args, **kwargs)
+
+    async def __aiter__(self):
+        """Asynchronously generate zipped data from the added files/data"""
+        async for x in _to_async_iter(self._zip):
+            yield x
+
+    @classmethod
+    @functools.wraps(ZipStream.from_path)
+    async def from_path(cls, path, *, compress_type=ZIP_STORED, 
compress_level=None, sized=None, **kwargs):
+        if sized is None:
+            sized = compress_type == ZIP_STORED
+
+        z = cls(
+            compress_type=compress_type,
+            compress_level=compress_level,
+            sized=sized
+        )
+        await z.add_path(path, **kwargs)
+        return z
+
+    @functools.wraps(ZipStream.add_path)
+    async def add_path(self, *args, **kwargs):
+        return await to_thread(self._zip.add_path, *args, **kwargs)
+
+    @functools.wraps(ZipStream.add)
+    async def add(self, data, *args, **kwargs):
+        return await to_thread(self._zip.add, data, *args, **kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/zipstream_ng-1.8.0/zipstream/ng.py 
new/zipstream_ng-1.9.0/zipstream/ng.py
--- old/zipstream_ng-1.8.0/zipstream/ng.py      2024-10-10 07:04:36.000000000 
+0200
+++ new/zipstream_ng-1.9.0/zipstream/ng.py      2025-08-29 02:46:34.000000000 
+0200
@@ -33,6 +33,11 @@
 )
 
 
+# Constants for compatibility modes
+PY313_COMPAT = sys.version_info < (3, 14)  # disable zstd
+PY36_COMPAT = sys.version_info < (3, 7)  # disable compress_level
+PY35_COMPAT = sys.version_info < (3, 6)  # backport ZipInfo functions, 
stringify path-like objects
+
 # Size of chunks to read out of files
 # Note that when compressing data the compressor will operate on bigger chunks
 # than this - it keeps a cache as new chunks are fed to it.
@@ -51,16 +56,22 @@
 # (includes "/" regardless of platform as per ZIP format specification)
 PATH_SEPARATORS = set(x for x in (os.sep, os.altsep, "/") if x)
 
-# Constants for compatibility modes
-PY36_COMPAT = sys.version_info < (3, 7)  # disable compress_level
-PY35_COMPAT = sys.version_info < (3, 6)  # backport ZipInfo functions, 
stringify path-like objects
+# zstd-related constants
+if not PY313_COMPAT:
+    from zipfile import ZIP_ZSTANDARD, ZSTANDARD_VERSION
+    from compression.zstd import CompressionParameter
+    ZSTD_LEVEL_BOUNDS = CompressionParameter.compression_level.bounds()
 
 
 __all__ = [
     # Defined classes
     "ZipStream", "ZipStreamInfo",
     # Compression constants (imported from zipfile)
-    "ZIP_STORED", "ZIP_DEFLATED", "BZIP2_VERSION", "ZIP_BZIP2", 
"LZMA_VERSION", "ZIP_LZMA",
+    "ZIP_STORED",
+    "ZIP_DEFLATED",
+    "ZIP_BZIP2", "BZIP2_VERSION",
+    "ZIP_LZMA", "LZMA_VERSION",
+    *(["ZIP_ZSTANDARD", "ZSTANDARD_VERSION"] if not PY313_COMPAT else []),
     # Helper functions
     "walk"
 ]
@@ -83,14 +94,34 @@
         __log__.warning(
             "compress_level has no effect when using ZIP_STORED/ZIP_LZMA"
         )
-    elif compress_type == ZIP_DEFLATED and not 0 <= compress_level <= 9:
-        raise ValueError(
-            "compress_level must be between 0 and 9 when using ZIP_DEFLATED"
-        )
-    elif compress_type == ZIP_BZIP2 and not 1 <= compress_level <= 9:
-        raise ValueError(
-            "compress_level must be between 1 and 9 when using ZIP_BZIP2"
-        )
+    elif compress_type == ZIP_DEFLATED:
+        if not 0 <= compress_level <= 9:
+            raise ValueError(
+                "compress_level must be between 0 and 9 when using 
ZIP_DEFLATED"
+            )
+    elif compress_type == ZIP_BZIP2:
+        if not 1 <= compress_level <= 9:
+            raise ValueError(
+                "compress_level must be between 1 and 9 when using ZIP_BZIP2"
+            )
+    elif not PY313_COMPAT and compress_type == ZIP_ZSTANDARD:
+        if not ZSTD_LEVEL_BOUNDS[0] <= compress_level <= ZSTD_LEVEL_BOUNDS[1]:
+            raise ValueError(
+                "compress_level must be between {} and {} when using 
ZIP_ZSTANDARD".format(
+                    *ZSTD_LEVEL_BOUNDS
+                )
+            )
+
+
+def _min_version_for_compress_type(compress_type, min_version=0):
+    """Ensure the compress_type is supported by the min_version"""
+    if compress_type == ZIP_BZIP2:
+        min_version = max(BZIP2_VERSION, min_version)
+    elif compress_type == ZIP_LZMA:
+        min_version = max(LZMA_VERSION, min_version)
+    elif not PY313_COMPAT and compress_type == ZIP_ZSTANDARD:
+        min_version = max(ZSTANDARD_VERSION, min_version)
+    return min_version
 
 
 def _timestamp_to_dos(ts):
@@ -175,11 +206,7 @@
             file_size = 0xFFFFFFFF
             compress_size = 0xFFFFFFFF
 
-        if self.compress_type == ZIP_BZIP2:
-            min_version = max(BZIP2_VERSION, min_version)
-        elif self.compress_type == ZIP_LZMA:
-            min_version = max(LZMA_VERSION, min_version)
-
+        min_version = _min_version_for_compress_type(self.compress_type, 
min_version)
         self.extract_version = max(min_version, self.extract_version)
         self.create_version = max(min_version, self.create_version)
         filename, flag_bits = self._encodeFilenameFlags()
@@ -313,11 +340,7 @@
             ) + extra_data
             min_version = ZIP64_VERSION
 
-        if self.compress_type == ZIP_BZIP2:
-            min_version = max(BZIP2_VERSION, min_version)
-        elif self.compress_type == ZIP_LZMA:
-            min_version = max(LZMA_VERSION, min_version)
-
+        min_version = _min_version_for_compress_type(self.compress_type, 
min_version)
         extract_version = max(min_version, self.extract_version)
         create_version = max(min_version, self.create_version)
         filename, flag_bits = self._encodeFilenameFlags()
@@ -338,7 +361,7 @@
             len(filename),
             len(extra_data),
             len(self.comment),
-            0,
+            0,  # disk number this file begins on
             self.internal_attr,
             self.external_attr,
             header_offset
@@ -500,19 +523,24 @@
 
         compress_type:
             The ZIP compression method to use when writing the archive, and
-            should be ZIP_STORED, ZIP_DEFLATED, ZIP_BZIP2 or ZIP_LZMA;
-            unrecognized values will cause NotImplementedError to be raised. If
-            ZIP_DEFLATED, ZIP_BZIP2 or ZIP_LZMA is specified but the
-            corresponding module (zlib, bz2 or lzma) is not available,
-            RuntimeError is raised. The default is ZIP_STORED.
+            should be ZIP_STORED, ZIP_DEFLATED, ZIP_BZIP2, ZIP_LZMA, or
+            ZIP_ZSTANDARD (Python 3.14+); unrecognized values will cause
+            NotImplementedError to be raised.
+            If ZIP_DEFLATED, ZIP_BZIP2, ZIP_LZMA, or ZIP_ZSTANDARD is specified
+            but the corresponding module (zlib, bz2, lzma, or compression.zstd)
+            is not available, RuntimeError is raised. The default is 
ZIP_STORED.
 
         compress_level:
             Controls the compression level to use when writing files to the
-            archive. When using ZIP_STORED or ZIP_LZMA it has no effect. When
-            using ZIP_DEFLATED integers 0 through 9 are accepted (see zlib for
-            more information). When using ZIP_BZIP2 integers 1 through 9 are
-            accepted (see bz2 for more information). Raises a ValueError if the
-            provided value isn't valid for the `compress_type`.
+            archive. When using ZIP_STORED or ZIP_LZMA it has no effect.
+            When using ZIP_DEFLATED integers 0 through 9 are accepted (see zlib
+            for more information).
+            When using ZIP_BZIP2 integers 1 through 9 are accepted (see bz2 for
+            more information).
+            When using ZIP_ZSTANDARD integers -7 though 22 are common (see
+            compression.zstd.CompressionParameter for more information).
+            Raises a ValueError if the provided value isn't valid for the
+            `compress_type`.
 
             Only available in Python 3.7+ (raises a ValueError if used on a
             lower version)
@@ -1129,22 +1157,27 @@
             centDirOffset > ZIP64_LIMIT or
             centDirSize > ZIP64_LIMIT
         ):
-            # Need to write the Zip64 end-of-archive records
+            # Need to also write a Zip64 end-of-archive record
             zip64EndRec = struct.pack(
                 structEndArchive64,
                 stringEndArchive64,
-                44, 45, 45, 0, 0,
+                44,  # size of this record after this point
+                     # (note: no "zip extensible data" is added so this is a 
constant)
+                45,  # version made by (Zip64 support)
+                45,  # version needed to extract (Zip64 support)
+                0,  # disk number this record is on
+                0,  # disk number that contains the start of the central 
directory
                 centDirCount,
                 centDirCount,
                 centDirSize,
-                centDirOffset
+                centDirOffset,
             )
             zip64LocRec = struct.pack(
                 structEndArchive64Locator,
                 stringEndArchive64Locator,
-                0,
+                0,  # disk number where the zip64EndRec starts
                 zip64EndRecStart,
-                1
+                1,  # total number of disks
             )
             yield self._track(zip64EndRec + zip64LocRec)
 
@@ -1155,7 +1188,8 @@
         endRec = struct.pack(
             structEndArchive,
             stringEndArchive,
-            0, 0,
+            0,  # disk number this record is on
+            0,  # disk number that contains the start of the central directory
             centDirCount,
             centDirCount,
             centDirSize,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/zipstream_ng-1.8.0/zipstream_ng.egg-info/PKG-INFO 
new/zipstream_ng-1.9.0/zipstream_ng.egg-info/PKG-INFO
--- old/zipstream_ng-1.8.0/zipstream_ng.egg-info/PKG-INFO       2024-10-10 
07:22:17.000000000 +0200
+++ new/zipstream_ng-1.9.0/zipstream_ng.egg-info/PKG-INFO       2025-08-29 
03:02:57.000000000 +0200
@@ -1,9 +1,9 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: zipstream-ng
-Version: 1.8.0
+Version: 1.9.0
 Summary: A modern and easy to use streamable zip file generator
 Home-page: https://github.com/pR0Ps/zipstream-ng
-License: LGPLv3
+License: LGPL-3.0-only
 Project-URL: Source, https://github.com/pR0Ps/zipstream-ng
 Project-URL: Changelog, 
https://github.com/pR0Ps/zipstream-ng/blob/master/CHANGELOG.md
 Classifier: Programming Language :: Python :: 3
@@ -15,19 +15,30 @@
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
 Classifier: Operating System :: OS Independent
 Classifier: Topic :: System :: Archiving :: Compression
-Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 
(LGPLv3)
 Requires-Python: >=3.5.0
 Description-Content-Type: text/markdown
 License-File: LICENSE
 Provides-Extra: tests
 Requires-Dist: pytest; extra == "tests"
 Requires-Dist: pytest-cov; extra == "tests"
+Dynamic: classifier
+Dynamic: description
+Dynamic: description-content-type
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: project-url
+Dynamic: provides-extra
+Dynamic: requires-python
+Dynamic: summary
 
 zipstream-ng
 ============
-[![Status](https://github.com/pR0Ps/zipstream-ng/workflows/tests/badge.svg)](https://github.com/pR0Ps/zipstream-ng/actions/workflows/tests.yml)
+[![Status](https://github.com/pR0Ps/zipstream-ng/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/pR0Ps/zipstream-ng/actions/workflows/tests.yml?query=branch%3Amaster)
 
[![Version](https://img.shields.io/pypi/v/zipstream-ng.svg)](https://pypi.org/project/zipstream-ng/)
 ![Python](https://img.shields.io/pypi/pyversions/zipstream-ng.svg)
 
@@ -41,6 +52,7 @@
  - Can calculate the total size of the resulting zip file before generation 
even begins.
  - Low memory usage: Since the zip is generated as it's requested, very little 
has to be kept in
    memory (peak usage of less than 20MB is typical, even for TBs of files).
+ - Performant: On-par or faster than using the standard library to create 
non-streamed zip files.
  - Flexible API: Typical use cases are simple, complicated ones are possible.
  - Supports zipping data from files, bytes, strings, and any other iterable 
objects.
  - Keeps track of the date of the most recently modified file added to the zip 
file.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/zipstream_ng-1.8.0/zipstream_ng.egg-info/SOURCES.txt 
new/zipstream_ng-1.9.0/zipstream_ng.egg-info/SOURCES.txt
--- old/zipstream_ng-1.8.0/zipstream_ng.egg-info/SOURCES.txt    2024-10-10 
07:22:18.000000000 +0200
+++ new/zipstream_ng-1.9.0/zipstream_ng.egg-info/SOURCES.txt    2025-08-29 
03:02:57.000000000 +0200
@@ -3,6 +3,7 @@
 setup.py
 tests/test_zipstream.py
 zipstream/__init__.py
+zipstream/_async.py
 zipstream/ng.py
 zipstream/server.py
 zipstream_ng.egg-info/PKG-INFO

Reply via email to