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 2026-03-01 22:13:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-greenlet (Old)
and /work/SRC/openSUSE:Factory/.python-greenlet.new.29461 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-greenlet"
Sun Mar 1 22:13:41 2026 rev:58 rq:1335240 version:3.3.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-greenlet/python-greenlet.changes
2026-02-01 22:02:32.401284113 +0100
+++
/work/SRC/openSUSE:Factory/.python-greenlet.new.29461/python-greenlet.changes
2026-03-01 22:13:43.347245002 +0100
@@ -1,0 +2,7 @@
+Wed Feb 25 09:53:31 UTC 2026 - John Paul Adrian Glaubitz
<[email protected]>
+
+- Update to 3.3.2
+ * Fix a crash on Python 3.10 if there are active greenlets during
+ interpreter shutdown. See PR 495 by Nicolas Bouvrette.
+
+-------------------------------------------------------------------
Old:
----
greenlet-3.3.1.tar.gz
New:
----
greenlet-3.3.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-greenlet.spec ++++++
--- /var/tmp/diff_new_pack.IxxB5E/_old 2026-03-01 22:13:44.007272128 +0100
+++ /var/tmp/diff_new_pack.IxxB5E/_new 2026-03-01 22:13:44.007272128 +0100
@@ -22,7 +22,7 @@
%{?sle15_python_module_pythons}
Name: python-greenlet
-Version: 3.3.1
+Version: 3.3.2
Release: 0
Summary: Lightweight in-process concurrent programming
License: MIT
++++++ greenlet-3.3.1.tar.gz -> greenlet-3.3.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/greenlet-3.3.1/CHANGES.rst
new/greenlet-3.3.2/CHANGES.rst
--- old/greenlet-3.3.1/CHANGES.rst 2026-01-23 16:28:32.000000000 +0100
+++ new/greenlet-3.3.2/CHANGES.rst 2026-02-20 21:14:45.000000000 +0100
@@ -2,6 +2,15 @@
Changes
=========
+3.3.2 (2026-02-20)
+==================
+
+- Fix a crash on Python 3.10 if there are active greenlets during
+ interpreter shutdown. See `PR 495
+ <https://github.com/python-greenlet/greenlet/pull/495>`_ by Nicolas
+ Bouvrette.
+
+
3.3.1 (2026-01-23)
==================
@@ -40,6 +49,19 @@
initial free-threaded support and a discussion of the current
known issues.
+3.2.5 (2026-02-20)
+==================
+
+.. note::
+
+ The 3.2.x series will be the last to support Python 3.9.
+
+- Backport the changes from PR 495 in release 3.3.2 for Python 3.9.
+
+.. note::
+
+ No Windows wheels will be published for this version.
+
3.2.4 (2025-08-07)
==================
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/greenlet-3.3.1/PKG-INFO new/greenlet-3.3.2/PKG-INFO
--- old/greenlet-3.3.1/PKG-INFO 2026-01-23 16:28:35.766325700 +0100
+++ new/greenlet-3.3.2/PKG-INFO 2026-02-20 21:14:48.818638300 +0100
@@ -1,10 +1,10 @@
Metadata-Version: 2.4
Name: greenlet
-Version: 3.3.1
+Version: 3.3.2
Summary: Lightweight in-process concurrent programming
Author-email: Alexey Borzenkov <[email protected]>
Maintainer-email: Jason Madden <[email protected]>
-License-Expression: MIT AND Python-2.0
+License-Expression: MIT AND PSF-2.0
Project-URL: Homepage, https://greenlet.readthedocs.io
Project-URL: Documentation, https://greenlet.readthedocs.io
Project-URL: Repository, https://github.com/python-greenlet/greenlet
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/greenlet-3.3.1/pyproject.toml
new/greenlet-3.3.2/pyproject.toml
--- old/greenlet-3.3.1/pyproject.toml 2026-01-23 16:28:32.000000000 +0100
+++ new/greenlet-3.3.2/pyproject.toml 2026-02-20 21:14:45.000000000 +0100
@@ -14,7 +14,7 @@
maintainers = [
{ name = "Jason Madden", email = "[email protected]" }
]
-license = "MIT AND Python-2.0"
+license = "MIT AND PSF-2.0"
license-files = [
'LICENSE',
'LICENSE.PSF',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/greenlet-3.3.1/src/greenlet/PyGreenlet.cpp
new/greenlet-3.3.2/src/greenlet/PyGreenlet.cpp
--- old/greenlet-3.3.1/src/greenlet/PyGreenlet.cpp 2026-01-23
16:28:32.000000000 +0100
+++ new/greenlet-3.3.2/src/greenlet/PyGreenlet.cpp 2026-02-20
21:14:45.000000000 +0100
@@ -189,6 +189,27 @@
static int
_green_dealloc_kill_started_non_main_greenlet(BorrowedGreenlet self)
{
+ // During interpreter finalization, we cannot safely throw GreenletExit
+ // into the greenlet. Doing so calls g_switch(), which performs a stack
+ // switch and runs Python code via _PyEval_EvalFrameDefault. On Python
+ // < 3.11, executing Python code in a partially-torn-down interpreter
+ // leads to SIGSEGV (greenlet 3.x) or SIGABRT (greenlet 2.x).
+ //
+ // Python 3.11+ restructured interpreter finalization internals (frame
+ // representation, data stack management, recursion tracking) so that
+ // g_switch() during finalization is safe. On older Pythons, we simply
+ // mark the greenlet dead without throwing, which avoids the crash at
+ // the cost of not running any cleanup code inside the greenlet.
+ //
+ // See: https://github.com/python-greenlet/greenlet/issues/411
+ // https://github.com/python-greenlet/greenlet/issues/351
+#if !GREENLET_PY311
+ if (_Py_IsFinalizing()) {
+ self->murder_in_place();
+ return 1;
+ }
+#endif
+
/* Hacks hacks hacks copied from instance_dealloc() */
/* Temporarily resurrect the greenlet. */
assert(self.REFCNT() == 0);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/greenlet-3.3.1/src/greenlet/TThreadState.hpp
new/greenlet-3.3.2/src/greenlet/TThreadState.hpp
--- old/greenlet-3.3.1/src/greenlet/TThreadState.hpp 2026-01-23
16:28:32.000000000 +0100
+++ new/greenlet-3.3.2/src/greenlet/TThreadState.hpp 2026-02-20
21:14:45.000000000 +0100
@@ -384,6 +384,26 @@
return;
}
+ // During interpreter finalization, Python APIs like
+ // PyImport_ImportModule are unsafe (the import machinery may
+ // be partially torn down). On Python < 3.11, perform only the
+ // minimal cleanup that is safe: clear our strong references so
+ // we don't leak, but skip the GC-based leak detection.
+ //
+ // Python 3.11+ restructured interpreter finalization so that
+ // these APIs remain safe during shutdown.
+#if !GREENLET_PY311
+ if (_Py_IsFinalizing()) {
+ this->tracefunc.CLEAR();
+ if (this->current_greenlet) {
+ this->current_greenlet->murder_in_place();
+ this->current_greenlet.CLEAR();
+ }
+ this->main_greenlet.CLEAR();
+ return;
+ }
+#endif
+
// We should not have an "origin" greenlet; that only exists
// for the temporary time during a switch, which should not
// be in progress as the thread dies.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/greenlet-3.3.1/src/greenlet/__init__.py
new/greenlet-3.3.2/src/greenlet/__init__.py
--- old/greenlet-3.3.1/src/greenlet/__init__.py 2026-01-23 16:28:32.000000000
+0100
+++ new/greenlet-3.3.2/src/greenlet/__init__.py 2026-02-20 21:14:45.000000000
+0100
@@ -25,7 +25,7 @@
###
# Metadata
###
-__version__ = '3.3.1'
+__version__ = '3.3.2'
from ._greenlet import _C_API # pylint:disable=no-name-in-module
###
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/greenlet-3.3.1/src/greenlet/tests/test_interpreter_shutdown.py
new/greenlet-3.3.2/src/greenlet/tests/test_interpreter_shutdown.py
--- old/greenlet-3.3.1/src/greenlet/tests/test_interpreter_shutdown.py
1970-01-01 01:00:00.000000000 +0100
+++ new/greenlet-3.3.2/src/greenlet/tests/test_interpreter_shutdown.py
2026-02-20 21:14:45.000000000 +0100
@@ -0,0 +1,320 @@
+# -*- coding: utf-8 -*-
+"""
+Tests for greenlet behavior during interpreter shutdown (Py_FinalizeEx).
+
+Prior to the safe finalization fix, active greenlets being deallocated
+during interpreter shutdown could trigger SIGSEGV or SIGABRT on Python
+< 3.11, because green_dealloc attempted to throw GreenletExit via
+g_switch() into a partially-torn-down interpreter.
+
+The fix adds _Py_IsFinalizing() guards (on Python < 3.11 only) that
+call murder_in_place() instead of g_switch() when the interpreter is
+shutting down, avoiding the crash at the cost of not running cleanup
+code inside the greenlet.
+
+These tests verify:
+ 1. No crashes on ANY Python version (the core safety guarantee).
+ 2. GreenletExit cleanup code runs correctly during normal thread exit
+ (the standard production path, e.g. uWSGI worker threads).
+"""
+import sys
+import subprocess
+import unittest
+import textwrap
+
+from greenlet.tests import TestCase
+
+
+class TestInterpreterShutdown(TestCase):
+
+ def _run_shutdown_script(self, script_body):
+ """
+ Run a Python script in a subprocess that exercises greenlet
+ during interpreter shutdown. Returns (returncode, stdout, stderr).
+ """
+ full_script = textwrap.dedent(script_body)
+ result = subprocess.run(
+ [sys.executable, '-c', full_script],
+ capture_output=True,
+ text=True,
+ timeout=30,
+ check=False,
+ )
+ return result.returncode, result.stdout, result.stderr
+
+ # -----------------------------------------------------------------
+ # Core safety tests: no crashes on any Python version
+ # -----------------------------------------------------------------
+
+ def test_active_greenlet_at_shutdown_no_crash(self):
+ """
+ An active (suspended) greenlet that is deallocated during
+ interpreter shutdown should not crash the process.
+
+ Before the fix, this would SIGSEGV on Python < 3.11 because
+ _green_dealloc_kill_started_non_main_greenlet tried to call
+ g_switch() during Py_FinalizeEx.
+ """
+ rc, stdout, stderr = self._run_shutdown_script("""\
+ import greenlet
+
+ def worker():
+ greenlet.getcurrent().parent.switch("from worker")
+ return "done"
+
+ g = greenlet.greenlet(worker)
+ result = g.switch()
+ assert result == "from worker", result
+ print("OK: exiting with active greenlet")
+ """)
+ self.assertEqual(rc, 0, f"Process crashed
(rc={rc}):\n{stdout}{stderr}")
+ self.assertIn("OK: exiting with active greenlet", stdout)
+
+ def test_multiple_active_greenlets_at_shutdown(self):
+ """
+ Multiple suspended greenlets at shutdown should all be cleaned
+ up without crashing.
+ """
+ rc, stdout, stderr = self._run_shutdown_script("""\
+ import greenlet
+
+ def worker(name):
+ greenlet.getcurrent().parent.switch(f"hello from {name}")
+ return "done"
+
+ greenlets = []
+ for i in range(10):
+ g = greenlet.greenlet(worker)
+ result = g.switch(f"g{i}")
+ greenlets.append(g)
+
+ print(f"OK: {len(greenlets)} active greenlets at shutdown")
+ """)
+ self.assertEqual(rc, 0, f"Process crashed
(rc={rc}):\n{stdout}{stderr}")
+ self.assertIn("OK: 10 active greenlets at shutdown", stdout)
+
+ def test_nested_greenlets_at_shutdown(self):
+ """
+ Nested (chained parent) greenlets at shutdown should not crash.
+ """
+ rc, stdout, stderr = self._run_shutdown_script("""\
+ import greenlet
+
+ def inner():
+ greenlet.getcurrent().parent.switch("inner done")
+
+ def outer():
+ g_inner = greenlet.greenlet(inner)
+ g_inner.switch()
+ greenlet.getcurrent().parent.switch("outer done")
+
+ g = greenlet.greenlet(outer)
+ result = g.switch()
+ assert result == "outer done", result
+ print("OK: nested greenlets at shutdown")
+ """)
+ self.assertEqual(rc, 0, f"Process crashed
(rc={rc}):\n{stdout}{stderr}")
+ self.assertIn("OK: nested greenlets at shutdown", stdout)
+
+ def test_threaded_greenlets_at_shutdown(self):
+ """
+ Greenlets in worker threads that are still referenced at
+ shutdown should not crash.
+ """
+ rc, stdout, stderr = self._run_shutdown_script("""\
+ import greenlet
+ import threading
+
+ results = []
+
+ def thread_worker():
+ def greenlet_func():
+ greenlet.getcurrent().parent.switch("from thread greenlet")
+ return "done"
+
+ g = greenlet.greenlet(greenlet_func)
+ val = g.switch()
+ results.append((g, val))
+
+ threads = []
+ for _ in range(3):
+ t = threading.Thread(target=thread_worker)
+ t.start()
+ threads.append(t)
+
+ for t in threads:
+ t.join()
+
+ print(f"OK: {len(results)} threaded greenlets at shutdown")
+ """)
+ self.assertEqual(rc, 0, f"Process crashed
(rc={rc}):\n{stdout}{stderr}")
+ self.assertIn("OK: 3 threaded greenlets at shutdown", stdout)
+
+ # -----------------------------------------------------------------
+ # Cleanup semantics tests
+ # -----------------------------------------------------------------
+ #
+ # Note on behavioral testing during interpreter shutdown:
+ #
+ # During Py_FinalizeEx, sys.stdout is set to None early, making
+ # print() a no-op. More importantly, an active greenlet in the
+ # module-level scope interferes with module dict clearing — the
+ # greenlet's dealloc path (which temporarily resurrects the object
+ # and performs a stack switch via g_switch) prevents reliable
+ # observation of cleanup behavior.
+ #
+ # The production crash (SIGSEGV/SIGABRT) occurs during thread-state
+ # cleanup in Py_FinalizeEx, not during module dict clearing. Our
+ # _Py_IsFinalizing() guard in _green_dealloc_kill_started_non_main_
+ # greenlet targets that path. The safety tests above verify that no
+ # crashes occur; the tests below verify that greenlet cleanup works
+ # correctly during normal thread exit (the most common code path).
+
+ def test_greenlet_cleanup_during_thread_exit(self):
+ """
+ When a thread exits normally while holding active greenlets,
+ GreenletExit IS thrown and cleanup code runs. This is the
+ standard cleanup path used in production (e.g. uWSGI worker
+ threads finishing a request).
+ """
+ rc, stdout, stderr = self._run_shutdown_script("""\
+ import os
+ import threading
+ import greenlet
+
+ _write = os.write
+
+ def thread_func():
+ def worker(_w=_write,
+ _GreenletExit=greenlet.GreenletExit):
+ try:
+ greenlet.getcurrent().parent.switch("suspended")
+ except _GreenletExit:
+ _w(1, b"CLEANUP: GreenletExit caught\\n")
+ raise
+
+ g = greenlet.greenlet(worker)
+ g.switch()
+ # Thread exits with active greenlet -> thread-state
+ # cleanup triggers GreenletExit
+
+ t = threading.Thread(target=thread_func)
+ t.start()
+ t.join()
+ print("OK: thread cleanup done")
+ """)
+ self.assertEqual(rc, 0, f"Process crashed
(rc={rc}):\n{stdout}{stderr}")
+ self.assertIn("OK: thread cleanup done", stdout)
+ self.assertIn("CLEANUP: GreenletExit caught", stdout)
+
+ def test_finally_block_during_thread_exit(self):
+ """
+ try/finally blocks in active greenlets run correctly when the
+ owning thread exits.
+ """
+ rc, stdout, stderr = self._run_shutdown_script("""\
+ import os
+ import threading
+ import greenlet
+
+ _write = os.write
+
+ def thread_func():
+ def worker(_w=_write):
+ try:
+ greenlet.getcurrent().parent.switch("suspended")
+ finally:
+ _w(1, b"FINALLY: cleanup executed\\n")
+
+ g = greenlet.greenlet(worker)
+ g.switch()
+
+ t = threading.Thread(target=thread_func)
+ t.start()
+ t.join()
+ print("OK: thread cleanup done")
+ """)
+ self.assertEqual(rc, 0, f"Process crashed
(rc={rc}):\n{stdout}{stderr}")
+ self.assertIn("OK: thread cleanup done", stdout)
+ self.assertIn("FINALLY: cleanup executed", stdout)
+
+ def test_many_greenlets_with_cleanup_at_shutdown(self):
+ """
+ Stress test: many active greenlets with cleanup code at shutdown.
+ Ensures no crashes regardless of deallocation order.
+ """
+ rc, stdout, stderr = self._run_shutdown_script("""\
+ import sys
+ import greenlet
+
+ cleanup_count = 0
+
+ def worker(idx):
+ global cleanup_count
+ try:
+ greenlet.getcurrent().parent.switch(f"ready-{idx}")
+ except greenlet.GreenletExit:
+ cleanup_count += 1
+ raise
+
+ greenlets = []
+ for i in range(50):
+ g = greenlet.greenlet(worker)
+ result = g.switch(i)
+ greenlets.append(g)
+
+ print(f"OK: {len(greenlets)} greenlets about to shut down")
+ # Note: we can't easily print cleanup_count during shutdown
+ # since it happens after the main module's code runs.
+ """)
+ self.assertEqual(rc, 0, f"Process crashed
(rc={rc}):\n{stdout}{stderr}")
+ self.assertIn("OK: 50 greenlets about to shut down", stdout)
+
+ def test_deeply_nested_greenlets_at_shutdown(self):
+ """
+ Deeply nested greenlet parent chains at shutdown.
+ Tests that the deallocation order doesn't cause issues.
+ """
+ rc, stdout, stderr = self._run_shutdown_script("""\
+ import greenlet
+
+ def level(depth, max_depth):
+ if depth < max_depth:
+ g = greenlet.greenlet(level)
+ g.switch(depth + 1, max_depth)
+ greenlet.getcurrent().parent.switch(f"depth-{depth}")
+
+ g = greenlet.greenlet(level)
+ result = g.switch(0, 10)
+ print(f"OK: nested to depth 10, got {result}")
+ """)
+ self.assertEqual(rc, 0, f"Process crashed
(rc={rc}):\n{stdout}{stderr}")
+ self.assertIn("OK: nested to depth 10", stdout)
+
+ def test_greenlet_with_traceback_at_shutdown(self):
+ """
+ A greenlet that has an active exception context when it's
+ suspended should not crash during shutdown cleanup.
+ """
+ rc, stdout, stderr = self._run_shutdown_script("""\
+ import greenlet
+
+ def worker():
+ try:
+ raise ValueError("test error")
+ except ValueError:
+ # Suspend while an exception is active on the stack
+ greenlet.getcurrent().parent.switch("suspended with exc")
+ return "done"
+
+ g = greenlet.greenlet(worker)
+ result = g.switch()
+ assert result == "suspended with exc"
+ print("OK: greenlet with active exception at shutdown")
+ """)
+ self.assertEqual(rc, 0, f"Process crashed
(rc={rc}):\n{stdout}{stderr}")
+ self.assertIn("OK: greenlet with active exception at shutdown", stdout)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/greenlet-3.3.1/src/greenlet/tests/test_leaks.py
new/greenlet-3.3.2/src/greenlet/tests/test_leaks.py
--- old/greenlet-3.3.1/src/greenlet/tests/test_leaks.py 2026-01-23
16:28:32.000000000 +0100
+++ new/greenlet-3.3.2/src/greenlet/tests/test_leaks.py 2026-02-20
21:14:45.000000000 +0100
@@ -16,6 +16,7 @@
from . import TestCase
from . import PY314
from . import RUNNING_ON_FREETHREAD_BUILD
+from . import WIN
from .leakcheck import fails_leakcheck
from .leakcheck import ignores_leakcheck
from .leakcheck import RUNNING_ON_MANYLINUX
@@ -365,6 +366,11 @@
# Like the above test, but what if there are a bunch of
# unfinished greenlets in a thread that dies?
# Does it matter if we deallocate in the thread or not?
+
+ # First, make sure we can get useful measurements. This will
+ # be skipped if not.
+ self.get_process_uss()
+
EXIT_COUNT = [0]
def f():
@@ -451,6 +457,17 @@
# Because the main greenlets from the background threads do not exit in a
timely fashion,
# we fail the object-based leakchecks.
def
test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_main(self):
+ # Between Feb 10 and Feb 20 2026, this test started failing on
+ # Github Actions, windows 3.14t. With no relevant code changes on
+ # our part. Both versions were 3.14.3 (same build). The only change
+ # is the Github actions "Runner Image". The working one was version
+ # 20260202.17.1, while the updated failing version was
+ # 20260217.31.1. Both report the same version of the operating system
+ # (Microsoft Windows Server 2025 10.0.26100).
+ #
+ # Reevaluate on future runner image releases.
+ if WIN and RUNNING_ON_FREETHREAD_BUILD and PY314:
+ self.skipTest("Windows 3.14t appears to leak. No other platform
does.")
self._check_untracked_memory_thread(deallocate_in_thread=False)
if __name__ == '__main__':
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/greenlet-3.3.1/src/greenlet.egg-info/PKG-INFO
new/greenlet-3.3.2/src/greenlet.egg-info/PKG-INFO
--- old/greenlet-3.3.1/src/greenlet.egg-info/PKG-INFO 2026-01-23
16:28:35.000000000 +0100
+++ new/greenlet-3.3.2/src/greenlet.egg-info/PKG-INFO 2026-02-20
21:14:48.000000000 +0100
@@ -1,10 +1,10 @@
Metadata-Version: 2.4
Name: greenlet
-Version: 3.3.1
+Version: 3.3.2
Summary: Lightweight in-process concurrent programming
Author-email: Alexey Borzenkov <[email protected]>
Maintainer-email: Jason Madden <[email protected]>
-License-Expression: MIT AND Python-2.0
+License-Expression: MIT AND PSF-2.0
Project-URL: Homepage, https://greenlet.readthedocs.io
Project-URL: Documentation, https://greenlet.readthedocs.io
Project-URL: Repository, https://github.com/python-greenlet/greenlet
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/greenlet-3.3.1/src/greenlet.egg-info/SOURCES.txt
new/greenlet-3.3.2/src/greenlet.egg-info/SOURCES.txt
--- old/greenlet-3.3.1/src/greenlet.egg-info/SOURCES.txt 2026-01-23
16:28:35.000000000 +0100
+++ new/greenlet-3.3.2/src/greenlet.egg-info/SOURCES.txt 2026-02-20
21:14:48.000000000 +0100
@@ -119,6 +119,7 @@
src/greenlet/tests/test_generator_nested.py
src/greenlet/tests/test_greenlet.py
src/greenlet/tests/test_greenlet_trash.py
+src/greenlet/tests/test_interpreter_shutdown.py
src/greenlet/tests/test_leaks.py
src/greenlet/tests/test_stack_saved.py
src/greenlet/tests/test_throw.py