Вы можете создать подкласс класса Retry
, чтобы добавить эту функцию.
Это полный поток взаимодействия с экземпляром Retry
для данной попытки подключения:
Retry.increment()
вызывается с текущим методом, URL-адресом, объектом ответа (если он есть) и исключением (если оно было вызвано) при каждом возникновении исключения или при возврате 30-кратного ответа о перенаправлении или Retry.is_retry()
метод возвращает истину.
.increment()
повторно выдаст ошибку (если она была), и объект был настроен не повторять этот определенный класс ошибок.
.increment()
вызывает Retry.new()
для создания обновленного экземпляра с обновлением всех соответствующих счетчиков и атрибутом history
с новым экземпляром RequestHistory()
(именованный кортеж).
.increment()
вызовет исключение MaxRetryError
, если Retry.is_exhausted()
вызвано на возвращаемое значение Retry.new()
true. is_exhausted()
возвращает значение true, если любой из отслеживаемых счетчиков упал ниже 0 (счетчики, установленные на None
, игнорируются).
.increment()
возвращает новый экземпляр Retry
.
- возвращаемое значение
Retry.increment()
заменяет старый отслеживаемый экземпляр Retry
. Если был редирект, то вызывается Retry.sleep_for_retry()
(спящий, если был заголовок Retry-After
), в противном случае вызывается Retry.sleep()
(который вызывает self.sleep_for_retry()
для соблюдения заголовка Retry-After
, в противном случае просто спит, если есть политика отсрочки). Затем выполняется рекурсивный вызов соединения с новым экземпляром Retry
.
Это дает вам 3 хороших пункта обратного вызова; в начале .increment()
, при создании нового экземпляра Retry
и в диспетчере контекста около super().increment()
, чтобы позволить обратному вызову наложить вето на исключение или обновить возвращенную политику повторов при выходе.
Вот как выглядит зацепка в начале .increment()
:
import logging
logger = getLogger(__name__)
class CallbackRetry(Retry):
def __init__(self, *args, **kwargs):
self._callback = kwargs.pop('callback', None)
super(CallbackRetry, self).__init__(*args, **kwargs)
def new(self, **kw):
# pass along the subclass additional information when creating
# a new instance.
kw['callback'] = self._callback
return super(CallbackRetry, self).new(**kw)
def increment(self, method, url, *args, **kwargs):
if self._callback:
try:
self._callback(url)
except Exception:
logger.exception('Callback raised an exception, ignoring')
return super(CallbackRetry, self).increment(method, url, *args, **kwargs)
Обратите внимание, что аргумент url
на самом деле представляет собой только URL-путь , чистая часть запроса не указывается (вам придется извлечь его из аргумента _pool
, он имеет * Атрибуты 1061 *, .host
и .port
).
Демо-версия:
>>> def retry_callback(url):
... print('Callback invoked with', url)
...
>>> s = requests.Session()
>>> retries = CallbackRetry(total=5, status_forcelist=[500, 502, 503, 504], callback=retry_callback)
>>> s.mount('http://', HTTPAdapter(max_retries=retries))
>>> s.get('http://httpstat.us/500')
Callback invoked with /500
Callback invoked with /500
Callback invoked with /500
Callback invoked with /500
Callback invoked with /500
Callback invoked with /500
Traceback (most recent call last):
File "/.../lib/python3.6/site-packages/requests/adapters.py", line 440, in send
timeout=timeout
File "/.../lib/python3.6/site-packages/urllib3/connectionpool.py", line 732, in urlopen
body_pos=body_pos, **response_kw)
File "/.../lib/python3.6/site-packages/urllib3/connectionpool.py", line 732, in urlopen
body_pos=body_pos, **response_kw)
File "/.../lib/python3.6/site-packages/urllib3/connectionpool.py", line 732, in urlopen
body_pos=body_pos, **response_kw)
[Previous line repeated 1 more times]
File "/.../lib/python3.6/site-packages/urllib3/connectionpool.py", line 712, in urlopen
retries = retries.increment(method, url, response=response, _pool=self)
File "<stdin>", line 8, in increment
File "/.../lib/python3.6/site-packages/urllib3/util/retry.py", line 388, in increment
raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='httpstat.us', port=80): Max retries exceeded with url: /500 (Caused by ResponseError('too many 500 error responses',))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/.../lib/python3.6/site-packages/requests/sessions.py", line 521, in get
return self.request('GET', url, **kwargs)
File "/.../lib/python3.6/site-packages/requests/sessions.py", line 508, in request
resp = self.send(prep, **send_kwargs)
File "/.../lib/python3.6/site-packages/requests/sessions.py", line 618, in send
r = adapter.send(request, **kwargs)
File "/.../lib/python3.6/site-packages/requests/adapters.py", line 499, in send
raise RetryError(e, request=request)
requests.exceptions.RetryError: HTTPConnectionPool(host='httpstat.us', port=80): Max retries exceeded with url: /500 (Caused by ResponseError('too many 500 error responses',))
Установка ловушки в методе .new()
позволит вам скорректировать политику для следующей попытки, а также проанализировать атрибут .history
, но не позволит избежать повторного вызова исключения.