@@ -3194,6 +3194,66 @@ test_thread_state_ensure_from_view_interp_switch(PyObject *self, PyObject *unuse
31943194 Py_RETURN_NONE ;
31953195}
31963196
3197+ /* Self interrupting context manager */
3198+
3199+ typedef struct {
3200+ PyObject_HEAD
3201+ int within ;
3202+ } SelfInterruptingContextManagerObject ;
3203+
3204+ static PyObject *
3205+ new_self_interrupting (PyTypeObject * type , PyObject * args , PyObject * kwds )
3206+ {
3207+ SelfInterruptingContextManagerObject * self =
3208+ (SelfInterruptingContextManagerObject * )type -> tp_alloc (type , 0 );
3209+ if (self != NULL ) {
3210+ self -> within = 0 ;
3211+ }
3212+ return (PyObject * )self ;
3213+ }
3214+
3215+ static PyObject *
3216+ self_interrupting_enter (PyObject * op , PyObject * Py_UNUSED (dummy ))
3217+ {
3218+ ((SelfInterruptingContextManagerObject * )op )-> within = 1 ;
3219+ PyThreadState * tstate = PyThreadState_Get ();
3220+ PyObject * ki = Py_NewRef (PyExc_KeyboardInterrupt );
3221+ PyObject * old_exc = _Py_atomic_exchange_ptr (& tstate -> async_exc , ki );
3222+ _Py_set_eval_breaker_bit (tstate , _PY_ASYNC_EXCEPTION_BIT );
3223+ Py_XDECREF (old_exc );
3224+
3225+ return Py_NewRef (op );
3226+ }
3227+
3228+ static PyObject *
3229+ self_interrupting_within (PyObject * op , PyObject * Py_UNUSED (dummy ))
3230+ {
3231+ return PyBool_FromLong (((SelfInterruptingContextManagerObject * )op )-> within );
3232+ }
3233+
3234+ static PyObject *
3235+ self_interrupting_exit (PyObject * op , PyObject * Py_UNUSED (args )) {
3236+ ((SelfInterruptingContextManagerObject * )op )-> within = 0 ;
3237+ Py_RETURN_NONE ;
3238+ }
3239+
3240+ static PyMethodDef self_interrupting_methods [] = {
3241+ {"__enter__" , self_interrupting_enter , METH_NOARGS , NULL },
3242+ {"within" , self_interrupting_within , METH_NOARGS , NULL },
3243+ {"__exit__" , self_interrupting_exit , METH_VARARGS , NULL },
3244+ {NULL , NULL } /* sentinel */
3245+ };
3246+
3247+ static PyTypeObject SelfInterruptingContextManager_Type = {
3248+ PyVarObject_HEAD_INIT (NULL , 0 )
3249+ "_testcapi.SelfInterruptingContextManager" ,
3250+ sizeof (SelfInterruptingContextManagerObject ),
3251+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE ,
3252+ .tp_new = new_self_interrupting ,
3253+ .tp_methods = self_interrupting_methods ,
3254+ };
3255+
3256+
31973257static PyMethodDef module_functions [] = {
31983258 {"get_configs" , get_configs , METH_NOARGS },
31993259 {"get_eval_frame_stats" , get_eval_frame_stats , METH_NOARGS , NULL },
@@ -3416,6 +3476,11 @@ module_exec(PyObject *module)
34163476 }
34173477#endif
34183478
3479+ if (PyType_Ready (& SelfInterruptingContextManager_Type ) < 0 ) {
3480+ return 1 ;
3481+ }
3482+ PyModule_AddObject (module , "SelfInterruptingContextManager" , (PyObject * )& SelfInterruptingContextManager_Type );
3483+
34193484 return 0 ;
34203485}
34213486
0 commit comments