Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-pyppmd for openSUSE:Factory 
checked in at 2026-02-17 16:52:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pyppmd (Old)
 and      /work/SRC/openSUSE:Factory/.python-pyppmd.new.1977 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pyppmd"

Tue Feb 17 16:52:46 2026 rev:6 rq:1333514 version:1.3.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pyppmd/python-pyppmd.changes      
2025-06-23 15:05:43.766876708 +0200
+++ /work/SRC/openSUSE:Factory/.python-pyppmd.new.1977/python-pyppmd.changes    
2026-02-17 16:57:50.705743241 +0100
@@ -1,0 +2,32 @@
+Sat Feb 14 08:38:38 UTC 2026 - Andreas Prittwitz <[email protected]>
+
+- Update to version 1.3.1
+  * Change BuildRequires for:
+    - python-devel to >= 3.10
+  * Add BuildRequires for:
+    - python-pytest-cov
+  * Remove extra export CFLAGS statement from %build section.
+    This statement makes test_ppmd_7_decode_chunks crash
+    with: Fatal Python error: Aborted (core dumped) with 
+    version 1.3.1.
+  * Disable test_ppmd8.py for automated test runs and
+    add separate test run for test_ppmd8.py to ensure it passes
+    reliably.
+    See comment in spec file.
+  * Fix publish CI/CD configuration
+  * Bump musllinux image musllinux_1_2
+  * Bump manylinux image manylinux_2_28
+- Changes in version 1.3.0
+  * Fix several issues in ThreadDecoder.c (#126)
+  * Fix the double call of Ppmd7_Free from both 
+    Ppmd7T_Free and Ppmd7Decoder_dealloc
+  * Fix the double call of Ppmd8_Free from both 
+    Ppmd8T_Free and Ppmd8Decoder_dealloc
+  * Fix the issue in PyPY (#126)
+  * Fix initialization order in ffi_build.py
+  * Fix eof handling in cffi_ppmd.py
+  * Add support for Python 3.14
+  * Add compile and link flag for building C++ with 
+    `-pthread` (#126)
+  * Minimum required python to be 3.10
+-------------------------------------------------------------------

Old:
----
  pyppmd-1.2.0.tar.gz

New:
----
  pyppmd-1.3.1.tar.gz

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

Other differences:
------------------
++++++ python-pyppmd.spec ++++++
--- /var/tmp/diff_new_pack.Sn3zBl/_old  2026-02-17 16:57:54.997923075 +0100
+++ /var/tmp/diff_new_pack.Sn3zBl/_new  2026-02-17 16:57:54.997923075 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-pyppmd
 #
-# 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
@@ -18,13 +18,13 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-pyppmd
-Version:        1.2.0
+Version:        1.3.1
 Release:        0
 Summary:        PPMd compression/decompression library
 License:        LGPL-2.1-or-later
-URL:            https://codeberg.org/miurahr/pyppmd
+URL:            https://github.com/miurahr/pyppmd
 Source:         
https://files.pythonhosted.org/packages/source/p/pyppmd/pyppmd-%{version}.tar.gz
-BuildRequires:  %{python_module devel >= 3.9}
+BuildRequires:  %{python_module devel >= 3.10}
 BuildRequires:  %{python_module pip}
 BuildRequires:  %{python_module setuptools_scm >= 6.0.1}
 BuildRequires:  %{python_module wheel}
@@ -35,6 +35,7 @@
 BuildRequires:  %{python_module py-cpuinfo}
 BuildRequires:  %{python_module pytest >= 6}
 BuildRequires:  %{python_module pytest-benchmark}
+BuildRequires:  %{python_module pytest-cov}
 BuildRequires:  %{python_module pytest-timeout}
 # /SECTION
 %python_subpackages
@@ -65,7 +66,6 @@
 sed -i 's/milliseconds=300/milliseconds=5000/g' tests/test_fuzzer.py
 
 %build
-export CFLAGS="%{optflags}"
 %pyproject_wheel
 
 %install
@@ -73,7 +73,10 @@
 %python_expand %fdupes %{buildroot}%{$python_sitearch}
 
 %check
-%pytest_arch --hypothesis-profile=obs
+# test_ppmd8 hangs randomly in automated pytest runs.
+# This test is run separately in the build process to ensure it passes.
+%pytest_arch --hypothesis-profile=obs -k "not test_ppmd8"
+%pytest_arch --hypothesis-profile=obs tests/test_ppmd8.py
 
 %files %{python_files}
 %doc Changelog.rst README.rst

++++++ pyppmd-1.2.0.tar.gz -> pyppmd-1.3.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyppmd-1.2.0/.github/workflows/publish-to-pypi.yml 
new/pyppmd-1.3.1/.github/workflows/publish-to-pypi.yml
--- old/pyppmd-1.2.0/.github/workflows/publish-to-pypi.yml      2025-05-01 
13:28:58.000000000 +0200
+++ new/pyppmd-1.3.1/.github/workflows/publish-to-pypi.yml      2025-11-27 
22:58:19.000000000 +0100
@@ -26,9 +26,9 @@
       - name: Fetch release tags
         run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
       - name: Set up Python ๐Ÿ
-        uses: actions/setup-python@v4
+        uses: actions/setup-python@v5
         with:
-          python-version: 3.11
+          python-version: 3.13
       - name: Install cibuildwheel
         run: |
           python -m pip install -U pip cibuildwheel
@@ -59,7 +59,7 @@
       - name: Fetch release tags
         run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
       - name: Set up Python ๐Ÿ
-        uses: actions/setup-python@v4
+        uses: actions/setup-python@v5
         with:
           python-version: 3.11
       - name: Build source distribution & wheels๐ŸŽก
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyppmd-1.2.0/.github/workflows/run-tox-tests.yml 
new/pyppmd-1.3.1/.github/workflows/run-tox-tests.yml
--- old/pyppmd-1.2.0/.github/workflows/run-tox-tests.yml        2025-05-01 
13:28:58.000000000 +0200
+++ new/pyppmd-1.3.1/.github/workflows/run-tox-tests.yml        2025-11-27 
22:58:19.000000000 +0100
@@ -18,16 +18,20 @@
       matrix:
         os: [ubuntu-24.04, windows-latest]
         python-version: [
-            "3.9",
             "3.10",
             "3.11",
             "3.12",
             "3.13",
+            "3.14",
             "pypy-3.10",
         ]
         include:
           - os: macos-latest
-            python-version: "3.11"
+            python-version: "3.14"
+          - os: ubuntu-24.04-arm
+            python-version: "3.13"
+          - os: windows-11-arm
+            python-version: "3.13"
         exclude:
           - os: windows-latest
             python-version: 'pypy-3.10'
@@ -37,10 +41,9 @@
         with:
           fetch-depth: 20
       - name: Setup python
-        uses: actions/setup-python@v5
+        uses: actions/setup-python@v6
         with:
           python-version: ${{ matrix.python-version }}
-          architecture: x64
       - name: Install dependencies
         run: |
           pip install -U pip tox wheel setuptools setuptools_scm[toml]
@@ -48,32 +51,3 @@
       - name: Test project with tox
         run: |
           tox
-
-  test_on_aarch64:
-    name: Test on ${{ matrix.arch }}
-    runs-on: ubuntu-22.04
-    strategy:
-      matrix:
-        arch: [aarch64]
-        distro: [ubuntu22.04]
-    steps:
-      - name: Checkout ๐Ÿ›Ž๏ธ
-        uses: actions/checkout@v4
-        with:
-          fetch-depth: 20
-      - name: Build & run test
-        uses: uraimo/run-on-arch-action@v2
-        with:
-          arch: ${{ matrix.arch }}
-          distro: ${{ matrix.distro }}
-          githubToken: ${{ github.token }}
-          install: |
-            apt-get update -q -y
-            apt-get install -q -y python3 python3-pip python3-dev 
build-essential gcc git
-            python3 -m pip install -U pip tox setuptools setuptools_scm[toml]
-          run: |
-            git config --global --add safe.directory ${GITHUB_WORKSPACE}
-            python3 -c "import platform;print('Machine type:', 
platform.machine())"
-            python3 -m tox -e py310
-          env: |
-            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyppmd-1.2.0/Changelog.rst 
new/pyppmd-1.3.1/Changelog.rst
--- old/pyppmd-1.2.0/Changelog.rst      2025-05-01 13:28:58.000000000 +0200
+++ new/pyppmd-1.3.1/Changelog.rst      2025-11-27 22:58:19.000000000 +0100
@@ -7,6 +7,36 @@
 `Unreleased`_
 =============
 
+v1.3.1_
+=======
+
+Fixed
+-----
+* Fix publish CI/CD configuration
+    * Bump musllinux image musllinux_1_2
+    * Bump manylinux image manylinux_2_28
+
+v1.3.0_
+=======
+
+Fixed
+-----
+* Fix several issues in ThreadDecoder.c (#126)
+    * Fix the double call of Ppmd7_Free from both Ppmd7T_Free and 
Ppmd7Decoder_dealloc
+    * Fix the double call of Ppmd8_Free from both Ppmd8T_Free and 
Ppmd8Decoder_dealloc
+* Fix the issue in PyPY (#126)
+    * Fix initialization order in ffi_build.py
+    * Fix eof handling in cffi_ppmd.py
+
+Added
+-----
+* Add support for Python 3.14
+
+Changed
+-------
+* Add compile and link flag for building C++ with `-pthread` (#126)
+* Minimum required python to be 3.10
+
 v1.2.0_
 =======
 
@@ -126,7 +156,9 @@
 
 
 .. History links
-.. _Unreleased: https://github.com/miurahr/pyppmd/compare/v1.2.0...HEAD
+.. _Unreleased: https://github.com/miurahr/pyppmd/compare/v1.3.1...HEAD
+.. _v1.3.1: https://github.com/miurahr/pyppmd/compare/v1.3.0...v1.3.1
+.. _v1.3.0: https://github.com/miurahr/pyppmd/compare/v1.2.0...v1.3.0
 .. _v1.2.0: https://github.com/miurahr/pyppmd/compare/v1.1.1...v1.2.0
 .. _v1.1.1: https://github.com/miurahr/pyppmd/compare/v1.0.0...v1.1.1
 .. _v1.1.0: https://github.com/miurahr/pyppmd/compare/v1.0.0...v1.1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyppmd-1.2.0/PKG-INFO new/pyppmd-1.3.1/PKG-INFO
--- old/pyppmd-1.2.0/PKG-INFO   2025-05-01 13:29:04.781758300 +0200
+++ new/pyppmd-1.3.1/PKG-INFO   2025-11-27 22:58:24.000000000 +0100
@@ -1,13 +1,13 @@
 Metadata-Version: 2.4
 Name: pyppmd
-Version: 1.2.0
+Version: 1.3.1
 Summary: PPMd compression/decompression library
 Author-email: Hiroshi Miura <[email protected]>
 License: LGPL-2.1-or-later
-Project-URL: Source, https://codeberg.org/miurahr/pyppmd
+Project-URL: Source, https://github.com/miurahr/pyppmd
 Project-URL: Homepage, https://pyppmd.readthedocs.io/
 Project-URL: Documentation, https://pyppmd.readthedocs.io/en/stable/
-Project-URL: Bug Tracker, https://codeberg.org/miurahr/pyppmd/issues
+Project-URL: Bug Tracker, https://github.com/miurahr/pyppmd/issues
 Project-URL: Changelog, https://pyppmd.readthedocs.io/en/latest/changelog.html
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Operating System :: MacOS :: MacOS X
@@ -16,16 +16,16 @@
 Classifier: Operating System :: POSIX :: Linux
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.9
 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: Programming Language :: Python :: 3 :: Only
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Requires-Python: >=3.9
+Requires-Python: >=3.10
 Description-Content-Type: text/x-rst
 License-File: LICENSE
 Provides-Extra: test
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyppmd-1.2.0/pyproject.toml 
new/pyppmd-1.3.1/pyproject.toml
--- old/pyppmd-1.2.0/pyproject.toml     2025-05-01 13:28:58.000000000 +0200
+++ new/pyppmd-1.3.1/pyproject.toml     2025-11-27 22:58:19.000000000 +0100
@@ -1,6 +1,6 @@
 [project]
 name = "pyppmd"
-requires-python = ">=3.9"
+requires-python = ">=3.10"
 description = "PPMd compression/decompression library"
 readme = "README.rst"
 license = {text = "LGPL-2.1-or-later"}
@@ -15,11 +15,11 @@
     "Operating System :: POSIX :: Linux",
     "Programming Language :: Python",
     "Programming Language :: Python :: 3",
-    "Programming Language :: Python :: 3.9",
     "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",
     "Programming Language :: Python :: 3 :: Only",
     "Programming Language :: Python :: Implementation :: CPython",
     "Programming Language :: Python :: Implementation :: PyPy",
@@ -55,10 +55,10 @@
 ]
 
 [project.urls]
-Source = "https://codeberg.org/miurahr/pyppmd";
+Source = "https://github.com/miurahr/pyppmd";
 Homepage = "https://pyppmd.readthedocs.io/";
 Documentation = "https://pyppmd.readthedocs.io/en/stable/";
-"Bug Tracker" = "https://codeberg.org/miurahr/pyppmd/issues";
+"Bug Tracker" = "https://github.com/miurahr/pyppmd/issues";
 Changelog = "https://pyppmd.readthedocs.io/en/latest/changelog.html";
 
 [build-system]
@@ -113,18 +113,18 @@
 norecursedirs = [".git", "_build", "tmp", ".eggs"]
 
 [tool.cibuildwheel]
-manylinux-x86_64-image = "manylinux2014"
-manylinux-i686-image = "manylinux2014"
-manylinux-aarch64-image = "manylinux2014"
-manylinux-ppc64le-image = "manylinux2014"
-manylinux-s390x-image = "manylinux2014"
-manylinux-pypy_x86_64-image = "manylinux2014"
-manylinux-pypy_aarch64-image = "manylinux2014"
-
-musllinux-x86_64-image = "musllinux_1_1"
-musllinux-aarch64-image = "musllinux_1_1"
-musllinux-ppc64le-image = "musllinux_1_1"
-musllinux-s390x-image = "musllinux_1_1"
+manylinux-x86_64-image = "manylinux_2_28"
+manylinux-i686-image = "manylinux_2_28"
+manylinux-aarch64-image = "manylinux_2_28"
+manylinux-ppc64le-image = "manylinux_2_28"
+manylinux-s390x-image = "manylinux_2_28"
+manylinux-pypy_x86_64-image = "manylinux_2_28"
+manylinux-pypy_aarch64-image = "manylinux_2_28"
+
+musllinux-x86_64-image = "musllinux_1_2"
+musllinux-aarch64-image = "musllinux_1_2"
+musllinux-ppc64le-image = "musllinux_1_2"
+musllinux-s390x-image = "musllinux_1_2"
 
 [tool.cibuildwheel.linux]
 archs = ["auto64", "aarch64"]
@@ -135,7 +135,7 @@
 [tool.tox]
 legacy_tox_ini = """
 [tox]
-envlist = check, py{39,310,311,312,313}, pypy39, docs
+envlist = check, py{310,311,312,313,314}, pypy310, docs
 
 [testenv]
 passenv =
@@ -145,8 +145,8 @@
 commands =
     python -m pytest -vv -s
 
-[testenv:pypy39]
-basepython = pypy3.9
+[testenv:pypy310]
+basepython = pypy3.10
 
 [testenv:check]
 basepython = python3.12
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyppmd-1.2.0/setup.py new/pyppmd-1.3.1/setup.py
--- old/pyppmd-1.2.0/setup.py   2025-05-01 13:28:58.000000000 +0200
+++ new/pyppmd-1.3.1/setup.py   2025-11-27 22:58:19.000000000 +0100
@@ -11,6 +11,8 @@
     "include_dirs": ["src/lib/ppmd", "src/lib/buffer"],
     "library_dirs": [],
     "libraries": [],
+    "extra_compile_args": [],
+    "extra_link_args": [],
     "sources": [
             "src/lib/ppmd/Ppmd7.c",
             "src/lib/ppmd/Ppmd8.c",
@@ -62,6 +64,11 @@
     def build_extensions(self):
         for extension in self.extensions:
             if self.compiler.compiler_type.lower() in ("unix", "mingw32"):
+                # Ensure pthread is used for synchronization primitives
+                extension.extra_compile_args.append("-pthread")
+                if not hasattr(extension, "extra_link_args") or 
extension.extra_link_args is None:
+                    extension.extra_link_args = []
+                extension.extra_link_args.append("-pthread")
                 if WARNING_AS_ERROR:
                     extension.extra_compile_args.append("-Werror")
             elif self.compiler.compiler_type.lower() == "msvc":
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyppmd-1.2.0/src/ext/ffi_build.py 
new/pyppmd-1.3.1/src/ext/ffi_build.py
--- old/pyppmd-1.2.0/src/ext/ffi_build.py       2025-05-01 13:28:58.000000000 
+0200
+++ new/pyppmd-1.3.1/src/ext/ffi_build.py       2025-11-27 22:58:19.000000000 
+0100
@@ -320,10 +320,14 @@
 
 int ppmd7_decompress_init(CPpmd7z_RangeDec *rc, BufferReader *reader, 
ppmd_info *info, IAllocPtr allocator)
 {
-    reader->Read = (Byte (*)(void *)) Reader;
+    /* Use threaded reader and initialize thread control BEFORE range init,
+       since RangeDec_Init will read from the stream immediately. */
+    reader->Read = (Byte (*)(void *)) Ppmd_thread_Reader;
+    reader->t = info;
+    info->in = reader->inBuffer;
+    Ppmd_thread_decode_init(info, allocator);
     rc->Stream = (IByteIn *) reader;
     Bool res = Ppmd7z_RangeDec_Init(rc);
-    Ppmd_thread_decode_init(info, allocator);
     return res;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyppmd-1.2.0/src/lib/buffer/ThreadDecoder.c 
new/pyppmd-1.3.1/src/lib/buffer/ThreadDecoder.c
--- old/pyppmd-1.2.0/src/lib/buffer/ThreadDecoder.c     2025-05-01 
13:28:58.000000000 +0200
+++ new/pyppmd-1.3.1/src/lib/buffer/ThreadDecoder.c     2025-11-27 
22:58:19.000000000 +0100
@@ -4,6 +4,7 @@
 
 #include "ThreadDecoder.h"
 #include "Buffer.h"
+#include <time.h>
 #ifdef __APPLE__
 #include <mach/clock.h>
 #include <mach/mach.h>
@@ -56,6 +57,11 @@
 }
 #endif
 
+/* cleanup helper for pthread_cleanup_push/pop */
+static void ppmd_mutex_unlock(void *m) {
+    pthread_mutex_unlock((pthread_mutex_t *)m);
+}
+
 Byte Ppmd_thread_Reader(const void *p) {
     BufferReader *bufferReader = (BufferReader *)p;
     ppmd_info *threadInfo = bufferReader->t;
@@ -65,10 +71,12 @@
         pthread_mutex_lock(&tc->mutex);
         tc->empty = True;
         pthread_cond_broadcast(&tc->inEmpty);
+        /* Ensure mutex is unlocked if this thread is cancelled while waiting 
*/
+        pthread_cleanup_push(ppmd_mutex_unlock, (void *)&tc->mutex);
         do {
             pthread_cond_wait(&tc->notEmpty, &tc->mutex);
         } while (tc->empty);
-        pthread_mutex_unlock(&tc->mutex);
+        pthread_cleanup_pop(1); /* unlocks mutex */
     }
     return *((const Byte *)inBuffer->src + inBuffer->pos++);
 }
@@ -102,19 +110,23 @@
     int i = 0;
     int result;
     while (i < max_length ) {
-        Bool inbuf_empty = reader->inBuffer->size == reader->inBuffer->pos;
         Bool outbuf_full = threadInfo->out->size == threadInfo->out->pos;
+        /*
+         * Only stop when output buffer is full. Do NOT stop just because
+         * input buffer appears empty, since decoder may still be able to
+         * produce symbols without consuming new input bytes. If more input
+         * is actually required, Reader() will block and signal controller
+         * via inEmpty condition.
+         */
         if (outbuf_full) {
             break;
         }
-        if (inbuf_empty && reader->inBuffer->size > 0) {
-            break;
-        }
         int c = Ppmd7_DecodeSymbol(cPpmd7, rc);
         if (c == PPMD_RESULT_EOF) {
             result = PPMD_RESULT_EOF;
             goto exit;
-        } else if (c == PPMD_RESULT_ERROR) {
+        }
+        if (c == PPMD_RESULT_ERROR) {
             result = PPMD_RESULT_ERROR;
             goto exit;
         }
@@ -130,6 +142,8 @@
     pthread_mutex_lock(&tc->mutex);
     threadInfo->result = result;
     tc->finished = True;
+    /* Wake controller that might be waiting on input-empty condition */
+    pthread_cond_broadcast(&tc->inEmpty);
     pthread_mutex_unlock(&tc->mutex);
     return NULL;
 }
@@ -178,12 +192,23 @@
 
 void Ppmd7T_Free(CPpmd7 *cPpmd7, ppmd_info *threadInfo, IAllocPtr allocator) {
     ppmd_thread_control_t *tc = (ppmd_thread_control_t *)threadInfo->t;
-    if (!(tc->finished)) {
+    if (tc && !(tc->finished)) {
+        /* Wake worker if it's waiting for input, then cancel and join */
+        pthread_mutex_lock(&tc->mutex);
+        tc->empty = False;
+        pthread_cond_broadcast(&tc->notEmpty);
+        pthread_mutex_unlock(&tc->mutex);
+
         pthread_cancel(tc->handle);
+        pthread_join(tc->handle, NULL);
         tc->finished = True;
     }
-    IAlloc_Free(allocator, tc);
-    Ppmd7_Free(cPpmd7, allocator);
+    if (tc) {
+        pthread_mutex_destroy(&tc->mutex);
+        pthread_cond_destroy(&tc->inEmpty);
+        pthread_cond_destroy(&tc->notEmpty);
+        IAlloc_Free(allocator, tc);
+    }
 }
 
 static void *
@@ -200,9 +225,10 @@
     int i = 0;
     int result;
     while (i < max_length ) {
-        Bool inbuf_empty = reader->inBuffer->size == reader->inBuffer->pos;
         Bool outbuf_full = threadInfo->out->size == threadInfo->out->pos;
-        if (inbuf_empty || outbuf_full) {
+        /* Only stop when output buffer is full; let Reader() handle
+         * input starvation by signaling controller. */
+        if (outbuf_full) {
             break;
         }
         int c = Ppmd8_DecodeSymbol(cPpmd8);
@@ -225,6 +251,8 @@
     pthread_mutex_lock(&tc->mutex);
     threadInfo->result = result;
     tc->finished = True;
+    /* Wake controller that might be waiting on input-empty condition */
+    pthread_cond_broadcast(&tc->inEmpty);
     pthread_mutex_unlock(&tc->mutex);
     return NULL;
 }
@@ -273,10 +301,21 @@
 
 void Ppmd8T_Free(CPpmd8 *cPpmd8, ppmd_info *threadInfo, IAllocPtr allocator) {
     ppmd_thread_control_t *tc = (ppmd_thread_control_t *)threadInfo->t;
-    if (!(tc->finished)) {
+    if (tc && !(tc->finished)) {
+        /* Wake worker if it's waiting for input, then cancel and join */
+        pthread_mutex_lock(&tc->mutex);
+        tc->empty = False;
+        pthread_cond_broadcast(&tc->notEmpty);
+        pthread_mutex_unlock(&tc->mutex);
+
         pthread_cancel(tc->handle);
+        pthread_join(tc->handle, NULL);
         tc->finished = True;
     }
-    IAlloc_Free(allocator, tc);
-    Ppmd8_Free(cPpmd8, allocator);
+    if (tc) {
+        pthread_mutex_destroy(&tc->mutex);
+        pthread_cond_destroy(&tc->inEmpty);
+        pthread_cond_destroy(&tc->notEmpty);
+        IAlloc_Free(allocator, tc);
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyppmd-1.2.0/src/pyppmd/cffi/cffi_ppmd.py 
new/pyppmd-1.3.1/src/pyppmd/cffi/cffi_ppmd.py
--- old/pyppmd-1.2.0/src/pyppmd/cffi/cffi_ppmd.py       2025-05-01 
13:28:58.000000000 +0200
+++ new/pyppmd-1.3.1/src/pyppmd/cffi/cffi_ppmd.py       2025-11-27 
22:58:19.000000000 +0100
@@ -557,6 +557,11 @@
         if not isinstance(length, int):
             raise PpmdError("Wrong length argument is specified.")
         self.lock.acquire()
+        # If EOF already reached in a previous call, subsequent decode calls
+        # should be no-ops and return empty bytes without touching 
freed/native state.
+        if getattr(self, "_eof", False):
+            self.lock.release()
+            return b""
         in_buf, use_input_buffer = self._setup_inBuffer(data)
         out, out_buf = self._setup_outBuffer()
         self.threadInfo.out = out_buf
@@ -578,7 +583,6 @@
                 self._needs_input = False
                 res = out.finish(out_buf)
                 self.lock.release()
-                self._free()
                 return res
             elif size == -2:
                 raise ValueError("Corrupted archive data.")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyppmd-1.2.0/src/pyppmd.egg-info/PKG-INFO 
new/pyppmd-1.3.1/src/pyppmd.egg-info/PKG-INFO
--- old/pyppmd-1.2.0/src/pyppmd.egg-info/PKG-INFO       2025-05-01 
13:29:04.000000000 +0200
+++ new/pyppmd-1.3.1/src/pyppmd.egg-info/PKG-INFO       2025-11-27 
22:58:24.000000000 +0100
@@ -1,13 +1,13 @@
 Metadata-Version: 2.4
 Name: pyppmd
-Version: 1.2.0
+Version: 1.3.1
 Summary: PPMd compression/decompression library
 Author-email: Hiroshi Miura <[email protected]>
 License: LGPL-2.1-or-later
-Project-URL: Source, https://codeberg.org/miurahr/pyppmd
+Project-URL: Source, https://github.com/miurahr/pyppmd
 Project-URL: Homepage, https://pyppmd.readthedocs.io/
 Project-URL: Documentation, https://pyppmd.readthedocs.io/en/stable/
-Project-URL: Bug Tracker, https://codeberg.org/miurahr/pyppmd/issues
+Project-URL: Bug Tracker, https://github.com/miurahr/pyppmd/issues
 Project-URL: Changelog, https://pyppmd.readthedocs.io/en/latest/changelog.html
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Operating System :: MacOS :: MacOS X
@@ -16,16 +16,16 @@
 Classifier: Operating System :: POSIX :: Linux
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.9
 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: Programming Language :: Python :: 3 :: Only
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Requires-Python: >=3.9
+Requires-Python: >=3.10
 Description-Content-Type: text/x-rst
 License-File: LICENSE
 Provides-Extra: test

Reply via email to