Skip to content

Commit

Permalink
Moving _get_pb_property_value from bigtable into core.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dhermes committed Jan 15, 2016
1 parent 3f18953 commit 6dfe5d4
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 46 deletions.
43 changes: 43 additions & 0 deletions gcloud/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,49 @@ def _datetime_to_pb_timestamp(when):
return timestamp_pb2.Timestamp(seconds=seconds, nanos=nanos)


def _has_field(message_pb, property_name):
"""Determine if a field is set on a protobuf.
:type message_pb: :class:`google.protobuf.message.Message`
:param message_pb: The message to check for ``property_name``.
:type property_name: str
:param property_name: The property value to check against.
:rtype: bool
:returns: Flag indicating if ``property_name`` is set on ``message_pb``.
"""
# NOTE: As of proto3, HasField() only works for message fields, not for
# singular (non-message) fields. First try to use HasField and
# if it fails (with a ValueError) we manually consult the fields.
try:
return message_pb.HasField(property_name)
except ValueError:
all_fields = set([field.name for field in message_pb._fields])
return property_name in all_fields


def _get_pb_property_value(message_pb, property_name):
"""Return a message field value.
:type message_pb: :class:`google.protobuf.message.Message`
:param message_pb: The message to check for ``property_name``.
:type property_name: str
:param property_name: The property value to check against.
:rtype: object
:returns: The value of ``property_name`` set on ``message_pb``.
:raises: :class:`ValueError <exceptions.ValueError>` if the result returned
from the ``message_pb`` does not contain the ``property_name``
value.
"""
if _has_field(message_pb, property_name):
return getattr(message_pb, property_name)
else:
raise ValueError('Message does not contain %s.' % (property_name,))


try:
from pytz import UTC # pylint: disable=unused-import,wrong-import-position
except ImportError:
Expand Down
25 changes: 1 addition & 24 deletions gcloud/bigtable/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from google.longrunning import operations_pb2

from gcloud._helpers import _pb_timestamp_to_datetime
from gcloud._helpers import _get_pb_property_value
from gcloud.bigtable._generated import bigtable_cluster_data_pb2 as data_pb2
from gcloud.bigtable._generated import (
bigtable_cluster_service_messages_pb2 as messages_pb2)
Expand Down Expand Up @@ -47,30 +48,6 @@
}


def _get_pb_property_value(message_pb, property_name):
"""Return a message field value.
:type message_pb: :class:`google.protobuf.message.Message`
:param message_pb: The message to check for ``property_name``.
:type property_name: str
:param property_name: The property value to check against.
:rtype: object
:returns: The value of ``property_name`` set on ``message_pb``.
:raises: :class:`ValueError <exceptions.ValueError>` if the result returned
from the ``message_pb`` does not contain the ``property_name``
value.
"""
# Make sure `property_name` is set on the response.
# NOTE: As of proto3, HasField() only works for message fields, not for
# singular (non-message) fields.
all_fields = set([field.name for field in message_pb._fields])
if property_name not in all_fields:
raise ValueError('Message does not contain %s.' % (property_name,))
return getattr(message_pb, property_name)


def _prepare_create_request(cluster):
"""Creates a protobuf request for a CreateCluster request.
Expand Down
22 changes: 0 additions & 22 deletions gcloud/bigtable/test_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,28 +631,6 @@ def test_list_tables_failure_name_bad_before(self):
self._list_tables_helper(table_id, table_name=bad_table_name)


class Test__get_pb_property_value(unittest2.TestCase):

def _callFUT(self, message_pb, property_name):
from gcloud.bigtable.cluster import _get_pb_property_value
return _get_pb_property_value(message_pb, property_name)

def test_it(self):
from gcloud.bigtable._generated import (
bigtable_cluster_data_pb2 as data_pb2)
serve_nodes = 119
cluster_pb = data_pb2.Cluster(serve_nodes=serve_nodes)
result = self._callFUT(cluster_pb, 'serve_nodes')
self.assertEqual(result, serve_nodes)

def test_with_value_unset_on_pb(self):
from gcloud.bigtable._generated import (
bigtable_cluster_data_pb2 as data_pb2)
cluster_pb = data_pb2.Cluster()
with self.assertRaises(ValueError):
self._callFUT(cluster_pb, 'serve_nodes')


class Test__prepare_create_request(unittest2.TestCase):

def _callFUT(self, cluster):
Expand Down
66 changes: 66 additions & 0 deletions gcloud/test__helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,72 @@ def test_it(self):
self.assertEqual(self._callFUT(dt_stamp), timestamp)


class Test__has_field(unittest2.TestCase):

def _callFUT(self, message_pb, property_name):
from gcloud._helpers import _has_field
return _has_field(message_pb, property_name)

def test_unset_simple_field(self):
from gcloud.bigtable._generated import (
bigtable_cluster_data_pb2 as data_pb2)
cluster_pb = data_pb2.Cluster()
self.assertFalse(self._callFUT(cluster_pb, 'serve_nodes'))

def test_set_simple_field(self):
from gcloud.bigtable._generated import (
bigtable_cluster_data_pb2 as data_pb2)
serve_nodes = 119
cluster_pb = data_pb2.Cluster(serve_nodes=serve_nodes)
self.assertTrue(self._callFUT(cluster_pb, 'serve_nodes'))

def test_unset_message_field(self):
from gcloud.bigtable._generated import (
bigtable_cluster_data_pb2 as data_pb2)
from gcloud.bigtable._generated.timestamp_pb2 import Timestamp
cluster_pb = data_pb2.Cluster()
# Check that no fields are attached to the protobuf message.
self.assertEqual(len(cluster_pb._fields), 0)
self.assertEqual(cluster_pb.delete_time, Timestamp())
# Check that the field is now attached to the protobuf message,
# even though the value is unset.
self.assertEqual([field.name for field in cluster_pb._fields.keys()],
['delete_time'])
# Make sure has field is still False.
self.assertFalse(self._callFUT(cluster_pb, 'delete_time'))

def test_set_message_field(self):
from gcloud.bigtable._generated import (
bigtable_cluster_data_pb2 as data_pb2)
from gcloud.bigtable._generated.timestamp_pb2 import Timestamp

timestamp = Timestamp()
cluster_pb = data_pb2.Cluster(delete_time=timestamp)
self.assertTrue(self._callFUT(cluster_pb, 'delete_time'))


class Test__get_pb_property_value(unittest2.TestCase):

def _callFUT(self, message_pb, property_name):
from gcloud._helpers import _get_pb_property_value
return _get_pb_property_value(message_pb, property_name)

def test_it(self):
from gcloud.bigtable._generated import (
bigtable_cluster_data_pb2 as data_pb2)
serve_nodes = 119
cluster_pb = data_pb2.Cluster(serve_nodes=serve_nodes)
result = self._callFUT(cluster_pb, 'serve_nodes')
self.assertEqual(result, serve_nodes)

def test_with_value_unset_on_pb(self):
from gcloud.bigtable._generated import (
bigtable_cluster_data_pb2 as data_pb2)
cluster_pb = data_pb2.Cluster()
with self.assertRaises(ValueError):
self._callFUT(cluster_pb, 'serve_nodes')


class _AppIdentity(object):

def __init__(self, app_id):
Expand Down

0 comments on commit 6dfe5d4

Please sign in to comment.