Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1Port of https://github.com/python-greenlet/greenlet/pull/396 2 3From 94979488f841fcb41bd2bd3b80b5c0b011af4c94 Mon Sep 17 00:00:00 2001 4From: Victor Stinner <vstinner@python.org> 5Date: Wed, 14 Feb 2024 16:37:42 +0100 6Subject: [PATCH 1/5] Fix #392: Port to Python 3.13 7 8* Replace C_RECURSION_LIMIT with Py_C_RECURSION_LIMIT. 9* Add Py_C_RECURSION_LIMIT for Python 3.12 and older. 10* Disable GREENLET_USE_CFRAME on Python 3.13. 11* Define Py_BUILD_CORE to include pycore_frame.h. 12--- 13 src/greenlet/TPythonState.cpp | 10 +++++++--- 14 src/greenlet/greenlet_cpython_compat.hpp | 13 +++++++++++-- 15 src/greenlet/greenlet_greenlet.hpp | 1 + 16 3 files changed, 19 insertions(+), 5 deletions(-) 17 18diff --git a/src/greenlet/TPythonState.cpp b/src/greenlet/TPythonState.cpp 19index 465d4174..c0dbf703 100644 20--- a/src/greenlet/TPythonState.cpp 21+++ b/src/greenlet/TPythonState.cpp 22@@ -130,11 +130,13 @@ void PythonState::operator<<(const PyThreadState *const tstate) noexcept 23 #if GREENLET_PY311 24 #if GREENLET_PY312 25 this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; 26- this->c_recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; 27+ this->c_recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining; 28 #else // not 312 29 this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; 30 #endif // GREENLET_PY312 31+ #if GREENLET_USE_CFRAME 32 this->current_frame = tstate->cframe->current_frame; 33+ #endif 34 this->datastack_chunk = tstate->datastack_chunk; 35 this->datastack_top = tstate->datastack_top; 36 this->datastack_limit = tstate->datastack_limit; 37@@ -199,12 +201,14 @@ void PythonState::operator>>(PyThreadState *const tstate) noexcept 38 #if GREENLET_PY311 39 #if GREENLET_PY312 40 tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth; 41- tstate->c_recursion_remaining = C_RECURSION_LIMIT - this->c_recursion_depth; 42+ tstate->c_recursion_remaining = Py_C_RECURSION_LIMIT - this->c_recursion_depth; 43 this->unexpose_frames(); 44 #else // \/ 3.11 45 tstate->recursion_remaining = tstate->recursion_limit - this->recursion_depth; 46 #endif // GREENLET_PY312 47+ #if GREENLET_USE_CFRAME 48 tstate->cframe->current_frame = this->current_frame; 49+ #endif 50 tstate->datastack_chunk = this->datastack_chunk; 51 tstate->datastack_top = this->datastack_top; 52 tstate->datastack_limit = this->datastack_limit; 53@@ -238,7 +242,7 @@ void PythonState::set_initial_state(const PyThreadState* const tstate) noexcept 54 #if GREENLET_PY312 55 this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; 56 // XXX: TODO: Comment from a reviewer: 57- // Should this be ``C_RECURSION_LIMIT - tstate->c_recursion_remaining``? 58+ // Should this be ``Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining``? 59 // But to me it looks more like that might not be the right 60 // initialization either? 61 this->c_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; 62diff --git a/src/greenlet/greenlet_cpython_compat.hpp b/src/greenlet/greenlet_cpython_compat.hpp 63index cdc1617f..c0fb94c5 100644 64--- a/src/greenlet/greenlet_cpython_compat.hpp 65+++ b/src/greenlet/greenlet_cpython_compat.hpp 66@@ -12,19 +12,24 @@ 67 68 #if PY_VERSION_HEX >= 0x30A00B1 69 # define GREENLET_PY310 1 70+#else 71+# define GREENLET_PY310 0 72+#endif 73+ 74 /* 75 Python 3.10 beta 1 changed tstate->use_tracing to a nested cframe member. 76 See https://github.com/python/cpython/pull/25276 77 We have to save and restore this as well. 78+ 79+Python 3.13 removed PyThreadState.cframe (GH-108035). 80 */ 81+#if GREENLET_PY310 && PY_VERSION_HEX < 0x30D0000 82 # define GREENLET_USE_CFRAME 1 83 #else 84 # define GREENLET_USE_CFRAME 0 85-# define GREENLET_PY310 0 86 #endif 87 88 89- 90 #if PY_VERSION_HEX >= 0x30B00A4 91 /* 92 Greenlet won't compile on anything older than Python 3.11 alpha 4 (see 93@@ -124,4 +129,8 @@ static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) 94 } 95 #endif 96 97+#if !defined(Py_C_RECURSION_LIMIT) && defined(C_RECURSION_LIMIT) 98+# define Py_C_RECURSION_LIMIT C_RECURSION_LIMIT 99+#endif 100+ 101 #endif /* GREENLET_CPYTHON_COMPAT_H */ 102diff --git a/src/greenlet/greenlet_greenlet.hpp b/src/greenlet/greenlet_greenlet.hpp 103index d52ce1fd..6da6841f 100644 104--- a/src/greenlet/greenlet_greenlet.hpp 105+++ b/src/greenlet/greenlet_greenlet.hpp 106@@ -23,6 +23,7 @@ using greenlet::refs::BorrowedGreenlet; 107 #endif 108 109 #if GREENLET_PY312 110+# define Py_BUILD_CORE 111 # include "internal/pycore_frame.h" 112 #endif 113 114 115From 00611d7567d09869973fe314f60575674cc877d8 Mon Sep 17 00:00:00 2001 116From: Victor Stinner <vstinner@python.org> 117Date: Mon, 3 Jun 2024 10:55:14 +0200 118Subject: [PATCH 3/5] Support delete_later 119 120--- 121 src/greenlet/TPythonState.cpp | 14 ++++++++++++-- 122 src/greenlet/greenlet.cpp | 4 ++++ 123 src/greenlet/greenlet_cpython_compat.hpp | 6 ++++++ 124 src/greenlet/greenlet_greenlet.hpp | 4 ++++ 125 4 files changed, 26 insertions(+), 2 deletions(-) 126 127diff --git a/src/greenlet/TPythonState.cpp b/src/greenlet/TPythonState.cpp 128index c0dbf703..bfb40cac 100644 129--- a/src/greenlet/TPythonState.cpp 130+++ b/src/greenlet/TPythonState.cpp 131@@ -18,7 +18,11 @@ PythonState::PythonState() 132 #else 133 ,recursion_depth(0) 134 #endif 135+#if GREENLET_PY313 136+ ,delete_later(nullptr) 137+#else 138 ,trash_delete_nesting(0) 139+#endif 140 #if GREENLET_PY311 141 ,current_frame(nullptr) 142 ,datastack_chunk(nullptr) 143@@ -145,7 +149,9 @@ void PythonState::operator<<(const PyThreadState *const tstate) noexcept 144 Py_XDECREF(frame); // PyThreadState_GetFrame gives us a new 145 // reference. 146 this->_top_frame.steal(frame); 147- #if GREENLET_PY312 148+ #if GREENLET_PY313 149+ this->delete_later = Py_XNewRef(tstate->delete_later); 150+ #elif GREENLET_PY312 151 this->trash_delete_nesting = tstate->trash.delete_nesting; 152 #else // not 312 153 this->trash_delete_nesting = tstate->trash_delete_nesting; 154@@ -213,7 +219,11 @@ void PythonState::operator>>(PyThreadState *const tstate) noexcept 155 tstate->datastack_top = this->datastack_top; 156 tstate->datastack_limit = this->datastack_limit; 157 this->_top_frame.relinquish_ownership(); 158- #if GREENLET_PY312 159+ #if GREENLET_PY313 160+ Py_XDECREF(tstate->delete_later); 161+ tstate->delete_later = this->delete_later; 162+ Py_CLEAR(this->delete_later); 163+ #elif GREENLET_PY312 164 tstate->trash.delete_nesting = this->trash_delete_nesting; 165 #else // not 3.12 166 tstate->trash_delete_nesting = this->trash_delete_nesting; 167diff --git a/src/greenlet/greenlet.cpp b/src/greenlet/greenlet.cpp 168index 5a9818e8..dfc748a8 100644 169--- a/src/greenlet/greenlet.cpp 170+++ b/src/greenlet/greenlet.cpp 171@@ -1328,6 +1328,7 @@ mod_enable_optional_cleanup(PyObject* UNUSED(module), PyObject* flag) 172 Py_RETURN_NONE; 173 } 174 175+#if !GREENLET_PY313 176 PyDoc_STRVAR(mod_get_tstate_trash_delete_nesting_doc, 177 "get_tstate_trash_delete_nesting() -> Integer\n" 178 "\n" 179@@ -1343,6 +1344,7 @@ mod_get_tstate_trash_delete_nesting(PyObject* UNUSED(module)) 180 return PyLong_FromLong(tstate->trash_delete_nesting); 181 #endif 182 } 183+#endif 184 185 static PyMethodDef GreenMethods[] = { 186 {"getcurrent", 187@@ -1356,7 +1358,9 @@ static PyMethodDef GreenMethods[] = { 188 {"get_total_main_greenlets", (PyCFunction)mod_get_total_main_greenlets, METH_NOARGS, mod_get_total_main_greenlets_doc}, 189 {"get_clocks_used_doing_optional_cleanup", (PyCFunction)mod_get_clocks_used_doing_optional_cleanup, METH_NOARGS, mod_get_clocks_used_doing_optional_cleanup_doc}, 190 {"enable_optional_cleanup", (PyCFunction)mod_enable_optional_cleanup, METH_O, mod_enable_optional_cleanup_doc}, 191+#if !GREENLET_PY313 192 {"get_tstate_trash_delete_nesting", (PyCFunction)mod_get_tstate_trash_delete_nesting, METH_NOARGS, mod_get_tstate_trash_delete_nesting_doc}, 193+#endif 194 {NULL, NULL} /* Sentinel */ 195 }; 196 197diff --git a/src/greenlet/greenlet_cpython_compat.hpp b/src/greenlet/greenlet_cpython_compat.hpp 198index c0fb94c5..ce5fd882 100644 199--- a/src/greenlet/greenlet_cpython_compat.hpp 200+++ b/src/greenlet/greenlet_cpython_compat.hpp 201@@ -55,6 +55,12 @@ Greenlet won't compile on anything older than Python 3.11 alpha 4 (see 202 # define GREENLET_PY312 0 203 #endif 204 205+#if PY_VERSION_HEX >= 0x30D0000 206+# define GREENLET_PY313 1 207+#else 208+# define GREENLET_PY313 0 209+#endif 210+ 211 #ifndef Py_SET_REFCNT 212 /* Py_REFCNT and Py_SIZE macros are converted to functions 213 https://bugs.python.org/issue39573 */ 214diff --git a/src/greenlet/greenlet_greenlet.hpp b/src/greenlet/greenlet_greenlet.hpp 215index 6da6841f..fbfdfbfc 100644 216--- a/src/greenlet/greenlet_greenlet.hpp 217+++ b/src/greenlet/greenlet_greenlet.hpp 218@@ -111,7 +111,11 @@ namespace greenlet 219 #else 220 int recursion_depth; 221 #endif 222+#if GREENLET_PY313 223+ PyObject *delete_later; 224+#else 225 int trash_delete_nesting; 226+#endif 227 #if GREENLET_PY311 228 _PyInterpreterFrame* current_frame; 229 _PyStackChunk* datastack_chunk; 230 231From b65558ec962d3d81ae09787ebca8686d233e2a4c Mon Sep 17 00:00:00 2001 232From: Victor Stinner <vstinner@python.org> 233Date: Wed, 5 Jun 2024 12:04:21 +0200 234Subject: [PATCH 4/5] Fix current_frame 235 236--- 237 src/greenlet/TPythonState.cpp | 8 ++++++-- 238 1 file changed, 6 insertions(+), 2 deletions(-) 239 240diff --git a/src/greenlet/TPythonState.cpp b/src/greenlet/TPythonState.cpp 241index bfb40cac..82eb34f0 100644 242--- a/src/greenlet/TPythonState.cpp 243+++ b/src/greenlet/TPythonState.cpp 244@@ -138,7 +138,9 @@ void PythonState::operator<<(const PyThreadState *const tstate) noexcept 245 #else // not 312 246 this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; 247 #endif // GREENLET_PY312 248- #if GREENLET_USE_CFRAME 249+ #if GREENLET_PY313 250+ this->current_frame = tstate->current_frame; 251+ #elif GREENLET_USE_CFRAME 252 this->current_frame = tstate->cframe->current_frame; 253 #endif 254 this->datastack_chunk = tstate->datastack_chunk; 255@@ -212,7 +214,9 @@ void PythonState::operator>>(PyThreadState *const tstate) noexcept 256 #else // \/ 3.11 257 tstate->recursion_remaining = tstate->recursion_limit - this->recursion_depth; 258 #endif // GREENLET_PY312 259- #if GREENLET_USE_CFRAME 260+ #if GREENLET_PY313 261+ tstate->current_frame = this->current_frame; 262+ #elif GREENLET_USE_CFRAME 263 tstate->cframe->current_frame = this->current_frame; 264 #endif 265 tstate->datastack_chunk = this->datastack_chunk; 266 267From b7cfc1748766cac351fe5fca32fa7c8cacdea2ae Mon Sep 17 00:00:00 2001 268From: Victor Stinner <vstinner@python.org> 269Date: Wed, 5 Jun 2024 12:17:28 +0200 270Subject: [PATCH 5/5] Update tests 271 272--- 273 src/greenlet/tests/test_greenlet.py | 4 +++- 274 src/greenlet/tests/test_greenlet_trash.py | 9 +++++++++ 275 2 files changed, 12 insertions(+), 1 deletion(-) 276 277diff --git a/src/greenlet/tests/test_greenlet.py b/src/greenlet/tests/test_greenlet.py 278index 51849cd6..259707ae 100644 279--- a/src/greenlet/tests/test_greenlet.py 280+++ b/src/greenlet/tests/test_greenlet.py 281@@ -471,7 +471,9 @@ def creator(): 282 # Unfortunately, this doesn't actually clear the references, they're in the 283 # fast local array. 284 if not wait_for_cleanup: 285- result[0].gr_frame.f_locals.clear() 286+ # f_locals has no clear method in Python 3.13 287+ if hasattr(result[0].gr_frame.f_locals, 'clear'): 288+ result[0].gr_frame.f_locals.clear() 289 else: 290 self.assertIsNone(result[0].gr_frame) 291 292diff --git a/src/greenlet/tests/test_greenlet_trash.py b/src/greenlet/tests/test_greenlet_trash.py 293index 8d9716e9..2bce8fd0 100644 294--- a/src/greenlet/tests/test_greenlet_trash.py 295+++ b/src/greenlet/tests/test_greenlet_trash.py 296@@ -29,8 +29,17 @@ 297 298 import unittest 299 300+try: 301+ from greenlet._greenlet import get_tstate_trash_delete_nesting 302+except ImportError: 303+ get_tstate_trash_delete_nesting = None 304+ 305+ 306 class TestTrashCanReEnter(unittest.TestCase): 307 308+ # Python 3.13 has not "trash delete nesting" anymore (but "delete later") 309+ @unittest.skipIf(get_tstate_trash_delete_nesting is None, 310+ 'need get_tstate_trash_delete_nesting()') 311 def test_it(self): 312 # Try several times to trigger it, because it isn't 100% 313 # reliable.