Estou chamando uma função em Python que sei que pode parar e me forçar a reiniciar o script.
Como chamo a função ou em que a envolvo para que, se demorar mais de 5 segundos, o script a cancele e faça outra coisa?
Estou chamando uma função em Python que sei que pode parar e me forçar a reiniciar o script.
Como chamo a função ou em que a envolvo para que, se demorar mais de 5 segundos, o script a cancele e faça outra coisa?
Você pode usar o pacote de sinal se estiver executando no UNIX:
In [1]: import signal
# Register an handler for the timeout
In [2]: def handler(signum, frame):
...: print("Forever is over!")
...: raise Exception("end of time")
...:
# This function *may* run for an indetermined time...
In [3]: def loop_forever():
...: import time
...: while 1:
...: print("sec")
...: time.sleep(1)
...:
...:
# Register the signal function handler
In [4]: signal.signal(signal.SIGALRM, handler)
Out[4]: 0
# Define a timeout for your function
In [5]: signal.alarm(10)
Out[5]: 0
In [6]: try:
...: loop_forever()
...: except Exception, exc:
...: print(exc)
....:
sec
sec
sec
sec
sec
sec
sec
sec
Forever is over!
end of time
# Cancel the timer if the function returned before timeout
# (ok, mine won't but yours maybe will :)
In [7]: signal.alarm(0)
Out[7]: 0
10 segundos após a chamada alarm.alarm(10)
, o manipulador é chamado. Isso gera uma exceção que você pode interceptar a partir do código Python comum.
Este módulo não funciona bem com threads (mas quem é?)
Observe que, como criamos uma exceção quando o tempo limite ocorre, ele pode ser capturado e ignorado dentro da função, por exemplo, em uma dessas funções:
def loop_forever():
while 1:
print('sec')
try:
time.sleep(10)
except:
continue
signal.alarm
e os relacionadosSIGALRM
não estão disponíveis nas plataformas Windows.signal.signal
--- todos eles funcionarão corretamente? Cadasignal.signal
chamada não cancela uma "simultânea"?Você pode usar
multiprocessing.Process
para fazer exatamente isso.Código
fonte
join()
. que faz com que o número x de subprocessos simultâneos esteja em execução até que eles terminem o trabalho ou a quantidade definida emjoin(10)
. Caso você tenha uma E / S de bloqueio para 10 processos, usando o join (10), você os configurou para aguardar todos eles no máximo 10 por EACH processo iniciado. Use o sinalizador daemon como este exemplo stackoverflow.com/a/27420072/2480481 . Claro que você pode passar a flagdaemon=True
diretamente paramultiprocessing.Process()
funcionar.terminate() ... Note that exit handlers and finally clauses, etc., will not be executed. Note that descendant processes of the process will not be terminated – they will simply become orphaned.
Eu publiquei uma essência que resolve esta questão / problema com um decorador e um
threading.Timer
. Aqui está com um colapso.Importações e configurações para compatibilidade
Foi testado com Python 2 e 3. Também deve funcionar em Unix / Linux e Windows.
Primeiro as importações. Eles tentam manter o código consistente, independentemente da versão do Python:
Use código independente da versão:
Agora importamos nossa funcionalidade da biblioteca padrão.
exit_after
decoradorEm seguida, precisamos de uma função para finalizar a
main()
partir do thread filho:E aqui está o próprio decorador:
Uso
E aqui está o uso que responde diretamente à sua pergunta sobre como sair após 5 segundos !:
Demo:
A segunda chamada de função não será concluída. Em vez disso, o processo deve sair com um retorno!
KeyboardInterrupt
nem sempre para um fio adormecidoObserve que o sono nem sempre será interrompido por uma interrupção do teclado, no Python 2 no Windows, por exemplo:
nem é provável que interrompa o código em execução nas extensões, a menos que verifique explicitamente
PyErr_CheckSignals()
, consulte Cython, Python e KeyboardInterrupt ignoradosEu evitaria dormir um fio por mais de um segundo, em qualquer caso - é uma eternidade no tempo do processador.
Para capturá-lo e fazer outra coisa, você pode capturar o KeyboardInterrupt.
fonte
thread.interrupt_main()
, por que não consigo gerar uma exceção diretamente?multiprocessing.connection.Client
com isso? - Tentando resolver: stackoverflow.com/questions/57817955/…Eu tenho uma proposta diferente, que é uma função pura (com a mesma API da sugestão de encadeamento) e parece funcionar bem (com base nas sugestões deste tópico)
fonte
timeout
. É muito melhor definir o padrão comoNone
e, na primeira linha da função, adicionarkwargs = kwargs or {}
. Args está bem porque as tuplas não são mutáveis.Passei por esse segmento ao procurar uma chamada de tempo limite em testes de unidade. Não encontrei nada simples nas respostas ou nos pacotes de terceiros; por isso, escrevi o decorador abaixo. Você pode entrar no código:
Então, é tão simples como esgotar o tempo limite de um teste ou qualquer função que você desejar:
fonte
Exception
dentro de func_wrapper e fazerpool.close()
após a captura para garantir que o encadeamento sempre morra depois, não importa o que aconteça . Então você pode jogarTimeoutError
ou o que quiser depois. Parece funcionar para mim.RuntimeError: can't start new thread
. Ainda funcionará se eu ignorá-lo ou há algo mais que eu possa fazer para contornar isso? Desde já, obrigado!O
stopit
pacote, encontrado no pypi, parece lidar bem com os tempos limite.Eu gosto do
@stopit.threading_timeoutable
decorador, que adiciona umtimeout
parâmetro à função decorada, que faz o que você espera, para a função.Confira no pypi: https://pypi.python.org/pypi/stopit
fonte
Existem muitas sugestões, mas nenhuma usa concurrent.futures, que eu acho que é a maneira mais legível de lidar com isso.
Super simples de ler e manter.
Fazemos um pool, enviamos um único processo e esperamos até 5 segundos antes de gerar um TimeoutError que você pode capturar e manipular da maneira que precisar.
Nativo para python 3.2+ e portado para 2.7 (futuros de instalação do pip).
A alternância entre fios e processos é tão simples como a substituição
ProcessPoolExecutor
comThreadPoolExecutor
.Se você deseja encerrar o processo com o tempo limite, sugiro pesquisar no Pebble .
fonte
Excelente, fácil de usar e confiável projeto de timeout do PyPi ( https://pypi.org/project/timeout-decorator/ )
instalação :
Uso :
fonte
Eu sou o autor de wrapt_timeout_decorator
A maioria das soluções apresentadas aqui funciona de maneira soberba no Linux à primeira vista - porque temos fork () e sinais () -, mas no Windows as coisas parecem um pouco diferentes. E quando se trata de sub-threads no Linux, você não pode mais usar sinais.
Para gerar um processo no Windows, ele precisa ser selecionável - e muitas funções decoradas ou métodos de classe não são.
Portanto, você precisa usar um seletor melhor, como endro e multiprocesso (não pickle e multiprocessamento) - é por isso que você não pode usar ProcessPoolExecutor (ou apenas com funcionalidade limitada).
Para o tempo limite em si - Você precisa definir o que o tempo limite significa - porque no Windows levará um tempo considerável (e não determinável) para gerar o processo. Isso pode ser complicado em tempos curtos. Vamos supor, a geração do processo leva cerca de 0,5 segundos (facilmente !!!). Se você der um tempo limite de 0,2 segundos, o que deve acontecer? A função deve expirar após 0,5 + 0,2 segundos (deixe o método funcionar por 0,2 segundos)? Ou o processo chamado expirará após 0,2 segundos (nesse caso, a função decorada SEMPRE excederá o tempo limite, porque nesse momento nem sequer é gerada)?
Os decoradores aninhados também podem ser desagradáveis e você não pode usar sinais em um sub-rosca. Se você deseja criar um decorador de plataforma cruzada verdadeiramente universal, tudo isso precisa ser levado em consideração (e testado).
Outros problemas estão passando exceções de volta para o chamador, bem como problemas de logon (se usado na função decorada - o log de arquivos em outro processo NÃO é suportado)
Tentei cobrir todos os casos extremos. Você pode examinar o pacote wrapt_timeout_decorator ou, pelo menos, testar suas próprias soluções inspiradas nos unittests usados lá.
@ Alexis Eggermont - infelizmente não tenho pontos suficientes para comentar - talvez alguém possa notificá-lo - acho que resolvi seu problema de importação.
fonte
timeout-decorator
não funciona no sistema windows, pois o windows não suportavasignal
bem.Se você usar o decorador de tempo limite no sistema Windows, obterá o seguinte
Alguns sugeriram usar,
use_signals=False
mas não funcionaram para mim.O autor @bitranox criou o seguinte pacote:
Exemplo de código:
Dá a seguinte exceção:
fonte
from wrapt_timeout_decorator import *
parece matar algumas das minhas outras importações. Por exemplo eu receboModuleNotFoundError: No module named 'google.appengine'
, mas eu não receber este erro se eu fizer wrapt_timeout_decorator não importaçãoPodemos usar sinais para o mesmo. Eu acho que o exemplo abaixo será útil para você. É muito simples comparado aos threads.
fonte
try: ... except: ...
é sempre uma má ideia.fonte
Eu tinha uma necessidade de nestable interrupções programadas (que SIGALARM não pode fazer) que não vai ficar bloqueado por time.sleep (que a abordagem baseada em threads não pode fazer). Acabei copiando e modificando levemente o código a partir daqui: http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/
O próprio código:
e um exemplo de uso:
fonte
Aqui está uma pequena melhoria na solução baseada em encadeamento fornecida.
O código abaixo suporta exceções :
Invocando-o com um tempo limite de 5 segundos:
fonte
runFunctionCatchExceptions()
certas funções Python, a obtenção de GIL fosse chamada. Por exemplo, o seguinte nunca, ou por muito tempo, o retorno se for chamado dentro da função:eval(2**9999999999**9999999999)
. Veja stackoverflow.com/questions/22138190/…