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-12-10 15:30:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-greenlet (Old)
 and      /work/SRC/openSUSE:Factory/.python-greenlet.new.1939 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-greenlet"

Wed Dec 10 15:30:04 2025 rev:56 rq:1321788 version:3.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-greenlet/python-greenlet.changes  
2025-09-02 17:58:07.169983962 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-greenlet.new.1939/python-greenlet.changes    
    2025-12-10 15:30:55.219238144 +0100
@@ -1,0 +2,12 @@
+Tue Dec  9 11:29:59 UTC 2025 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- Update to 3.3.0
+  * Drop support for Python 3.9.
+  * Switch to distributing manylinux_2_28 wheels instead of manylinux2014
+    wheels. Likewise, switch from musllinux_1_1 to 1_2.
+  * Add initial support for free-threaded builds of CPython 3.14. Due to
+    limitations, we do not distribute binary wheels for free-threaded
+    CPython on Windows. (Free-threaded CPython 3.13 may work, but is
+    untested and unsupported.)
+
+-------------------------------------------------------------------

Old:
----
  greenlet-3.2.4.tar.gz

New:
----
  greenlet-3.3.0.tar.gz

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

Other differences:
------------------
++++++ python-greenlet.spec ++++++
--- /var/tmp/diff_new_pack.IY9hFh/_old  2025-12-10 15:30:56.983312774 +0100
+++ /var/tmp/diff_new_pack.IY9hFh/_new  2025-12-10 15:30:57.003313619 +0100
@@ -22,7 +22,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-greenlet
-Version:        3.2.4
+Version:        3.3.0
 Release:        0
 Summary:        Lightweight in-process concurrent programming
 License:        MIT

++++++ greenlet-3.2.4.tar.gz -> greenlet-3.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/.github/workflows/tests.yml 
new/greenlet-3.3.0/.github/workflows/tests.yml
--- old/greenlet-3.2.4/.github/workflows/tests.yml      2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/.github/workflows/tests.yml      2025-12-04 
15:19:40.000000000 +0100
@@ -22,14 +22,23 @@
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
-        python-version: [3.9, "3.10", "3.11", "3.12", "3.13", "3.14.0-rc.1"]
+        python-version:
+          - "3.10"
+          - "3.11"
+          - "3.12"
+          - "3.13"
+          - "3.14"
+          - "3.14t"
+
         # Recall the macOS builds upload built wheels so all supported versions
         # need to run on mac.
-        os: [ubuntu-latest, macos-latest]
+        os:
+          - ubuntu-latest
+          - macos-latest
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v5
     - name: Set up Python
-      uses: actions/setup-python@v5
+      uses: actions/setup-python@v6
       with:
         python-version: ${{ matrix.python-version }}
         cache: 'pip'
@@ -42,8 +51,8 @@
     - name: Install greenlet (non-Mac)
       if: ${{ ! startsWith(runner.os, 'Mac') }}
       run: |
-        # Stupid setuptools doesn't want you running 'python setup.py' anymore,
-        # but stupid pip hides all the intersting compiler output by default, 
and the
+        # setuptools doesn't want you running 'python setup.py' anymore,
+        # but pip hides all the intersting compiler output by default, and the
         # only way to get anything useful out is to ask *everything* to be 
verbose,
         # which is much more junk than we need to wade through, making it hard 
to
         # see what we want. What's the point of having warnings at all if we 
can't
@@ -95,10 +104,24 @@
         python -c 'import greenlet._greenlet as G; assert 
G.GREENLET_USE_STANDARD_THREADING'
         python -m unittest discover -v greenlet.tests
     - name: Doctest
+      env:
+        # XXX: On 3.14t, when the thread-local bytecode cache is
+        # enabled, sphinx crashes on module cleanup: there is a
+        # reference to pdb.set_trace in ``glob``, and when the
+        # shutdown sequence tries to clear that value, it segfaults
+        # dec-reffing it after taking it out of the module dict. The
+        # object appears to be corrupt, but it is utterly unclear how
+        # we could have done this. It is 100% reliable on bath Mac and
+        # Linux. It can be traced down to a very simple doctest
+        # snippet in greenlet_gc.rst, but running that same snippet
+        # standalone in a unit test doesn't produce the error.
+        #
+        # So this is a temporary workaround.
+        PYTHON_TLBC: "0"
       run: |
         sphinx-build -b doctest -d docs/_build/doctrees2 docs 
docs/_build/doctest2
     - name: Lint
-      if: matrix.python-version == '3.12' && startsWith(runner.os, 'Linux')
+      if: matrix.python-version == '3.14' && startsWith(runner.os, 'Linux')
       # We only need to do this on one version.
       # We do this here rather than a separate job to avoid the compilation 
overhead.
       run: |
@@ -121,9 +144,9 @@
       # required for all workflows
       security-events: write
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v5
       - name: Set up Python
-        uses: actions/setup-python@v5
+        uses: actions/setup-python@v6
         with:
           python-version: "3.10"
           cache: 'pip'
@@ -150,9 +173,9 @@
     name: RiscV 64
     steps:
       - name: checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
       - name: Set up Python
-        uses: actions/setup-python@v5
+        uses: actions/setup-python@v6
         with:
           python-version: "3.13"
       - name: Set up QEMU
@@ -170,22 +193,22 @@
     # We use a regular Python matrix entry to share as much code as possible.
     strategy:
       matrix:
-        python-version: [3.9]
+        python-version:
+          - "3.14"
         image:
           - manylinux_2_28_x86_64
-          - manylinux2014_aarch64
-          - manylinux2014_ppc64le
-          - manylinux2014_s390x
-          - manylinux2014_x86_64
-          - musllinux_1_1_x86_64
-          - musllinux_1_1_aarch64
+          - manylinux_2_28_aarch64
+          - manylinux_2_28_ppc64le
+          - manylinux_2_28_s390x
+          - musllinux_1_2_x86_64
+          - musllinux_1_2_aarch64
     name: ${{ matrix.image }}
 
     steps:
       - name: checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
       - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python@v5
+        uses: actions/setup-python@v6
         with:
           python-version: ${{ matrix.python-version }}
       - name: Set up QEMU
@@ -202,7 +225,7 @@
           path: wheelhouse/*whl
           name: ${{ matrix.image }}_wheels.zip
       - name: Publish package to PyPI
-        uses: pypa/[email protected]
+        uses: pypa/[email protected]
         if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
         with:
           user: __token__
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/CHANGES.rst 
new/greenlet-3.3.0/CHANGES.rst
--- old/greenlet-3.2.4/CHANGES.rst      2025-08-07 15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/CHANGES.rst      2025-12-04 15:19:40.000000000 +0100
@@ -2,6 +2,28 @@
  Changes
 =========
 
+3.3.0 (2025-12-04)
+==================
+
+- Drop support for Python 3.9.
+- Switch to distributing manylinux_2_28 wheels instead of
+  manylinux2014 wheels. Likewise, switch from musllinux_1_1 to 1_2.
+- Add initial support for free-threaded builds of CPython 3.14. Due to
+  limitations, we do not distribute binary wheels for free-threaded
+  CPython on Windows. (Free-threaded CPython 3.13 may work, but is
+  untested and unsupported.)
+
+  .. caution::
+
+     Under some rare scenarios with free-threaded 3.14, the
+     interpreter may crash on accessing a variable or attribute or
+     when shutting down. If this happens, try disabling the
+     thread-local bytecode cache. See the greenlet documentation for
+     more details. See `PR 472 by T. Wouters
+     <https://github.com/python-greenlet/greenlet/pull/472>`_ for the
+     initial free-threaded support and a discussion of the current
+     known issues.
+
 3.2.4 (2025-08-07)
 ==================
 
@@ -16,7 +38,7 @@
   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
+  experimental JIT. See `issue 460
   <https://github.com/python-greenlet/greenlet/issues/460>`_.
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/PKG-INFO new/greenlet-3.3.0/PKG-INFO
--- old/greenlet-3.2.4/PKG-INFO 2025-08-07 15:13:39.288525800 +0200
+++ new/greenlet-3.3.0/PKG-INFO 2025-12-04 15:19:48.997744800 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: greenlet
-Version: 3.2.4
+Version: 3.3.0
 Summary: Lightweight in-process concurrent programming
 Home-page: https://greenlet.readthedocs.io/
 Author: Alexey Borzenkov
@@ -21,14 +21,14 @@
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3 :: Only
-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: Operating System :: OS Independent
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Requires-Python: >=3.9
+Requires-Python: >=3.10
 Description-Content-Type: text/x-rst
 License-File: LICENSE
 License-File: LICENSE.PSF
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/appveyor.yml 
new/greenlet-3.3.0/appveyor.yml
--- old/greenlet-3.2.4/appveyor.yml     2025-08-07 15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/appveyor.yml     2025-12-04 15:19:40.000000000 +0100
@@ -39,8 +39,10 @@
 
     # Fully supported 64-bit versions, with testing. This should be
     # all the current (non EOL) versions.
+    # NOTE: As of 3.14.0, CPython doesn't distribute
+    # free-threaded builds, so we have no way to create those.
     - PYTHON: "C:\\Python314-x64"
-      PYTHON_VERSION: "3.14.0rc1"
+      PYTHON_VERSION: "3.14.0"
       PYTHON_ARCH: "64"
       PYTHON_EXE: python
 
@@ -68,24 +70,6 @@
       PYTHON_EXE: python
 
 
-    - PYTHON: "C:\\Python39-x64"
-      PYTHON_ARCH: "64"
-      PYTHON_VERSION: "3.9.x"
-      PYTHON_EXE: python
-
-
-
-    # Tested 32-bit versions. A small, hand-picked selection covering
-    # important variations. No need to include newer versions of
-    # cpython here, 32-bit x86 windows is on the way out.
-
-    - PYTHON: "C:\\Python39"
-      PYTHON_ARCH: "32"
-      PYTHON_VERSION: "3.9.x"
-      PYTHON_EXE: python
-
-
-
     # Untested 64-bit versions. We don't expect any variance here from
     # the other tested 64-bit versions, OR they are very EOL
 
@@ -137,6 +121,7 @@
         & appveyor\install.ps1;
       }
 
+  - ps: "ls \"${env:PYTHON}\""
   # Prepend newly installed Python to the PATH of this build (this cannot be
   # done from inside the powershell script as it would require to restart
   # the parent CMD process).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/docs/caveats.rst 
new/greenlet-3.3.0/docs/caveats.rst
--- old/greenlet-3.2.4/docs/caveats.rst 2025-08-07 15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/docs/caveats.rst 2025-12-04 15:19:40.000000000 +0100
@@ -33,17 +33,23 @@
 
 See :issue:`143` for an example.
 
-Free-threading Is Not Supported
-===============================
+Free-threading Is Experimental
+==============================
 
-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.
+Beginning with greenlet 3.3.0, support for Python 3.14's free-threaded
+mode is enabled. Use caution, as it has only limited testing.
 
-In addition, there are known issues running greenlets in a
-free-threaded CPython. These include:
+There are known issues running greenlets in a free-threaded CPython.
+These include:
 
+- As with any threaded program, use caution when forking. Greenlet
+  maintains internal locks and forking at the wrong time might result
+  in the child process hanging.
 - Garbage collection differences may cause ``GreenletExit`` to no
   longer be raised in certain multi-threaded scenarios.
 - There may be other memory leaks.
+- It may be necessary to disable the thread-local bytecode cache (and
+  hence the specializing interpreter) to avoid a rare crash. If your
+  process crashes on accessing an attribute or object, or at shutdown
+  during module cleanup, try setting the environment variable
+  ``PYTHON_TLBC=0`` or using the ``-X tlbc=0`` argument.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/docs/index.rst 
new/greenlet-3.3.0/docs/index.rst
--- old/greenlet-3.2.4/docs/index.rst   2025-08-07 15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/docs/index.rst   2025-12-04 15:19:40.000000000 +0100
@@ -80,8 +80,8 @@
 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
+Beginning with greenlet 3.3, free-threaded builds of CPython are
+supported, but they have limited testing and some other limitations. For more
 on free-threading and greenlet, see :doc:`caveats`.
 
 .. rubric:: How are greenlets different from threads?
@@ -106,9 +106,9 @@
 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`.
+Beginning with greenlet 3.3, you can combine threads and greenlets on
+free-threaded builds of CPython to take advantage of the strengths of
+both, but there may be limitations. 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.4/make-manylinux 
new/greenlet-3.3.0/make-manylinux
--- old/greenlet-3.2.4/make-manylinux   2025-08-07 15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/make-manylinux   2025-12-04 15:19:40.000000000 +0100
@@ -23,6 +23,12 @@
     # It may also violate standards compliance in a few ways. Rather than 
opt-out with -fno-fast-math,
     # we use O3, which has neither of those problems.
     export CFLAGS="-O3 -DNDEBUG -Wall"
+    # 2025-10-21: Sometime after 2025-10-01, the manylinux_ppc64le
+    # environment started shipping a version of Python 3.14 that wants to use
+    # clang++ by default. That's not installed here, and I can't get yum to 
work
+    # to try to install it, but luckily g++ works just as well --- that's what
+    # everything else uses.
+    export CXX="g++"
     # Build in an isolated directory
     mkdir /tmp/build
     cd /tmp/build
@@ -35,9 +41,9 @@
     which auditwheel
     echo "Installed Python versions"
     ls -l /opt/python
-    for variant in `ls -d /opt/python/cp{314,313,39,310,311,312}*`; do
-        if [[ "$variant" == *t ]]; then
-            echo "Skipping no-gil build. The GIL is required."
+    for variant in  /opt/python/cp{314,313,312,311,310}*; do
+        if [[ "$variant" == *3t ]]; then
+            echo "Skipping no-gil build for 3.13; only 3.14 is fully 
supported."
             continue
         fi
         export PATH="$variant/bin:$OPATH"
@@ -46,7 +52,7 @@
         python -mpip install -U pip
         python -mpip install -U setuptools wheel
         python -mpip wheel -v --wheel-dir ./dist .
-        python -mpip install -U .[test]
+        python -mpip install -U ".[test]"
         python -m unittest discover -v greenlet.tests
         PATH="$OPATH" auditwheel repair dist/greenlet*.whl
         cp wheelhouse/greenlet*.whl /greenlet/wheelhouse
@@ -61,5 +67,5 @@
 
 # Mount the current directory as /greenlet
 # Can't use -i on Travis with arm64, "input device not a tty"
-docker run --rm -v "$(pwd):/greenlet"  
${DOCKER_IMAGE:-quay.io/pypa/manylinux2014_x86_64} /greenlet/$(basename $0)
+docker run --rm -v "$(pwd):/greenlet"  
${DOCKER_IMAGE:-quay.io/pypa/manylinux_2_28_x86_64} /greenlet/$(basename $0)
 ls -l wheelhouse
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/setup.py new/greenlet-3.3.0/setup.py
--- old/greenlet-3.2.4/setup.py 2025-08-07 15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/setup.py 2025-12-04 15:19:40.000000000 +0100
@@ -244,11 +244,11 @@
         'Programming Language :: Python',
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3 :: Only',
-        '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',
         'Operating System :: OS Independent',
         'Topic :: Software Development :: Libraries :: Python Modules'
     ],
@@ -263,6 +263,6 @@
             'setuptools',
         ],
     },
-    python_requires=">=3.9",
+    python_requires=">=3.10",
     zip_safe=False,
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/PyGreenlet.cpp 
new/greenlet-3.3.0/src/greenlet/PyGreenlet.cpp
--- old/greenlet-3.2.4/src/greenlet/PyGreenlet.cpp      2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/PyGreenlet.cpp      2025-12-04 
15:19:40.000000000 +0100
@@ -741,7 +741,30 @@
     .tp_alloc=PyType_GenericAlloc,                  /* tp_alloc */
     .tp_new=(newfunc)green_new,                          /* tp_new */
     .tp_free=PyObject_GC_Del,                   /* tp_free */
+#ifndef Py_GIL_DISABLED
+/*
+  We may have been handling this wrong all along.
+
+  It shows as a problem with the GIL disabled. In builds of 3.14 with
+  assertions enabled, we break the garbage collector if we *ever*
+  return false from this function. The docs say this is to distinguish
+  some objects that are collectable vs some that are not, specifically
+  giving the example of PyTypeObject as the only place this is done,
+  where it distinguishes between static types like this one (allocated
+  by the C runtime at load time) and dynamic heap types (created at
+  runtime as objects). With the GIL disabled, all allocations that are
+  potentially collectable go in the mimalloc heap, and the collector
+  asserts that tp_is_gc() is true for them as it walks through the
+  heap object by object. Since we set the Py_TPFLAGS_HAS_GC bit, we
+  are always allocated in that mimalloc heap, so we must always be
+  collectable.
+
+  XXX: TODO: Could this be responsible for some apparent leaks, even
+  on GIL builds, at least in 3.14? See if we can catch an assertion
+  failure in the GC on regular 3.14 as well.
+ */
     .tp_is_gc=(inquiry)green_is_gc,         /* tp_is_gc */
+#endif
 };
 
 #endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/PyModule.cpp 
new/greenlet-3.3.0/src/greenlet/PyModule.cpp
--- old/greenlet-3.2.4/src/greenlet/PyModule.cpp        2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/PyModule.cpp        2025-12-04 
15:19:40.000000000 +0100
@@ -144,7 +144,7 @@
 static PyObject*
 mod_get_clocks_used_doing_optional_cleanup(PyObject* UNUSED(module))
 {
-    std::clock_t& clocks = ThreadState::clocks_used_doing_gc();
+    std::clock_t clocks = ThreadState::clocks_used_doing_gc();
 
     if (clocks == std::clock_t(-1)) {
         Py_RETURN_NONE;
@@ -168,15 +168,15 @@
         return nullptr;
     }
 
-    std::clock_t& clocks = ThreadState::clocks_used_doing_gc();
     if (is_true) {
+        std::clock_t clocks = ThreadState::clocks_used_doing_gc();
         // If we already have a value, we don't want to lose it.
         if (clocks == std::clock_t(-1)) {
-            clocks = 0;
+            ThreadState::set_clocks_used_doing_gc(0);
         }
     }
     else {
-        clocks = std::clock_t(-1);
+        ThreadState::set_clocks_used_doing_gc(std::clock_t(-1));
     }
     Py_RETURN_NONE;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/TGreenlet.hpp 
new/greenlet-3.3.0/src/greenlet/TGreenlet.hpp
--- old/greenlet-3.2.4/src/greenlet/TGreenlet.hpp       2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/TGreenlet.hpp       2025-12-04 
15:19:40.000000000 +0100
@@ -34,6 +34,9 @@
 #else
 #  include "internal/pycore_interpframe.h"
 #endif
+#ifdef Py_GIL_DISABLED
+#   include "internal/pycore_tstate.h"
+#endif
 #endif
 
 // XXX: TODO: Work to remove all virtual functions
@@ -122,6 +125,10 @@
         //      Assertion `tstate->current_executor == NULL' failed.
         // see https://github.com/python-greenlet/greenlet/issues/460
         PyObject* current_executor;
+        _PyStackRef* stackpointer;
+    #ifdef Py_GIL_DISABLED
+        _PyCStackRef* c_stack_refs;
+    #endif
 #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.4/src/greenlet/TMainGreenlet.cpp 
new/greenlet-3.3.0/src/greenlet/TMainGreenlet.cpp
--- old/greenlet-3.2.4/src/greenlet/TMainGreenlet.cpp   2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/TMainGreenlet.cpp   2025-12-04 
15:19:40.000000000 +0100
@@ -14,11 +14,18 @@
 
 #include "TGreenlet.hpp"
 
+#ifdef Py_GIL_DISABLED
+#include <atomic>
+#endif
 
-
-// Protected by the GIL. Incremented when we create a main greenlet,
-// in a new thread, decremented when it is destroyed.
+// Incremented when we create a main greenlet, in a new thread, decremented
+// when it is destroyed.
+#ifdef Py_GIL_DISABLED
+static std::atomic<Py_ssize_t> G_TOTAL_MAIN_GREENLETS(0);
+#else
+// Protected by the GIL.
 static Py_ssize_t G_TOTAL_MAIN_GREENLETS;
+#endif
 
 namespace greenlet {
 greenlet::PythonAllocator<MainGreenlet> MainGreenlet::allocator;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/TPythonState.cpp 
new/greenlet-3.3.0/src/greenlet/TPythonState.cpp
--- old/greenlet-3.2.4/src/greenlet/TPythonState.cpp    2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/TPythonState.cpp    2025-12-04 
15:19:40.000000000 +0100
@@ -15,6 +15,10 @@
 #if GREENLET_PY314
     ,py_recursion_depth(0)
     ,current_executor(nullptr)
+    ,stackpointer(nullptr)
+    #ifdef Py_GIL_DISABLED
+    ,c_stack_refs(nullptr)
+    #endif
 #elif GREENLET_PY312
     ,py_recursion_depth(0)
     ,c_recursion_depth(0)
@@ -138,6 +142,9 @@
   #if GREENLET_PY314
     this->py_recursion_depth = tstate->py_recursion_limit - 
tstate->py_recursion_remaining;
     this->current_executor = tstate->current_executor;
+    #ifdef Py_GIL_DISABLED
+    this->c_stack_refs = ((_PyThreadStateImpl*)tstate)->c_stack_refs;
+    #endif
   #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;
@@ -157,6 +164,14 @@
     Py_XDECREF(frame);  // PyThreadState_GetFrame gives us a new
                         // reference.
     this->_top_frame.steal(frame);
+  #if GREENLET_PY314
+    if (this->top_frame()) {
+        this->stackpointer = this->_top_frame->f_frame->stackpointer;
+    }
+    else {
+        this->stackpointer = nullptr;
+    }
+  #endif
   #if GREENLET_PY313
     this->delete_later = Py_XNewRef(tstate->delete_later);
   #elif GREENLET_PY312
@@ -216,6 +231,9 @@
   #if GREENLET_PY314
     tstate->py_recursion_remaining = tstate->py_recursion_limit - 
this->py_recursion_depth;
     tstate->current_executor = this->current_executor;
+    #ifdef Py_GIL_DISABLED
+    ((_PyThreadStateImpl*)tstate)->c_stack_refs = this->c_stack_refs;
+    #endif
     this->unexpose_frames();
   #elif GREENLET_PY312
     tstate->py_recursion_remaining = tstate->py_recursion_limit - 
this->py_recursion_depth;
@@ -232,6 +250,11 @@
     tstate->datastack_chunk = this->datastack_chunk;
     tstate->datastack_top = this->datastack_top;
     tstate->datastack_limit = this->datastack_limit;
+#if GREENLET_PY314 && defined(Py_GIL_DISABLED)
+    if (this->top_frame()) {
+        this->_top_frame->f_frame->stackpointer = this->stackpointer;
+    }
+#endif
     this->_top_frame.relinquish_ownership();
   #if GREENLET_PY313
     Py_XDECREF(tstate->delete_later);
@@ -286,6 +309,16 @@
     if (own_top_frame) {
         Py_VISIT(this->_top_frame.borrow());
     }
+#if GREENLET_PY314
+    // TODO: Should we be visiting the c_stack_refs objects?
+    // CPython uses a specific macro to do that which takes into
+    // account boxing and null values and then calls
+    // ``_PyGC_VisitStackRef``, but we don't have access to that, and
+    // we can't duplicate it ourself (because it compares
+    // ``visitproc`` to another function we can't access).
+    // The naive way of looping over c_stack_refs->ref and visiting
+    // those crashes the process (at least with GIL disabled).
+#endif
     return 0;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/TThreadState.hpp 
new/greenlet-3.3.0/src/greenlet/TThreadState.hpp
--- old/greenlet-3.2.4/src/greenlet/TThreadState.hpp    2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/TThreadState.hpp    2025-12-04 
15:19:40.000000000 +0100
@@ -3,6 +3,7 @@
 
 #include <ctime>
 #include <stdexcept>
+#include <atomic>
 
 #include "greenlet_internal.hpp"
 #include "greenlet_refs.hpp"
@@ -65,18 +66,13 @@
  * invoke destructors; the C++11 version is the most portable solution
  * I found). When the thread exits, we can drop references and
  * otherwise manipulate greenlets and frames that we know can no
- * longer be switched to. For compilers that don't support C++11
- * thread locals, we have a solution that uses the python thread
- * dictionary, though it may not collect everything as promptly as
- * other compilers do, if some other library is using the thread
- * dictionary and has a cycle or extra reference.
+ * longer be switched to.
  *
  * There are two small wrinkles. The first is that when the thread
  * exits, it is too late to actually invoke Python APIs: the Python
  * thread state is gone, and the GIL is released. To solve *this*
  * problem, our destructor uses ``Py_AddPendingCall`` to transfer the
- * destruction work to the main thread. (This is not an issue for the
- * dictionary solution.)
+ * destruction work to the main thread.
  *
  * The second is that once the thread exits, the thread local object
  * is invalid and we can't even access a pointer to it, so we can't
@@ -118,7 +114,11 @@
     void* exception_state;
 #endif
 
+#ifdef Py_GIL_DISABLED
+    static std::atomic<std::clock_t> _clocks_used_doing_gc;
+#else
     static std::clock_t _clocks_used_doing_gc;
+#endif
     static ImmortalString get_referrers_name;
     static PythonAllocator<ThreadState> allocator;
 
@@ -160,7 +160,7 @@
     static void init()
     {
         ThreadState::get_referrers_name = "get_referrers";
-        ThreadState::_clocks_used_doing_gc = 0;
+        ThreadState::set_clocks_used_doing_gc(0);
     }
 
     ThreadState()
@@ -349,9 +349,31 @@
     /**
      * Set to std::clock_t(-1) to disable.
      */
-    inline static std::clock_t& clocks_used_doing_gc()
+    inline static std::clock_t clocks_used_doing_gc()
     {
+#ifdef Py_GIL_DISABLED
+        return 
ThreadState::_clocks_used_doing_gc.load(std::memory_order_relaxed);
+#else
         return ThreadState::_clocks_used_doing_gc;
+#endif
+    }
+
+    inline static void set_clocks_used_doing_gc(std::clock_t value)
+    {
+#ifdef Py_GIL_DISABLED
+        ThreadState::_clocks_used_doing_gc.store(value, 
std::memory_order_relaxed);
+#else
+        ThreadState::_clocks_used_doing_gc = value;
+#endif
+    }
+
+    inline static void add_clocks_used_doing_gc(std::clock_t value)
+    {
+#ifdef Py_GIL_DISABLED
+        ThreadState::_clocks_used_doing_gc.fetch_add(value, 
std::memory_order_relaxed);
+#else
+        ThreadState::_clocks_used_doing_gc += value;
+#endif
     }
 
     ~ThreadState()
@@ -390,7 +412,7 @@
             PyGreenlet* old_main_greenlet = this->main_greenlet.borrow();
             Py_ssize_t cnt = this->main_greenlet.REFCNT();
             this->main_greenlet.CLEAR();
-            if (ThreadState::_clocks_used_doing_gc != std::clock_t(-1)
+            if (ThreadState::clocks_used_doing_gc() != std::clock_t(-1)
                 && cnt == 2 && Py_REFCNT(old_main_greenlet) == 1) {
                 // Highly likely that the reference is somewhere on
                 // the stack, not reachable by GC. Verify.
@@ -444,7 +466,7 @@
                         }
                     }
                     std::clock_t end = std::clock();
-                    ThreadState::_clocks_used_doing_gc += (end - begin);
+                    ThreadState::add_clocks_used_doing_gc(end - begin);
                 }
             }
         }
@@ -486,7 +508,11 @@
 
 ImmortalString ThreadState::get_referrers_name(nullptr);
 PythonAllocator<ThreadState> ThreadState::allocator;
+#ifdef Py_GIL_DISABLED
+std::atomic<std::clock_t> ThreadState::_clocks_used_doing_gc(0);
+#else
 std::clock_t ThreadState::_clocks_used_doing_gc(0);
+#endif
 
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/TThreadStateCreator.hpp 
new/greenlet-3.3.0/src/greenlet/TThreadStateCreator.hpp
--- old/greenlet-3.2.4/src/greenlet/TThreadStateCreator.hpp     2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/TThreadStateCreator.hpp     2025-12-04 
15:19:40.000000000 +0100
@@ -15,6 +15,8 @@
 
 typedef void (*ThreadStateDestructor)(ThreadState* const);
 
+// Only one of these, auto created per thread as a thread_local.
+// Constructing the state constructs the MainGreenlet.
 template<ThreadStateDestructor Destructor>
 class ThreadStateCreator
 {
@@ -36,8 +38,6 @@
 
 public:
 
-    // Only one of these, auto created per thread.
-    // Constructing the state constructs the MainGreenlet.
     ThreadStateCreator() :
         _state((ThreadState*)1)
     {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/TThreadStateDestroy.cpp 
new/greenlet-3.3.0/src/greenlet/TThreadStateDestroy.cpp
--- old/greenlet-3.2.4/src/greenlet/TThreadStateDestroy.cpp     2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/TThreadStateDestroy.cpp     2025-12-04 
15:19:40.000000000 +0100
@@ -33,6 +33,7 @@
     MarkGreenletDeadAndQueueCleanup(ThreadState* const state)
     {
 #if GREENLET_BROKEN_THREAD_LOCAL_CLEANUP_JUST_LEAK
+        // One rare platform.
         return;
 #endif
         // We are *NOT* holding the GIL. Our thread is in the middle
@@ -70,7 +71,11 @@
     static bool
     MarkGreenletDeadIfNeeded(ThreadState* const state)
     {
-        if (state && state->has_main_greenlet()) {
+        if (!state) {
+            return false;
+        }
+        LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
+        if (state->has_main_greenlet()) {
             // mark the thread as dead ASAP.
             // this is racy! If we try to throw or switch to a
             // greenlet from this thread from some other thread before
@@ -114,7 +119,7 @@
             // from the queue; its next iteration will go ahead and delete the 
item we just added.
             // And the pending call we schedule here will have no work to do.
             int result = AddPendingCall(
-                           PendingCallback_DestroyQueueWithGIL,
+                           PendingCallback_DestroyQueue,
                             nullptr);
             if (result < 0) {
                 // Hmm, what can we do here?
@@ -126,10 +131,11 @@
     }
 
     static int
-    PendingCallback_DestroyQueueWithGIL(void* UNUSED(arg))
+    PendingCallback_DestroyQueue(void* UNUSED(arg))
     {
-        // We're holding the GIL here, so no Python code should be able to
-        // run to call ``os.fork()``.
+        // We're may or may not be holding the GIL here (depending on
+        // Py_GIL_DISABLED), so calls to ``os.fork()`` may or may not
+        // be possible.
         while (1) {
             ThreadState* to_destroy;
             {
@@ -144,15 +150,15 @@
             // Drop the lock while we do the actual deletion.
             // This allows other calls to MarkGreenletDeadAndQueueCleanup
             // to enter and add to our queue.
-            DestroyOneWithGIL(to_destroy);
+            DestroyOne(to_destroy);
         }
         return 0;
     }
 
     static void
-    DestroyOneWithGIL(const ThreadState* const state)
+    DestroyOne(const ThreadState* const state)
     {
-        // Holding the GIL.
+        // May or may not be holding the GIL (depending on Py_GIL_DISABLED).
         // Passed a non-shared pointer to the actual thread state.
         // state -> main greenlet
         assert(state->has_main_greenlet());
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/__init__.py 
new/greenlet-3.3.0/src/greenlet/__init__.py
--- old/greenlet-3.2.4/src/greenlet/__init__.py 2025-08-07 15:13:36.000000000 
+0200
+++ new/greenlet-3.3.0/src/greenlet/__init__.py 2025-12-04 15:19:40.000000000 
+0100
@@ -25,7 +25,7 @@
 ###
 # Metadata
 ###
-__version__ = '3.2.4'
+__version__ = '3.3.0'
 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.4/src/greenlet/greenlet.cpp 
new/greenlet-3.3.0/src/greenlet/greenlet.cpp
--- old/greenlet-3.2.4/src/greenlet/greenlet.cpp        2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/greenlet.cpp        2025-12-04 
15:19:40.000000000 +0100
@@ -291,6 +291,9 @@
         //      << "\n\tPyGreenlet     : " << sizeof(PyGreenlet)
         //      << endl;
 
+#ifdef Py_GIL_DISABLED
+        PyUnstable_Module_SetGIL(m.borrow(), Py_MOD_GIL_NOT_USED);
+#endif
         return m.borrow(); // But really it's the main reference.
     }
     catch (const LockInitError& e) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/greenlet_allocator.hpp 
new/greenlet-3.3.0/src/greenlet/greenlet_allocator.hpp
--- old/greenlet-3.2.4/src/greenlet/greenlet_allocator.hpp      2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/greenlet_allocator.hpp      2025-12-04 
15:19:40.000000000 +0100
@@ -10,30 +10,6 @@
 
 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).
@@ -60,10 +36,16 @@
         T* allocate(size_t number_objects, const void* UNUSED(hint)=0)
         {
             void* p;
-            if (number_objects == 1)
+            if (number_objects == 1) {
+#ifdef Py_GIL_DISABLED
+                p = PyMem_Malloc(sizeof(T) * number_objects);
+#else
                 p = PyObject_Malloc(sizeof(T));
-            else
+#endif
+            }
+            else {
                 p = PyMem_Malloc(sizeof(T) * number_objects);
+            }
             return static_cast<T*>(p);
         }
 
@@ -71,10 +53,15 @@
         {
             void* p = t;
             if (n == 1) {
+#ifdef Py_GIL_DISABLED
+                PyMem_Free(p);
+#else
                 PyObject_Free(p);
+#endif
             }
-            else
+            else {
                 PyMem_Free(p);
+            }
         }
         // This member is deprecated in C++17 and removed in C++20
         template< class U >
@@ -83,7 +70,7 @@
         };
 
     };
-#endif // allocator type
+
 }
 
 #endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/greenlet_slp_switch.hpp 
new/greenlet-3.3.0/src/greenlet/greenlet_slp_switch.hpp
--- old/greenlet-3.2.4/src/greenlet/greenlet_slp_switch.hpp     2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/greenlet_slp_switch.hpp     2025-12-04 
15:19:40.000000000 +0100
@@ -36,7 +36,11 @@
 // running this code, the thread isn't exiting. This also nets us a
 // 10-12% speed improvement.
 
+#if Py_GIL_DISABLED
+thread_local greenlet::Greenlet* switching_thread_state = nullptr;
+#else
 static greenlet::Greenlet* volatile switching_thread_state = nullptr;
+#endif
 
 
 extern "C" {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/tests/_test_extension.c 
new/greenlet-3.3.0/src/greenlet/tests/_test_extension.c
--- old/greenlet-3.2.4/src/greenlet/tests/_test_extension.c     2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/tests/_test_extension.c     2025-12-04 
15:19:40.000000000 +0100
@@ -10,8 +10,32 @@
 
 #define TEST_MODULE_NAME "_test_extension"
 
+// CAUTION: MSVC is stupidly picky:
+//
+// "The compiler ignores, without warning, any __declspec keywords
+// placed after * or & and in front of the variable identifier in a
+// declaration."
+// (https://docs.microsoft.com/en-us/cpp/cpp/declspec?view=msvc-160)
+//
+// So pointer return types must be handled differently (because of the
+// trailing *), or you get inscrutable compiler warnings like "error
+// C2059: syntax error: ''"
+//
+// In C23, there is a standard syntax for attributes, and
+// GCC defines an attribute to use with this: [[gnu:noinline]].
+// In the future, this is expected to become standard.
+
+#if defined(__GNUC__) || defined(__clang__)
+/* We used to check for GCC 4+ or 3.4+, but those compilers are
+   laughably out of date. Just assume they support it. */
+#    define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
+#elif defined(_MSC_VER)
+/* We used to check for  && (_MSC_VER >= 1300) but that's also out of date. */
+#    define UNUSED(x) UNUSED_ ## x
+#endif
+
 static PyObject*
-test_switch(PyObject* self, PyObject* greenlet)
+test_switch(PyObject* UNUSED(self), PyObject* greenlet)
 {
     PyObject* result = NULL;
 
@@ -33,7 +57,7 @@
 }
 
 static PyObject*
-test_switch_kwargs(PyObject* self, PyObject* args, PyObject* kwargs)
+test_switch_kwargs(PyObject* UNUSED(self), PyObject* args, PyObject* kwargs)
 {
     PyGreenlet* g = NULL;
     PyObject* result = NULL;
@@ -58,7 +82,7 @@
 }
 
 static PyObject*
-test_getcurrent(PyObject* self)
+test_getcurrent(PyObject* UNUSED(self))
 {
     PyGreenlet* g = PyGreenlet_GetCurrent();
     if (g == NULL || !PyGreenlet_Check(g) || !PyGreenlet_ACTIVE(g)) {
@@ -72,7 +96,7 @@
 }
 
 static PyObject*
-test_setparent(PyObject* self, PyObject* arg)
+test_setparent(PyObject* UNUSED(self), PyObject* arg)
 {
     PyGreenlet* current;
     PyGreenlet* greenlet = NULL;
@@ -97,7 +121,7 @@
 }
 
 static PyObject*
-test_new_greenlet(PyObject* self, PyObject* callable)
+test_new_greenlet(PyObject* UNUSED(self), PyObject* callable)
 {
     PyObject* result = NULL;
     PyGreenlet* greenlet = PyGreenlet_New(callable, NULL);
@@ -117,21 +141,21 @@
 }
 
 static PyObject*
-test_raise_dead_greenlet(PyObject* self)
+test_raise_dead_greenlet(PyObject* UNUSED(self))
 {
     PyErr_SetString(PyExc_GreenletExit, "test GreenletExit exception.");
     return NULL;
 }
 
 static PyObject*
-test_raise_greenlet_error(PyObject* self)
+test_raise_greenlet_error(PyObject* UNUSED(self))
 {
     PyErr_SetString(PyExc_GreenletError, "test greenlet.error exception");
     return NULL;
 }
 
 static PyObject*
-test_throw(PyObject* self, PyGreenlet* g)
+test_throw(PyObject* UNUSED(self), PyGreenlet* g)
 {
     const char msg[] = "take that sucka!";
     PyObject* msg_obj = Py_BuildValue("s", msg);
@@ -144,7 +168,7 @@
 }
 
 static PyObject*
-test_throw_exact(PyObject* self, PyObject* args)
+test_throw_exact(PyObject* UNUSED(self), PyObject* args)
 {
     PyGreenlet* g = NULL;
     PyObject* typ = NULL;
@@ -227,5 +251,8 @@
     }
 
     PyGreenlet_Import();
+#ifdef Py_GIL_DISABLED
+    PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED);
+#endif
     return module;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/greenlet-3.2.4/src/greenlet/tests/_test_extension_cpp.cpp 
new/greenlet-3.3.0/src/greenlet/tests/_test_extension_cpp.cpp
--- old/greenlet-3.2.4/src/greenlet/tests/_test_extension_cpp.cpp       
2025-08-07 15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/tests/_test_extension_cpp.cpp       
2025-12-04 15:19:40.000000000 +0100
@@ -81,7 +81,7 @@
 
 
 static PyObject*
-py_test_exception_throw_nonstd(PyObject* self, PyObject* args)
+py_test_exception_throw_nonstd(PyObject* UNUSED(self), PyObject* args)
 {
     if (!PyArg_ParseTuple(args, ""))
         return NULL;
@@ -91,7 +91,7 @@
 }
 
 static PyObject*
-py_test_exception_throw_std(PyObject* self, PyObject* args)
+py_test_exception_throw_std(PyObject* UNUSED(self), PyObject* args)
 {
     if (!PyArg_ParseTuple(args, ""))
         return NULL;
@@ -101,7 +101,7 @@
 }
 
 static PyObject*
-py_test_call(PyObject* self, PyObject* arg)
+py_test_call(PyObject* UNUSED(self), PyObject* arg)
 {
     PyObject* noargs = PyTuple_New(0);
     PyObject* ret = PyObject_Call(arg, noargs, nullptr);
@@ -121,7 +121,7 @@
  * segfault the process.
  */
 static PyObject*
-test_exception_switch_and_do_in_g2(PyObject* self, PyObject* args)
+test_exception_switch_and_do_in_g2(PyObject* UNUSED(self), PyObject* args)
 {
     PyObject* g2func = NULL;
     PyObject* result = NULL;
@@ -221,6 +221,9 @@
     p_test_exception_throw_nonstd = test_exception_throw_nonstd;
     p_test_exception_throw_std = test_exception_throw_std;
     p_test_exception_switch_recurse = test_exception_switch_recurse;
+#ifdef Py_GIL_DISABLED
+    PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED);
+#endif
 
     return module;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/tests/leakcheck.py 
new/greenlet-3.3.0/src/greenlet/tests/leakcheck.py
--- old/greenlet-3.2.4/src/greenlet/tests/leakcheck.py  2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/tests/leakcheck.py  2025-12-04 
15:19:40.000000000 +0100
@@ -172,7 +172,7 @@
         # to try to reverse the order of arguments...which leads
         # to the explosion of mock objects. We don't want that, so we implement
         # the check manually.
-        if kind == type(self._include_object_p):
+        if kind == type(self._include_object_p): # pylint: 
disable=unidiomatic-typecheck
             try:
                 # pylint:disable=not-callable
                 exact_method_equals = self._include_object_p.__eq__(obj)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/tests/test_gc.py 
new/greenlet-3.3.0/src/greenlet/tests/test_gc.py
--- old/greenlet-3.2.4/src/greenlet/tests/test_gc.py    2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/tests/test_gc.py    2025-12-04 
15:19:40.000000000 +0100
@@ -11,7 +11,7 @@
 # which is no longer optional.
 assert greenlet.GREENLET_USE_GC
 
-class GCTests(TestCase):
+class TestGC(TestCase):
     def test_dead_circular_ref(self):
         o = weakref.ref(greenlet.greenlet(greenlet.getcurrent).switch())
         gc.collect()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet/tests/test_greenlet.py 
new/greenlet-3.3.0/src/greenlet/tests/test_greenlet.py
--- old/greenlet-3.2.4/src/greenlet/tests/test_greenlet.py      2025-08-07 
15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet/tests/test_greenlet.py      2025-12-04 
15:19:40.000000000 +0100
@@ -1134,8 +1134,9 @@
 
 class TestRepr(TestCase):
 
-    def assertEndsWith(self, got, suffix):
-        self.assertTrue(got.endswith(suffix), (got, suffix))
+    if not hasattr(TestCase, 'assertEndsWith'): # Added in 3.14
+        def assertEndsWith(self, s, suffix, msg=None):
+            self.assertTrue(s.endswith(suffix), (s, suffix, msg))
 
     def test_main_while_running(self):
         r = repr(greenlet.getcurrent())
@@ -1349,5 +1350,16 @@
             output
         )
 
+class TestModule(TestCase):
+
+    @unittest.skipUnless(hasattr(sys, '_is_gil_enabled'),
+                         "Needs 3.13 and above for sys._is_gil_enabled")
+    def test_no_gil_on_free_threaded(self):
+
+        if RUNNING_ON_FREETHREAD_BUILD:
+            self.assertFalse(sys._is_gil_enabled())
+        else:
+            self.assertTrue(sys._is_gil_enabled())
+
 if __name__ == '__main__':
     unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/src/greenlet.egg-info/PKG-INFO 
new/greenlet-3.3.0/src/greenlet.egg-info/PKG-INFO
--- old/greenlet-3.2.4/src/greenlet.egg-info/PKG-INFO   2025-08-07 
15:13:39.000000000 +0200
+++ new/greenlet-3.3.0/src/greenlet.egg-info/PKG-INFO   2025-12-04 
15:19:48.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: greenlet
-Version: 3.2.4
+Version: 3.3.0
 Summary: Lightweight in-process concurrent programming
 Home-page: https://greenlet.readthedocs.io/
 Author: Alexey Borzenkov
@@ -21,14 +21,14 @@
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3 :: Only
-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: Operating System :: OS Independent
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Requires-Python: >=3.9
+Requires-Python: >=3.10
 Description-Content-Type: text/x-rst
 License-File: LICENSE
 License-File: LICENSE.PSF
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/greenlet-3.2.4/tox.ini new/greenlet-3.3.0/tox.ini
--- old/greenlet-3.2.4/tox.ini  2025-08-07 15:13:36.000000000 +0200
+++ new/greenlet-3.3.0/tox.ini  2025-12-04 15:19:40.000000000 +0100
@@ -1,6 +1,6 @@
 [tox]
 envlist =
-    py{37,38,39,310,311,312,313},py{310,311,312,313}-ns,docs
+    
py{37,38,39,310,311,312,313,314},py{310,311,312,313,314}-ns,docs,py314t,tsan-314#,tsan-314t
 
 [testenv]
 commands =
@@ -21,3 +21,13 @@
     sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
     sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
 extras = docs
+
+[testenv:tsan-314t]
+basepython = /usr/local/python-builds/tsan/bin/python3.14t
+passenv =
+    TSAN_OPTIONS
+
+[testenv:tsan-314]
+basepython = /usr/local/python-builds/default-tsan/bin/python3.14
+passenv =
+    TSAN_OPTIONS

Reply via email to