Skip to content

Commit 7f819f3

Browse files
author
Chris Rossi
authored
fix: accept bytes or str as base value for JsonProperty (#380)
For `JsonProperty`, even though we write and normally read `bytes`, when retrieving a `JsonProperty` in a projection query, for some reason, we get an already decoded string, which cause NDB to file with projection queries that read JSON properties. Fixes #378
1 parent 107cf4d commit 7f819f3

File tree

3 files changed

+30
-7
lines changed

3 files changed

+30
-7
lines changed

packages/google-cloud-ndb/google/cloud/ndb/model.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,7 +2033,7 @@ def _check_property(self, rest=None, require_indexed=True):
20332033
"""
20342034
if require_indexed and not self._indexed:
20352035
raise InvalidPropertyError(
2036-
"Property is unindexed {}".format(self._name)
2036+
"Property is unindexed: {}".format(self._name)
20372037
)
20382038

20392039
if rest:
@@ -3049,12 +3049,16 @@ def _from_base_type(self, value):
30493049
"""Convert a value from the "base" value type for this property.
30503050
30513051
Args:
3052-
value (bytes): The value to be converted.
3052+
value (Union[bytes, str]): The value to be converted.
30533053
30543054
Returns:
30553055
Any: The ``value`` (ASCII bytes or string) loaded as JSON.
30563056
"""
3057-
return json.loads(value.decode("ascii"))
3057+
# We write and retrieve `bytes` normally, but for some reason get back
3058+
# `str` from a projection query.
3059+
if not isinstance(value, six.text_type):
3060+
value = value.decode("ascii")
3061+
return json.loads(value)
30583062

30593063

30603064
@functools.total_ordering

packages/google-cloud-ndb/tests/system/test_query.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,3 +1627,22 @@ class SomeKind(ndb.Model):
16271627
assert len(results) == 2
16281628
assert results[0].foo == 2
16291629
assert results[1].foo == 3
1630+
1631+
1632+
@pytest.mark.usefixtures("client_context")
1633+
def test_projection_with_json_property(dispose_of):
1634+
"""Regression test for #378
1635+
1636+
https://github.com/googleapis/python-ndb/issues/378
1637+
"""
1638+
1639+
class SomeKind(ndb.Model):
1640+
foo = ndb.JsonProperty(indexed=True)
1641+
1642+
key = SomeKind(foo={"hi": "mom!"}).put()
1643+
dispose_of(key._key)
1644+
1645+
eventually(SomeKind.query().fetch, _length_equals(1))
1646+
1647+
results = SomeKind.query().fetch(projection=[SomeKind.foo])
1648+
assert results[0].foo == {"hi": "mom!"}

packages/google-cloud-ndb/tests/unit/test_model.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2253,11 +2253,11 @@ def test__from_base_type():
22532253
assert prop._from_base_type(value) == expected
22542254

22552255
@staticmethod
2256-
def test__from_base_type_invalid():
2256+
def test__from_base_type_str():
22572257
prop = model.JsonProperty(name="json-val")
2258-
if six.PY3: # pragma: NO PY2 COVER # pragma: NO BRANCH
2259-
with pytest.raises(AttributeError):
2260-
prop._from_base_type("{}")
2258+
value = u'[14,true,{"a":null,"b":"\\u2603"}]'
2259+
expected = [14, True, {"a": None, "b": u"\N{snowman}"}]
2260+
assert prop._from_base_type(value) == expected
22612261

22622262

22632263
class TestUser:

0 commit comments

Comments
 (0)