@@ -3207,6 +3207,66 @@ test_thread_state_ensure_from_view_interp_switch(PyObject *self, PyObject *unuse
32073207 Py_RETURN_NONE ;
32083208}
32093209
3210+ /* Self interrupting context manager */
3211+
3212+ typedef struct {
3213+ PyObject_HEAD
3214+ int within ;
3215+ } SelfInterruptingContextManagerObject ;
3216+
3217+ static PyObject *
3218+ new_self_interrupting (PyTypeObject * type , PyObject * args , PyObject * kwds )
3219+ {
3220+ SelfInterruptingContextManagerObject * self =
3221+ (SelfInterruptingContextManagerObject * )type -> tp_alloc (type , 0 );
3222+ if (self != NULL ) {
3223+ self -> within = 0 ;
3224+ }
3225+ return (PyObject * )self ;
3226+ }
3227+
3228+ static PyObject *
3229+ self_interrupting_enter (PyObject * op , PyObject * Py_UNUSED (dummy ))
3230+ {
3231+ ((SelfInterruptingContextManagerObject * )op )-> within = 1 ;
3232+ PyThreadState * tstate = PyThreadState_Get ();
3233+ PyObject * ki = Py_NewRef (PyExc_KeyboardInterrupt );
3234+ PyObject * old_exc = _Py_atomic_exchange_ptr (& tstate -> async_exc , ki );
3235+ _Py_set_eval_breaker_bit (tstate , _PY_ASYNC_EXCEPTION_BIT );
3236+ Py_XDECREF (old_exc );
3237+
3238+ return Py_NewRef (op );
3239+ }
3240+
3241+ static PyObject *
3242+ self_interrupting_within (PyObject * op , PyObject * Py_UNUSED (dummy ))
3243+ {
3244+ return PyBool_FromLong (((SelfInterruptingContextManagerObject * )op )-> within );
3245+ }
3246+
3247+ static PyObject *
3248+ self_interrupting_exit (PyObject * op , PyObject * Py_UNUSED (args )) {
3249+ ((SelfInterruptingContextManagerObject * )op )-> within = 0 ;
3250+ Py_RETURN_NONE ;
3251+ }
3252+
3253+ static PyMethodDef self_interrupting_methods [] = {
3254+ {"__enter__" , self_interrupting_enter , METH_NOARGS , NULL },
3255+ {"within" , self_interrupting_within , METH_NOARGS , NULL },
3256+ {"__exit__" , self_interrupting_exit , METH_VARARGS , NULL },
3257+ {NULL , NULL } /* sentinel */
3258+ };
3259+
3260+ static PyTypeObject SelfInterruptingContextManager_Type = {
3261+ PyVarObject_HEAD_INIT (NULL , 0 )
3262+ "_testcapi.SelfInterruptingContextManager" ,
3263+ sizeof (SelfInterruptingContextManagerObject ),
3264+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE ,
3265+ .tp_new = new_self_interrupting ,
3266+ .tp_methods = self_interrupting_methods ,
3267+ };
3268+
3269+
32103270static PyMethodDef module_functions [] = {
32113271 {"get_configs" , get_configs , METH_NOARGS },
32123272 {"get_eval_frame_stats" , get_eval_frame_stats , METH_NOARGS , NULL },
@@ -3429,6 +3489,11 @@ module_exec(PyObject *module)
34293489 }
34303490#endif
34313491
3492+ if (PyType_Ready (& SelfInterruptingContextManager_Type ) < 0 ) {
3493+ return 1 ;
3494+ }
3495+ PyModule_AddObject (module , "SelfInterruptingContextManager" , (PyObject * )& SelfInterruptingContextManager_Type );
3496+
34323497 return 0 ;
34333498}
34343499
0 commit comments