20

Estoy incorporando el intérprete de Python a un programa C. Sin embargo, podría suceder que mientras se ejecute algún script de Python a través del PyRun_SimpleString() se ejecute en bucle infinito o se ejecute durante demasiado tiempo. Considere PyRun_SimpleString("while 1: pass"); Al evitar que el programa principal se bloquee, pensé que podía ejecutar el intérprete en un hilo.Detener Python incorporado

¿Cómo dejo de ejecutar el script de python en el intérprete incorporado ejecutando un hilo sin matar todo el proceso?

¿Es posible pasar una excepción al intérprete? ¿Debo envolver el script bajo algún otro script que escuche las señales?

PS: Yo podría correr el pitón en un proceso separado, pero esto no es lo que quiero - a menos que sea el último recurso ...


Actualización:

Por lo tanto, funciona ahora ¡Gracias, Denis Otkidach, una vez más!

Si veo esto bien, debe hacer dos cosas: decirle al intérprete que se detenga y return -1 en el mismo subproceso que su PyRun_SimpleString().

Para detenerlo, tiene algunas posibilidades: PyErr_SetString(PyExc_KeyboardInterrupt, "...") o PyErr_SetInterrupt() - el primero puede dejar Python ejecutando algunas instrucciones más y luego se detiene, el último detiene la ejecución inmediatamente.

Para return -1 utiliza Py_AddPendingCall() para inyectar una llamada a función en la ejecución de Python. Los documentos lo mencionan desde la versión 2.7 y 3.1, pero también se ejecuta en Pythons anteriores (2.6 aquí). De 2.7 y 3.1 también debería ser seguro para subprocesos, lo que significa que puede invocarlo sin adquirir GIL (?).

Así se podría reescribir el ejemplo abajo:

int quit() { 
    PyErr_SetInterrupt(); 
    return -1; 
} 

Respuesta

10

Puede usar Py_AddPendingCall() para agregar una excepción de aumento de función que se invocará en el próximo intervalo de comprobación (consulte los documentos en sys.setcheckinterval() para obtener más información). He aquí un ejemplo con Py_Exit() llamada (que hace trabajos para mí, pero probablemente no es lo que necesita), reemplazarlo con Py_Finalize() o uno de PyErr_Set*():

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


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

Esto debería ser suficiente para cualquier código Python puro. Pero tenga en cuenta que algunas funciones C pueden ejecutarse durante un tiempo como una sola operación (había un ejemplo con la búsqueda regexp de larga ejecución, pero no estoy seguro de que siga siendo relevante).

+0

Este aspecto * muy * interesante pero ¿no es para el Python más nuevo? (2.7 alfa salió solo hoy.) Sin embargo, podría actualizar a Python 3.1, pero ¿entonces qué? ¿Qué función debo registrar a través de 'Py_AddPendingCall()'? 'PyErr_SetString()'? 'Py_Exit()'? 'Py_Finalize()'? –

+0

Py_AddPendingCall existe al menos desde python 2.0 (definido en ceval.h).He agregado un código de ejemplo a mi respuesta. –

+0

Gracias! ¡Funciona! –

1

Bueno, el intérprete de python tendría que estar en ejecución en un hilo separado del programa incrustación o simplemente se le nunca tienen la oportunidad de interrumpir el intérprete.

¿Cuándo lo tiene, quizás pueda usar una de las llamadas de excepción API de Python para activar una excepción en el intérprete? Ver Python C API Exceptions

Cuestiones relacionadas