Requests: les exceptions max-retries-exceeded sont déroutantes

CrĂ©Ă© le 15 fĂ©vr. 2013  Â·  39Commentaires  Â·  Source: psf/requests

salut,
par exemple:

>>> 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)

(en supposant que rien n'Ă©coute sur le port 1111)

l'exception indique "Nombre maximum de tentatives dĂ©passĂ©". J'ai trouvĂ© cela dĂ©routant car je n'ai spĂ©cifiĂ© aucun paramĂštre liĂ© aux nouvelles tentatives. en fait, je ne trouve aucune documentation sur la spĂ©cification du nombre de tentatives. aprĂšs avoir parcouru le code, il semble que urllib3 soit le transport sous-jacent et qu'il soit appelĂ© avec max_retries=0 (donc, en fait, il n'y a pas de tentatives). et les requĂȘtes enveloppent simplement l'exception. c'est donc comprĂ©hensible, mais cela confond l'utilisateur final (dĂ©veloppeur final) ? Je pense que quelque chose de mieux devrait ĂȘtre fait ici, d'autant plus qu'il est trĂšs facile d'obtenir cette erreur.

Feature Request

Commentaire le plus utile

Je suis d'accord, c'est assez dĂ©routant. Les requĂȘtes ne rĂ©essayent jamais (il dĂ©finit les tentatives = 0 pour HTTPConnectionPool d'urllib3), donc l'erreur serait beaucoup plus Ă©vidente sans les Ă©lĂ©ments HTTPConnectionPool/MaxRetryError. Je n'avais pas rĂ©alisĂ© que les requĂȘtes utilisaient urllib3 jusqu'Ă  maintenant, lorsque j'ai dĂ» me plonger dans le code source des deux bibliothĂšques pour m'aider Ă  dĂ©terminer le nombre de tentatives effectuĂ©es :

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)",),)

Idéalement, l'exception ressemblerait à quelque chose comme ceci :

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

Tous les 39 commentaires

Les requĂȘtes enveloppent l'exception pour la commoditĂ© des utilisateurs. L'exception d'origine fait partie du message bien que le Traceback soit trompeur. Je vais rĂ©flĂ©chir Ă  comment amĂ©liorer ça.

Je pense qu'il faut une nouvelle tentative automatique pour ignorer quelques erreurs

Je suis d'accord, c'est assez dĂ©routant. Les requĂȘtes ne rĂ©essayent jamais (il dĂ©finit les tentatives = 0 pour HTTPConnectionPool d'urllib3), donc l'erreur serait beaucoup plus Ă©vidente sans les Ă©lĂ©ments HTTPConnectionPool/MaxRetryError. Je n'avais pas rĂ©alisĂ© que les requĂȘtes utilisaient urllib3 jusqu'Ă  maintenant, lorsque j'ai dĂ» me plonger dans le code source des deux bibliothĂšques pour m'aider Ă  dĂ©terminer le nombre de tentatives effectuĂ©es :

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)",),)

Idéalement, l'exception ressemblerait à quelque chose comme ceci :

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

Ce serait l'idéal. Le problÚme est d'envelopper ces exceptions comme nous le faisons. Ils constituent une excellente API mais une mauvaise expérience de débogage. J'ai une idée sur la façon de le réparer et de conserver toutes les informations

Nous devons Ă©galement considĂ©rer le cas oĂč un utilisateur _fait_ une nouvelle tentative de configuration, auquel cas cette exception est appropriĂ©e.

@Lukasa , je ne suis pas sûr que vous ayez besoin d'en tenir compte - Kenneth a déclaré ici que les demandes ne devraient pas explicitement prendre en charge les nouvelles tentatives dans le cadre de son API.

D'accord, mais il n'y a aucun moyen d'empĂȘcher un utilisateur de le faire.

Mon plan, pour mémoire, est de descendre aussi loin que possible jusqu'à l'exception de niveau le plus bas et de l'utiliser à la place. Le problÚme avec l'exemple de @benhoyt est qu'il semble que l'exception d'erreur de socket ne nous soit pas disponible. (Juste en regardant ce qu'il a collé. Je n'ai pas encore essayé de le reproduire et de jouer avec.)

L'exemple de @gabor rend cela facile à reproduire. Attrapant l'exception qui est levée, j'ai fait ce qui suit:

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

Donc, le mieux que nous puissions faire est d'utiliser uniquement le message stockĂ© dans e.args[0].args[0] ce qui pourrait Ă©galement ĂȘtre dĂ©routant, mais probablement moins que ce que @benhoyt a rencontrĂ©. Quoi qu'il en soit, nous n'analyserons pas les messages d'erreur pour essayer d'obtenir plus ou moins de dĂ©tails, car ce serait tout simplement de la folie.

@ sigmavirus24 , je suis d'accord que l'analyse de chaßne dans les exceptions est une idée terrible. Cependant, MaxRetryError d'urllib3 expose déjà un attribut reason qui contient l'exception sous-jacente (voir le code source ). Vous pouvez donc obtenir ce que vous voulez avec e.args[0].reason .

Donc en continuant avec l'exemple ci-dessus, e.args[0].reason est une instance 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')

Belle prise @benhoyt. Je ne suis pas aussi familier avec urllib3 que j'aimerais l'ĂȘtre.

Si cela ressemble vraiment à ce que vous avez montré, c'est-à-dire.
requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=1111): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 61] Connection refused)

alors je ne pouvais pas rĂȘver meilleure exception, vraiment.

@piotr-dobrogost, le principal problÚme (pour moi) était le fait qu'il parle de "max tentatives dépassées", alors qu'il n'y a aucune nouvelle tentative impliquée. Au début, je pensais que c'était le service Web que j'utilisais en disant cela, alors je les ai contactés. Puis, en creusant plus loin, j'ai découvert qu'il s'agissait d'une bizarrerie urllib3. Vous pouvez donc voir la confusion.

Avez-vous manqué (Caused by <class 'socket.error'>: [Errno 61] Connection refused) partie de l'exception ?

Oui, tu as raison, tout est là. Mais comme je l'ai mentionné, cela m'a manqué au début, car la MaxRetryError est un faux-fuyant.

Cette histoire de tentatives maximales me rend toujours folle. Est-ce que quelqu'un s'inquiÚte si je plonge et vois si je ne peux pas mettre en place un PR pour écraser le message de nouvelles tentatives ?

Je ne veux pas apparaĂźtre de nulle part, mais j'utilise des tonnes de requĂȘtes dans le travail Python que nous effectuons chez Cloudant. Nous obtenons des pages qui incluent les nouvelles tentatives, et cela peut ĂȘtre un faux-fuyant.

La rĂ©ponse est _peut-ĂȘtre_.

Le problĂšme est que, mĂȘme si par dĂ©faut nous n'effectuons aucune nouvelle tentative, vous pouvez configurer les requĂȘtes pour rĂ©essayer automatiquement les requĂȘtes ayant Ă©chouĂ©. Dans ces situations, le MaxRetryError nous avons emballĂ© est tout Ă  fait raisonnable. Si vous pouvez trouver une solution qui laisse le MaxRetryError en place alors qu'il devrait l'ĂȘtre, mais le supprime lorsque vous pouvez garantir qu'aucune nouvelle tentative n'a Ă©tĂ© faite, nous l'Ă©tudierons. =)

@Lukasa merci, je me rafraßchis sur l'arriéré ici. Si j'ai l'occasion de plonger, je vais certainement tendre la main.

Il me semble presque que le bon endroit pour le changement est dans urllib3? Il est logique de dĂ©clencher MaxRetryError dans le contexte des tentatives automatiques, mais dans le cas de zĂ©ro tentative (peut-ĂȘtre l'expĂ©rience des requĂȘtes naĂŻves), cela peut ĂȘtre dĂ©routant.

Dans urllib3, il semble que les erreurs dĂ©routantes puissent ĂȘtre dĂ©clenchĂ©es ici via des requĂȘtes. Ce serait presque bien de ne lever une MaxRetryError que lorsque retries==0 and max_retries!=0 . Si max_retries==0 lĂšve Ă  la place une simple RequestError a Ă©tĂ© levĂ©e Ă  la place.

Je vois que l'urllib3 tel qu'il est utilisĂ© par les requĂȘtes existe dans un package inclus -- juste curieux, pourquoi est-ce ? Quoi qu'il en soit, ce ne sont que quelques idĂ©es que je voulais lancer lĂ -bas. Je suis toujours en train de rattraper les bases de code.

Que le correctif appartienne ou non Ă  urllib3 dĂ©pend totalement de @shazow. Étant donnĂ© que urllib3 par dĂ©faut _fait_ une nouvelle tentative (3 fois IIRC), il se peut qu'il veuille conserver le comportement d'urllib3 tel quel. Lui faire un ping pour avoir son avis.

Nous vendons urllib3 pour éviter certains problÚmes de dépendance. Essentiellement, cela signifie que nous fonctionnons toujours avec une version connue de urllib3. Cela a été discuté avec une longueur atroce dans les 1384 et 1812 si vous voulez les détails granuleux.

Ouf grave mais instructif. @shazow ce ne sont que quelques réflexions que j'ai eues - lever une RequestError plutÎt que MaxRetryError comme ci-dessus. Vraiment, je pense que je comprends mieux la MaxRetryError aprÚs avoir vérifié urlopen .

Double Ă©dition : Vraiment mĂȘme juste un kwarg donc on peut raise MaxRetryError(retries=0) et modifier le message sur retries==0 .

Que diriez-vous d'un retries=False qui désactiverait complÚtement les nouvelles tentatives et lÚverait toujours l'exception d'origine au lieu de MaxRetryError ?

Être capable de faire la distinction entre demander Ă  urlopen de ne pas rĂ©essayer et le faire compter jusqu'Ă  0 sur le nombre de tentatives serait utile. Il est choquant de voir le MaxRetryError lorsque vous n'avez pas demandĂ© de nouvelles tentatives.

Si quelqu'un souhaite faire un patch + test pour cela, ce serait apprécié. :)

@shazow super, je serais

\O/

^Je me demandais s'il y avait un patch publié ? Ce problÚme semble avoir un an.

Pas Ă  ma connaissance. =)

retries=False devrait lever l'exception d'origine Ă  partir de la v1.9, pas de wrapping.

pensées de @kevinburke ?

Besoin d'un peu plus de temps

Kevin Burke
téléphone : 925.271.7005 | vingtmillisecondes.com

Le dimanche 5 octobre 2014 Ă  10h37, Ian Cordasco [email protected]
a Ă©crit:

@kevinburke https://github.com/kevinburke pensées?

-
RĂ©pondez directement Ă  cet e-mail ou consultez-le sur GitHub
https://github.com/kennethreitz/requests/issues/1198#issuecomment -57945403
.

Oui c'est corrigé je pense

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'))

Pourriez-vous s'il vous plaßt me dire comment cela a été résolu, car je reçois aussi un problÚme de connexion refusée de mon cÎté. Dans mon script python, j'essaie de connecter le serveur RPC

@SiddheshS Ce problÚme a été résolu en reformulant certaines exceptions : cela n'a rien à voir avec l'erreur réelle de refus de connexion. Pour demander de l'aide sur un problÚme, vous devriez envisager d'utiliser Stack Overflow .

J'ai rencontrĂ© le mĂȘme problĂšme . cela arrivait occasionnellement. comment y remĂ©dier, est-ce que quelqu'un peut m'aider ? .Merci.

request.exceptions.ConnectionError : HTTPSConnectionPool(host='api.xxxx.com', port=443) : Nombre maximal de tentatives dĂ©passĂ© avec l'URL : /v2/goods/?category=0&sort_type=2&page_size=3&page_num=13&t=0&count=110 (causĂ© par NewConnectionError(': Échec de l'Ă©tablissement d'une nouvelle connexion : [Errno 110] La connexion a expirĂ©',))
Traceback (appel le plus récent en dernier) :
Fichier "test.py", ligne 335, dans
principale()
Fichier "test.py", ligne 290, dans main
résultat = get_goods_info()
Fichier "test.py", ligne 67, dans get_goods_info
résultat = request.get(url)
Fichier "/usr/local/lib/python2.7/site-packages/requests/api.py", ligne 69, dans get
return request('get', url, params=params, *_kwargs)
Fichier "/usr/local/lib/python2.7/site-packages/requests/api.py", ligne 50, dans la requĂȘte
réponse = session.request(method=method, url=url, *_kwargs)
Fichier "/usr/local/lib/python2.7/site-packages/requests/sessions.py", ligne 468, dans request
resp = self.send(prep, *_send_kwargs)
Fichier "/usr/local/lib/python2.7/site-packages/requests/sessions.py", ligne 576, en envoi
r = adapter.send(requĂȘte, *_kwargs)
Fichier "/usr/local/lib/python2.7/site-packages/requests/adapters.py", ligne 423, en envoi
augmenter ConnectionError(e, request=request)

@nkjulia La tentative de connexion

Je me suis aussi trompé par cela....

@kevinburke comment votre problÚme a-t-il été résolu aprÚs avoir obtenu une erreur de connexion refusée ? Pourrais-tu s'il te plait conseiller mon pote. AIT

Ignorer mon pote de poste. J'avais plusieurs versions de pythons dans ma machine, car elle n'Ă©tait pas en mesure de choisir la bonne et lançait une erreur. Publier ce message en pensant que cela peut ĂȘtre utile Ă  quelqu'un.

J'ai rencontrĂ© le mĂȘme problĂšme . cela arrivait occasionnellement. comment y remĂ©dier, est-ce que quelqu'un peut m'aider ? .Merci.

request.exceptions.ConnectionError : HTTPSConnectionPool(host='api.xxxx.com', port=443) : Nombre maximal de tentatives dĂ©passĂ© avec l'URL : /v2/goods/?category=0&sort_type=2&page_size=3&page_num=13&t=0&count=110 (causĂ© par NewConnectionError(': Échec de l'Ă©tablissement d'une nouvelle connexion : [Errno 110] La connexion a expirĂ©',))
Traceback (appel le plus récent en dernier) :
Fichier "test.py", ligne 335, dans
principale()
Fichier "test.py", ligne 290, dans main
résultat = get_goods_info()
Fichier "test.py", ligne 67, dans get_goods_info
résultat = request.get(url)
Fichier "/usr/local/lib/python2.7/site-packages/requests/api.py", ligne 69, dans get
return request('get', url, params=params, *_kwargs)
Fichier "/usr/local/lib/python2.7/site-packages/requests/api.py", ligne 50, dans la requĂȘte
réponse = session.request(method=method, url=url, *_kwargs)
Fichier "/usr/local/lib/python2.7/site-packages/requests/sessions.py", ligne 468, dans request
resp = self.send(prep, *_send_kwargs)
Fichier "/usr/local/lib/python2.7/site-packages/requests/sessions.py", ligne 576, en envoi
r = adapter.send(requĂȘte, *_kwargs)
Fichier "/usr/local/lib/python2.7/site-packages/requests/adapters.py", ligne 423, en envoi
augmenter ConnectionError(e, request=request)

Si ce problÚme a été résolu, merci de me donner quelques conseils.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

Matt3o12 picture Matt3o12  Â·  3Commentaires

remram44 picture remram44  Â·  4Commentaires

justlurking picture justlurking  Â·  3Commentaires

8key picture 8key  Â·  3Commentaires

avinassh picture avinassh  Â·  4Commentaires