Skip to content

Commit c518472

Browse files
authored
fix: Resolve issue where num_retries would have no effect (#1244)
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/google-api-python-client/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [x] Ensure the tests and linter pass - [x] Code coverage does not decrease (if any source code was changed) - [x] Appropriate docs were updated (if necessary) Fixes #1243 🦕
1 parent 22807c9 commit c518472

File tree

2 files changed

+38
-8
lines changed

2 files changed

+38
-8
lines changed

googleapiclient/http.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ def _should_retry_response(resp_status, content):
9494
Returns:
9595
True if the response should be retried, otherwise False.
9696
"""
97+
reason = None
98+
9799
# Retry on 5xx errors.
98100
if resp_status >= 500:
99101
return True
@@ -113,9 +115,22 @@ def _should_retry_response(resp_status, content):
113115
try:
114116
data = json.loads(content.decode("utf-8"))
115117
if isinstance(data, dict):
116-
reason = data["error"].get("status")
117-
if reason is None:
118-
reason = data["error"]["errors"][0]["reason"]
118+
# There are many variations of the error json so we need
119+
# to determine the keyword which has the error detail. Make sure
120+
# that the order of the keywords below isn't changed as it can
121+
# break user code. If the "errors" key exists, we must use that
122+
# first.
123+
# See Issue #1243
124+
# https://github.com/googleapis/google-api-python-client/issues/1243
125+
error_detail_keyword = next((kw for kw in ["errors", "status", "message"] if kw in data["error"]), "")
126+
127+
if error_detail_keyword:
128+
reason = data["error"][error_detail_keyword]
129+
130+
if isinstance(reason, list) and len(reason) > 0:
131+
reason = reason[0]
132+
if "reason" in reason:
133+
reason = reason["reason"]
119134
else:
120135
reason = data[0]["error"]["errors"]["reason"]
121136
except (UnicodeDecodeError, ValueError, KeyError):

tests/test_http.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ def test_media_io_base_next_chunk_retries(self):
401401
({"status": "500"}, ""),
402402
({"status": "503"}, ""),
403403
({"status": "200", "location": "location"}, ""),
404-
({"status": "403"}, USER_RATE_LIMIT_EXCEEDED_RESPONSE),
404+
({"status": "403"}, USER_RATE_LIMIT_EXCEEDED_RESPONSE_NO_STATUS),
405405
({"status": "403"}, RATE_LIMIT_EXCEEDED_RESPONSE),
406406
({"status": "429"}, ""),
407407
({"status": "200"}, "{}"),
@@ -834,7 +834,7 @@ def test_media_io_base_download_unknown_media_size(self):
834834
--batch_foobarbaz--"""
835835

836836

837-
USER_RATE_LIMIT_EXCEEDED_RESPONSE = """{
837+
USER_RATE_LIMIT_EXCEEDED_RESPONSE_NO_STATUS = """{
838838
"error": {
839839
"errors": [
840840
{
@@ -848,6 +848,20 @@ def test_media_io_base_download_unknown_media_size(self):
848848
}
849849
}"""
850850

851+
USER_RATE_LIMIT_EXCEEDED_RESPONSE_WITH_STATUS = """{
852+
"error": {
853+
"errors": [
854+
{
855+
"domain": "usageLimits",
856+
"reason": "userRateLimitExceeded",
857+
"message": "User Rate Limit Exceeded"
858+
}
859+
],
860+
"code": 403,
861+
"message": "User Rate Limit Exceeded",
862+
"status": "PERMISSION_DENIED"
863+
}
864+
}"""
851865

852866
RATE_LIMIT_EXCEEDED_RESPONSE = """{
853867
"error": {
@@ -981,10 +995,11 @@ def test_retry_connection_errors_resumable(self):
981995
self.assertEqual({u"foo": u"bar"}, response)
982996

983997
def test_retry(self):
984-
num_retries = 5
985-
resp_seq = [({"status": "500"}, "")] * (num_retries - 3)
998+
num_retries = 6
999+
resp_seq = [({"status": "500"}, "")] * (num_retries - 4)
9861000
resp_seq.append(({"status": "403"}, RATE_LIMIT_EXCEEDED_RESPONSE))
987-
resp_seq.append(({"status": "403"}, USER_RATE_LIMIT_EXCEEDED_RESPONSE))
1001+
resp_seq.append(({"status": "403"}, USER_RATE_LIMIT_EXCEEDED_RESPONSE_NO_STATUS))
1002+
resp_seq.append(({"status": "403"}, USER_RATE_LIMIT_EXCEEDED_RESPONSE_WITH_STATUS))
9881003
resp_seq.append(({"status": "429"}, ""))
9891004
resp_seq.append(({"status": "200"}, "{}"))
9901005

0 commit comments

Comments
 (0)