Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5b67daa

Browse files
authored
fix: optimized protobuf access for performance (#155)
More efficiently uses proto-plus wrappers, as well as inner protobuf attribute access, to greatly reduce the performance costs seen in version 2.0.0 (which stemmed from the introduction of proto-plus). The size of the performance improvement scales with the number of attributes on each Entity, but in general, speeds once again closely approximate those from 1.15. Fixes #145 Fixes #150
1 parent 31bb053 commit 5b67daa

File tree

2 files changed

+33
-19
lines changed

2 files changed

+33
-19
lines changed

google/cloud/datastore/helpers.py

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,21 @@ def _get_meaning(value_pb, is_list=False):
4949
"""
5050
meaning = None
5151
if is_list:
52+
53+
values = (
54+
value_pb._pb.array_value.values
55+
if hasattr(value_pb, "_pb")
56+
else value_pb.array_value.values
57+
)
58+
5259
# An empty list will have no values, hence no shared meaning
5360
# set among them.
54-
if len(value_pb.array_value.values) == 0:
61+
if len(values) == 0:
5562
return None
5663

5764
# We check among all the meanings, some of which may be None,
5865
# the rest which may be enum/int values.
59-
all_meanings = [
60-
_get_meaning(sub_value_pb) for sub_value_pb in value_pb.array_value.values
61-
]
66+
all_meanings = [_get_meaning(sub_value_pb) for sub_value_pb in values]
6267
unique_meanings = set(all_meanings)
6368
if len(unique_meanings) == 1:
6469
# If there is a unique meaning, we preserve it.
@@ -119,11 +124,8 @@ def entity_from_protobuf(pb):
119124
:rtype: :class:`google.cloud.datastore.entity.Entity`
120125
:returns: The entity derived from the protobuf.
121126
"""
122-
123-
if not getattr(pb, "_pb", False):
124-
# Coerce raw pb type into proto-plus pythonic type.
125-
proto_pb = entity_pb2.Entity(pb)
126-
pb = pb
127+
if not isinstance(pb, entity_pb2.Entity):
128+
proto_pb = entity_pb2.Entity.wrap(pb)
127129
else:
128130
proto_pb = pb
129131
pb = pb._pb
@@ -152,7 +154,7 @@ def entity_from_protobuf(pb):
152154
if is_list and len(value) > 0:
153155
exclude_values = set(
154156
value_pb.exclude_from_indexes
155-
for value_pb in value_pb.array_value.values
157+
for value_pb in value_pb._pb.array_value.values
156158
)
157159
if len(exclude_values) != 1:
158160
raise ValueError(
@@ -402,33 +404,36 @@ def _get_value_from_value_pb(value):
402404
"""
403405
if not getattr(value, "_pb", False):
404406
# Coerce raw pb type into proto-plus pythonic type.
405-
value = entity_pb2.Value(value)
407+
value = entity_pb2.Value.wrap(value)
406408

407409
value_type = value._pb.WhichOneof("value_type")
408410

409411
if value_type == "timestamp_value":
412+
# Do not access `._pb` here, as that returns a Timestamp proto,
413+
# but this should return a Pythonic `DatetimeWithNanoseconds` value,
414+
# which is found at `value.timestamp_value`
410415
result = value.timestamp_value
411416

412417
elif value_type == "key_value":
413-
result = key_from_protobuf(value.key_value)
418+
result = key_from_protobuf(value._pb.key_value)
414419

415420
elif value_type == "boolean_value":
416-
result = value.boolean_value
421+
result = value._pb.boolean_value
417422

418423
elif value_type == "double_value":
419-
result = value.double_value
424+
result = value._pb.double_value
420425

421426
elif value_type == "integer_value":
422-
result = value.integer_value
427+
result = value._pb.integer_value
423428

424429
elif value_type == "string_value":
425-
result = value.string_value
430+
result = value._pb.string_value
426431

427432
elif value_type == "blob_value":
428-
result = value.blob_value
433+
result = value._pb.blob_value
429434

430435
elif value_type == "entity_value":
431-
result = entity_from_protobuf(value.entity_value)
436+
result = entity_from_protobuf(value._pb.entity_value)
432437

433438
elif value_type == "array_value":
434439
result = [
@@ -437,7 +442,7 @@ def _get_value_from_value_pb(value):
437442

438443
elif value_type == "geo_point_value":
439444
result = GeoPoint(
440-
value.geo_point_value.latitude, value.geo_point_value.longitude,
445+
value._pb.geo_point_value.latitude, value._pb.geo_point_value.longitude,
441446
)
442447

443448
elif value_type == "null_value":

tests/unit/test_helpers.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,15 @@ def test_entity_no_key(self):
140140
self.assertIsNone(entity.key)
141141
self.assertEqual(dict(entity), {})
142142

143+
def test_pb2_entity_no_key(self):
144+
from google.cloud.datastore_v1.types import entity as entity_pb2
145+
146+
entity_pb = entity_pb2.Entity()
147+
entity = self._call_fut(entity_pb)
148+
149+
self.assertIsNone(entity.key)
150+
self.assertEqual(dict(entity), {})
151+
143152
def test_entity_with_meaning(self):
144153
from google.cloud.datastore_v1.types import entity as entity_pb2
145154
from google.cloud.datastore.helpers import _new_value_pb

0 commit comments

Comments
 (0)