20

J'introduis l'interpréteur Python dans un programme C. Cependant, il peut arriver que l'exécution de certains scripts python via PyRun_SimpleString() se déroule en boucle infinie ou s'exécute trop longtemps. Envisager PyRun_SimpleString("while 1: pass"); En empêchant le programme principal de bloquer j'ai pensé que je pourrais exécuter l'interprète dans un fil. Comment arrêter l'exécution du script python dans l'interpréteur intégré s'exécutant dans un thread sans arrêter le processus entier?Arrêt de Python incorporé

Est-il possible de transmettre une exception à l'interpréteur? Dois-je envelopper le script sous un autre script qui écouterait les signaux?

PS: Je pourrais courir le python dans un processus distinct, mais ce n'est pas ce que je veux - à moins que ce soit le dernier recours ...


Mise à jour:

Ainsi, il fonctionne maintenant. Merci Denis Otkidach, encore une fois!

Si je vois bien, vous devez faire deux choses: dites à l'interpréteur d'arrêter et return -1 dans le même thread que votre PyRun_SimpleString() est en cours d'exécution. Pour stopper, on a quelques possibilités: PyErr_SetString(PyExc_KeyboardInterrupt, "...") ou PyErr_SetInterrupt() - le premier peut laisser Python exécuter quelques instructions de plus puis il s'arrête, le dernier stoppe immédiatement l'exécution.

Pour return -1, vous utilisez Py_AddPendingCall() pour injecter un appel de fonction dans l'exécution Python. Les docs le mentionnent depuis la version 2.7 et 3.1 mais il fonctionne aussi sur les versions antérieures de Python (2.6 ici). De 2.7 et 3.1 il devrait aussi être thread-safe, ce qui signifie que vous pouvez l'appeler sans acquérir GIL (?).

Donc, on pourrait réécrire l'exemple ci-dessous:

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

Répondre

10

Vous pouvez utiliser Py_AddPendingCall() pour ajouter une exception de levée de fonction à appeler lors du prochain intervalle de vérification (voir docs sur sys.setcheckinterval() pour plus d'informations). Voici un exemple avec Py_Exit() appel (ce qui ne fonctionne pour moi, mais probablement pas ce que vous avez besoin), le remplacer par Py_Finalize() ou l'un des PyErr_Set*():

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


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

Cela devrait être suffisant pour tout code pur python. Mais notez, que certaines fonctions C peuvent fonctionner pendant un moment en une seule opération (il y avait un exemple avec une longue recherche d'expression rationnelle, mais je ne suis pas sûr que ce soit toujours pertinent).

+0

Ce look * très intéressant mais n'est-ce pas le tout nouveau Python? (2.7 alpha est sorti juste aujourd'hui.) Cependant, je pourrais passer à Python 3.1 - mais quoi alors? Quelle fonction devrais-je enregistrer via 'Py_AddPendingCall()'? 'PyErr_SetString()'? 'Py_Exit()'? 'Py_Finalize()'? –

+0

Py_AddPendingCall existe au moins à partir de python 2.0 (défini dans ceval.h).J'ai ajouté un exemple de code à ma réponse. –

+0

Merci! Ça marche! –

1

Eh bien l'interpréteur python devrait être en cours d'exécution dans un thread séparé du programme d'intégration ou vous simplement jamais la chance d'interrompre l'interprète.

Lorsque vous en avez, peut-être pouvez-vous utiliser l'un des appels d'exception de l'API Python pour déclencher une exception dans l'interpréteur? Voir Python C API Exceptions

Questions connexes