Requests: исключения max-retries-exceeded сбивают с толку

Созданный на 15 февр. 2013  ·  39Комментарии  ·  Источник: psf/requests

Привет,
Например:

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

(при условии, что порт 1111 не прослушивается)

исключение говорит: «Превышено максимальное количество попыток». меня это сбивает с толку, потому что я не указал никаких параметров, связанных с повторной попыткой. Фактически, я не могу найти никакой документации об указании числа повторов. после прохождения кода кажется, что urllib3 является базовым транспортом и вызывается с max_retries = 0 (так что на самом деле повторных попыток нет). а запросы просто обертывают исключение. так понятно, а конечного пользователя (конечного разработчика) это смущает? Я думаю, что здесь нужно сделать что-то получше, особенно учитывая, что эту ошибку очень легко получить.

Feature Request

Самый полезный комментарий

Я согласен, это довольно сбивает с толку. Запросы никогда не повторяют попытки (он устанавливает retries = 0 для HTTPConnectionPool urllib3), поэтому ошибка была бы гораздо более очевидной без материала HTTPConnectionPool / MaxRetryError. Я не осознавал, что запросы, использующие urllib3, до сих пор, когда мне пришлось погрузиться в исходный код обеих библиотек, чтобы помочь мне выяснить, сколько повторных попыток он делал:

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

В идеале исключение должно выглядеть примерно так:

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

Все 39 Комментарий

Запросы обертывают исключение для удобства пользователей. Исходное исключение является частью сообщения, хотя Traceback вводит в заблуждение. Я подумаю, как это исправить.

Я думаю, нужна автоматическая повторная попытка, чтобы игнорировать несколько ошибок

Я согласен, это довольно сбивает с толку. Запросы никогда не повторяют попытки (он устанавливает retries = 0 для HTTPConnectionPool urllib3), поэтому ошибка была бы гораздо более очевидной без материала HTTPConnectionPool / MaxRetryError. Я не осознавал, что запросы, использующие urllib3, до сих пор, когда мне пришлось погрузиться в исходный код обеих библиотек, чтобы помочь мне выяснить, сколько повторных попыток он делал:

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

В идеале исключение должно выглядеть примерно так:

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

Это было бы идеально. Проблема заключается в том, чтобы обернуть эти исключения, как это делаем мы. Это отличный API, но плохой опыт отладки. У меня есть идея, как это исправить и сохранить всю информацию

Нам также необходимо рассмотреть случай, когда пользователь _does_ configure повторяет попытку, и в этом случае это исключение является подходящим.

@Lukasa , я не уверен, что вам нужно это учитывать - Кеннет сказал здесь, что запросы явно не должны поддерживать повторные попытки как часть своего API.

Верно, но нет никакого способа помешать пользователю это сделать.

Мой план для протокола состоит в том, чтобы пройти как можно дальше вниз до исключения самого низкого уровня и использовать его вместо этого. Проблема с примером @benhoyt заключается в том, что кажется, что исключение ошибки сокета нам недоступно. (Просто взглянув на то, что он наклеил. Я еще не пытался воспроизвести это и поиграть с этим.)

Пример @gabor позволяет легко воспроизвести это. Поймав возникшее исключение, я сделал следующее:

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

Поэтому лучшее, что мы могли сделать, - это использовать только сообщение, хранящееся в e.args[0].args[0] что тоже потенциально может сбивать с толку, но, вероятно, в меньшей степени, чем то, с чем столкнулся @benhoyt . В любом случае мы не будем анализировать сообщения об ошибках, чтобы попытаться получить больше или меньше деталей, потому что это было бы полным безумием.

@ sigmavirus24 , я согласен, что синтаксический анализ строк в исключениях - ужасная идея. Однако MaxRetryError urllib3 уже предоставляет атрибут reason который содержит базовое исключение (см. Исходный код ). Таким образом, вы можете получить то, что хотите, с помощью e.args[0].reason .

Итак, продолжая пример выше, e.args[0].reason - это экземпляр 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')

Хороший улов @benhoyt. Я не так хорошо знаком с urllib3, как хотелось бы.

Если это действительно выглядит так, как вы показали, т.е.
requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=1111): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 61] Connection refused)

Тогда я действительно не мог мечтать о лучшем исключении.

@ piotr-dobrogost, основная проблема (для меня) заключалась в том, что он говорит о "превышении максимального числа повторных попыток", когда повторных попыток нет вообще. Сначала я подумал, что это веб-сервис, который я использую, говоря это, поэтому я связался с ними. Затем, покопавшись дальше, я обнаружил, что это причуда urllib3. Итак, вы видите путаницу.

Вы пропустили (Caused by <class 'socket.error'>: [Errno 61] Connection refused) часть исключения?

Да, ты прав - там все есть. Но, как я уже упоминал, я сначала пропустил это, потому что MaxRetryError - отвлекающий маневр.

Эта штука с максимальным количеством повторений всегда сводит меня с ума. Кто-нибудь будет возражать, если я нырну и посмотрю, не смогу ли я собрать PR, чтобы подавить сообщение о повторных попытках?

Я не хочу появляться из ниоткуда, но я использую тонны запросов в работе над Python, которую мы выполняем в Cloudant. Мы получаем страницы, на которых есть повторные попытки, и это может быть отвлекающим маневром.

Ответ _maybe_.

Проблема в том, что, хотя по умолчанию мы не выполняем никаких повторных попыток, вы можете настроить запросы для автоматического повторения неудачных запросов. В таких ситуациях обернутое нами MaxRetryError вполне разумно. Если вы можете придумать решение, которое оставляет MaxRetryError на месте, когда оно должно быть, но удаляет его, когда вы можете гарантировать, что не было предпринято никаких повторных попыток, мы его рассмотрим. знак равно

@Lukasa спасибо, я

Мне почти кажется, что правильное место для изменения находится в urllib3? MaxRetryError имеет смысл вызывать в контексте автоматических повторных попыток, но в случае нулевых повторных попыток (возможно, при наивных запросах) это может сбивать с толку.

В urllib3 кажется, что запутанные ошибки могут быть вызваны здесь через запросы. Было бы неплохо вызывать MaxRetryError только тогда, когда retries==0 and max_retries!=0 . Если вместо этого max_retries==0 вызывает простую ошибка .

Я вижу, что urllib3, используемый запросами, существует во включенном пакете - просто любопытно, почему это так? В любом случае, это всего лишь несколько идей, которые я хотел выкинуть. Я все еще разбираюсь в кодовых базах.

Принадлежит ли исправление к urllib3, полностью зависит от @shazow. Учитывая, что urllib3 по умолчанию _does_ retry (3 раза IIRC), возможно, он хочет сохранить поведение urllib3 как есть. Пингуем его, чтобы узнать его мнение.

Мы поставляем urllib3, чтобы избежать некоторых проблем с зависимостями. По сути, это означает, что мы всегда работаем против известной версии urllib3. Это мучительно подробно обсуждалось в №1384 и №1812, если вам нужны подробные детали.

Уф, суровый, но информативный. @shazow, это всего лишь несколько мыслей, которые у меня возникли - вызвать RequestError, а не MaxRetryError, как указано выше. На самом деле я думаю, что лучше понимаю MaxRetryError после проверки urlopen .

Двойное редактирование: На самом деле даже просто kwarg, так что можно raise MaxRetryError(retries=0) и изменить сообщение на retries==0 .

Как насчет retries=False который полностью отключит повторные попытки и всегда будет вызывать исходное исключение вместо MaxRetryError ?

Было бы полезно иметь возможность различать запрос urlopen на отсутствие повторных попыток и обратный отсчет количества повторных попыток до 0. Очень неприятно видеть MaxRetryError, когда вы не запрашивали повторные попытки.

Если кто-то захочет сделать для этого патч + тест, мы будем признательны. :)

@shazow отлично, я буду

\ O /

^ Мне было интересно, был ли выпущен какой-нибудь патч? Этому выпуску, кажется, уже год.

Насколько я знаю, нет. знак равно

retries=False должен вызывать исходное исключение начиная с версии 1.9, без переноса.

@kevinburke мысли?

Нужно еще немного времени

Кевин Берк
телефон: 925.271.7005 | twentymilliseconds.com

В воскресенье, 5 октября 2014 г., в 10:37, Ян Кордаско [email protected]
написал:

@kevinburke https://github.com/kevinburke мысли?

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/kennethreitz/requests/issues/1198#issuecomment -57945403
.

Да, это было исправлено, я думаю

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

Не могли бы вы рассказать мне, как это было решено, так как я тоже получаю отказ в соединении с моей стороны. В моем скрипте python я пытаюсь подключить RPC-сервер

@SiddheshS Эта проблема была исправлена ​​путем изменения формулировки некоторых исключений: это не имеет ничего общего с фактической ошибкой отказа в соединении. Чтобы обратиться за помощью в решении проблемы, вам следует подумать об использовании Stack Overflow .

Я столкнулся с той же проблемой. это случалось изредка. как исправить, может кто-нибудь мне поможет? .Благодарность.

request.exceptions.ConnectionError: HTTPSConnectionPool (host = 'api.xxxx.com', port = 443): Превышено максимальное количество повторных попыток с url: / v2 / goods /? category = 0 & sort_type = 2 & page_size = 3 & page_num = 13 & t = 0 & count = 110 (Вызвано автор: NewConnectionError (': Не удалось установить новое соединение: [Errno 110] Время ожидания соединения истекло ',))
Отслеживание (последний вызов последний):
Файл "test.py", строка 335, в
главный()
Файл "test.py", строка 290, в основном
результат = get_goods_info ()
Файл "test.py", строка 67, в get_goods_info
результат = requests.get (URL)
Файл "/usr/local/lib/python2.7/site-packages/requests/api.py", строка 69, в get
запрос на возврат ('get', url, params = params, * _kwargs)
Файл "/usr/local/lib/python2.7/site-packages/requests/api.py", строка 50, в запросе
response = session.request (метод = метод, url = url, * _kwargs)
Файл "/usr/local/lib/python2.7/site-packages/requests/sessions.py", строка 468, в запросе
resp = self.send (подготовка, * _send_kwargs)
Файл "/usr/local/lib/python2.7/site-packages/requests/sessions.py", строка 576, в отправке
r = adapter.send (запрос, * _kwargs)
Файл "/usr/local/lib/python2.7/site-packages/requests/adapters.py", строка 423, в отправке
поднять ConnectionError (e, request = request)

@nkjulia Время

Я тоже заблуждался на этом ....

@kevinburke, как была решена ваша проблема после получения сообщения об отказе в соединении? Не могли бы вы посоветовать приятелю. TIA

Игнорируйте моего товарища по почте. У меня было несколько версий питонов на моей машине, из-за которых он не мог выбрать правильный и выдавал ошибку. Публикация этой мысли может быть кому-то полезна.

Я столкнулся с той же проблемой. это случалось изредка. как исправить, может кто-нибудь мне поможет? .Благодарность.

request.exceptions.ConnectionError: HTTPSConnectionPool (host = 'api.xxxx.com', port = 443): Превышено максимальное количество повторных попыток с url: / v2 / goods /? category = 0 & sort_type = 2 & page_size = 3 & page_num = 13 & t = 0 & count = 110 (Вызвано автор: NewConnectionError (': Не удалось установить новое соединение: [Errno 110] Время ожидания соединения истекло ',))
Отслеживание (последний вызов последний):
Файл "test.py", строка 335, в
главный()
Файл "test.py", строка 290, в основном
результат = get_goods_info ()
Файл "test.py", строка 67, в get_goods_info
результат = requests.get (URL)
Файл "/usr/local/lib/python2.7/site-packages/requests/api.py", строка 69, в get
запрос на возврат ('get', url, params = params, * _kwargs)
Файл "/usr/local/lib/python2.7/site-packages/requests/api.py", строка 50, в запросе
response = session.request (метод = метод, url = url, * _kwargs)
Файл "/usr/local/lib/python2.7/site-packages/requests/sessions.py", строка 468, в запросе
resp = self.send (подготовка, * _send_kwargs)
Файл "/usr/local/lib/python2.7/site-packages/requests/sessions.py", строка 576, в отправке
r = adapter.send (запрос, * _kwargs)
Файл "/usr/local/lib/python2.7/site-packages/requests/adapters.py", строка 423, в отправке
поднять ConnectionError (e, request = request)

Если проблема решена, дайте мне совет.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги