From 6414b1fe145a358c67a2292f86f6082e6dd5c7b2 Mon Sep 17 00:00:00 2001 From: Danny Hermes Date: Sun, 3 Jan 2016 14:04:12 -0800 Subject: [PATCH] Adding helpers for interacting with properties in Entity protobuf. This is because the repeated `property` message field in `Entity` becomes a `properties` map field in `v1beta3`. This makes adding new properties and iterating through all properties very different, so we add a helper to ease the transition from `v1beta2` to `v1beta3`. --- gcloud/datastore/helpers.py | 65 ++++++--- gcloud/datastore/test_batch.py | 42 +++--- gcloud/datastore/test_client.py | 26 ++-- gcloud/datastore/test_connection.py | 12 +- gcloud/datastore/test_helpers.py | 210 +++++++++++++++++----------- gcloud/datastore/test_query.py | 6 +- 6 files changed, 221 insertions(+), 140 deletions(-) diff --git a/gcloud/datastore/helpers.py b/gcloud/datastore/helpers.py index b7488e1b3d3b..0133bcec07b3 100644 --- a/gcloud/datastore/helpers.py +++ b/gcloud/datastore/helpers.py @@ -115,6 +115,37 @@ def _get_meaning(value_pb, is_list=False): return meaning +def _new_value_pb(entity_pb, name): + """Add (by name) a new ``Value`` protobuf to an entity protobuf. + + :type entity_pb: :class:`gcloud.datastore._generated.entity_pb2.Entity` + :param entity_pb: An entity protobuf to add a new property to. + + :type name: string + :param name: The name of the new property. + + :rtype: :class:`gcloud.datastore._generated.entity_pb2.Value` + :returns: The new ``Value`` protobuf that was added to the entity. + """ + property_pb = entity_pb.property.add() + property_pb.name = name + return property_pb.value + + +def _property_tuples(entity_pb): + """Iterator of name, ``Value`` tuples from entity properties. + + :type entity_pb: :class:`gcloud.datastore._generated.entity_pb2.Entity` + :param entity_pb: An entity protobuf to add a new property to. + + :rtype: :class:`generator` + :returns: An iterator that yields tuples of a name and ``Value`` + corresponding to properties on the entity. + """ + for property_pb in entity_pb.property: + yield property_pb.name, property_pb.value + + def entity_from_protobuf(pb): """Factory method for creating an entity based on a protobuf. @@ -135,22 +166,21 @@ def entity_from_protobuf(pb): entity_meanings = {} exclude_from_indexes = [] - for property_pb in pb.property: - value = _get_value_from_value_pb(property_pb.value) - prop_name = property_pb.name + for prop_name, value_pb in _property_tuples(pb): + value = _get_value_from_value_pb(value_pb) entity_props[prop_name] = value # Check if the property has an associated meaning. - meaning = _get_meaning(property_pb.value, - is_list=isinstance(value, list)) + is_list = isinstance(value, list) + meaning = _get_meaning(value_pb, is_list=is_list) if meaning is not None: entity_meanings[prop_name] = (meaning, value) - # Check if property_pb.value was indexed. Lists need to be - # special-cased and we require all `indexed` values in a list agree. - if isinstance(value, list): + # Check if ``value_pb`` was indexed. Lists need to be special-cased + # and we require all ``indexed`` values in a list agree. + if is_list: indexed_values = set(value_pb.indexed - for value_pb in property_pb.value.list_value) + for value_pb in value_pb.list_value) if len(indexed_values) != 1: raise ValueError('For a list_value, subvalues must either all ' 'be indexed or all excluded from indexes.') @@ -158,7 +188,7 @@ def entity_from_protobuf(pb): if not indexed_values.pop(): exclude_from_indexes.append(prop_name) else: - if not property_pb.value.indexed: + if not value_pb.indexed: exclude_from_indexes.append(prop_name) entity = Entity(key=key, exclude_from_indexes=exclude_from_indexes) @@ -186,19 +216,16 @@ def entity_to_protobuf(entity): if value_is_list and len(value) == 0: continue - prop = entity_pb.property.add() - # Set the name of the property. - prop.name = name - + value_pb = _new_value_pb(entity_pb, name) # Set the appropriate value. - _set_protobuf_value(prop.value, value) + _set_protobuf_value(value_pb, value) # Add index information to protobuf. if name in entity.exclude_from_indexes: if not value_is_list: - prop.value.indexed = False + value_pb.indexed = False - for sub_value in prop.value.list_value: + for sub_value in value_pb.list_value: sub_value.indexed = False # Add meaning information to protobuf. @@ -209,10 +236,10 @@ def entity_to_protobuf(entity): if orig_value is value: # For lists, we set meaning on each sub-element. if value_is_list: - for sub_value_pb in prop.value.list_value: + for sub_value_pb in value_pb.list_value: sub_value_pb.meaning = meaning else: - prop.value.meaning = meaning + value_pb.meaning = meaning return entity_pb diff --git a/gcloud/datastore/test_batch.py b/gcloud/datastore/test_batch.py index 1261eacb7269..12474be53aaf 100644 --- a/gcloud/datastore/test_batch.py +++ b/gcloud/datastore/test_batch.py @@ -94,6 +94,8 @@ def test_put_entity_w_partial_key(self): self.assertEqual(batch._partial_key_entities, [entity]) def test_put_entity_w_completed_key(self): + from gcloud.datastore.helpers import _property_tuples + _DATASET = 'DATASET' _PROPERTIES = { 'foo': 'bar', @@ -112,17 +114,20 @@ def test_put_entity_w_completed_key(self): mutated_entity = _mutated_pb(self, batch.mutations, 'upsert') self.assertEqual(mutated_entity.key, key._key) - props = dict([(prop.name, prop.value) - for prop in mutated_entity.property]) - self.assertTrue(props['foo'].indexed) - self.assertFalse(props['baz'].indexed) - self.assertTrue(props['spam'].indexed) - self.assertFalse(props['spam'].list_value[0].indexed) - self.assertFalse(props['spam'].list_value[1].indexed) - self.assertFalse(props['spam'].list_value[2].indexed) - self.assertFalse('frotz' in props) + + prop_dict = dict(_property_tuples(mutated_entity)) + self.assertEqual(len(prop_dict), 3) + self.assertTrue(prop_dict['foo'].indexed) + self.assertFalse(prop_dict['baz'].indexed) + self.assertTrue(prop_dict['spam'].indexed) + self.assertFalse(prop_dict['spam'].list_value[0].indexed) + self.assertFalse(prop_dict['spam'].list_value[1].indexed) + self.assertFalse(prop_dict['spam'].list_value[2].indexed) + self.assertFalse('frotz' in prop_dict) def test_put_entity_w_completed_key_prefixed_dataset_id(self): + from gcloud.datastore.helpers import _property_tuples + _DATASET = 'DATASET' _PROPERTIES = { 'foo': 'bar', @@ -141,15 +146,16 @@ def test_put_entity_w_completed_key_prefixed_dataset_id(self): mutated_entity = _mutated_pb(self, batch.mutations, 'upsert') self.assertEqual(mutated_entity.key, key._key) - props = dict([(prop.name, prop.value) - for prop in mutated_entity.property]) - self.assertTrue(props['foo'].indexed) - self.assertFalse(props['baz'].indexed) - self.assertTrue(props['spam'].indexed) - self.assertFalse(props['spam'].list_value[0].indexed) - self.assertFalse(props['spam'].list_value[1].indexed) - self.assertFalse(props['spam'].list_value[2].indexed) - self.assertFalse('frotz' in props) + + prop_dict = dict(_property_tuples(mutated_entity)) + self.assertEqual(len(prop_dict), 3) + self.assertTrue(prop_dict['foo'].indexed) + self.assertFalse(prop_dict['baz'].indexed) + self.assertTrue(prop_dict['spam'].indexed) + self.assertFalse(prop_dict['spam'].list_value[0].indexed) + self.assertFalse(prop_dict['spam'].list_value[1].indexed) + self.assertFalse(prop_dict['spam'].list_value[2].indexed) + self.assertFalse('frotz' in prop_dict) def test_delete_w_partial_key(self): _DATASET = 'DATASET' diff --git a/gcloud/datastore/test_client.py b/gcloud/datastore/test_client.py index c7f540d7052f..6713d8a964e0 100644 --- a/gcloud/datastore/test_client.py +++ b/gcloud/datastore/test_client.py @@ -17,6 +17,7 @@ def _make_entity_pb(dataset_id, kind, integer_id, name=None, str_val=None): from gcloud.datastore._generated import entity_pb2 + from gcloud.datastore.helpers import _new_value_pb entity_pb = entity_pb2.Entity() entity_pb.key.partition_id.dataset_id = dataset_id @@ -24,9 +25,8 @@ def _make_entity_pb(dataset_id, kind, integer_id, name=None, str_val=None): path_element.kind = kind path_element.id = integer_id if name is not None and str_val is not None: - prop = entity_pb.property.add() - prop.name = name - prop.value.string_value = str_val + value_pb = _new_value_pb(entity_pb, name) + value_pb.string_value = str_val return entity_pb @@ -608,6 +608,7 @@ def test_put_multi_w_single_empty_entity(self): self.assertRaises(ValueError, client.put_multi, Entity()) def test_put_multi_no_batch_w_partial_key(self): + from gcloud.datastore.helpers import _property_tuples from gcloud.datastore.test_batch import _Entity from gcloud.datastore.test_batch import _Key from gcloud.datastore.test_batch import _KeyPB @@ -629,12 +630,16 @@ def test_put_multi_no_batch_w_partial_key(self): inserts = list(mutation.insert_auto_id) self.assertEqual(len(inserts), 1) self.assertEqual(inserts[0].key, key.to_protobuf()) - properties = list(inserts[0].property) - self.assertEqual(properties[0].name, 'foo') - self.assertEqual(properties[0].value.string_value, u'bar') + + prop_list = list(_property_tuples(inserts[0])) + self.assertTrue(len(prop_list), 1) + name, value_pb = prop_list[0] + self.assertEqual(name, 'foo') + self.assertEqual(value_pb.string_value, u'bar') self.assertTrue(transaction_id is None) def test_put_multi_existing_batch_w_completed_key(self): + from gcloud.datastore.helpers import _property_tuples from gcloud.datastore.test_batch import _Entity from gcloud.datastore.test_batch import _Key from gcloud.datastore.test_batch import _mutated_pb @@ -650,9 +655,12 @@ def test_put_multi_existing_batch_w_completed_key(self): self.assertEqual(result, None) mutated_entity = _mutated_pb(self, CURR_BATCH.mutations, 'upsert') self.assertEqual(mutated_entity.key, key.to_protobuf()) - properties = list(mutated_entity.property) - self.assertEqual(properties[0].name, 'foo') - self.assertEqual(properties[0].value.string_value, u'bar') + + prop_list = list(_property_tuples(mutated_entity)) + self.assertTrue(len(prop_list), 1) + name, value_pb = prop_list[0] + self.assertEqual(name, 'foo') + self.assertEqual(value_pb.string_value, u'bar') def test_delete(self): _called_with = [] diff --git a/gcloud/datastore/test_connection.py b/gcloud/datastore/test_connection.py index 2f49f4bde06d..5deef1912705 100644 --- a/gcloud/datastore/test_connection.py +++ b/gcloud/datastore/test_connection.py @@ -670,6 +670,7 @@ def test_commit_wo_transaction(self): from gcloud._testing import _Monkey from gcloud.datastore._generated import datastore_pb2 from gcloud.datastore import connection as MUT + from gcloud.datastore.helpers import _new_value_pb DATASET_ID = 'DATASET' key_pb = self._make_key_pb(DATASET_ID) @@ -677,9 +678,8 @@ def test_commit_wo_transaction(self): mutation = datastore_pb2.Mutation() insert = mutation.upsert.add() insert.key.CopyFrom(key_pb) - prop = insert.property.add() - prop.name = 'foo' - prop.value.string_value = u'Foo' + value_pb = _new_value_pb(insert, 'foo') + value_pb.string_value = u'Foo' conn = self._makeOne() URI = '/'.join([ conn.api_base_url, @@ -717,6 +717,7 @@ def test_commit_w_transaction(self): from gcloud._testing import _Monkey from gcloud.datastore._generated import datastore_pb2 from gcloud.datastore import connection as MUT + from gcloud.datastore.helpers import _new_value_pb DATASET_ID = 'DATASET' key_pb = self._make_key_pb(DATASET_ID) @@ -724,9 +725,8 @@ def test_commit_w_transaction(self): mutation = datastore_pb2.Mutation() insert = mutation.upsert.add() insert.key.CopyFrom(key_pb) - prop = insert.property.add() - prop.name = 'foo' - prop.value.string_value = u'Foo' + value_pb = _new_value_pb(insert, 'foo') + value_pb.string_value = u'Foo' conn = self._makeOne() URI = '/'.join([ conn.api_base_url, diff --git a/gcloud/datastore/test_helpers.py b/gcloud/datastore/test_helpers.py index de2f7ed41d78..84a410d1560a 100644 --- a/gcloud/datastore/test_helpers.py +++ b/gcloud/datastore/test_helpers.py @@ -15,6 +15,47 @@ import unittest2 +class Test__new_value_pb(unittest2.TestCase): + + def _callFUT(self, entity_pb, name): + from gcloud.datastore.helpers import _new_value_pb + return _new_value_pb(entity_pb, name) + + def test_it(self): + from gcloud.datastore._generated import entity_pb2 + + entity_pb = entity_pb2.Entity() + name = 'foo' + result = self._callFUT(entity_pb, name) + + self.assertTrue(isinstance(result, entity_pb2.Value)) + self.assertEqual(len(entity_pb.property), 1) + self.assertEqual(entity_pb.property[0].name, name) + self.assertEqual(entity_pb.property[0].value, result) + + +class Test__property_tuples(unittest2.TestCase): + + def _callFUT(self, entity_pb): + from gcloud.datastore.helpers import _property_tuples + return _property_tuples(entity_pb) + + def test_it(self): + import types + from gcloud.datastore._generated import entity_pb2 + from gcloud.datastore.helpers import _new_value_pb + + entity_pb = entity_pb2.Entity() + name1 = 'foo' + name2 = 'bar' + val_pb1 = _new_value_pb(entity_pb, name1) + val_pb2 = _new_value_pb(entity_pb, name2) + + result = self._callFUT(entity_pb) + self.assertTrue(isinstance(result, types.GeneratorType)) + self.assertEqual(list(result), [(name1, val_pb1), (name2, val_pb2)]) + + class Test_entity_from_protobuf(unittest2.TestCase): def _callFUT(self, val): @@ -23,6 +64,7 @@ def _callFUT(self, val): def test_it(self): from gcloud.datastore._generated import entity_pb2 + from gcloud.datastore.helpers import _new_value_pb _DATASET_ID = 'DATASET' _KIND = 'KIND' @@ -30,30 +72,27 @@ def test_it(self): entity_pb = entity_pb2.Entity() entity_pb.key.partition_id.dataset_id = _DATASET_ID entity_pb.key.path_element.add(kind=_KIND, id=_ID) - prop_pb = entity_pb.property.add() - prop_pb.name = 'foo' - prop_pb.value.string_value = 'Foo' - unindexed_prop_pb = entity_pb.property.add() - unindexed_prop_pb.name = 'bar' - unindexed_prop_pb.value.integer_value = 10 - unindexed_prop_pb.value.indexed = False + value_pb = _new_value_pb(entity_pb, 'foo') + value_pb.string_value = 'Foo' - list_prop_pb1 = entity_pb.property.add() - list_prop_pb1.name = 'baz' - list_pb1 = list_prop_pb1.value.list_value + unindexed_val_pb = _new_value_pb(entity_pb, 'bar') + unindexed_val_pb.integer_value = 10 + unindexed_val_pb.indexed = False - unindexed_value_pb = list_pb1.add() - unindexed_value_pb.integer_value = 11 - unindexed_value_pb.indexed = False + list_val_pb1 = _new_value_pb(entity_pb, 'baz') + list_pb1 = list_val_pb1.list_value - list_prop_pb2 = entity_pb.property.add() - list_prop_pb2.name = 'qux' - list_pb2 = list_prop_pb2.value.list_value + unindexed_list_val_pb = list_pb1.add() + unindexed_list_val_pb.integer_value = 11 + unindexed_list_val_pb.indexed = False - indexed_value_pb = list_pb2.add() - indexed_value_pb.integer_value = 12 - indexed_value_pb.indexed = True + list_val_pb2 = _new_value_pb(entity_pb, 'qux') + list_pb2 = list_val_pb2.list_value + + indexed_list_val_pb = list_pb2.add() + indexed_list_val_pb.integer_value = 12 + indexed_list_val_pb.indexed = True entity = self._callFUT(entity_pb) self.assertEqual(entity.kind, _KIND) @@ -72,6 +111,7 @@ def test_it(self): def test_mismatched_value_indexed(self): from gcloud.datastore._generated import entity_pb2 + from gcloud.datastore.helpers import _new_value_pb _DATASET_ID = 'DATASET' _KIND = 'KIND' @@ -80,9 +120,8 @@ def test_mismatched_value_indexed(self): entity_pb.key.partition_id.dataset_id = _DATASET_ID entity_pb.key.path_element.add(kind=_KIND, id=_ID) - list_prop_pb = entity_pb.property.add() - list_prop_pb.name = 'baz' - list_pb = list_prop_pb.value.list_value + list_val_pb = _new_value_pb(entity_pb, 'baz') + list_pb = list_val_pb.list_value unindexed_value_pb1 = list_pb.add() unindexed_value_pb1.integer_value = 10 @@ -106,12 +145,13 @@ def test_entity_no_key(self): def test_entity_with_meaning(self): from gcloud.datastore._generated import entity_pb2 + from gcloud.datastore.helpers import _new_value_pb entity_pb = entity_pb2.Entity() - prop = entity_pb.property.add() - prop.value.meaning = meaning = 9 - prop.value.string_value = val = u'something' - prop.name = name = 'hello' + name = 'hello' + value_pb = _new_value_pb(entity_pb, name) + value_pb.meaning = meaning = 9 + value_pb.string_value = val = u'something' entity = self._callFUT(entity_pb) self.assertEqual(entity.key, None) @@ -120,6 +160,7 @@ def test_entity_with_meaning(self): def test_nested_entity_no_key(self): from gcloud.datastore._generated import entity_pb2 + from gcloud.datastore.helpers import _new_value_pb DATASET_ID = 's~FOO' KIND = 'KIND' @@ -128,18 +169,16 @@ def test_nested_entity_no_key(self): INSIDE_VALUE = 1337 entity_inside = entity_pb2.Entity() - inside_prop = entity_inside.property.add() - inside_prop.name = INSIDE_NAME - inside_prop.value.integer_value = INSIDE_VALUE + inside_val_pb = _new_value_pb(entity_inside, INSIDE_NAME) + inside_val_pb.integer_value = INSIDE_VALUE entity_pb = entity_pb2.Entity() entity_pb.key.partition_id.dataset_id = DATASET_ID element = entity_pb.key.path_element.add() element.kind = KIND - outside_prop = entity_pb.property.add() - outside_prop.name = OUTSIDE_NAME - outside_prop.value.entity_value.CopyFrom(entity_inside) + outside_val_pb = _new_value_pb(entity_pb, OUTSIDE_NAME) + outside_val_pb.entity_value.CopyFrom(entity_inside) entity = self._callFUT(entity_pb) self.assertEqual(entity.key.dataset_id, DATASET_ID) @@ -159,18 +198,20 @@ def _callFUT(self, entity): return entity_to_protobuf(entity) def _compareEntityProto(self, entity_pb1, entity_pb2): - import operator + from gcloud.datastore.helpers import _property_tuples + self.assertEqual(entity_pb1.key, entity_pb2.key) - name_getter = operator.attrgetter('name') - prop_list1 = sorted(entity_pb1.property, key=name_getter) - prop_list2 = sorted(entity_pb2.property, key=name_getter) - self.assertEqual(len(prop_list1), len(prop_list2)) - for val1, val2 in zip(prop_list1, prop_list2): - if val1.value.HasField('entity_value'): - self.assertEqual(val1.name, val2.name) - self.assertEqual(val1.value.meaning, val2.value.meaning) - self._compareEntityProto(val1.value.entity_value, - val2.value.entity_value) + value_list1 = sorted(_property_tuples(entity_pb1)) + value_list2 = sorted(_property_tuples(entity_pb2)) + self.assertEqual(len(value_list1), len(value_list2)) + for pair1, pair2 in zip(value_list1, value_list2): + name1, val1 = pair1 + name2, val2 = pair2 + self.assertEqual(name1, name2) + if val1.HasField('entity_value'): + self.assertEqual(val1.meaning, val2.meaning) + self._compareEntityProto(val1.entity_value, + val2.entity_value) else: self.assertEqual(val1, val2) @@ -204,6 +245,7 @@ def test_key_only(self): def test_simple_fields(self): from gcloud.datastore._generated import entity_pb2 from gcloud.datastore.entity import Entity + from gcloud.datastore.helpers import _new_value_pb entity = Entity() name1 = 'foo' @@ -213,12 +255,10 @@ def test_simple_fields(self): entity_pb = self._callFUT(entity) expected_pb = entity_pb2.Entity() - prop1 = expected_pb.property.add() - prop1.name = name1 - prop1.value.integer_value = value1 - prop2 = expected_pb.property.add() - prop2.name = name2 - prop2.value.string_value = value2 + val_pb1 = _new_value_pb(expected_pb, name1) + val_pb1.integer_value = value1 + val_pb2 = _new_value_pb(expected_pb, name2) + val_pb2.string_value = value2 self._compareEntityProto(entity_pb, expected_pb) @@ -234,6 +274,7 @@ def test_with_empty_list(self): def test_inverts_to_protobuf(self): from gcloud.datastore._generated import entity_pb2 + from gcloud.datastore.helpers import _new_value_pb from gcloud.datastore.helpers import entity_from_protobuf original_pb = entity_pb2.Entity() @@ -247,36 +288,30 @@ def test_inverts_to_protobuf(self): elem2.name = 'Spades' # Add an integer property. - prop1 = original_pb.property.add() - prop1.name = 'foo' - prop1.value.integer_value = 1337 - prop1.value.indexed = False + val_pb1 = _new_value_pb(original_pb, 'foo') + val_pb1.integer_value = 1337 + val_pb1.indexed = False # Add a string property. - prop2 = original_pb.property.add() - prop2.name = 'bar' - prop2.value.string_value = u'hello' + val_pb2 = _new_value_pb(original_pb, 'bar') + val_pb2.string_value = u'hello' # Add a nested (entity) property. - prop3 = original_pb.property.add() - prop3.name = 'entity-baz' + val_pb3 = _new_value_pb(original_pb, 'entity-baz') sub_pb = entity_pb2.Entity() - sub_prop1 = sub_pb.property.add() - sub_prop1.name = 'x' - sub_prop1.value.double_value = 3.14 - sub_prop2 = sub_pb.property.add() - sub_prop2.name = 'y' - sub_prop2.value.double_value = 2.718281828 - prop3.value.meaning = 9 - prop3.value.entity_value.CopyFrom(sub_pb) + sub_val_pb1 = _new_value_pb(sub_pb, 'x') + sub_val_pb1.double_value = 3.14 + sub_val_pb2 = _new_value_pb(sub_pb, 'y') + sub_val_pb2.double_value = 2.718281828 + val_pb3.meaning = 9 + val_pb3.entity_value.CopyFrom(sub_pb) # Add a list property. - prop4 = original_pb.property.add() - prop4.name = 'list-quux' - list_val1 = prop4.value.list_value.add() + val_pb4 = _new_value_pb(original_pb, 'list-quux') + list_val1 = val_pb4.list_value.add() list_val1.indexed = False list_val1.meaning = meaning = 22 list_val1.blob_value = b'\xe2\x98\x83' - list_val2 = prop4.value.list_value.add() + list_val2 = val_pb4.list_value.add() list_val2.indexed = False list_val2.meaning = meaning list_val2.blob_value = b'\xe2\x98\x85' @@ -293,6 +328,7 @@ def test_inverts_to_protobuf(self): def test_meaning_with_change(self): from gcloud.datastore._generated import entity_pb2 from gcloud.datastore.entity import Entity + from gcloud.datastore.helpers import _new_value_pb entity = Entity() name = 'foo' @@ -301,9 +337,8 @@ def test_meaning_with_change(self): entity_pb = self._callFUT(entity) expected_pb = entity_pb2.Entity() - prop = expected_pb.property.add() - prop.name = name - prop.value.integer_value = value + value_pb = _new_value_pb(expected_pb, name) + value_pb.integer_value = value # NOTE: No meaning is used since the value differs from the # value stored. self._compareEntityProto(entity_pb, expected_pb) @@ -523,14 +558,15 @@ def test_unicode(self): def test_entity(self): from gcloud.datastore._generated import entity_pb2 from gcloud.datastore.entity import Entity + from gcloud.datastore.helpers import _new_value_pb pb = entity_pb2.Value() entity_pb = pb.entity_value entity_pb.key.path_element.add(kind='KIND') entity_pb.key.partition_id.dataset_id = 'DATASET' - prop_pb = entity_pb.property.add() - prop_pb.name = 'foo' - prop_pb.value.string_value = 'Foo' + + value_pb = _new_value_pb(entity_pb, 'foo') + value_pb.string_value = 'Foo' entity = self._callFUT(pb) self.assertTrue(isinstance(entity, Entity)) self.assertEqual(entity['foo'], 'Foo') @@ -654,30 +690,34 @@ def test_unicode(self): def test_entity_empty_wo_key(self): from gcloud.datastore.entity import Entity + from gcloud.datastore.helpers import _property_tuples pb = self._makePB() entity = Entity() self._callFUT(pb, entity) value = pb.entity_value self.assertEqual(value.key.SerializeToString(), b'') - props = list(value.property) - self.assertEqual(len(props), 0) + self.assertEqual(len(list(_property_tuples(value))), 0) def test_entity_w_key(self): from gcloud.datastore.entity import Entity + from gcloud.datastore.helpers import _property_tuples from gcloud.datastore.key import Key + name = 'foo' + value = u'Foo' pb = self._makePB() key = Key('KIND', 123, dataset_id='DATASET') entity = Entity(key=key) - entity['foo'] = u'Foo' + entity[name] = value self._callFUT(pb, entity) - value = pb.entity_value - self.assertEqual(value.key, key.to_protobuf()) - props = list(value.property) - self.assertEqual(len(props), 1) - self.assertEqual(props[0].name, 'foo') - self.assertEqual(props[0].value.string_value, u'Foo') + entity_pb = pb.entity_value + self.assertEqual(entity_pb.key, key.to_protobuf()) + + prop_dict = dict(_property_tuples(entity_pb)) + self.assertEqual(len(prop_dict), 1) + self.assertEqual(list(prop_dict.keys()), [name]) + self.assertEqual(prop_dict[name].string_value, value) def test_list(self): pb = self._makePB() diff --git a/gcloud/datastore/test_query.py b/gcloud/datastore/test_query.py index f1d3a3a169a8..0869d96adefe 100644 --- a/gcloud/datastore/test_query.py +++ b/gcloud/datastore/test_query.py @@ -328,6 +328,7 @@ def _makeOne(self, *args, **kw): def _addQueryResults(self, connection, cursor=_END, more=False): from gcloud.datastore._generated import entity_pb2 from gcloud.datastore._generated import query_pb2 + from gcloud.datastore.helpers import _new_value_pb MORE = query_pb2.QueryResultBatch.NOT_FINISHED NO_MORE = query_pb2.QueryResultBatch.MORE_RESULTS_AFTER_LIMIT @@ -337,9 +338,8 @@ def _addQueryResults(self, connection, cursor=_END, more=False): path_element = entity_pb.key.path_element.add() path_element.kind = self._KIND path_element.id = _ID - prop = entity_pb.property.add() - prop.name = 'foo' - prop.value.string_value = u'Foo' + value_pb = _new_value_pb(entity_pb, 'foo') + value_pb.string_value = u'Foo' connection._results.append( ([entity_pb], cursor, MORE if more else NO_MORE))