Función de tiempo de espera si tarda demasiado en finalizar [duplicar]
Tengo un script de shell que recorre un archivo de texto que contiene URL que quiero visitar y tomar capturas de pantalla.
Todo esto está hecho y simple. El script inicializa una clase que, cuando se ejecuta, crea una captura de pantalla de cada sitio de la lista. Algunos sitios tardan muchísimo en cargarse y es posible que otros no se carguen en absoluto. Así que quiero envolver la función de captura de pantalla en un script de tiempo de espera, haciendo que la función regrese False
si no puede finalizar en 10 segundos.
Estoy contento con la solución más simple posible, ¿tal vez configurar un temporizador asincrónico que devolverá Falso después de 10 segundos sin importar lo que realmente suceda dentro de la función?
El proceso para cronometrar una operación se describe en la documentación de signal .
La idea básica es utilizar controladores de señales para configurar una alarma durante un intervalo de tiempo y generar una excepción una vez que expire el temporizador.
Tenga en cuenta que esto sólo funcionará en UNIX.
Aquí hay una implementación que crea un decorador (guarde el siguiente código como timeout.py
).
import errno
import os
import signal
import functools
class TimeoutError(Exception):
pass
def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
def decorator(func):
def _handle_timeout(signum, frame):
raise TimeoutError(error_message)
@functools.wraps(func)
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(seconds)
try:
result = func(*args, **kwargs)
finally:
signal.alarm(0)
return result
return wrapper
return decorator
Esto crea un decorador llamado @timeout
que se puede aplicar a cualquier función de ejecución prolongada.
Entonces, en el código de tu aplicación, puedes usar el decorador de esta manera:
from timeout import timeout
# Timeout a long running function with the default expiry of 10 seconds.
@timeout
def long_running_function1():
...
# Timeout after 5 seconds
@timeout(5)
def long_running_function2():
...
# Timeout after 30 seconds, with the error "Connection timed out"
@timeout(30, os.strerror(errno.ETIMEDOUT))
def long_running_function3():
...
Reescribí la respuesta de David usando la with
declaración, te permite hacer esto:
with timeout(seconds=3):
time.sleep(4)
Lo que generará un TimeoutError.
El código todavía se usa signal
y, por lo tanto, solo UNIX:
import signal
class timeout:
def __init__(self, seconds=1, error_message='Timeout'):
self.seconds = seconds
self.error_message = error_message
def handle_timeout(self, signum, frame):
raise TimeoutError(self.error_message)
def __enter__(self):
signal.signal(signal.SIGALRM, self.handle_timeout)
signal.alarm(self.seconds)
def __exit__(self, type, value, traceback):
signal.alarm(0)