Skip to content

Commit 0e99f7b

Browse files
committed
Merge pull request #246 from tseaver/223-marshal_list_values
Fix #223: marshal list values from protobufs
2 parents c2c1d59 + fc4353f commit 0e99f7b

File tree

3 files changed

+84
-36
lines changed

3 files changed

+84
-36
lines changed

gcloud/datastore/_helpers.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ def _get_protobuf_attribute_and_value(val):
7272
return name + '_value', value
7373

7474

75-
def _get_value_from_protobuf(pb):
76-
"""Given a protobuf for a Property, get the correct value.
75+
def _get_value_from_value_pb(value_pb):
76+
"""Given a protobuf for a Value, get the correct value.
7777
7878
The Cloud Datastore Protobuf API returns a Property Protobuf
7979
which has one value set and the rest blank.
@@ -82,38 +82,59 @@ def _get_value_from_protobuf(pb):
8282
Some work is done to coerce the return value into a more useful type
8383
(particularly in the case of a timestamp value, or a key value).
8484
85-
:type pb: :class:`gcloud.datastore.datastore_v1_pb2.Property`
86-
:param pb: The Property Protobuf.
85+
:type value_pb: :class:`gcloud.datastore.datastore_v1_pb2.Value`
86+
:param value_pb: The Value Protobuf.
8787
8888
:returns: The value provided by the Protobuf.
8989
"""
9090

91-
if pb.value.HasField('timestamp_microseconds_value'):
92-
microseconds = pb.value.timestamp_microseconds_value
91+
result = None
92+
if value_pb.HasField('timestamp_microseconds_value'):
93+
microseconds = value_pb.timestamp_microseconds_value
9394
naive = (datetime.utcfromtimestamp(0) +
9495
timedelta(microseconds=microseconds))
95-
return naive.replace(tzinfo=pytz.utc)
96+
result = naive.replace(tzinfo=pytz.utc)
9697

97-
elif pb.value.HasField('key_value'):
98-
return Key.from_protobuf(pb.value.key_value)
98+
elif value_pb.HasField('key_value'):
99+
result = Key.from_protobuf(value_pb.key_value)
99100

100-
elif pb.value.HasField('boolean_value'):
101-
return pb.value.boolean_value
101+
elif value_pb.HasField('boolean_value'):
102+
result = value_pb.boolean_value
102103

103-
elif pb.value.HasField('double_value'):
104-
return pb.value.double_value
104+
elif value_pb.HasField('double_value'):
105+
result = value_pb.double_value
105106

106-
elif pb.value.HasField('integer_value'):
107-
return pb.value.integer_value
107+
elif value_pb.HasField('integer_value'):
108+
result = value_pb.integer_value
108109

109-
elif pb.value.HasField('string_value'):
110-
return pb.value.string_value
110+
elif value_pb.HasField('string_value'):
111+
result = value_pb.string_value
111112

112-
elif pb.value.HasField('entity_value'):
113-
return Entity.from_protobuf(pb.value.entity_value)
113+
elif value_pb.HasField('entity_value'):
114+
result = Entity.from_protobuf(value_pb.entity_value)
114115

115-
else:
116-
return None
116+
elif value_pb.list_value:
117+
result = [_get_value_from_value_pb(x) for x in value_pb.list_value]
118+
119+
return result
120+
121+
122+
def _get_value_from_property_pb(property_pb):
123+
"""Given a protobuf for a Property, get the correct value.
124+
125+
The Cloud Datastore Protobuf API returns a Property Protobuf
126+
which has one value set and the rest blank.
127+
This function retrieves the the one value provided.
128+
129+
Some work is done to coerce the return value into a more useful type
130+
(particularly in the case of a timestamp value, or a key value).
131+
132+
:type property_pb: :class:`gcloud.datastore.datastore_v1_pb2.Property`
133+
:param property_pb: The Property Protobuf.
134+
135+
:returns: The value provided by the Protobuf.
136+
"""
137+
return _get_value_from_value_pb(property_pb.value)
117138

118139

119140
def _set_protobuf_value(value_pb, val):

gcloud/datastore/entity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def from_protobuf(cls, pb, dataset=None): # pylint: disable=invalid-name
157157
entity = cls.from_key(key)
158158

159159
for property_pb in pb.property:
160-
value = _helpers._get_value_from_protobuf(property_pb)
160+
value = _helpers._get_value_from_property_pb(property_pb)
161161
entity[property_pb.name] = value
162162

163163
return entity

gcloud/datastore/test__helpers.py

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -94,19 +94,19 @@ def test_object(self):
9494
self.assertRaises(ValueError, self._callFUT, object())
9595

9696

97-
class Test__get_value_from_protobuf(unittest2.TestCase):
97+
class Test__get_value_from_value_pb(unittest2.TestCase):
9898

9999
def _callFUT(self, pb):
100-
from gcloud.datastore._helpers import _get_value_from_protobuf
100+
from gcloud.datastore._helpers import _get_value_from_value_pb
101101

102-
return _get_value_from_protobuf(pb)
102+
return _get_value_from_value_pb(pb)
103103

104104
def _makePB(self, attr_name, value):
105-
from gcloud.datastore.datastore_v1_pb2 import Property
105+
from gcloud.datastore.datastore_v1_pb2 import Value
106106

107-
prop = Property()
108-
setattr(prop.value, attr_name, value)
109-
return prop
107+
pb = Value()
108+
setattr(pb, attr_name, value)
109+
return pb
110110

111111
def test_datetime(self):
112112
import calendar
@@ -119,17 +119,17 @@ def test_datetime(self):
119119
self.assertEqual(self._callFUT(pb), utc)
120120

121121
def test_key(self):
122-
from gcloud.datastore.datastore_v1_pb2 import Property
122+
from gcloud.datastore.datastore_v1_pb2 import Value
123123
from gcloud.datastore.dataset import Dataset
124124
from gcloud.datastore.key import Key
125125

126126
_DATASET = 'DATASET'
127127
_KIND = 'KIND'
128128
_ID = 1234
129129
_PATH = [{'kind': _KIND, 'id': _ID}]
130-
pb = Property()
130+
pb = Value()
131131
expected = Key(dataset=Dataset(_DATASET), path=_PATH).to_protobuf()
132-
pb.value.key_value.CopyFrom(expected)
132+
pb.key_value.CopyFrom(expected)
133133
found = self._callFUT(pb)
134134
self.assertEqual(found.to_protobuf(), expected)
135135

@@ -154,23 +154,50 @@ def test_unicode(self):
154154
self.assertEqual(self._callFUT(pb), u'str')
155155

156156
def test_entity(self):
157-
from gcloud.datastore.datastore_v1_pb2 import Property
157+
from gcloud.datastore.datastore_v1_pb2 import Value
158158
from gcloud.datastore.entity import Entity
159159

160-
pb = Property()
161-
entity_pb = pb.value.entity_value
160+
pb = Value()
161+
entity_pb = pb.entity_value
162162
prop_pb = entity_pb.property.add()
163163
prop_pb.name = 'foo'
164164
prop_pb.value.string_value = 'Foo'
165165
entity = self._callFUT(pb)
166166
self.assertTrue(isinstance(entity, Entity))
167167
self.assertEqual(entity['foo'], 'Foo')
168168

169+
def test_list(self):
170+
from gcloud.datastore.datastore_v1_pb2 import Value
171+
172+
pb = Value()
173+
list_pb = pb.list_value
174+
item_pb = list_pb.add()
175+
item_pb.string_value = 'Foo'
176+
item_pb = list_pb.add()
177+
item_pb.string_value = 'Bar'
178+
items = self._callFUT(pb)
179+
self.assertEqual(items, ['Foo', 'Bar'])
180+
169181
def test_unknown(self):
182+
from gcloud.datastore.datastore_v1_pb2 import Value
183+
184+
pb = Value()
185+
self.assertEqual(self._callFUT(pb), None) # XXX desirable?
186+
187+
188+
class Test__get_value_from_property_pb(unittest2.TestCase):
189+
190+
def _callFUT(self, pb):
191+
from gcloud.datastore._helpers import _get_value_from_property_pb
192+
193+
return _get_value_from_property_pb(pb)
194+
195+
def test_it(self):
170196
from gcloud.datastore.datastore_v1_pb2 import Property
171197

172198
pb = Property()
173-
self.assertEqual(self._callFUT(pb), None) # XXX desirable?
199+
pb.value.string_value = 'value'
200+
self.assertEqual(self._callFUT(pb), 'value')
174201

175202

176203
class Test_set_protobuf_value(unittest2.TestCase):

0 commit comments

Comments
 (0)