Requests: Las excepciones de max-retries-excedidos son confusas

Creado en 15 feb. 2013  ·  39Comentarios  ·  Fuente: psf/requests

Hola,
por ejemplo:

>>> requests.get('http://localhost:1111')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "requests/api.py", line 55, in get
    return request('get', url, **kwargs)
  File "requests/api.py", line 44, in request
    return session.request(method=method, url=url, **kwargs)
  File "requests/sessions.py", line 312, in request
    resp = self.send(prep, **send_kwargs)
  File "requests/sessions.py", line 413, in send
    r = adapter.send(request, **kwargs)
  File "requests/adapters.py", line 223, in send
    raise ConnectionError(e)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=1111): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 61] Connection refused)

(asumiendo que nada está escuchando en el puerto 1111)

la excepción dice "Se superó el número máximo de reintentos". Encontré esto confuso porque no especifiqué ningún parámetro relacionado con reintentos. de hecho, no puedo encontrar ninguna documentación sobre cómo especificar el recuento de reintentos. después de revisar el código, parece que urllib3 es el transporte subyacente, y se llama con max_retries = 0 (por lo que, de hecho, no hay reintentos). y las solicitudes simplemente envuelven la excepción. por lo que es comprensible, pero confunde al usuario final (desarrollador final)? Creo que se debería hacer algo mejor aquí, especialmente teniendo en cuenta que es muy fácil obtener este error.

Feature Request

Comentario más útil

Estoy de acuerdo en que esto es bastante confuso. Las solicitudes nunca reintentos (establece los reintentos = 0 para HTTPConnectionPool de urllib3), por lo que el error sería mucho más obvio sin las cosas HTTPConnectionPool / MaxRetryError. No me di cuenta de que las solicitudes usaban urllib3 hasta ahora, cuando tuve que sumergirme en el código fuente de ambas bibliotecas para ayudarme a averiguar cuántos reintentos estaba haciendo:

ConnectionError(MaxRetryError("HTTPSConnectionPool(host='api.venere.com', port=443): \
    Max retries exceeded with url: /xhi-1.0/services/XHI_HotelAvail.json (\
    Caused by <class 'socket.error'>: [Errno 10054] \
    An existing connection was forcibly closed by the remote host)",),)

Idealmente, la excepción se vería así:

ConnectionError(<class 'socket.error'>: [Errno 10054] \
    An existing connection was forcibly closed by the remote host))

Todos 39 comentarios

Requests envuelve la excepción para la conveniencia de los usuarios. La excepción original es parte del mensaje, aunque Traceback es engañoso. Pensaré en cómo mejorar esto.

Creo que necesito un reintento automático para ignorar algunos errores

Estoy de acuerdo en que esto es bastante confuso. Las solicitudes nunca reintentos (establece los reintentos = 0 para HTTPConnectionPool de urllib3), por lo que el error sería mucho más obvio sin las cosas HTTPConnectionPool / MaxRetryError. No me di cuenta de que las solicitudes usaban urllib3 hasta ahora, cuando tuve que sumergirme en el código fuente de ambas bibliotecas para ayudarme a averiguar cuántos reintentos estaba haciendo:

ConnectionError(MaxRetryError("HTTPSConnectionPool(host='api.venere.com', port=443): \
    Max retries exceeded with url: /xhi-1.0/services/XHI_HotelAvail.json (\
    Caused by <class 'socket.error'>: [Errno 10054] \
    An existing connection was forcibly closed by the remote host)",),)

Idealmente, la excepción se vería así:

ConnectionError(<class 'socket.error'>: [Errno 10054] \
    An existing connection was forcibly closed by the remote host))

Eso sería ideal. El problema es envolver estas excepciones como lo hacemos nosotros. Hacen una gran API pero una mala experiencia de depuración. Sin embargo, tengo una idea de cómo solucionarlo y conservar toda la información.

También deberíamos considerar el caso en el que un usuario _configura_ reintentos, en cuyo caso esta excepción es apropiada.

@Lukasa , no estoy seguro de que aquí que las solicitudes no deberían admitir explícitamente los reintentos como parte de su API.

Correcto, pero no hay forma de evitar que un usuario lo haga realmente.

Mi plan, para que conste, es recorrer lo más abajo posible hasta la excepción de nivel más bajo y usarla en su lugar. El problema con el ejemplo de @benhoyt es que parece que la excepción de error de socket no está disponible para nosotros. (Con solo mirar lo que ha pegado. Todavía no he intentado reproducirlo y jugar con él).

El ejemplo de @gabor realmente hace que esto sea fácil de reproducir. Al detectar la excepción que se generó, hice lo siguiente:

>>> e
ConnectionError(MaxRetryError("HTTPConnectionPool(host='localhost', port=1111): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 111] Connection refused)",),)
>>> e.args
(MaxRetryError("HTTPConnectionPool(host='localhost', port=1111): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 111] Connection refused)",),)
>>> e.args[0].args
("HTTPConnectionPool(host='localhost', port=1111): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 111] Connection refused)",)
>>> e.args[0].args[0]
"HTTPConnectionPool(host='localhost', port=1111): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 111] Connection refused)"
>>> isinstance(e.args[0].args[0], str)
True

Entonces, lo mejor que pudimos hacer es usar solo el mensaje almacenado en e.args[0].args[0] que también podría ser confuso, pero probablemente menos de lo que encontró @benhoyt . De cualquier manera, no analizaremos los mensajes de error para tratar de obtener más o menos detalles porque eso sería una locura total.

@ sigmavirus24 , estoy de acuerdo en que el análisis de cadenas en excepciones es una idea terrible. Sin embargo, MaxRetryError de urllib3 ya expone un atributo reason que contiene la excepción subyacente (ver código fuente ). Para que pueda obtener lo que desea con e.args[0].reason .

Entonces, continuando con el ejemplo anterior, e.args[0].reason es una instancia de socket.error :

>>> requests.get('http://localhost:1111')
Traceback (most recent call last):
  ...
requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=1111): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 10061] No connection could be made because the target machine actively refused it)
>>> e = sys.last_value
>>> e
ConnectionError(MaxRetryError("HTTPConnectionPool(host='localhost', port=1111): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 10061] No connection could be made because the target machine actively refused it)",),)
>>> e.args[0]
MaxRetryError("HTTPConnectionPool(host='localhost', port=1111): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 10061] No connection could be made because the target machine actively refused it)",)
>>> e.args[0].reason
error(10061, 'No connection could be made because the target machine actively refused it')

Buen partido @benhoyt. No estoy tan familiarizado con urllib3 como me gustaría.

Si realmente se ve como lo mostró, es decir.
requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=1111): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 61] Connection refused)

entonces no podría soñar con una mejor excepción, de verdad.

@ piotr-dobrogost, el principal problema (para mí) fue el hecho de que habla de "reintentos máximos excedidos", cuando no hay reintentos involucrados en absoluto. Al principio pensé que era el servicio web que estaba usando diciendo eso, así que los contacté. Luego, investigando más, descubrí que se trataba de una peculiaridad de urllib3. Entonces puedes ver la confusión.

¿Ha perdido (Caused by <class 'socket.error'>: [Errno 61] Connection refused) parte de la excepción?

Sí, tienes razón, está todo ahí. Pero como mencioné, me perdí eso al principio, porque MaxRetryError es una pista falsa.

Esta cosa de reintentos máximos siempre me vuelve loco. ¿A alguien le importa si me sumerjo y veo si no puedo armar un PR para aplastar el mensaje de reintentos?

No es mi intención aparecer de la nada, pero uso toneladas de solicitudes en el trabajo de Python que hacemos en Cloudant. Obtenemos páginas que incluyen lo de reintentos, y puede ser una pista falsa.

La respuesta es _quizás_.

El problema es que, aunque de forma predeterminada no realizamos ningún reintento, puede configurar Solicitudes para reintentar automáticamente solicitudes fallidas. En esas situaciones, el MaxRetryError que hemos envuelto es totalmente razonable. Si puede encontrar una solución que deje el MaxRetryError en su lugar cuando debería estarlo, pero lo elimine cuando pueda garantizar que no se han realizado reintentos, lo consideraremos. =)

@Lukasa gracias, me estoy actualizando sobre el trabajo pendiente aquí. Si tengo la oportunidad de sumergirme, definitivamente me acercaré.

Casi me parece que el lugar correcto para el cambio es urllib3. MaxRetryError tiene sentido generar en el contexto de reintentos automáticos, pero en el caso de cero reintentos (quizás la experiencia de solicitudes ingenuas) puede ser confuso.

En urllib3, parece que los errores confusos se pueden activar aquí a través de solicitudes. Casi sería bueno generar un MaxRetryError solo cuando retries==0 and max_retries!=0 . Si max_retries==0 lugar, se generó un RequestError simple en

Veo que urllib3, tal como lo usan las solicitudes, existe en un paquete incluido, solo por curiosidad, ¿por qué? De todos modos, estas eran solo algunas ideas que quería lanzar. Todavía me estoy poniendo al día con las bases de código.

Si la solución pertenece o no a urllib3 depende totalmente de @shazow. Dado que urllib3 por defecto _does_ reintenta (3 veces IIRC), puede ser que quiera mantener el comportamiento de urllib3 como está. Haciendo ping para obtener su opinión.

Vendemos urllib3 para evitar algunos problemas de dependencia. Básicamente, significa que siempre estamos operando contra una versión conocida de urllib3. Esto se ha discutido con una extensión insoportable en el n. ° 1384 y n. ° 1812 si desea los detalles crudos.

Uf valiente pero informativo. @shazow, estos son solo algunos de los pensamientos que tuve: generar un RequestError en lugar de MaxRetryError como se indicó anteriormente. Realmente creo que entiendo mejor el MaxRetryError después de revisar urlopen .

Edición doble: Realmente incluso solo un kwarg para que uno pueda raise MaxRetryError(retries=0) y alterar el mensaje en retries==0 .

¿Qué tal un retries=False que deshabilitaría los reintentos por completo y siempre generaría la excepción original en lugar de MaxRetryError ?

Sería útil poder distinguir entre pedir a urlopen que no haya reintentos y hacer que cuente hacia atrás hasta 0 en el número de reintentos. Es discordante ver el MaxRetryError cuando no solicitó reintentos.

Si alguien quisiera hacer una prueba de parche + para esto, se lo agradecería. :)

@shazow genial, estaría

\ O /

^ Me preguntaba si se lanzó algún parche. Este problema parece tener un año.

No que yo sepa. =)

retries=False debería generar la excepción original a partir de v1.9, sin envoltura.

@kevinburke pensamientos?

Necesito un poco mas de tiempo

Kevin Burke
teléfono: 925.271.7005 | twentymilliseconds.com

El domingo, 5 de octubre de 2014 a las 10:37 a.m., Ian Cordasco [email protected]
escribió:

@kevinburke https://github.com/kevinburke ¿pensamientos?

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/kennethreitz/requests/issues/1198#issuecomment -57945403
.

Sí, esto ha sido arreglado, creo

requests.get('http://localhost:11211')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "requests/api.py", line 60, in get
    return request('get', url, **kwargs)
  File "requests/api.py", line 49, in request
    return session.request(method=method, url=url, **kwargs)
  File "requests/sessions.py", line 457, in request
    resp = self.send(prep, **send_kwargs)
  File "requests/sessions.py", line 569, in send
    r = adapter.send(request, **kwargs)
  File "requests/adapters.py", line 407, in send
    raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', error(61, 'Connection refused'))

¿Podría decirme cómo se ha resuelto esto, ya que también estoy recibiendo un problema de conexión rechazada al final? En mi secuencia de comandos de Python, estoy tratando de conectar el servidor RPC

@SiddheshS Este problema se solucionó reformulando algunas excepciones: no tiene nada que ver con el error de conexión rechazada real. Para pedir ayuda con un problema, debería considerar usar Stack Overflow .

Encontré el mismo problema. sucedió de vez en cuando. cómo solucionarlo, ¿hay alguien que pueda ayudarme? .Gracias.

request.exceptions.ConnectionError: HTTPSConnectionPool (host = 'api.xxxx.com', port = 443): se superó el número máximo de reintentos con url: / v2 / goods /? category = 0 & sort_type = 2 & page_size = 3 & page_num = 13 & t = 0 & count = 110 (Causado por NewConnectionError (': No se pudo establecer una nueva conexión: [Errno 110] Se agotó el tiempo de espera de la conexión ',))
Rastreo (llamadas recientes más última):
Archivo "test.py", línea 335, en
principal()
Archivo "test.py", línea 290, en main
resultado = get_goods_info ()
Archivo "test.py", línea 67, en get_goods_info
resultado = solicitudes.get (url)
Archivo "/usr/local/lib/python2.7/site-packages/requests/api.py", línea 69, en get
solicitud de devolución ('get', url, params = params, * _kwargs)
Archivo "/usr/local/lib/python2.7/site-packages/requests/api.py", línea 50, en solicitud
response = session.request (método = método, url = url, * _kwargs)
Archivo "/usr/local/lib/python2.7/site-packages/requests/sessions.py", línea 468, en solicitud
resp = self.send (preparación, * _send_kwargs)
Archivo "/usr/local/lib/python2.7/site-packages/requests/sessions.py", línea 576, en envío
r = adapter.send (solicitud, * _kwargs)
Archivo "/usr/local/lib/python2.7/site-packages/requests/adapters.py", línea 423, en envío
elevar ConnectionError (e, request = request)

@nkjulia Se

También me equivoqué con esto ...

@kevinburke ¿cómo se resolvió su problema después de recibir un error de conexión rechazada? ¿Podrías aconsejarme amigo? TIA

Ignora a mi compañero de puesto. Tenía varias versiones de pitones en mi máquina debido a que no podía elegir la correcta y estaba arrojando un error. Publicar este pensamiento puede ser útil para alguien.

Encontré el mismo problema. sucedió de vez en cuando. cómo solucionarlo, ¿hay alguien que pueda ayudarme? .Gracias.

request.exceptions.ConnectionError: HTTPSConnectionPool (host = 'api.xxxx.com', port = 443): se superó el número máximo de reintentos con url: / v2 / goods /? category = 0 & sort_type = 2 & page_size = 3 & page_num = 13 & t = 0 & count = 110 (Causado por NewConnectionError (': No se pudo establecer una nueva conexión: [Errno 110] Se agotó el tiempo de espera de la conexión ',))
Rastreo (llamadas recientes más última):
Archivo "test.py", línea 335, en
principal()
Archivo "test.py", línea 290, en main
resultado = get_goods_info ()
Archivo "test.py", línea 67, en get_goods_info
resultado = solicitudes.get (url)
Archivo "/usr/local/lib/python2.7/site-packages/requests/api.py", línea 69, en get
solicitud de devolución ('get', url, params = params, * _kwargs)
Archivo "/usr/local/lib/python2.7/site-packages/requests/api.py", línea 50, en solicitud
response = session.request (método = método, url = url, * _kwargs)
Archivo "/usr/local/lib/python2.7/site-packages/requests/sessions.py", línea 468, en solicitud
resp = self.send (preparación, * _send_kwargs)
Archivo "/usr/local/lib/python2.7/site-packages/requests/sessions.py", línea 576, en envío
r = adapter.send (solicitud, * _kwargs)
Archivo "/usr/local/lib/python2.7/site-packages/requests/adapters.py", línea 423, en envío
elevar ConnectionError (e, request = request)

Si este problema se ha resuelto, por favor, avísenme.

¿Fue útil esta página
0 / 5 - 0 calificaciones