20

Я внедряю интерпретатор Python в программу на C. Однако может случиться, что при запуске какого-либо скрипта python через PyRun_SimpleString() будет выполняться бесконечный цикл или выполнить слишком долго. Рассмотрим PyRun_SimpleString("while 1: pass"); В предотвращении блокировки основной программы я думал, что могу запустить интерпретатор в потоке.Остановка встроенного Python

Как прекратить выполнение скрипта python во встроенном интерпретаторе, запущенном в потоке, не убивая весь процесс?

Можно ли передать исключение интерпретатору? Должен ли я обернуть скрипт под другим скриптом, который будет слушать сигналы?

PS: Я мог бы запустить питона в отдельном процессе, но это не то, что я хочу - если это не последняя инстанция ...


Update:

Итак, это работает сейчас. Спасибо, Денис Откидах, еще раз!

Если я вижу это правильно, вам нужно сделать две вещи: сказать интерпретатору остановиться и return -1 в том же потоке, что и ваш PyRun_SimpleString().

Чтобы остановиться, у вас есть несколько возможностей: PyErr_SetString(PyExc_KeyboardInterrupt, "...") или PyErr_SetInterrupt() - первый может оставить Python еще несколько инструкций, а затем он остановится, а затем он немедленно прекратит выполнение.

К return -1 вы используете Py_AddPendingCall() для ввода вызова функции в выполнение Python. Документы упоминают его с версии 2.7 и 3.1, но он работает и с более ранними Pythons (здесь 2.6). Начиная с 2.7 и 3.1, он также должен быть потокобезопасным, то есть вы можете назвать его, не приобретая GIL (?).

Так можно переписать пример ниже:

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

ответ

10

Вы можете использовать Py_AddPendingCall(), чтобы добавить исключение функции повышения, которое должно быть вызвано в следующий интервал проверки (см. Документы на странице sys.setcheckinterval() для получения дополнительной информации). Вот пример с Py_Exit() вызова (который делает работы для меня, но, вероятно, не то, что вам нужно), замените его Py_Finalize() или один из PyErr_Set*():

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


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

Это должно быть достаточно для любого чистого-кода Python. Но обратите внимание, что некоторые функции C могут выполняться некоторое время как одна операция (был пример с длинным запуском поиска в регулярном выражении, но я не уверен, что это все еще актуально).

+0

Этот вид * очень * интересный, но не для самого нового Python? (2,7 альфа вышла только сегодня.) Однако я мог бы перейти на Python 3.1 - но что тогда? Какую функцию я должен зарегистрировать через 'Py_AddPendingCall()'? 'PyErr_SetString()'? 'Py_Exit()'? 'Py_Finalize()'? –

+0

Py_AddPendingCall существует, по крайней мере, из python 2.0 (определен в ceval.h).Я добавил пример кода к моему ответу. –

+0

Спасибо! Оно работает! –

1

Ну интерпретатор питона должен быть запущен в отдельном потоке из программы погружения или вы просто не получите шанс прервать переводчик.

Если у вас есть это, возможно, вы можете использовать один из вызовов исключения API Python для запуска исключения в интерпретаторе? См. Python C API Exceptions