Skip to content

Commit 6708e8e

Browse files
authored
Rework automatic retry. (#253)
Previously, retries were done recursively, which could lead to large stacks and even stack overflow. This commit comprises a refactoring of automatic retry. * BoxResponse is moved to its own module (from box_session) * A new class, BoxRequest, exists to keep track of information about a specific API request while it's being made or retried. * BoxSession was refactored so that retries happen iteratively: - _make_request was renamed _send_request and takes a BoxRequest. - _retry_request_if_necessary was renamed _get_retry_request_callable and it now returns a callable that can retry a request, rather than actually retrying it. _prepare_and_send_request now retries failed requests in a loop * The retry strategy has changed so that 429 responses won't be retried indefinitely - we'll give up after 10 failures regardless of whether they are 5xx responses or 429s.
1 parent cbc7038 commit 6708e8e

File tree

13 files changed

+227
-189
lines changed

13 files changed

+227
-189
lines changed

boxsdk/exception.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ def __init__(
4545
:param code:
4646
The 'code' field of the failed response
4747
:type code:
48-
`unicode`
48+
`unicode` or None
4949
:param message:
5050
A message to associate with the exception, e.g. 'message' field of the json in the failed response
5151
:type message:
52-
`unicode`
52+
`unicode` or None
5353
:param request_id:
5454
The 'request_id' field of the json in the failed response
5555
:type request_id:
56-
`unicode`
56+
`unicode` or None
5757
:param headers:
5858
The HTTP headers in the failed response
5959
:type headers:
@@ -69,7 +69,7 @@ def __init__(
6969
:param context_info:
7070
The context_info returned in the failed response.
7171
:type context_info:
72-
`dict`
72+
`dict` or None
7373
:param network_response:
7474
The failed response
7575
:type network_response:

boxsdk/session/box_request.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# coding: utf-8
2+
3+
from __future__ import unicode_literals, absolute_import
4+
5+
import attr
6+
7+
8+
@attr.s(slots=True)
9+
class BoxRequest(object):
10+
"""Represents a Box API request.
11+
12+
:param url: The URL being requested.
13+
:type url: `unicode`
14+
:param method: The HTTP method to use for the request.
15+
:type method: `unicode` or None
16+
:param headers: HTTP headers to include with the request.
17+
:type headers: `dict` or None
18+
:param auto_session_renewal: Whether or not the session can be automatically renewed if the request fails.
19+
:type auto_session_renewal: `bool` or None
20+
:param expect_json_response: Whether or not the API response must be JSON.
21+
:type expect_json_response: `bool` or None
22+
"""
23+
url = attr.ib()
24+
method = attr.ib(default='GET')
25+
headers = attr.ib(default=attr.Factory(dict))
26+
auto_session_renewal = attr.ib(default=True)
27+
expect_json_response = attr.ib(default=True)

boxsdk/session/box_response.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# coding: utf-8
2+
3+
from __future__ import unicode_literals, absolute_import
4+
5+
6+
class BoxResponse(object):
7+
"""Represents a response to a Box API request."""
8+
9+
def __init__(self, network_response):
10+
self._network_response = network_response
11+
12+
def json(self):
13+
"""Return the parsed JSON response.
14+
15+
:rtype:
16+
`dict` or `list` or `str` or `int` or `float`
17+
"""
18+
return self._network_response.json()
19+
20+
@property
21+
def content(self):
22+
"""Return the content of the response body.
23+
24+
:rtype:
25+
varies
26+
"""
27+
return self._network_response.content
28+
29+
@property
30+
def ok(self):
31+
"""Return whether or not the request was successful.
32+
33+
:rtype:
34+
`bool`
35+
"""
36+
# pylint:disable=invalid-name
37+
return self._network_response.ok
38+
39+
@property
40+
def status_code(self):
41+
"""Return the HTTP status code of the response.
42+
43+
:rtype:
44+
`int`
45+
"""
46+
return self._network_response.status_code
47+
48+
@property
49+
def network_response(self):
50+
"""Return the underlying network response.
51+
52+
:rtype:
53+
:class:`NetworkResponse`
54+
"""
55+
return self._network_response
56+
57+
def __repr__(self):
58+
return '<Box Response[{status_code}]>'.format(status_code=self.status_code)

0 commit comments

Comments
 (0)