Skip to content

Commit 6dfe5d4

Browse files
committed
Moving _get_pb_property_value from bigtable into core.
Doing this so that we can factor out `_has_field` and use it across packages. This is because `HasField()` works differently in `proto3` and we use this behavior from time to time.
1 parent 3f18953 commit 6dfe5d4

File tree

4 files changed

+110
-46
lines changed

4 files changed

+110
-46
lines changed

gcloud/_helpers.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,49 @@ def _datetime_to_pb_timestamp(when):
388388
return timestamp_pb2.Timestamp(seconds=seconds, nanos=nanos)
389389

390390

391+
def _has_field(message_pb, property_name):
392+
"""Determine if a field is set on a protobuf.
393+
394+
:type message_pb: :class:`google.protobuf.message.Message`
395+
:param message_pb: The message to check for ``property_name``.
396+
397+
:type property_name: str
398+
:param property_name: The property value to check against.
399+
400+
:rtype: bool
401+
:returns: Flag indicating if ``property_name`` is set on ``message_pb``.
402+
"""
403+
# NOTE: As of proto3, HasField() only works for message fields, not for
404+
# singular (non-message) fields. First try to use HasField and
405+
# if it fails (with a ValueError) we manually consult the fields.
406+
try:
407+
return message_pb.HasField(property_name)
408+
except ValueError:
409+
all_fields = set([field.name for field in message_pb._fields])
410+
return property_name in all_fields
411+
412+
413+
def _get_pb_property_value(message_pb, property_name):
414+
"""Return a message field value.
415+
416+
:type message_pb: :class:`google.protobuf.message.Message`
417+
:param message_pb: The message to check for ``property_name``.
418+
419+
:type property_name: str
420+
:param property_name: The property value to check against.
421+
422+
:rtype: object
423+
:returns: The value of ``property_name`` set on ``message_pb``.
424+
:raises: :class:`ValueError <exceptions.ValueError>` if the result returned
425+
from the ``message_pb`` does not contain the ``property_name``
426+
value.
427+
"""
428+
if _has_field(message_pb, property_name):
429+
return getattr(message_pb, property_name)
430+
else:
431+
raise ValueError('Message does not contain %s.' % (property_name,))
432+
433+
391434
try:
392435
from pytz import UTC # pylint: disable=unused-import,wrong-import-position
393436
except ImportError:

gcloud/bigtable/cluster.py

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from google.longrunning import operations_pb2
2121

2222
from gcloud._helpers import _pb_timestamp_to_datetime
23+
from gcloud._helpers import _get_pb_property_value
2324
from gcloud.bigtable._generated import bigtable_cluster_data_pb2 as data_pb2
2425
from gcloud.bigtable._generated import (
2526
bigtable_cluster_service_messages_pb2 as messages_pb2)
@@ -47,30 +48,6 @@
4748
}
4849

4950

50-
def _get_pb_property_value(message_pb, property_name):
51-
"""Return a message field value.
52-
53-
:type message_pb: :class:`google.protobuf.message.Message`
54-
:param message_pb: The message to check for ``property_name``.
55-
56-
:type property_name: str
57-
:param property_name: The property value to check against.
58-
59-
:rtype: object
60-
:returns: The value of ``property_name`` set on ``message_pb``.
61-
:raises: :class:`ValueError <exceptions.ValueError>` if the result returned
62-
from the ``message_pb`` does not contain the ``property_name``
63-
value.
64-
"""
65-
# Make sure `property_name` is set on the response.
66-
# NOTE: As of proto3, HasField() only works for message fields, not for
67-
# singular (non-message) fields.
68-
all_fields = set([field.name for field in message_pb._fields])
69-
if property_name not in all_fields:
70-
raise ValueError('Message does not contain %s.' % (property_name,))
71-
return getattr(message_pb, property_name)
72-
73-
7451
def _prepare_create_request(cluster):
7552
"""Creates a protobuf request for a CreateCluster request.
7653

gcloud/bigtable/test_cluster.py

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -631,28 +631,6 @@ def test_list_tables_failure_name_bad_before(self):
631631
self._list_tables_helper(table_id, table_name=bad_table_name)
632632

633633

634-
class Test__get_pb_property_value(unittest2.TestCase):
635-
636-
def _callFUT(self, message_pb, property_name):
637-
from gcloud.bigtable.cluster import _get_pb_property_value
638-
return _get_pb_property_value(message_pb, property_name)
639-
640-
def test_it(self):
641-
from gcloud.bigtable._generated import (
642-
bigtable_cluster_data_pb2 as data_pb2)
643-
serve_nodes = 119
644-
cluster_pb = data_pb2.Cluster(serve_nodes=serve_nodes)
645-
result = self._callFUT(cluster_pb, 'serve_nodes')
646-
self.assertEqual(result, serve_nodes)
647-
648-
def test_with_value_unset_on_pb(self):
649-
from gcloud.bigtable._generated import (
650-
bigtable_cluster_data_pb2 as data_pb2)
651-
cluster_pb = data_pb2.Cluster()
652-
with self.assertRaises(ValueError):
653-
self._callFUT(cluster_pb, 'serve_nodes')
654-
655-
656634
class Test__prepare_create_request(unittest2.TestCase):
657635

658636
def _callFUT(self, cluster):

gcloud/test__helpers.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,72 @@ def test_it(self):
526526
self.assertEqual(self._callFUT(dt_stamp), timestamp)
527527

528528

529+
class Test__has_field(unittest2.TestCase):
530+
531+
def _callFUT(self, message_pb, property_name):
532+
from gcloud._helpers import _has_field
533+
return _has_field(message_pb, property_name)
534+
535+
def test_unset_simple_field(self):
536+
from gcloud.bigtable._generated import (
537+
bigtable_cluster_data_pb2 as data_pb2)
538+
cluster_pb = data_pb2.Cluster()
539+
self.assertFalse(self._callFUT(cluster_pb, 'serve_nodes'))
540+
541+
def test_set_simple_field(self):
542+
from gcloud.bigtable._generated import (
543+
bigtable_cluster_data_pb2 as data_pb2)
544+
serve_nodes = 119
545+
cluster_pb = data_pb2.Cluster(serve_nodes=serve_nodes)
546+
self.assertTrue(self._callFUT(cluster_pb, 'serve_nodes'))
547+
548+
def test_unset_message_field(self):
549+
from gcloud.bigtable._generated import (
550+
bigtable_cluster_data_pb2 as data_pb2)
551+
from gcloud.bigtable._generated.timestamp_pb2 import Timestamp
552+
cluster_pb = data_pb2.Cluster()
553+
# Check that no fields are attached to the protobuf message.
554+
self.assertEqual(len(cluster_pb._fields), 0)
555+
self.assertEqual(cluster_pb.delete_time, Timestamp())
556+
# Check that the field is now attached to the protobuf message,
557+
# even though the value is unset.
558+
self.assertEqual([field.name for field in cluster_pb._fields.keys()],
559+
['delete_time'])
560+
# Make sure has field is still False.
561+
self.assertFalse(self._callFUT(cluster_pb, 'delete_time'))
562+
563+
def test_set_message_field(self):
564+
from gcloud.bigtable._generated import (
565+
bigtable_cluster_data_pb2 as data_pb2)
566+
from gcloud.bigtable._generated.timestamp_pb2 import Timestamp
567+
568+
timestamp = Timestamp()
569+
cluster_pb = data_pb2.Cluster(delete_time=timestamp)
570+
self.assertTrue(self._callFUT(cluster_pb, 'delete_time'))
571+
572+
573+
class Test__get_pb_property_value(unittest2.TestCase):
574+
575+
def _callFUT(self, message_pb, property_name):
576+
from gcloud._helpers import _get_pb_property_value
577+
return _get_pb_property_value(message_pb, property_name)
578+
579+
def test_it(self):
580+
from gcloud.bigtable._generated import (
581+
bigtable_cluster_data_pb2 as data_pb2)
582+
serve_nodes = 119
583+
cluster_pb = data_pb2.Cluster(serve_nodes=serve_nodes)
584+
result = self._callFUT(cluster_pb, 'serve_nodes')
585+
self.assertEqual(result, serve_nodes)
586+
587+
def test_with_value_unset_on_pb(self):
588+
from gcloud.bigtable._generated import (
589+
bigtable_cluster_data_pb2 as data_pb2)
590+
cluster_pb = data_pb2.Cluster()
591+
with self.assertRaises(ValueError):
592+
self._callFUT(cluster_pb, 'serve_nodes')
593+
594+
529595
class _AppIdentity(object):
530596

531597
def __init__(self, app_id):

0 commit comments

Comments
 (0)