From 1d0f1ad0aa7a933e8d03f855d9b5eed7e00e775d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 19 Sep 2026 10:34:26 +0200 Subject: [PATCH 1/2] gh-157695: Rename HAVE_FLOAT16 macro to _Py_HAVE_FLOAT16 (#157752) --- Objects/floatobject.c | 4 ++-- configure | 2 +- configure.ac | 2 +- pyconfig.h.in | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Objects/floatobject.c b/Objects/floatobject.c index e379770e10031d2..1bb5311ac678a5d 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -1895,7 +1895,7 @@ int PyFloat_Pack2(double x, char *data, int le) { unsigned char *p = (unsigned char *)data; -#if HAVE_FLOAT16 +#if _Py_HAVE_FLOAT16 /* Conversion can change NaNs type or alter payload. Here we just fallback to the generic code, instead of providing workarounds as for single/double precision. */ @@ -2115,7 +2115,7 @@ double PyFloat_Unpack2(const char *data, int le) { unsigned char *p = (unsigned char *)data; -#if HAVE_FLOAT16 +#if _Py_HAVE_FLOAT16 _Float16 x16; if ((_PY_FLOAT_LITTLE_ENDIAN && !le) || (_PY_FLOAT_BIG_ENDIAN && le)) { diff --git a/configure b/configure index 1115be256afb495..9af81bf7cc67d1d 100755 --- a/configure +++ b/configure @@ -16780,7 +16780,7 @@ printf "%s\n" "$ac_cv_float16_supported" >&6; } if test "x$ac_cv_float16_supported" = xyes then : -printf "%s\n" "#define HAVE_FLOAT16 1" >>confdefs.h +printf "%s\n" "#define _Py_HAVE_FLOAT16 1" >>confdefs.h fi diff --git a/configure.ac b/configure.ac index 902a822e43a42f0..92d7c17a6b448ba 100644 --- a/configure.ac +++ b/configure.ac @@ -4486,7 +4486,7 @@ int main(void) [ac_cv_float16_supported=no], [ac_cv_float16_supported=no])])) AS_VAR_IF([ac_cv_float16_supported], [yes], - [AC_DEFINE([HAVE_FLOAT16], [1], + [AC_DEFINE([_Py_HAVE_FLOAT16], [1], [Defined if _Float16 C type is supported])]) dnl Check for libmpdec >= 2.5.0 diff --git a/pyconfig.h.in b/pyconfig.h.in index e798a061ab6d094..2bf45a290a4ed17 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -497,9 +497,6 @@ /* Define if you have the 'ffi_prep_closure_loc' function. */ #undef HAVE_FFI_PREP_CLOSURE_LOC -/* Defined if _Float16 C type is supported */ -#undef HAVE_FLOAT16 - /* Define to 1 if you have the 'flock' function. */ #undef HAVE_FLOCK @@ -2210,6 +2207,9 @@ AND the target architecture has native SIMD (not just API availability) */ #undef _Py_HAVE_EFFICIENT_BUILTIN_SHUFFLEVECTOR +/* Defined if _Float16 C type is supported */ +#undef _Py_HAVE_FLOAT16 + /* Define to 1 if libgcc __register_frame and __deregister_frame are linkable. */ #undef _Py_HAVE_LIBGCC_EH_FRAME_REGISTRATION From 5b343636bae9b89cb5632ca07b01e1570e24653d Mon Sep 17 00:00:00 2001 From: Timofei Ivankov <128279579+deadlovelll@users.noreply.github.com> Date: Sat, 19 Sep 2026 13:37:07 +0300 Subject: [PATCH 2/2] gh-157301: Fix asyncio event loop hanging on a failed eager task start (#157302) --- Lib/asyncio/tasks.py | 11 +++- Lib/test/test_asyncio/test_tasks.py | 62 +++++++++++++++++++ ...-09-11-14-14-51.gh-issue-157301.QxcE2r.rst | 2 + Modules/_asynciomodule.c | 13 ++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-09-11-14-14-51.gh-issue-157301.QxcE2r.rst diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 29c5d9af9b4029c..cf4787db1730597 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -103,11 +103,20 @@ def __init__(self, coro, *, loop=None, name=None, context=None, self._coro = coro if context is None: self._context = contextvars.copy_context() + elif not isinstance(context, contextvars.Context): + # gh-157301: the passed value must be a contextvars.Context + self._log_destroy_pending = False + raise TypeError('a contextvars.Context was expected, ' + f'got {type(context).__name__}') else: self._context = context if eager_start and self._loop.is_running(): - self.__eager_start() + try: + self.__eager_start() + except: + self._log_destroy_pending = False + raise else: self._loop.call_soon(self.__step, context=self._context) _py_register_task(self) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 86d90359fa4e585..570810a231b48d2 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2635,6 +2635,68 @@ async def main(): finally: loop.close() + def test_context_not_a_context(self): + # gh-157301 + async def coro(): + pass + + loop = asyncio.new_event_loop() + c = coro() + try: + with self.assertRaises(TypeError): + self.new_task(loop, c, context='not a context') + finally: + c.close() + loop.close() + + def test_context_not_a_context_leaves_loop_usable(self): + # gh-157301 + async def coro(): + pass + + async def main(): + c = coro() + try: + with self.assertRaises(TypeError): + self.new_task(loop, c, context='not a context', + eager_start=True) + finally: + c.close() + await asyncio.sleep(0) + + loop = asyncio.new_event_loop() + loop.call_later(support.SHORT_TIMEOUT, loop.stop) + try: + loop.run_until_complete(self.new_task(loop, main())) + finally: + loop.close() + + def test_context_already_entered_leaves_loop_usable(self): + # gh-157301 + async def coro(): + pass + + async def main(): + ctx = contextvars.copy_context() + + def inside(): + c = coro() + try: + with self.assertRaises(RuntimeError): + self.new_task(loop, c, context=ctx, eager_start=True) + finally: + c.close() + + ctx.run(inside) + await asyncio.sleep(0) + + loop = asyncio.new_event_loop() + loop.call_later(support.SHORT_TIMEOUT, loop.stop) + try: + loop.run_until_complete(self.new_task(loop, main())) + finally: + loop.close() + def test_context_2(self): cvar = contextvars.ContextVar('cvar', default='nope') diff --git a/Misc/NEWS.d/next/Library/2026-09-11-14-14-51.gh-issue-157301.QxcE2r.rst b/Misc/NEWS.d/next/Library/2026-09-11-14-14-51.gh-issue-157301.QxcE2r.rst new file mode 100644 index 000000000000000..9a1d2e435be9066 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-11-14-14-51.gh-issue-157301.QxcE2r.rst @@ -0,0 +1,2 @@ +Fix :class:`asyncio.Task` hanging the event loop when an eager start fails +to enter the task's context. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 8c90b0b1517ae4e..9d047c39d27e256 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -2312,6 +2312,13 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, if (self->task_context == NULL) { return -1; } + } else if (!PyContext_CheckExact(context)) { + // gh-157301: the passed value must be a contextvars.Context + self->task_log_destroy_pending = 0; + PyErr_Format(PyExc_TypeError, + "a contextvars.Context was expected, got %T", + context); + return -1; } else { Py_XSETREF(self->task_context, Py_NewRef(context)); } @@ -3458,7 +3465,13 @@ task_eager_start(_PyThreadStateImpl *ts, asyncio_state *state, TaskObj *task) // it will continue as a regular (non-eager) asyncio task register_task(ts, task); + assert(PyContext_CheckExact(task->task_context)); if (_PyContext_Enter(&ts->base, task->task_context) == -1) { + // gh-157301: a failed enter must not leave the task current and registered + task->task_log_destroy_pending = 0; + PyObject *curtask = swap_current_task(ts, task->task_loop, prevtask); + Py_XDECREF(curtask); + unregister_task(task); Py_DECREF(prevtask); return -1; }