Stopping embedded Python

Well the python interpreter would have to be running in a separate thread from the embedding program or you will simply never get a chance to interrupt the interpreter.

When you have that, perhaps you can use one of the Python API exception calls to trigger an exception in the interpreter? See Python C API Exceptions


I wanted to be able to interrupt any actively running Python code block, including cancelling an infinite loop or just some long-running code. In my application, single-threaded Python code is submitted and evaluated. An interrupt request can come at any time. This question and answer were crucial in helping me actually achieve that.

Looking at this question in 2019, the solution here did not work for me; I tried to use Py_AddPendingCall(&quit, NULL); in quite a few different ways but the called function never executed as expected, or at all. This caused the Python engine to hang and eventually crash when I submitted Py_FinalizeEx(). I finally found what I needed in this very useful answer to a similar question.

After I start the Python engine, I retrieve the thread id using the threading package in Python code. I parse that into a c long value and save it.

The interrupt function is actually quite simple:
PyGILState_Ensure()
PyThreadState_SetAsyncExc(threadId, PyExc_Exception) using the thread ID that I save from earlier and a generic exception type
PyGILState_Release()


You can use Py_AddPendingCall() to add a function raising exception to be called on next check interval (see docs on sys.setcheckinterval() for more info). Here is an example with Py_Exit() call (which does works for me, but probably is not what you need), replace it with Py_Finalize() or one of PyErr_Set*():

int quit(void *) {
    Py_Exit(0);
}


PyGILState_STATE state = PyGILState_Ensure();
Py_AddPendingCall(&quit, NULL);
PyGILState_Release(state);

This should be enough for any pure-python code. But note, that some C functions can run for a while as a single operation (there was an example with long running regexp search, but I'm not sure it's still relevant).