Estoy llamando a una función en Python que sé que puede detenerse y obligarme a reiniciar el script.
¿Cómo llamo a la función o en qué la envuelvo para que, si tarda más de 5 segundos, el script la cancela y hace algo más?
Estoy llamando a una función en Python que sé que puede detenerse y obligarme a reiniciar el script.
¿Cómo llamo a la función o en qué la envuelvo para que, si tarda más de 5 segundos, el script la cancela y hace algo más?
Puede usar el paquete de señal si está ejecutando en 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 después de la llamada alarm.alarm(10)
, se llama al controlador. Esto genera una excepción que puede interceptar del código normal de Python.
Este módulo no funciona bien con hilos (pero, ¿quién lo hace?)
Tenga en cuenta que, dado que activamos una excepción cuando se agota el tiempo de espera, puede terminar atrapada e ignorada dentro de la función, por ejemplo, una de esas funciones:
def loop_forever():
while 1:
print('sec')
try:
time.sleep(10)
except:
continue
signal.alarm
y lo relacionadoSIGALRM
no está disponible en plataformas Windows.signal.signal
--- ¿funcionarán todos correctamente? ¿Cadasignal.signal
llamada no cancelará una "concurrente"?Puedes usar
multiprocessing.Process
para hacer exactamente eso.Código
fuente
join()
. eso hace que su número x de subprocesos concurrentes se ejecuten hasta que terminen su trabajo, o la cantidad definida enjoin(10)
. En caso de que tenga una E / S de bloqueo para 10 procesos, utilizando join (10) los ha configurado para que esperen todos ellos como máximo 10 por CADA proceso que ha comenzado. Utilice el indicador de daemon como este ejemplo stackoverflow.com/a/27420072/2480481 . Por supuesto, puede pasar la banderadaemon=True
directamente a lamultiprocessing.Process()
función.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.
Publiqué un esencia que resuelve esta pregunta / problema con un decorador y a
threading.Timer
. Aquí está con un desglose.Importaciones y configuraciones para compatibilidad
Fue probado con Python 2 y 3. También debería funcionar en Unix / Linux y Windows.
Primero las importaciones. Estos intentan mantener el código consistente independientemente de la versión de Python:
Usar código independiente de la versión:
Ahora hemos importado nuestra funcionalidad de la biblioteca estándar.
exit_after
decoradorA continuación, necesitamos una función para terminar el
main()
hilo secundario:Y aquí está el decorador en sí:
Uso
¡Y aquí está el uso que responde directamente a su pregunta sobre salir después de 5 segundos !:
Manifestación:
¡La segunda llamada a función no finalizará, en su lugar, el proceso debería salir con un rastreo!
KeyboardInterrupt
no siempre detiene un hilo dormidoTenga en cuenta que el sueño no siempre será interrumpido por una interrupción del teclado, en Python 2 en Windows, por ejemplo:
ni es probable que interrumpa el código que se ejecuta en extensiones a menos que lo compruebe explícitamente
PyErr_CheckSignals()
, vea Cython, Python y KeyboardInterrupt ignoradoEn cualquier caso, evitaría dormir un hilo más de un segundo, eso es un eón en tiempo de procesador.
Para atraparlo y hacer otra cosa, puede atrapar el KeyboardInterrupt.
fuente
thread.interrupt_main()
, por qué no puedo plantear directamente una excepción?multiprocessing.connection.Client
con esto? - Intentando resolver: stackoverflow.com/questions/57817955/…Tengo una propuesta diferente que es una función pura (con la misma API que la sugerencia de subprocesos) y parece funcionar bien (según las sugerencias de este hilo)
fuente
timeout
. Es mucho mejor establecer el valor predeterminadoNone
y, en la primera línea de la función, agregarkwargs = kwargs or {}
. Args está bien porque las tuplas no son mutables.Me encontré con este hilo al buscar una llamada de tiempo de espera en las pruebas unitarias. No encontré nada simple en las respuestas o los paquetes de terceros, así que escribí el decorador a continuación, puede colocarlo en el código:
Entonces es tan simple como agotar el tiempo de espera de una prueba o cualquier función que desee:
fuente
Exception
dentro de func_wrapper y hacerpool.close()
después de la captura para asegurarse de que el hilo siempre muere después, pase lo que pase . Entonces puedes lanzarTimeoutError
o lo que quieras después. Parece funcionar para mi.RuntimeError: can't start new thread
. ¿Funcionará si lo ignoro o hay algo más que pueda hacer para solucionar esto? ¡Gracias por adelantado!El
stopit
paquete, que se encuentra en pypi, parece manejar bien los tiempos de espera.Me gusta el
@stopit.threading_timeoutable
decorador, que agrega untimeout
parámetro a la función decorada, que hace lo que espera, detiene la función.Compruébalo en pypi: https://pypi.python.org/pypi/stopit
fuente
Hay muchas sugerencias, pero ninguna con futuros concurrentes, que creo que es la forma más legible de manejar esto.
Súper simple de leer y mantener.
Hacemos un grupo, enviamos un solo proceso y luego esperamos hasta 5 segundos antes de generar un TimeoutError que pueda detectar y manejar según lo necesite.
Originario de python 3.2+ y con respaldo de 2.7 (futuros de instalación de pip).
Cambiar entre hilos y procesos es tan simple como reemplazarlo
ProcessPoolExecutor
porThreadPoolExecutor
.Si desea finalizar el proceso en el tiempo de espera, le sugiero que busque en Pebble .
fuente
Excelente, fácil de usar y confiable decorador de tiempo de espera del proyecto PyPi ( https://pypi.org/project/timeout-decorator/ )
instalación :
Uso :
fuente
Soy el autor de wrapt_timeout_decorator
La mayoría de las soluciones presentadas aquí funcionan maravillosamente bajo Linux a primera vista, porque tenemos fork () y señales (), pero en Windows las cosas se ven un poco diferentes. Y cuando se trata de subprocesos en Linux, ya no puede usar señales.
Para generar un proceso en Windows, debe ser seleccionable, y muchas funciones decoradas o métodos de clase no lo son.
Por lo tanto, debe usar un mejor selector como eneldo y multiproceso (no encurtido y multiprocesamiento), es por eso que no puede usar ProcessPoolExecutor (o solo con funcionalidad limitada).
Para el tiempo de espera en sí mismo: debe definir qué significa el tiempo de espera, porque en Windows tomará un tiempo considerable (y no determinable) para generar el proceso. Esto puede ser complicado en tiempos de espera cortos. Supongamos que generar el proceso lleva aproximadamente 0,5 segundos (¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡!!! Si le das un tiempo de espera de 0.2 segundos, ¿qué debería pasar? ¿Debe la función agotar el tiempo de espera después de 0.5 + 0.2 segundos (así que deje que el método se ejecute durante 0.2 segundos)? ¿O debería agotar el tiempo del proceso llamado después de 0.2 segundos (en ese caso, la función decorada SIEMPRE expira, porque en ese tiempo ni siquiera se genera)?
También los decoradores anidados pueden ser desagradables y no se pueden usar señales en un subproceso. Si desea crear un decorador multiplataforma verdaderamente universal, todo esto debe tenerse en cuenta (y probarse).
Otros problemas son pasar excepciones a la persona que llama, así como problemas de registro (si se usa en la función decorada, el registro a archivos en otro proceso NO es compatible)
Traté de cubrir todos los casos extremos. Puede consultar el paquete wrapt_timeout_decorator, o al menos probar sus propias soluciones inspiradas en las pruebas unitarias utilizadas allí.
@Alexis Eggermont - desafortunadamente no tengo suficientes puntos para comentar - tal vez alguien más pueda notificarte - Creo que resolví tu problema de importación.
fuente
timeout-decorator
no funciona en el sistema de Windows, ya que Windows no era compatiblesignal
.Si usa timeout-decorator en el sistema de Windows obtendrá lo siguiente
Algunos sugirieron usar
use_signals=False
pero no funcionaron para mí.El autor @bitranox creó el siguiente paquete:
Código de muestra:
Da la siguiente excepción:
fuente
from wrapt_timeout_decorator import *
parece matar algunas de mis otras importaciones. Por ejemplo, obtengoModuleNotFoundError: No module named 'google.appengine'
, pero no obtengo este error si no importo wrapt_timeout_decoratorPodemos usar señales para lo mismo. Creo que el siguiente ejemplo te será útil. Es muy simple en comparación con los hilos.
fuente
try: ... except: ...
siempre es una mala idea.fuente
Necesitaba interrupciones temporizadas anidables (que SIGALARM no puede hacer) que no se bloqueen con time.sleep (que el enfoque basado en hilos no puede hacer). Terminé copiando y modificando ligeramente el código desde aquí: http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/
El código en sí:
y un ejemplo de uso:
fuente
Aquí hay una ligera mejora en la solución basada en hilos dada.
El siguiente código admite excepciones :
Invocándolo con un tiempo de espera de 5 segundos:
fuente
runFunctionCatchExceptions()
ciertas funciones de Python se llamara obtener GIL. Por ejemplo, lo siguiente sería nunca o por mucho tiempo, de vuelta si llama dentro de la función:eval(2**9999999999**9999999999)
. Ver stackoverflow.com/questions/22138190/…