Skip to content
This repository was archived by the owner on Mar 31, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/retry_timeout.rst
Original file line numberDiff line numberDiff line change
Expand Up@@ -133,14 +133,14 @@ explicit policy in your code.

from google.api_core.retry import Retry
from google.cloud.storage.retry import ConditionalRetryPolicy
from google.cloud.storage.retry import is_etag_in_json
from google.cloud.storage.retry import is_etag_in_data

def is_retryable(exc):
... # as above

my_retry_policy = Retry(predicate=is_retryable)
my_cond_policy = ConditionalRetryPolicy(
my_retry_policy, conditional_predicate=is_etag_in_json)
my_retry_policy, conditional_predicate=is_etag_in_data)
bucket = client.get_bucket(BUCKET_NAME, retry=my_cond_policy)


Expand Down
29 changes: 14 additions & 15 deletions google/cloud/storage/retry.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,8 +18,6 @@
from google.api_core import retry
from google.auth import exceptions as auth_exceptions

import json


# ConnectionError is a built-in exception only in Python3 and not in Python2.
try:
Expand DownExpand Up@@ -120,20 +118,21 @@ def is_metageneration_specified(query_params):
return if_metageneration_match


def is_etag_in_data(data):
"""Return True if an etag is contained in the request body.

:type data: dict or None
:param data: A dict representing the request JSON body. If not passed, returns False.
"""
return data is not None and "etag" in data


def is_etag_in_json(data):
"""Return True if an etag is contained in the JSON body.

Indended for use on calls with relatively short JSON payloads."""
try:
content = json.loads(data)
if content.get("etag"):
return True
# Though this method should only be called when a JSON body is expected,
# the retry policy should be robust to unexpected payloads.
# In Python 3 a JSONDecodeError is possible, but it is a subclass of ValueError.
except (ValueError, TypeError):
pass
return False
"""
``is_etag_in_json`` is supported for backwards-compatibility reasons only;
please use ``is_etag_in_data`` instead.
"""
return is_etag_in_data(data)


DEFAULT_RETRY_IF_GENERATION_SPECIFIED = ConditionalRetryPolicy(
Expand Down
18 changes: 6 additions & 12 deletions tests/unit/test_retry.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -166,30 +166,24 @@ def test_w_if_metageneration_match(self):
self.assertTrue(self._call_fut(query_params))


class Test_is_etag_in_json(unittest.TestCase):
class Test_is_etag_in_data(unittest.TestCase):
def _call_fut(self, data):
from google.cloud.storage import retry

return retry.is_etag_in_json(data)
return retry.is_etag_in_data(data)

@staticmethod
def _make_json_data(**kw):
import json

return json.dumps(kw)

def test_w_empty(self):
data = self._make_json_data()
def test_w_none(self):
data = None

self.assertFalse(self._call_fut(data))

def test_w_etag_in_data(self):
data = self._make_json_data(etag="123")
data = {"etag": "123"}

self.assertTrue(self._call_fut(data))

def test_w_empty_data(self):
data = ""
data = {}

self.assertFalse(self._call_fut(data))

Expand Down