Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-cymem for openSUSE:Factory checked in at 2026-03-05 17:14:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-cymem (Old) and /work/SRC/openSUSE:Factory/.python-cymem.new.561 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-cymem" Thu Mar 5 17:14:58 2026 rev:5 rq:1336591 version:2.0.13 Changes: -------- --- /work/SRC/openSUSE:Factory/python-cymem/python-cymem.changes 2025-01-22 16:32:19.659960296 +0100 +++ /work/SRC/openSUSE:Factory/.python-cymem.new.561/python-cymem.changes 2026-03-05 17:18:15.834390360 +0100 @@ -1,0 +2,9 @@ +Wed Mar 4 21:28:28 UTC 2026 - Dirk Müller <[email protected]> + +- update to 2.0.13: + * Adds wheels for Python 3.14, and critical sections to allow + thread-safe support of free-threaded Python + * Update to more recent cibuildwheel, to build wheels for + Python v3.13 + +------------------------------------------------------------------- Old: ---- cymem-2.0.11.tar.gz New: ---- cymem-2.0.13.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-cymem.spec ++++++ --- /var/tmp/diff_new_pack.GxSUQp/_old 2026-03-05 17:18:16.594421814 +0100 +++ /var/tmp/diff_new_pack.GxSUQp/_new 2026-03-05 17:18:16.594421814 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-cymem # -# 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,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-cymem -Version: 2.0.11 +Version: 2.0.13 Release: 0 Summary: Manage calls to calloc/free through Cython License: MIT ++++++ cymem-2.0.11.tar.gz -> cymem-2.0.13.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cymem-2.0.11/PKG-INFO new/cymem-2.0.13/PKG-INFO --- old/cymem-2.0.11/PKG-INFO 2025-01-16 21:57:24.485214700 +0100 +++ new/cymem-2.0.13/PKG-INFO 2025-11-14 10:49:51.946309000 +0100 @@ -1,6 +1,6 @@ -Metadata-Version: 2.2 +Metadata-Version: 2.4 Name: cymem -Version: 2.0.11 +Version: 2.0.13 Summary: Manage calls to calloc/free through Cython Home-page: https://github.com/explosion/cymem Author: Matthew Honnibal @@ -14,19 +14,15 @@ Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Programming Language :: Cython -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -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 Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta Classifier: Topic :: Scientific/Engineering +Requires-Python: >=3.9,<3.15 Description-Content-Type: text/markdown License-File: LICENSE Dynamic: author @@ -36,6 +32,8 @@ Dynamic: description-content-type Dynamic: home-page Dynamic: license +Dynamic: license-file +Dynamic: requires-python Dynamic: summary <a href="https://explosion.ai"><img src="https://explosion.ai/assets/img/logo.svg" width="125" height="125" align="right" /></a> @@ -235,3 +233,28 @@ from cymem.cymem cimport Pool, WrapMalloc, WrapFree cdef Pool mem = Pool(WrapMalloc(priv_malloc), WrapFree(priv_free)) ``` + +## Thread Safety + +As of version 2.0.12, `cymem.Pool` is thread-safe when used with CPython 3.13+ +free-threaded builds (PEP 703). All operations on the Pool, including `alloc()`, +`free()`, and `realloc()`, can be safely called from multiple threads concurrently. + +**Key guarantees:** +- Multiple threads can safely call `alloc()`, `free()`, and `realloc()` on the + same `Pool` instance. +- The Pool's internal bookkeeping (`addresses` dict and `size` accounting) is + protected from race conditions. Reading the internal state without + holding a lock on the `Pool` instance via a critical section is not + thread-safe. + +**Important notes:** +- Individual Pool instances are thread-safe, but you are still responsible for + proper synchronization when accessing the memory contents themselves. Note + that holding a lock on the Pool itself is typically not the right approach: + since malloc is thread-safe, memory allocated by separate calls to `alloc` + can be safely accessed concurrently without locking. You should only + synchronize access to specific memory regions that are being shared across + threads, using fine-grained locks appropriate to your use case rather than a + coarse-grained lock on the entire `Pool`. +- Custom memory allocators need to be thread-safe themselves. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cymem-2.0.11/README.md new/cymem-2.0.13/README.md --- old/cymem-2.0.11/README.md 2025-01-16 21:57:19.000000000 +0100 +++ new/cymem-2.0.13/README.md 2025-11-14 10:49:44.000000000 +0100 @@ -195,3 +195,28 @@ from cymem.cymem cimport Pool, WrapMalloc, WrapFree cdef Pool mem = Pool(WrapMalloc(priv_malloc), WrapFree(priv_free)) ``` + +## Thread Safety + +As of version 2.0.12, `cymem.Pool` is thread-safe when used with CPython 3.13+ +free-threaded builds (PEP 703). All operations on the Pool, including `alloc()`, +`free()`, and `realloc()`, can be safely called from multiple threads concurrently. + +**Key guarantees:** +- Multiple threads can safely call `alloc()`, `free()`, and `realloc()` on the + same `Pool` instance. +- The Pool's internal bookkeeping (`addresses` dict and `size` accounting) is + protected from race conditions. Reading the internal state without + holding a lock on the `Pool` instance via a critical section is not + thread-safe. + +**Important notes:** +- Individual Pool instances are thread-safe, but you are still responsible for + proper synchronization when accessing the memory contents themselves. Note + that holding a lock on the Pool itself is typically not the right approach: + since malloc is thread-safe, memory allocated by separate calls to `alloc` + can be safely accessed concurrently without locking. You should only + synchronize access to specific memory regions that are being shared across + threads, using fine-grained locks appropriate to your use case rather than a + coarse-grained lock on the entire `Pool`. +- Custom memory allocators need to be thread-safe themselves. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cymem-2.0.11/cymem/about.py new/cymem-2.0.13/cymem/about.py --- old/cymem-2.0.11/cymem/about.py 2025-01-16 21:57:19.000000000 +0100 +++ new/cymem-2.0.13/cymem/about.py 2025-11-14 10:49:44.000000000 +0100 @@ -1,5 +1,5 @@ __title__ = "cymem" -__version__ = "2.0.11" +__version__ = "2.0.13" __summary__ = "Manage calls to calloc/free through Cython" __uri__ = "https://github.com/explosion/cymem" __author__ = "Matthew Honnibal" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cymem-2.0.11/cymem/cymem.pyx new/cymem-2.0.13/cymem/cymem.pyx --- old/cymem-2.0.11/cymem/cymem.pyx 2025-01-16 21:57:19.000000000 +0100 +++ new/cymem-2.0.13/cymem/cymem.pyx 2025-11-14 10:49:44.000000000 +0100 @@ -1,7 +1,7 @@ -# cython: embedsignature=True +# cython: embedsignature=True, freethreading_compatible=True +cimport cython from cpython.mem cimport PyMem_Malloc, PyMem_Free -from cpython.ref cimport Py_INCREF, Py_DECREF from libc.string cimport memset from libc.string cimport memcpy import warnings @@ -44,17 +44,29 @@ addresses (dict): The currently allocated addresses and their sizes. Read-only. pymalloc (PyMalloc): The allocator to use (default uses PyMem_Malloc). pyfree (PyFree): The free to use (default uses PyMem_Free). + + Thread-safety: + All public methods in this class can be safely called from multiple threads. + Testing the thread-safety of this class can be done by checking out the repository + at https://github.com/lysnikolaou/test-cymem-threadsafety and following the + instructions on the README. """ def __cinit__(self, PyMalloc pymalloc=Default_Malloc, PyFree pyfree=Default_Free): + # size, addresses and refs are mutable. note that operations on + # dicts and lists are atomic. self.size = 0 self.addresses = {} self.refs = [] + + # pymalloc and pyfree are immutable. self.pymalloc = pymalloc self.pyfree = pyfree def __dealloc__(self): + # No need for locking here since the methods will be unreachable + # by the time __dealloc__ is called cdef size_t addr if self.addresses is not None: for addr in self.addresses: @@ -73,8 +85,17 @@ if p == NULL: raise MemoryError("Error assigning %d bytes" % (number * elem_size)) memset(p, 0, number * elem_size) - self.addresses[<size_t>p] = number * elem_size - self.size += number * elem_size + + # We need a critical section here so that addresses and size get + # updated atomically. If we were to acquire a critical section on self, + # mutating the dictionary would try to acquire a critical section on + # the dictionary and therefore release the critical section on self. + # Acquiring a critical section on self.addresses works because that's + # the only C API that gets called inside the block and acquiring the + # critical section on the top-most held lock does not release it. + with cython.critical_section(self.addresses): + self.addresses[<size_t>p] = number * elem_size + self.size += number * elem_size return p cdef void* realloc(self, void* p, size_t new_size) except NULL: @@ -82,19 +103,36 @@ a non-NULL pointer to the new block. new_size must be larger than the original. - If p is not in the Pool or new_size is 0, a MemoryError is raised. + If p is not in the Pool or new_size isn't larger than the previous size, + a ValueError is raised. """ - if <size_t>p not in self.addresses: - raise ValueError("Pointer %d not found in Pool %s" % (<size_t>p, self.addresses)) - if new_size == 0: - raise ValueError("Realloc requires new_size > 0") - assert new_size > self.addresses[<size_t>p] - cdef void* new_ptr = self.alloc(1, new_size) + cdef size_t old_size + cdef void* new_ptr + + new_ptr = self.pymalloc.malloc(new_size) if new_ptr == NULL: raise MemoryError("Error reallocating to %d bytes" % new_size) - memcpy(new_ptr, p, self.addresses[<size_t>p]) - self.free(p) - self.addresses[<size_t>new_ptr] = new_size + + # See comment in alloc on why we're acquiring a critical section on + # self.addresses instead of self. + with cython.critical_section(self.addresses): + try: + old_size = self.addresses.pop(<size_t>p) + except KeyError: + self.pyfree.free(new_ptr) + raise ValueError("Pointer %d not found in Pool %s" % (<size_t>p, self.addresses)) + + if old_size >= new_size: + self.addresses[<size_t>p] = old_size + self.pyfree.free(new_ptr) + raise ValueError("Realloc requires new_size > previous size") + + memcpy(new_ptr, p, old_size) + memset(<char*> new_ptr + old_size, 0, new_size - old_size) + self.size += new_size - old_size + self.addresses[<size_t>new_ptr] = new_size + + self.pyfree.free(p) return new_ptr cdef void free(self, void* p) except *: @@ -105,10 +143,17 @@ If p is not in Pool.addresses, a KeyError is raised. """ - self.size -= self.addresses.pop(<size_t>p) + cdef size_t size + + # See comment in alloc on why we're acquiring a critical section on + # self.addresses instead of self. + with cython.critical_section(self.addresses): + size = self.addresses.pop(<size_t>p) + self.size -= size self.pyfree.free(p) def own_pyref(self, object py_ref): + # Calling append here is atomic, no critical section needed. self.refs.append(py_ref) @@ -142,9 +187,9 @@ raise MemoryError("Error assigning %d bytes" % number * elem_size) memset(self.ptr, 0, number * elem_size) - property addr: - def __get__(self): - return <size_t>self.ptr + @property + def addr(self): + return <size_t>self.ptr def __dealloc__(self): if self.ptr != NULL: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cymem-2.0.11/cymem.egg-info/PKG-INFO new/cymem-2.0.13/cymem.egg-info/PKG-INFO --- old/cymem-2.0.11/cymem.egg-info/PKG-INFO 2025-01-16 21:57:24.000000000 +0100 +++ new/cymem-2.0.13/cymem.egg-info/PKG-INFO 2025-11-14 10:49:51.000000000 +0100 @@ -1,6 +1,6 @@ -Metadata-Version: 2.2 +Metadata-Version: 2.4 Name: cymem -Version: 2.0.11 +Version: 2.0.13 Summary: Manage calls to calloc/free through Cython Home-page: https://github.com/explosion/cymem Author: Matthew Honnibal @@ -14,19 +14,15 @@ Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Programming Language :: Cython -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -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 Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta Classifier: Topic :: Scientific/Engineering +Requires-Python: >=3.9,<3.15 Description-Content-Type: text/markdown License-File: LICENSE Dynamic: author @@ -36,6 +32,8 @@ Dynamic: description-content-type Dynamic: home-page Dynamic: license +Dynamic: license-file +Dynamic: requires-python Dynamic: summary <a href="https://explosion.ai"><img src="https://explosion.ai/assets/img/logo.svg" width="125" height="125" align="right" /></a> @@ -235,3 +233,28 @@ from cymem.cymem cimport Pool, WrapMalloc, WrapFree cdef Pool mem = Pool(WrapMalloc(priv_malloc), WrapFree(priv_free)) ``` + +## Thread Safety + +As of version 2.0.12, `cymem.Pool` is thread-safe when used with CPython 3.13+ +free-threaded builds (PEP 703). All operations on the Pool, including `alloc()`, +`free()`, and `realloc()`, can be safely called from multiple threads concurrently. + +**Key guarantees:** +- Multiple threads can safely call `alloc()`, `free()`, and `realloc()` on the + same `Pool` instance. +- The Pool's internal bookkeeping (`addresses` dict and `size` accounting) is + protected from race conditions. Reading the internal state without + holding a lock on the `Pool` instance via a critical section is not + thread-safe. + +**Important notes:** +- Individual Pool instances are thread-safe, but you are still responsible for + proper synchronization when accessing the memory contents themselves. Note + that holding a lock on the Pool itself is typically not the right approach: + since malloc is thread-safe, memory allocated by separate calls to `alloc` + can be safely accessed concurrently without locking. You should only + synchronize access to specific memory regions that are being shared across + threads, using fine-grained locks appropriate to your use case rather than a + coarse-grained lock on the entire `Pool`. +- Custom memory allocators need to be thread-safe themselves. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cymem-2.0.11/pyproject.toml new/cymem-2.0.13/pyproject.toml --- old/cymem-2.0.11/pyproject.toml 2025-01-16 21:57:19.000000000 +0100 +++ new/cymem-2.0.13/pyproject.toml 2025-11-14 10:49:44.000000000 +0100 @@ -1,15 +1,15 @@ [build-system] requires = [ "setuptools", - "cython>=0.25", + "cython>=3.1", ] build-backend = "setuptools.build_meta" [tool.cibuildwheel] build = "*" -skip = "pp* cp36* cp37* cp38*" +skip = "cp38*" test-skip = "" -free-threaded-support = false +enable = ["cpython-freethreading"] archs = ["native"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cymem-2.0.11/setup.py new/cymem-2.0.13/setup.py --- old/cymem-2.0.11/setup.py 2025-01-16 21:57:19.000000000 +0100 +++ new/cymem-2.0.13/setup.py 2025-11-14 10:49:44.000000000 +0100 @@ -89,6 +89,7 @@ setup( name="cymem", + python_requires=">=3.9,<3.15", zip_safe=False, packages=PACKAGES, package_data={"": ["*.pyx", "*.pxd"]}, @@ -100,8 +101,8 @@ version=about["__version__"], url=about["__uri__"], license=about["__license__"], - ext_modules=cythonize(ext_modules, language_level=2), - setup_requires=["cython>=0.25"], + ext_modules=cythonize(ext_modules), + setup_requires=["cython>=3.1"], classifiers=[ "Environment :: Console", "Intended Audience :: Developers", @@ -111,18 +112,13 @@ "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Programming Language :: Cython", - "Programming Language :: Python :: 2.6", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3.3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "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", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: Free Threading :: 2 - Beta", "Topic :: Scientific/Engineering", ], cmdclass={"build_ext": build_ext_subclass},
