Función de tiempo de espera si tarda demasiado en finalizar [duplicar]

Resuelto Christoffer asked hace 14 años • 2 respuestas

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 Falsesi 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?

Christoffer avatar Feb 17 '10 22:02 Christoffer
Aceptado

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 @timeoutque 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():
    ...
David Narayan avatar Feb 17 '2010 16:02 David Narayan

Reescribí la respuesta de David usando la withdeclaración, te permite hacer esto:

with timeout(seconds=3):
    time.sleep(4)

Lo que generará un TimeoutError.

El código todavía se usa signaly, 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)
Thomas Ahle avatar Mar 12 '2014 10:03 Thomas Ahle