1
from __future__ import absolute_import
5
from ..exceptions import (
12
from ..packages import six
15
log = logging.getLogger(__name__)
19
""" Retry configuration.
21
Each retry attempt will create a new Retry object with updated values, so
22
they can be safely reused.
24
Retries can be defined as a default for a pool::
26
retries = Retry(connect=5, read=2, redirect=5)
27
http = PoolManager(retries=retries)
28
response = http.request('GET', 'http://example.com/')
30
Or per-request (which overrides the default for the pool)::
32
response = http.request('GET', 'http://example.com/', retries=Retry(10))
34
Retries can be disabled by passing ``False``::
36
response = http.request('GET', 'http://example.com/', retries=False)
38
Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless
39
retries are disabled, in which case the causing exception will be raised.
42
Total number of retries to allow. Takes precedence over other counts.
44
Set to ``None`` to remove this constraint and fall back on other
45
counts. It's a good idea to set this to some sensibly-high value to
46
account for unexpected edge cases and avoid infinite retry loops.
48
Set to ``0`` to fail on the first retry.
50
Set to ``False`` to disable and imply ``raise_on_redirect=False``.
53
How many connection-related errors to retry on.
55
These are errors raised before the request is sent to the remote server,
56
which we assume has not triggered the server to process the request.
58
Set to ``0`` to fail on the first retry of this type.
61
How many times to retry on read errors.
63
These errors are raised after the request was sent to the server, so the
64
request may have side-effects.
66
Set to ``0`` to fail on the first retry of this type.
69
How many redirects to perform. Limit this to avoid infinite redirect
72
A redirect is a HTTP response with a status code 301, 302, 303, 307 or
75
Set to ``0`` to fail on the first retry of this type.
77
Set to ``False`` to disable and imply ``raise_on_redirect=False``.
79
:param iterable method_whitelist:
80
Set of uppercased HTTP method verbs that we should retry on.
82
By default, we only retry on methods which are considered to be
83
idempotent (multiple requests with the same parameters end with the
84
same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`.
86
Set to a ``False`` value to retry on any verb.
88
:param iterable status_forcelist:
89
A set of integer HTTP status codes that we should force a retry on.
90
A retry is initiated if the request method is in ``method_whitelist``
91
and the response status code is in ``status_forcelist``.
93
By default, this is disabled with ``None``.
95
:param float backoff_factor:
96
A backoff factor to apply between attempts after the second try
97
(most errors are resolved immediately by a second try without a
98
delay). urllib3 will sleep for::
100
{backoff factor} * (2 ^ ({number of total retries} - 1))
102
seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep
103
for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer
104
than :attr:`Retry.BACKOFF_MAX`.
106
By default, backoff is disabled (set to 0).
108
:param bool raise_on_redirect: Whether, if the number of redirects is
109
exhausted, to raise a MaxRetryError, or to return a response with a
110
response code in the 3xx range.
112
:param bool raise_on_status: Similar meaning to ``raise_on_redirect``:
113
whether we should raise an exception, or return a response,
114
if status falls in ``status_forcelist`` range and retries have
118
DEFAULT_METHOD_WHITELIST = frozenset([
119
'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'])
121
#: Maximum backoff time.
124
def __init__(self, total=10, connect=None, read=None, redirect=None,
125
method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None,
126
backoff_factor=0, raise_on_redirect=True, raise_on_status=True,
130
self.connect = connect
133
if redirect is False or total is False:
135
raise_on_redirect = False
137
self.redirect = redirect
138
self.status_forcelist = status_forcelist or set()
139
self.method_whitelist = method_whitelist
140
self.backoff_factor = backoff_factor
141
self.raise_on_redirect = raise_on_redirect
142
self.raise_on_status = raise_on_status
143
self._observed_errors = _observed_errors # TODO: use .history instead?
148
connect=self.connect, read=self.read, redirect=self.redirect,
149
method_whitelist=self.method_whitelist,
150
status_forcelist=self.status_forcelist,
151
backoff_factor=self.backoff_factor,
152
raise_on_redirect=self.raise_on_redirect,
153
raise_on_status=self.raise_on_status,
154
_observed_errors=self._observed_errors,
157
return type(self)(**params)
160
def from_int(cls, retries, redirect=True, default=None):
161
""" Backwards-compatibility for the old retries format."""
163
retries = default if default is not None else cls.DEFAULT
165
if isinstance(retries, Retry):
168
redirect = bool(redirect) and None
169
new_retries = cls(retries, redirect=redirect)
170
log.debug("Converted retries value: %r -> %r", retries, new_retries)
173
def get_backoff_time(self):
174
""" Formula for computing the current backoff
178
if self._observed_errors <= 1:
181
backoff_value = self.backoff_factor * (2 ** (self._observed_errors - 1))
182
return min(self.BACKOFF_MAX, backoff_value)
185
""" Sleep between retry attempts using an exponential backoff.
187
By default, the backoff factor is 0 and this method will return
190
backoff = self.get_backoff_time()
195
def _is_connection_error(self, err):
196
""" Errors when we're fairly sure that the server did not receive the
197
request, so it should be safe to retry.
199
return isinstance(err, ConnectTimeoutError)
201
def _is_read_error(self, err):
202
""" Errors that occur after the request has been started, so we should
203
assume that the server began processing it.
205
return isinstance(err, (ReadTimeoutError, ProtocolError))
207
def is_forced_retry(self, method, status_code):
208
""" Is this method/status code retryable? (Based on method/codes whitelists)
210
if self.method_whitelist and method.upper() not in self.method_whitelist:
213
return self.status_forcelist and status_code in self.status_forcelist
215
def is_exhausted(self):
216
""" Are we out of retries? """
217
retry_counts = (self.total, self.connect, self.read, self.redirect)
218
retry_counts = list(filter(None, retry_counts))
222
return min(retry_counts) < 0
224
def increment(self, method=None, url=None, response=None, error=None,
225
_pool=None, _stacktrace=None):
226
""" Return a new Retry object with incremented retry counters.
228
:param response: A response object, or None, if the server did not
230
:type response: :class:`~urllib3.response.HTTPResponse`
231
:param Exception error: An error encountered during the request, or
232
None if the response was received successfully.
234
:return: A new ``Retry`` object.
236
if self.total is False and error:
237
# Disabled, indicate to re-raise the error.
238
raise six.reraise(type(error), error, _stacktrace)
241
if total is not None:
244
_observed_errors = self._observed_errors
245
connect = self.connect
247
redirect = self.redirect
250
if error and self._is_connection_error(error):
253
raise six.reraise(type(error), error, _stacktrace)
254
elif connect is not None:
256
_observed_errors += 1
258
elif error and self._is_read_error(error):
261
raise six.reraise(type(error), error, _stacktrace)
262
elif read is not None:
264
_observed_errors += 1
266
elif response and response.get_redirect_location():
268
if redirect is not None:
270
cause = 'too many redirects'
273
# Incrementing because of a server error like a 500 in
274
# status_forcelist and a the given method is in the whitelist
275
_observed_errors += 1
276
cause = ResponseError.GENERIC_ERROR
277
if response and response.status:
278
cause = ResponseError.SPECIFIC_ERROR.format(
279
status_code=response.status)
281
new_retry = self.new(
283
connect=connect, read=read, redirect=redirect,
284
_observed_errors=_observed_errors)
286
if new_retry.is_exhausted():
287
raise MaxRetryError(_pool, url, error or ResponseError(cause))
289
log.debug("Incremented Retry for (url='%s'): %r", url, new_retry)
294
return ('{cls.__name__}(total={self.total}, connect={self.connect}, '
295
'read={self.read}, redirect={self.redirect})').format(
296
cls=type(self), self=self)
299
# For backwards compatibility (equivalent to pre-v1.9):
300
Retry.DEFAULT = Retry(3)