Pergunta

Eu estou incorporando interpretador Python para um programa C. No entanto, pode acontecer que durante a execução de um script python via PyRun_SimpleString() vai correr em loop infinito ou executar por muito tempo. Considere PyRun_SimpleString("while 1: pass"); Em impedindo o programa principal para bloquear eu pensei que eu poderia executar o interpretador em um fio.

Como faço para parar de executar o script python no incorporado interpretador em execução em um segmento sem matar todo o processo?

É possível passar uma exceção para o intérprete? Devo envolver o script em algum outro script que iria ouvir sinais?

PS: Eu poderia executar o python em um processo separado, mas isso não é o que eu quero - a menos que seja o último recurso ...


Update:

Assim, ele funciona agora. Obrigado Denis Otkidach, mais uma vez!

Se eu ver este direito, você tem que fazer duas coisas: dizer a intérprete de parar e return -1 no mesmo segmento como o seu PyRun_SimpleString () está em execução

.

Para parar, a pessoa tem algumas possibilidades:. PyErr_SetString(PyExc_KeyboardInterrupt, "...") ou PyErr_SetInterrupt() - o primeiro pode deixar Python rodando mais algumas instruções e, em seguida, ele pára, o mais tarde interrompe a execução imediatamente

Para return -1 você usa Py_AddPendingCall() para injetar uma chamada de função em execução Python. Os documentos são mencioná-lo desde a versão 2.7 e 3.1, mas ele roda em Pythons anteriores, bem como (2.6 aqui). De 2,7 e 3,1 também deve ser thread-safe, ou seja, você pode chamá-lo sem adquirir GIL (?).

Portanto, pode reescrever o exemplo abaixo:

int quit() {
    PyErr_SetInterrupt();
    return -1;
}
Foi útil?

Solução

Você pode usar Py_AddPendingCall() para adicionar uma exceção função de captação de ser chamado no próximo intervalo de verificação (ver docs em sys.setcheckinterval() para mais informações). Aqui está um exemplo com a chamada Py_Exit() (que faz trabalhos para mim, mas provavelmente não é o que você precisa), substituí-lo com Py_Finalize() ou um dos PyErr_Set*():

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


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

Isso deve ser suficiente para qualquer código puro-python. Mas nota, que algumas funções C pode ser executado por um tempo como uma única operação (houve um exemplo com longa duração de busca regexp, mas eu não tenho certeza ainda é relevante).

Outras dicas

Bem, o interpretador Python teria que ser executado em um segmento separado do programa de embutir ou você simplesmente nunca vai ter a chance de interromper o intérprete.

Quando você tem que, talvez, você pode usar uma das chamadas de exceção API do Python para acionar uma exceção no intérprete? Consulte Python C Exceções API

Eu queria ser capaz de interromper qualquer bloco de código Python funcionando ativamente, inclusive cancelando um loop infinito ou apenas um código de longa duração. Na minha aplicação, código Python single-threaded está apresentadas e avaliadas. Um pedido de interrupção pode vir a qualquer momento. Esta pergunta e resposta foram cruciais em ajudar-me realmente conseguir isso.

Olhando para esta questão em 2019, a solução aqui não funcionou para mim; Eu tentei usar Py_AddPendingCall(&quit, NULL); em algumas maneiras diferentes, mas a função chamada não executado como esperado, ou em tudo. Isso fez com que o motor Python para pendurar e eventualmente falhar quando eu submeti Py_FinalizeEx(). Eu finalmente encontrei o que eu precisava neste muito útil resposta a uma pergunta similar.

Depois de ligar o motor Python, eu recuperar o identificador do tópico usando o pacote threading em código Python. Eu analisar isso em um valor longo c e guardá-lo.

A função de interrupção é realmente muito simples:
PyGILState_Ensure()
PyThreadState_SetAsyncExc(threadId, PyExc_Exception) usando o ID da thread que eu salvar de mais cedo e um genérico tipo de exceção
PyGILState_Release()

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top