Question

J'intègre un interpréteur Python dans un programme C. Cependant, il peut arriver que lors de l’exécution de scripts python via PyRun_SimpleString () , l’exécution en boucle infinie ou l’exécution dure trop longtemps. Considérez PyRun_SimpleString ("while 1: pass"); En empêchant le programme principal de se bloquer, je pensais pouvoir exécuter l'interpréteur dans un thread.

Comment puis-je arrêter d'exécuter le script python dans un interpréteur intégré s'exécutant dans un thread sans tuer l'ensemble du processus?

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

PS: Je pourrais exécuter le python dans un processus séparé, mais ce n’est pas ce que je veux - à moins que ce soit le dernier recours ...

Mise à jour:

Donc, ça marche maintenant. Merci Denis Otkidach, encore une fois!

Si je comprends bien, vous devez faire deux choses: demander à l'interpréteur de s'arrêter et renvoyer -1 dans le même fil que votre PyRun_SimpleString () est en cours d'exécution.

Pour arrêter, on a quelques possibilités: PyErr_SetString (PyExc_KeyboardInterrupt, "...") ou PyErr_SetInterrupt () - le premier peut laisser Python en cours d'exécution quelques instructions supplémentaires et puis il s’arrête, le dernier arrête immédiatement l’exécution.

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

On pourrait donc réécrire l'exemple ci-dessous:

int quit() {
    PyErr_SetInterrupt();
    return -1;
}
Était-ce utile?

La solution

Vous pouvez utiliser Py_AddPendingCall () pour ajouter une exception de levée de fonction à appeler lors du prochain intervalle de contrôle (voir la documentation sur sys.setcheckinterval () pour plus d'informations). Voici un exemple d’appel avec Py_Exit () (qui fonctionne pour moi, mais n’est probablement pas ce dont vous avez besoin), remplacez-le 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 suffire pour tout code pur-python. Notez cependant que certaines fonctions C peuvent être exécutées pendant un certain temps en une seule opération (il y avait un exemple de longue recherche regexp, mais je ne suis pas sûr que ce soit toujours pertinent).

Autres conseils

L’interprète python devra s’exécuter dans un thread distinct du programme d’incorporation ou vous n’aurez jamais la possibilité de l’interrompre.

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

.

Je souhaitais pouvoir interrompre tout bloc de code Python en cours d'exécution, y compris l'annulation d'une boucle infinie ou tout simplement un code à exécution longue. Dans ma candidature, le code Python à un seul thread est soumis et évalué. Une demande d'interruption peut arriver à tout moment. Cette question et cette réponse ont été essentielles pour m'aider à atteindre cet objectif.

En regardant cette question en 2019, la solution n’a pas fonctionné pour moi; J'ai essayé d'utiliser Py_AddPendingCall (& quit, NULL); de différentes manières, mais la fonction appelée ne s'est jamais exécutée comme prévu, voire pas du tout. Cela a entraîné le blocage du moteur Python et son blocage éventuel lorsque j'ai soumis Py_FinalizeEx () . J'ai enfin trouvé ce dont j'avais besoin dans cette réponse utile à une question similaire.

Après avoir démarré le moteur Python, je récupère l'ID de thread à l'aide du package threading en code Python. J'analyse cela dans une valeur c long et le sauvegarde.

La fonction d'interruption est en réalité assez simple:
PyGILState_Ensure ()
PyThreadState_SetAsyncExc (threadId, PyExc_Exception) à l'aide de l'ID de thread que j'ai enregistré précédemment et d'un type d'exception générique

PyGILState_Release ()

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top