2012-02-25 18 views
26

Cuando llega el tiempo de espera urllib2.request, se produce una excepción de urllib2.URLError. ¿Cuál es la manera pitónica de volver a intentar establecer una conexión?Cómo reintentar urllib2.request cuando falla?

+1

Esta pregunta debería responder la tuya: http://stackoverflow.com/questions/2712524/handling-urllib2s-timeout-python –

+2

No pregunté cómo atrapar la expection. Quería saber si hay una forma pitónica de intentar establecer la conexión. – iTayb

+0

Lo siento, asumí que el problema estaba en detectar que se había alcanzado el tiempo de espera, no en restablecer la conexión. ¿No podría llamar a urlopen() en el bloque de excepción? –

Respuesta

52

Yo usaría un decorador retry. Hay otros por ahí, pero este funciona bastante bien. He aquí cómo usted puede utilizarlo:

@retry(urllib2.URLError, tries=4, delay=3, backoff=2) 
def urlopen_with_retry(): 
    return urllib2.urlopen("http://example.com") 

Esto volverá a intentar la función si se eleva URLError. Consulte el enlace de arriba para la documentación de los parámetros, pero básicamente lo intentará de nuevo un máximo de 4 veces, con un retardo de retroceso exponencial que se duplica cada vez, p. 3 segundos, 6 segundos, 12 segundos.

+1

Este es un fragmento realmente genial. ¿Conoces una alternativa, pero como administrador de contexto? –

+0

Hmm, creo que probablemente podría volver a escribirlo como un administrador de contexto con bastante facilidad, pero no tengo uno de manera informal. – jterrace

+0

No es fácil de hacer, ya que no hay una manera fácil de capturar el bloque dentro de la instrucción with. Necesitas una profunda introspección. –

3

para reintentar el tiempo de espera se puede coger la excepción como @Karl Barker suggested in the comment:

assert ntries >= 1 
for _ in range(ntries): 
    try: 
     page = urlopen(request, timeout=timeout) 
     break # success 
    except URLError as err: 
     if not isinstance(err.reason, socket.timeout): 
      raise # propagate non-timeout errors 
else: # all ntries failed 
    raise err # re-raise the last timeout error 
# use page here 
4

Hay algunas bibliotecas por ahí que se especializan en esto.

Uno es backoff, que está diseñado con una sensibilidad particularmente funcional. Los decoradores reciben callables arbitrarios que devuelven generadores que producen valores de retardo sucesivos. Un retroceso exponencial simple con un tiempo máximo de reintentos de 32 segundos se podría definir como:

@backoff.on_exception(backoff.expo, 
         urllib2.URLError, 
         max_value=32) 
def url_open(url): 
    return urllib2.urlopen("http://example.com") 

Otra es retrying que tiene una funcionalidad muy similar pero una API donde reintento parámetros se especifican a modo de args de palabras clave predefinidas.

Cuestiones relacionadas