Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-greenlet for openSUSE:Factory checked in at 2025-09-02 17:58:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-greenlet (Old) and /work/SRC/openSUSE:Factory/.python-greenlet.new.1977 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-greenlet" Tue Sep 2 17:58:00 2025 rev:55 rq:1302236 version:3.2.4 Changes: -------- --- /work/SRC/openSUSE:Factory/python-greenlet/python-greenlet.changes 2025-06-14 16:17:15.933119463 +0200 +++ /work/SRC/openSUSE:Factory/.python-greenlet.new.1977/python-greenlet.changes 2025-09-02 17:58:07.169983962 +0200 @@ -1,0 +2,13 @@ +Mon Sep 1 12:37:08 UTC 2025 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- Update to 3.2.4 + * Various small build/test changes for less common configurations (e.g., + building CPython with assertions enabled but NOT debugging), + contributed by Michał Górny. Note that while greenlet will BUILD in + a free-threaded Python, it will cause the GIL to be allocated and + used, and memory may leak. Also note that these configurations + are not tested by this project's CI. + * Fix an assertion error on debug builds of Python 3.14 when using the + experimental JIT. See PR 460. + +------------------------------------------------------------------- Old: ---- greenlet-3.2.3.tar.gz New: ---- greenlet-3.2.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-greenlet.spec ++++++ --- /var/tmp/diff_new_pack.MBgYLb/_old 2025-09-02 17:58:07.830011737 +0200 +++ /var/tmp/diff_new_pack.MBgYLb/_new 2025-09-02 17:58:07.830011737 +0200 @@ -22,7 +22,7 @@ %{?sle15_python_module_pythons} Name: python-greenlet -Version: 3.2.3 +Version: 3.2.4 Release: 0 Summary: Lightweight in-process concurrent programming License: MIT ++++++ greenlet-3.2.3.tar.gz -> greenlet-3.2.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/.github/workflows/tests.yml new/greenlet-3.2.4/.github/workflows/tests.yml --- old/greenlet-3.2.3/.github/workflows/tests.yml 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/.github/workflows/tests.yml 2025-08-07 15:13:36.000000000 +0200 @@ -22,7 +22,7 @@ runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.9, "3.10", "3.11", "3.12", "3.13", "3.14.0-beta.2"] + python-version: [3.9, "3.10", "3.11", "3.12", "3.13", "3.14.0-rc.1"] # Recall the macOS builds upload built wheels so all supported versions # need to run on mac. os: [ubuntu-latest, macos-latest] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/CHANGES.rst new/greenlet-3.2.4/CHANGES.rst --- old/greenlet-3.2.3/CHANGES.rst 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/CHANGES.rst 2025-08-07 15:13:36.000000000 +0200 @@ -2,6 +2,24 @@ Changes ========= +3.2.4 (2025-08-07) +================== + +.. note:: + + The 3.2.x series will be the last to support Python 3.9. + +- Various small build/test changes for less common configurations (e.g., + building CPython with assertions enabled but NOT debugging), + contributed by Michał Górny. Note that while greenlet will BUILD in + a free-threaded Python, it will cause the GIL to be allocated and + used, and memory may leak. Also note that these configurations + are not tested by this project's CI. +- Fix an assertion error on debug builds of Python 3.14 when using the + experimental JIT. See :issue:`460 + <https://github.com/python-greenlet/greenlet/issues/460>`_. + + 3.2.3 (2025-06-05) ================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/PKG-INFO new/greenlet-3.2.4/PKG-INFO --- old/greenlet-3.2.3/PKG-INFO 2025-06-05 18:08:19.273496000 +0200 +++ new/greenlet-3.2.4/PKG-INFO 2025-08-07 15:13:39.288525800 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: greenlet -Version: 3.2.3 +Version: 3.2.4 Summary: Lightweight in-process concurrent programming Home-page: https://greenlet.readthedocs.io/ Author: Alexey Borzenkov @@ -38,6 +38,7 @@ Provides-Extra: test Requires-Dist: objgraph; extra == "test" Requires-Dist: psutil; extra == "test" +Requires-Dist: setuptools; extra == "test" Dynamic: author Dynamic: author-email Dynamic: classifier diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/appveyor.yml new/greenlet-3.2.4/appveyor.yml --- old/greenlet-3.2.3/appveyor.yml 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/appveyor.yml 2025-08-07 15:13:36.000000000 +0200 @@ -40,7 +40,7 @@ # Fully supported 64-bit versions, with testing. This should be # all the current (non EOL) versions. - PYTHON: "C:\\Python314-x64" - PYTHON_VERSION: "3.14.0b2" + PYTHON_VERSION: "3.14.0rc1" PYTHON_ARCH: "64" PYTHON_EXE: python diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/docs/caveats.rst new/greenlet-3.2.4/docs/caveats.rst --- old/greenlet-3.2.3/docs/caveats.rst 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/docs/caveats.rst 2025-08-07 15:13:36.000000000 +0200 @@ -32,3 +32,18 @@ lead to a hang. See :issue:`143` for an example. + +Free-threading Is Not Supported +=============================== + +Beginning with 3.14 (and experimental in 3.13), CPython may be built +in a free-threaded mode where the GIL is not used by default. greenlet +does not support this mode (although it will build with it), and using +greenlet in such an interpreter will cause the GIL to be enabled. + +In addition, there are known issues running greenlets in a +free-threaded CPython. These include: + +- Garbage collection differences may cause ``GreenletExit`` to no + longer be raised in certain multi-threaded scenarios. +- There may be other memory leaks. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/docs/index.rst new/greenlet-3.2.4/docs/index.rst --- old/greenlet-3.2.3/docs/index.rst 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/docs/index.rst 2025-08-07 15:13:36.000000000 +0200 @@ -80,6 +80,10 @@ when they execute, and since they are coroutines, many greenlets can exist in a single native thread. +Note that greenlet will cause a free-threaded build of Python to +allocate a GIL, so no actual free-threading will take place. For more +on free-threading and greenlet, see :doc:`caveats`. + .. rubric:: How are greenlets different from threads? Threads (in theory) are preemptive and parallel [#f1]_, meaning that multiple @@ -102,6 +106,10 @@ require fewer resources; it is often practical to have many more greenlets than it is threads. +Note that greenlet will cause a free-threaded build of Python to +allocate a GIL, so no actual free-threading will take place. For more +on free-threading and greenlet, see :doc:`caveats`. + .. _race conditions: https://en.wikipedia.org/wiki/Race_condition .. _deadlocks: https://docs.microsoft.com/en-us/troubleshoot/dotnet/visual-basic/race-conditions-deadlocks#when-deadlocks-occur diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/setup.py new/greenlet-3.2.4/setup.py --- old/greenlet-3.2.3/setup.py 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/setup.py 2025-08-07 15:13:36.000000000 +0200 @@ -260,6 +260,7 @@ 'test': [ 'objgraph', 'psutil', + 'setuptools', ], }, python_requires=">=3.9", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/PyGreenlet.cpp new/greenlet-3.2.4/src/greenlet/PyGreenlet.cpp --- old/greenlet-3.2.3/src/greenlet/PyGreenlet.cpp 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/PyGreenlet.cpp 2025-08-07 15:13:36.000000000 +0200 @@ -56,8 +56,15 @@ PyGreenlet* o = (PyGreenlet*)PyBaseObject_Type.tp_new(type, mod_globs->empty_tuple, mod_globs->empty_dict); if (o) { - new UserGreenlet(o, GET_THREAD_STATE().state().borrow_current()); + // Recall: borrowing or getting the current greenlet + // causes the "deleteme list" to get cleared. So constructing a greenlet + // can do things like cause other greenlets to get finalized. + UserGreenlet* c = new UserGreenlet(o, GET_THREAD_STATE().state().borrow_current()); assert(Py_REFCNT(o) == 1); + // Also: This looks like a memory leak, but isn't. Constructing the + // C++ object assigns it to the pimpl pointer of the Python object (o); + // we'll need that later. + assert(c == o->pimpl); } return o; } @@ -236,20 +243,26 @@ * and call ``PyObject_CallFinalizerFromDealloc``, * but that's only supported in Python 3.4+; see * Modules/_io/iobase.c for an example. + * TODO: We no longer run on anything that old, switch to finalizers. * * The following approach is copied from iobase.c in CPython 2.7. * (along with much of this function in general). Here's their * comment: * * When called from a heap type's dealloc, the type will be - * decref'ed on return (see e.g. subtype_dealloc in typeobject.c). */ + * decref'ed on return (see e.g. subtype_dealloc in typeobject.c). + * + * On free-threaded builds of CPython, the type is meant to be immortal + * so we probably shouldn't mess with this? See + * test_issue_245_reference_counting_subclass_no_threads + */ if (PyType_HasFeature(self.TYPE(), Py_TPFLAGS_HEAPTYPE)) { Py_INCREF(self.TYPE()); } PyObject_GC_Track((PyObject*)self); - _Py_DEC_REFTOTAL; + GREENLET_Py_DEC_REFTOTAL; #ifdef COUNT_ALLOCS --Py_TYPE(self)->tp_frees; --Py_TYPE(self)->tp_allocs; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/TGreenlet.cpp new/greenlet-3.2.4/src/greenlet/TGreenlet.cpp --- old/greenlet-3.2.3/src/greenlet/TGreenlet.cpp 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/TGreenlet.cpp 2025-08-07 15:13:36.000000000 +0200 @@ -562,6 +562,7 @@ // be able to raise an exception. // That's mostly OK! Since we can't add it to a list, our refcount // won't increase, and we'll go ahead with the DECREFs later. + ThreadState *const thread_state = this->thread_state(); if (thread_state) { thread_state->delete_when_thread_running(this->self()); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/TGreenlet.hpp new/greenlet-3.2.4/src/greenlet/TGreenlet.hpp --- old/greenlet-3.2.3/src/greenlet/TGreenlet.hpp 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/TGreenlet.hpp 2025-08-07 15:13:36.000000000 +0200 @@ -116,6 +116,12 @@ #endif #if GREENLET_PY314 int py_recursion_depth; + // I think this is only used by the JIT. At least, + // we only got errors not switching it when the JIT was enabled. + // Python/generated_cases.c.h:12469: _PyEval_EvalFrameDefault: + // Assertion `tstate->current_executor == NULL' failed. + // see https://github.com/python-greenlet/greenlet/issues/460 + PyObject* current_executor; #elif GREENLET_PY312 int py_recursion_depth; int c_recursion_depth; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/TPythonState.cpp new/greenlet-3.2.4/src/greenlet/TPythonState.cpp --- old/greenlet-3.2.3/src/greenlet/TPythonState.cpp 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/TPythonState.cpp 2025-08-07 15:13:36.000000000 +0200 @@ -14,6 +14,7 @@ #endif #if GREENLET_PY314 ,py_recursion_depth(0) + ,current_executor(nullptr) #elif GREENLET_PY312 ,py_recursion_depth(0) ,c_recursion_depth(0) @@ -136,6 +137,7 @@ #if GREENLET_PY311 #if GREENLET_PY314 this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; + this->current_executor = tstate->current_executor; #elif GREENLET_PY312 this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; this->c_recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining; @@ -213,6 +215,7 @@ #if GREENLET_PY311 #if GREENLET_PY314 tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth; + tstate->current_executor = this->current_executor; this->unexpose_frames(); #elif GREENLET_PY312 tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth; @@ -262,6 +265,7 @@ this->_top_frame = nullptr; #if GREENLET_PY314 this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; + this->current_executor = tstate->current_executor; #elif GREENLET_PY312 this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; // XXX: TODO: Comment from a reviewer: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/__init__.py new/greenlet-3.2.4/src/greenlet/__init__.py --- old/greenlet-3.2.3/src/greenlet/__init__.py 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/__init__.py 2025-08-07 15:13:36.000000000 +0200 @@ -25,7 +25,7 @@ ### # Metadata ### -__version__ = '3.2.3' +__version__ = '3.2.4' from ._greenlet import _C_API # pylint:disable=no-name-in-module ### diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/greenlet_allocator.hpp new/greenlet-3.2.4/src/greenlet/greenlet_allocator.hpp --- old/greenlet-3.2.3/src/greenlet/greenlet_allocator.hpp 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/greenlet_allocator.hpp 2025-08-07 15:13:36.000000000 +0200 @@ -5,10 +5,35 @@ #include <Python.h> #include <memory> #include "greenlet_compiler_compat.hpp" +#include "greenlet_cpython_compat.hpp" namespace greenlet { +#if defined(Py_GIL_DISABLED) +// Python on free threaded builds says this +// (https://docs.python.org/3/howto/free-threading-extensions.html#memory-allocation-apis): +// +// For thread-safety, the free-threaded build requires that only +// Python objects are allocated using the object domain, and that all +// Python object are allocated using that domain. +// +// This turns out to be important because the GC implementation on +// free threaded Python uses internal mimalloc APIs to find allocated +// objects. If we allocate non-PyObject objects using that API, then +// Bad Things could happen, including crashes and improper results. +// So in that case, we revert to standard C++ allocation. + + template <class T> + struct PythonAllocator : public std::allocator<T> { + // This member is deprecated in C++17 and removed in C++20 + template< class U > + struct rebind { + typedef PythonAllocator<U> other; + }; + }; + +#else // This allocator is stateless; all instances are identical. // It can *ONLY* be used when we're sure we're holding the GIL // (Python's allocators require the GIL). @@ -58,6 +83,7 @@ }; }; +#endif // allocator type } #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/greenlet_cpython_compat.hpp new/greenlet-3.2.4/src/greenlet/greenlet_cpython_compat.hpp --- old/greenlet-3.2.3/src/greenlet/greenlet_cpython_compat.hpp 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/greenlet_cpython_compat.hpp 2025-08-07 15:13:36.000000000 +0200 @@ -73,7 +73,9 @@ # define Py_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt) #endif -#ifndef _Py_DEC_REFTOTAL +#ifdef _Py_DEC_REFTOTAL +# define GREENLET_Py_DEC_REFTOTAL _Py_DEC_REFTOTAL +#else /* _Py_DEC_REFTOTAL macro has been removed from Python 3.9 by: https://github.com/python/cpython/commit/49932fec62c616ec88da52642339d83ae719e924 @@ -81,12 +83,12 @@ */ # ifdef Py_REF_DEBUG # if GREENLET_PY312 -# define _Py_DEC_REFTOTAL +# define GREENLET_Py_DEC_REFTOTAL # else -# define _Py_DEC_REFTOTAL _Py_RefTotal-- +# define GREENLET_Py_DEC_REFTOTAL _Py_RefTotal-- # endif # else -# define _Py_DEC_REFTOTAL +# define GREENLET_Py_DEC_REFTOTAL # endif #endif // Define these flags like Cython does if we're on an old version. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/tests/__init__.py new/greenlet-3.2.4/src/greenlet/tests/__init__.py --- old/greenlet-3.2.3/src/greenlet/tests/__init__.py 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/tests/__init__.py 2025-08-07 15:13:36.000000000 +0200 @@ -5,6 +5,7 @@ """ import os import sys +import sysconfig import unittest from gc import collect @@ -36,6 +37,13 @@ RUNNING_ON_CI = RUNNING_ON_TRAVIS or RUNNING_ON_APPVEYOR RUNNING_ON_MANYLINUX = os.environ.get('GREENLET_MANYLINUX') +# Is the current interpreter free-threaded?) Note that this +# isn't the same as whether the GIL is enabled, this is the build-time +# value. Certain CPython details, like the garbage collector, +# work very differently on potentially-free-threaded builds than +# standard builds. +RUNNING_ON_FREETHREAD_BUILD = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) + class TestCaseMetaClass(type): # wrap each test method with # a) leak checks diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/tests/leakcheck.py new/greenlet-3.2.4/src/greenlet/tests/leakcheck.py --- old/greenlet-3.2.3/src/greenlet/tests/leakcheck.py 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/tests/leakcheck.py 2025-08-07 15:13:36.000000000 +0200 @@ -136,6 +136,18 @@ # presumably. IGNORED_TYPES = () #(tuple, dict, types.FrameType, types.TracebackType) + # Names of types that should be ignored. Use this when we cannot + # or don't want to import the class directly. + IGNORED_TYPE_NAMES = ( + # This appears in Python3.14 with the JIT enabled. It + # doesn't seem to be directly exposed to Python; the only way to get + # one is to cause code to get jitted and then look for all objects + # and find one with this name. But they multiply as code + # executes and gets jitted, in ways we don't want to rely on. + # So just ignore it. + 'uop_executor', + ) + def __init__(self, testcase, function): self.testcase = testcase self.function = function @@ -179,9 +191,14 @@ return False - if kind in self.ignored_types or kind in self.IGNORED_TYPES: + if ( + kind in self.ignored_types + or kind in self.IGNORED_TYPES + or kind.__name__ in self.IGNORED_TYPE_NAMES + ): return False + return True def _growth(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/tests/test_greenlet.py new/greenlet-3.2.4/src/greenlet/tests/test_greenlet.py --- old/greenlet-3.2.3/src/greenlet/tests/test_greenlet.py 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/tests/test_greenlet.py 2025-08-07 15:13:36.000000000 +0200 @@ -13,6 +13,7 @@ from . import RUNNING_ON_MANYLINUX from . import PY313 from . import PY314 +from . import RUNNING_ON_FREETHREAD_BUILD from .leakcheck import fails_leakcheck @@ -245,7 +246,6 @@ someref.append(g1) del g1 gc.collect() - bg_glet_created_running_and_no_longer_ref_in_bg.set() fg_ref_released.wait(3) @@ -261,8 +261,20 @@ self.assertEqual(seen, []) self.assertEqual(len(someref), 1) del someref[:] - gc.collect() - # g1 is not released immediately because it's from another thread + if not RUNNING_ON_FREETHREAD_BUILD: + # The free-threaded GC is very different. In 3.14rc1, + # the free-threaded GC traverses ``g1``, realizes it is + # not referenced from anywhere else IT cares about, + # calls ``tp_clear`` and then ``green_dealloc``. This causes + # the greenlet to lose its reference to the main greenlet and thread + # in which it was running, which means we can no longer throw an + # exception into it, preventing the rest of this test from working. + # Standard 3.14 traverses the object but doesn't ``tp_clear`` or + # ``green_dealloc`` it. + gc.collect() + # g1 is not released immediately because it's from another thread; + # switching back to that thread will allocate a greenlet and thus + # trigger deletion actions. self.assertEqual(seen, []) fg_ref_released.set() bg_should_be_clear.wait(3) @@ -720,7 +732,18 @@ Greenlet(greenlet_main).switch() del self.glets - self.assertEqual(sys.getrefcount(Greenlet), initial_refs) + if RUNNING_ON_FREETHREAD_BUILD: + # Free-threaded builds make types immortal, which gives us + # weird numbers here, and we actually do APPEAR to end + # up with one more reference than we started with, at least on 3.14. + # If we change the code in green_dealloc to avoid increffing the type + # (which fixed this initial bug), then our leakchecks find other objects + # that have leaked, including a tuple, a dict, and a type. So that's not the + # right solution. Instead we change the test: + # XXX: FIXME: Is there a better way? + self.assertGreaterEqual(sys.getrefcount(Greenlet), initial_refs) + else: + self.assertEqual(sys.getrefcount(Greenlet), initial_refs) @unittest.skipIf( PY313 and RUNNING_ON_MANYLINUX, @@ -775,9 +798,12 @@ # non-deterministic. Presumably the memory layouts are different initial_refs = sys.getrefcount(MyGreenlet) thread_ready_events = [] - for _ in range( - initial_refs + 45 - ): + thread_count = initial_refs + 45 + if RUNNING_ON_FREETHREAD_BUILD: + # types are immortal, so this is a HUGE number most likely, + # and we can't create that many threads. + thread_count = 50 + for _ in range(thread_count): event = Event() thread = Thread(target=thread_main, args=(event,)) thread_ready_events.append(event) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/tests/test_leaks.py new/greenlet-3.2.4/src/greenlet/tests/test_leaks.py --- old/greenlet-3.2.3/src/greenlet/tests/test_leaks.py 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/tests/test_leaks.py 2025-08-07 15:13:36.000000000 +0200 @@ -15,10 +15,12 @@ import greenlet from . import TestCase from . import PY314 +from . import RUNNING_ON_FREETHREAD_BUILD from .leakcheck import fails_leakcheck from .leakcheck import ignores_leakcheck from .leakcheck import RUNNING_ON_MANYLINUX + # pylint:disable=protected-access assert greenlet.GREENLET_USE_GC # Option to disable this was removed in 1.0 @@ -39,6 +41,14 @@ cls.EXTANT_INSTANCES.clear() +def fails_leakcheck_except_on_free_thraded(func): + if RUNNING_ON_FREETHREAD_BUILD: + # These all seem to pass on free threading because + # of the changes to the garbage collector + return func + return fails_leakcheck(func) + + class TestLeaks(TestCase): def test_arg_refs(self): @@ -265,7 +275,7 @@ finally: greenlet._greenlet.enable_optional_cleanup(True) - @fails_leakcheck + @fails_leakcheck_except_on_free_thraded def test_issue251_issue252_need_to_collect_in_background(self): # Between greenlet 1.1.2 and the next version, this was still # failing because the leak of the list still exists when we @@ -286,7 +296,7 @@ # for some reason, but I've never seen it pass on macOS. self._check_issue251(manually_collect_background=False) - @fails_leakcheck + @fails_leakcheck_except_on_free_thraded def test_issue251_issue252_need_to_collect_in_background_cleanup_disabled(self): self.expect_greenlet_leak = True greenlet._greenlet.enable_optional_cleanup(False) @@ -295,7 +305,7 @@ finally: greenlet._greenlet.enable_optional_cleanup(True) - @fails_leakcheck + @fails_leakcheck_except_on_free_thraded def test_issue251_issue252_explicit_reference_not_collectable(self): self._check_issue251( manually_collect_background=False, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet/tests/test_tracing.py new/greenlet-3.2.4/src/greenlet/tests/test_tracing.py --- old/greenlet-3.2.3/src/greenlet/tests/test_tracing.py 2025-06-05 18:08:17.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet/tests/test_tracing.py 2025-08-07 15:13:36.000000000 +0200 @@ -1,5 +1,6 @@ from __future__ import print_function import sys +import sysconfig import greenlet import unittest @@ -7,9 +8,16 @@ from . import PY312 # https://discuss.python.org/t/cpython-3-12-greenlet-and-tracing-profiling-how-to-not-crash-and-get-correct-results/33144/2 -DEBUG_BUILD_PY312 = ( - PY312 and hasattr(sys, 'gettotalrefcount'), - "Broken on debug builds of Python 3.12" +# When build variables are available, OPT is the best way of detecting +# the build with assertions enabled. Otherwise, fallback to detecting PyDEBUG +# build. +ASSERTION_BUILD_PY312 = ( + PY312 and ( + "-DNDEBUG" not in sysconfig.get_config_var("OPT").split() + if sysconfig.get_config_var("OPT") is not None + else hasattr(sys, 'gettotalrefcount') + ), + "Broken on assertion-enabled builds of Python 3.12" ) class SomeError(Exception): @@ -198,7 +206,7 @@ self._check_trace_events_from_greenlet_sets_profiler(X(), tracer) - @unittest.skipIf(*DEBUG_BUILD_PY312) + @unittest.skipIf(*ASSERTION_BUILD_PY312) def test_trace_events_multiple_greenlets_switching(self): tracer = PythonTracer() @@ -236,7 +244,7 @@ ('c_call', '__exit__'), ]) - @unittest.skipIf(*DEBUG_BUILD_PY312) + @unittest.skipIf(*ASSERTION_BUILD_PY312) def test_trace_events_multiple_greenlets_switching_siblings(self): # Like the first version, but get both greenlets running first # as "siblings" and then establish the tracing. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet.egg-info/PKG-INFO new/greenlet-3.2.4/src/greenlet.egg-info/PKG-INFO --- old/greenlet-3.2.3/src/greenlet.egg-info/PKG-INFO 2025-06-05 18:08:19.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet.egg-info/PKG-INFO 2025-08-07 15:13:39.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: greenlet -Version: 3.2.3 +Version: 3.2.4 Summary: Lightweight in-process concurrent programming Home-page: https://greenlet.readthedocs.io/ Author: Alexey Borzenkov @@ -38,6 +38,7 @@ Provides-Extra: test Requires-Dist: objgraph; extra == "test" Requires-Dist: psutil; extra == "test" +Requires-Dist: setuptools; extra == "test" Dynamic: author Dynamic: author-email Dynamic: classifier diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/greenlet-3.2.3/src/greenlet.egg-info/requires.txt new/greenlet-3.2.4/src/greenlet.egg-info/requires.txt --- old/greenlet-3.2.3/src/greenlet.egg-info/requires.txt 2025-06-05 18:08:19.000000000 +0200 +++ new/greenlet-3.2.4/src/greenlet.egg-info/requires.txt 2025-08-07 15:13:39.000000000 +0200 @@ -6,3 +6,4 @@ [test] objgraph psutil +setuptools