Issue 24342: Let wrapper set by sys.set_coroutine_wrapper fail gracefully
diff --git a/Python/ceval.c b/Python/ceval.c
index bb2c0b9..2a1db17 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -3921,7 +3921,6 @@
 
     if (co->co_flags & CO_GENERATOR) {
         PyObject *gen;
-        PyObject *coroutine_wrapper;
 
         /* Don't need to keep the reference to f_back, it will be set
          * when the generator is resumed. */
@@ -3935,14 +3934,9 @@
         if (gen == NULL)
             return NULL;
 
-        if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) {
-            coroutine_wrapper = _PyEval_GetCoroutineWrapper();
-            if (coroutine_wrapper != NULL) {
-                PyObject *wrapped =
-                            PyObject_CallFunction(coroutine_wrapper, "N", gen);
-                gen = wrapped;
-            }
-        }
+        if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))
+            return _PyEval_ApplyCoroutineWrapper(gen);
+
         return gen;
     }
 
@@ -4408,6 +4402,33 @@
 }
 
 PyObject *
+_PyEval_ApplyCoroutineWrapper(PyObject *gen)
+{
+    PyObject *wrapped;
+    PyThreadState *tstate = PyThreadState_GET();
+    PyObject *wrapper = tstate->coroutine_wrapper;
+
+    if (tstate->in_coroutine_wrapper) {
+        assert(wrapper != NULL);
+        PyErr_Format(PyExc_RuntimeError,
+                     "coroutine wrapper %.150R attempted "
+                     "to recursively wrap %.150R",
+                     wrapper,
+                     gen);
+        return NULL;
+    }
+
+    if (wrapper == NULL) {
+        return gen;
+    }
+
+    tstate->in_coroutine_wrapper = 1;
+    wrapped = PyObject_CallFunction(wrapper, "N", gen);
+    tstate->in_coroutine_wrapper = 0;
+    return wrapped;
+}
+
+PyObject *
 PyEval_GetBuiltins(void)
 {
     PyFrameObject *current_frame = PyEval_GetFrame();