Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: revise and rename is_etag_in_json(data) #483

Merged
merged 10 commits into from Jul 9, 2021
18 changes: 5 additions & 13 deletions google/cloud/storage/retry.py
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 Down Expand Up @@ -123,17 +121,11 @@ def is_metageneration_specified(query_params):
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
:type data: dict or None
:param data: A dict containing the JSON body. If not passed, returns False.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think we could be clearer that the dict represents the JSON body rather than containing it. Also, this is specifically intended for calls where metadata is sent in the request body.

It seems like it'd be better to actually just rename this function to is_etag_in_data but I guess technically this is part of the public surface of the library (even if we don't expect external callers to use it directly)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could rename it and leave is_etag_in_json around as a backward-compatibility alias.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be something like this:

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):
    """
    ``is_etag_in_json`` is supported for backwards-compatibility reasons only;
    please use ``is_etag_in_data`` instead.
    """
    return is_etag_in_data(data)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


Intended for use on calls with relatively short JSON payloads."""
return data is not None and "etag" in data


DEFAULT_RETRY_IF_GENERATION_SPECIFIED = ConditionalRetryPolicy(
Expand Down
14 changes: 4 additions & 10 deletions tests/unit/test_retry.py
Expand Up @@ -172,24 +172,18 @@ def _call_fut(self, data):

return retry.is_etag_in_json(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