From 0351c7b13f29ed3d2a14a2a6a023c7c3e428fde5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Nov 2026 16:07:23 +0000 Subject: [PATCH 1/2] Initial plan From d9b9d076abb6e47d4604bcf3d77367aef2ae8ec2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Nov 2026 16:08:35 +0000 Subject: [PATCH 2/2] Apply remaining changes Co-authored-by: Xmader <16057139+Xmader@users.noreply.github.com> --- src/PyEventLoop.cc | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/PyEventLoop.cc b/src/PyEventLoop.cc index c62c8f12..9f730d93 100644 --- a/src/PyEventLoop.cc +++ b/src/PyEventLoop.cc @@ -142,6 +142,26 @@ PyEventLoop PyEventLoop::_loopNotFound() { PyEventLoop PyEventLoop::_getLoopOnThread(PyThreadState *tstate) { // Modified from Python 3.9 `get_running_loop` https://github.com/python/cpython/blob/7cb3a44/Modules/_asynciomodule.c#L241-L278 + // For the current thread, ask asyncio itself instead of reading CPython's private thread-state layout, + // which differs between patch releases (e.g. 3.14.3 vs 3.14.4). + if (tstate == PyThreadState_Get()) { + PyObject *asyncio = PyImport_ImportModule("asyncio"); + if (asyncio == NULL) { + return _loopNotFound(); + } + PyObject *loop = PyObject_CallMethod(asyncio, "_get_running_loop", NULL); + Py_DECREF(asyncio); + if (loop == NULL) { + PyErr_Clear(); + return _loopNotFound(); + } + if (loop == Py_None) { + Py_DECREF(loop); + return _loopNotFound(); + } + return PyEventLoop(loop); // takes the new reference + } + PyObject *ts_dict; #if PY_VERSION_HEX >= 0x030d0000 // Python version is greater than 3.13 // The private `_PyThreadState_GetDict(tstate)` API gets removed in Python 3.13. @@ -204,6 +224,14 @@ PyEventLoop PyEventLoop::_getLoopOnThread(PyThreadState *tstate) { // (we only need the first few fields of `_PyThreadStateImpl`) }; + #if PY_VERSION_HEX >= 0x030e0000 && PY_VERSION_HEX < 0x030f0000 + // Python 3.14.4 added a pointer field to the public `PyThreadState`, shifting the offsets of the private fields. + // Refuse to read another thread's state when the runtime and build-time layouts differ. + if ((PY_VERSION_HEX >= 0x030e0400) != (Py_Version >= 0x030e0400)) { + return _loopNotFound(); + } + #endif + // Modified from https://github.com/python/cpython/blob/v3.13.0rc1/Modules/_asynciomodule.c#L3205-L3210 PyObject *loop = ((PyThreadStateHolder *)tstate)->asyncio_running_loop; if (loop == NULL) {