From 1a79c899bac0f0e3e25bd7bee2af42091297f18e Mon Sep 17 00:00:00 2001
From: Tres Seaver <tseaver@palladion.com>
Date: Thu, 9 Oct 2014 10:16:41 -0400
Subject: [PATCH 01/10] Add explicit support for 'entity_value' in protobufs.

---
 gcloud/datastore/helpers.py      |  6 ++++++
 gcloud/datastore/test_helpers.py | 23 +++++++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/gcloud/datastore/helpers.py b/gcloud/datastore/helpers.py
index 2e8aa5f80bda..cae7070c8762 100644
--- a/gcloud/datastore/helpers.py
+++ b/gcloud/datastore/helpers.py
@@ -5,6 +5,7 @@
 from google.protobuf.internal.type_checkers import Int64ValueChecker
 import pytz
 
+from gcloud.datastore.entity import Entity
 from gcloud.datastore.key import Key
 
 INT_VALUE_CHECKER = Int64ValueChecker()
@@ -60,6 +61,8 @@ def get_protobuf_attribute_and_value(val):
         name, value = 'integer', long(val)  # Always cast to a long.
     elif isinstance(val, basestring):
         name, value = 'string', val
+    elif isinstance(val, Entity):
+        name, value = 'entity', val
     else:
         raise ValueError("Unknown protobuf attr type %s" % type(val))
 
@@ -103,5 +106,8 @@ def get_value_from_protobuf(pb):
     elif pb.value.HasField('string_value'):
         return pb.value.string_value
 
+    elif pb.value.HasField('entity_value'):
+        return Entity.from_protobuf(pb.value.entity_value) # dataset?
+
     else:
         return None
diff --git a/gcloud/datastore/test_helpers.py b/gcloud/datastore/test_helpers.py
index 3a77c570b5a9..c8ea33bc362a 100644
--- a/gcloud/datastore/test_helpers.py
+++ b/gcloud/datastore/test_helpers.py
@@ -83,6 +83,13 @@ def test_unicode(self):
         self.assertEqual(name, 'string_value')
         self.assertEqual(value, u'str')
 
+    def test_entity(self):
+        from gcloud.datastore.entity import Entity
+        entity = Entity()
+        name, value = self._callFUT(entity)
+        self.assertEqual(name, 'entity_value')
+        self.assertTrue(value is entity)
+
     def test_object(self):
         self.assertRaises(ValueError, self._callFUT, object())
 
@@ -146,6 +153,22 @@ def test_unicode(self):
         pb = self._makePB('string_value', u'str')
         self.assertEqual(self._callFUT(pb), u'str')
 
+    def test_entity(self):
+        from gcloud.datastore.datastore_v1_pb2 import Property
+        from gcloud.datastore.entity import Entity
+
+        _DATASET = 'DATASET'
+        _KIND = 'KIND'
+        _ID = 1234
+        pb = Property()
+        entity_pb = pb.value.entity_value
+        prop_pb = entity_pb.property.add()
+        prop_pb.name = 'foo'
+        prop_pb.value.string_value = 'Foo'
+        entity = self._callFUT(pb)
+        self.assertTrue(isinstance(entity, Entity))
+        self.assertEqual(entity['foo'], 'Foo')
+
     def test_unknown(self):
         from gcloud.datastore.datastore_v1_pb2 import Property
         pb = Property()

From e5954fcd22941b4221387a76a1e562b2e8097d70 Mon Sep 17 00:00:00 2001
From: Tres Seaver <tseaver@palladion.com>
Date: Thu, 9 Oct 2014 10:18:35 -0400
Subject: [PATCH 02/10] Lint removal.

---
 gcloud/datastore/helpers.py      | 2 +-
 gcloud/datastore/test_helpers.py | 3 ---
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/gcloud/datastore/helpers.py b/gcloud/datastore/helpers.py
index cae7070c8762..29d66f337ce5 100644
--- a/gcloud/datastore/helpers.py
+++ b/gcloud/datastore/helpers.py
@@ -107,7 +107,7 @@ def get_value_from_protobuf(pb):
         return pb.value.string_value
 
     elif pb.value.HasField('entity_value'):
-        return Entity.from_protobuf(pb.value.entity_value) # dataset?
+        return Entity.from_protobuf(pb.value.entity_value)  # XXX dataset?
 
     else:
         return None
diff --git a/gcloud/datastore/test_helpers.py b/gcloud/datastore/test_helpers.py
index c8ea33bc362a..664eac3a2892 100644
--- a/gcloud/datastore/test_helpers.py
+++ b/gcloud/datastore/test_helpers.py
@@ -157,9 +157,6 @@ def test_entity(self):
         from gcloud.datastore.datastore_v1_pb2 import Property
         from gcloud.datastore.entity import Entity
 
-        _DATASET = 'DATASET'
-        _KIND = 'KIND'
-        _ID = 1234
         pb = Property()
         entity_pb = pb.value.entity_value
         prop_pb = entity_pb.property.add()

From c8f9a7a8cbf756c08c133f210cda73987b554547 Mon Sep 17 00:00:00 2001
From: Tres Seaver <tseaver@palladion.com>
Date: Thu, 9 Oct 2014 11:30:08 -0400
Subject: [PATCH 03/10] Add helpers.set_protobuf_value.

Handles compount fields (entity, key) not assignable via setattr.
---
 gcloud/datastore/helpers.py      |  36 +++++++++++
 gcloud/datastore/test_helpers.py | 105 +++++++++++++++++++++++++++++++
 2 files changed, 141 insertions(+)

diff --git a/gcloud/datastore/helpers.py b/gcloud/datastore/helpers.py
index 29d66f337ce5..fc183088335a 100644
--- a/gcloud/datastore/helpers.py
+++ b/gcloud/datastore/helpers.py
@@ -111,3 +111,39 @@ def get_value_from_protobuf(pb):
 
     else:
         return None
+
+
+def set_protobuf_value(value_pb, val):
+    """Assign 'val' to the correct subfield of 'value_pb'.
+
+    The Protobuf API uses different attribute names
+    based on value types rather than inferring the type.
+
+    Some value types (entities, keys, lists) cannot be directly assigned;
+    this function handles them correctly.
+
+    :type value_pb: :class:`gcloud.datastore.datastore_v1_pb2.Value`
+    :param value: The value protobuf to which the value is being assigned.
+
+    :type val: `datetime.datetime`, bool, float, integer, string
+               :class:`gcloud.datastore.key.Key`,
+               :class:`gcloud.datastore.entity.Entity`,
+    :param val: The value to be assigned.
+    """
+    attr, val = get_protobuf_attribute_and_value(val)
+    if attr == 'key_value':
+        value_pb.key_value.CopyFrom(val)
+    elif attr == 'entity_value':
+        e_pb = value_pb.entity_value
+        e_pb.Clear()
+        key = val.key()
+        if key is None:
+            e_pb.key.CopyFrom(Key().to_protobuf())
+        else:
+            e_pb.key.CopyFrom(key.to_protobuf())
+        for k, v in val.items():
+            p_pb = e_pb.property.add()
+            p_pb.name = k
+            set_protobuf_value(p_pb.value, v)
+    else:  # scalar, just assign
+        setattr(value_pb, attr, val)
diff --git a/gcloud/datastore/test_helpers.py b/gcloud/datastore/test_helpers.py
index 664eac3a2892..2d4bb9a62086 100644
--- a/gcloud/datastore/test_helpers.py
+++ b/gcloud/datastore/test_helpers.py
@@ -168,5 +168,110 @@ def test_entity(self):
 
     def test_unknown(self):
         from gcloud.datastore.datastore_v1_pb2 import Property
+
         pb = Property()
         self.assertEqual(self._callFUT(pb), None)  # XXX desirable?
+
+
+class Test_set_protobuf_value(unittest2.TestCase):
+
+    def _callFUT(self, value_pb, val):
+        from gcloud.datastore.helpers import set_protobuf_value
+
+        return set_protobuf_value(value_pb, val)
+
+    def _makePB(self):
+        from gcloud.datastore.datastore_v1_pb2 import Value
+
+        return Value()
+
+    def test_datetime(self):
+        import calendar
+        import datetime
+        import pytz
+
+        pb = self._makePB()
+        utc = datetime.datetime(2014, 9, 16, 10, 19, 32, 4375, pytz.utc)
+        self._callFUT(pb, utc)
+        value = pb.timestamp_microseconds_value
+        self.assertEqual(value / 1000000, calendar.timegm(utc.timetuple()))
+        self.assertEqual(value % 1000000, 4375)
+
+    def test_key(self):
+        from gcloud.datastore.dataset import Dataset
+        from gcloud.datastore.key import Key
+
+        _DATASET = 'DATASET'
+        _KIND = 'KIND'
+        _ID = 1234
+        _PATH = [{'kind': _KIND, 'id': _ID}]
+        pb = self._makePB()
+        key = Key(dataset=Dataset(_DATASET), path=_PATH)
+        self._callFUT(pb, key)
+        value = pb.key_value
+        self.assertEqual(value, key.to_protobuf())
+
+    def test_bool(self):
+        pb = self._makePB()
+        self._callFUT(pb, False)
+        value = pb.boolean_value
+        self.assertEqual(value, False)
+
+    def test_float(self):
+        pb = self._makePB()
+        self._callFUT(pb, 3.1415926)
+        value = pb.double_value
+        self.assertEqual(value, 3.1415926)
+
+    def test_int(self):
+        pb = self._makePB()
+        self._callFUT(pb, 42)
+        value = pb.integer_value
+        self.assertEqual(value, 42)
+
+    def test_long(self):
+        pb = self._makePB()
+        must_be_long = (1 << 63) - 1
+        self._callFUT(pb, must_be_long)
+        value = pb.integer_value
+        self.assertEqual(value, must_be_long)
+
+    def test_native_str(self):
+        pb = self._makePB()
+        self._callFUT(pb, 'str')
+        value = pb.string_value
+        self.assertEqual(value, 'str')
+
+    def test_unicode(self):
+        pb = self._makePB()
+        self._callFUT(pb, u'str')
+        value = pb.string_value
+        self.assertEqual(value, u'str')
+
+    def test_entity_empty_wo_key(self):
+        from gcloud.datastore.entity import Entity
+        from gcloud.datastore.key import Key
+
+        pb = self._makePB()
+        entity = Entity()
+        self._callFUT(pb, entity)
+        value = pb.entity_value
+        self.assertEqual(value.key, Key().to_protobuf())
+        props = list(value.property)
+        self.assertEqual(len(props), 0)
+
+    def test_entity_w_key(self):
+        from gcloud.datastore.entity import Entity
+        from gcloud.datastore.key import Key
+
+        pb = self._makePB()
+        key = Key(path=[{'kind': 'KIND', 'id': 123}])
+        entity = Entity().key(key)
+        entity['foo'] = 'Foo'
+        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, 'Foo')

From 37304c9c4941ac52a2c0ce0246b6da20c1141a6d Mon Sep 17 00:00:00 2001
From: Tres Seaver <tseaver@palladion.com>
Date: Thu, 9 Oct 2014 11:41:19 -0400
Subject: [PATCH 04/10] Use helpers.set_protobuf_value to support composite
 types.

---
 gcloud/datastore/connection.py      |  3 +--
 gcloud/datastore/query.py           |  3 +--
 gcloud/datastore/test_connection.py | 26 ++++++++++++++++++++++++++
 gcloud/datastore/test_query.py      | 23 +++++++++++++++++++++++
 4 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/gcloud/datastore/connection.py b/gcloud/datastore/connection.py
index 529bdcfaff06..7f97efa2141c 100644
--- a/gcloud/datastore/connection.py
+++ b/gcloud/datastore/connection.py
@@ -323,8 +323,7 @@ def save_entity(self, dataset_id, key_pb, properties):
             prop.name = name
 
             # Set the appropriate value.
-            pb_attr, pb_value = helpers.get_protobuf_attribute_and_value(value)
-            setattr(prop.value, pb_attr, pb_value)
+            helpers.set_protobuf_value(prop.value, value)
 
         # If this is in a transaction, we should just return True. The
         # transaction will handle assigning any keys as necessary.
diff --git a/gcloud/datastore/query.py b/gcloud/datastore/query.py
index 76a6ed12ae0e..990c075652d1 100644
--- a/gcloud/datastore/query.py
+++ b/gcloud/datastore/query.py
@@ -131,8 +131,7 @@ def filter(self, expression, value):
         property_filter.operator = operator
 
         # Set the value to filter on based on the type.
-        attr_name, pb_value = helpers.get_protobuf_attribute_and_value(value)
-        setattr(property_filter.value, attr_name, pb_value)
+        helpers.set_protobuf_value(property_filter.value, value)
         return clone
 
     def ancestor(self, ancestor):
diff --git a/gcloud/datastore/test_connection.py b/gcloud/datastore/test_connection.py
index d0cb0da81921..2a2af9209cfb 100644
--- a/gcloud/datastore/test_connection.py
+++ b/gcloud/datastore/test_connection.py
@@ -699,6 +699,32 @@ def mutation(self):
         mutation = conn.mutation()
         self.assertEqual(len(mutation.upsert), 1)
 
+    def test_save_entity_w_transaction_nested_entity(self):
+        from gcloud.datastore.connection import datastore_pb
+        from gcloud.datastore.dataset import Dataset
+        from gcloud.datastore.entity import Entity
+        from gcloud.datastore.key import Key
+
+        mutation = datastore_pb.Mutation()
+
+        class Xact(object):
+            def mutation(self):
+                return mutation
+        DATASET_ID = 'DATASET'
+        nested = Entity()
+        nested['bar'] = 'Bar'
+        key_pb = Key(dataset=Dataset(DATASET_ID),
+                     path=[{'kind': 'Kind', 'id': 1234}]).to_protobuf()
+        rsp_pb = datastore_pb.CommitResponse()
+        conn = self._makeOne()
+        conn.transaction(Xact())
+        http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString())
+        result = conn.save_entity(DATASET_ID, key_pb, {'foo': nested})
+        self.assertEqual(result, True)
+        self.assertEqual(http._called_with, None)
+        mutation = conn.mutation()
+        self.assertEqual(len(mutation.upsert), 1)
+
     def test_delete_entities_wo_transaction(self):
         from gcloud.datastore.connection import datastore_pb
         from gcloud.datastore.dataset import Dataset
diff --git a/gcloud/datastore/test_query.py b/gcloud/datastore/test_query.py
index 74f6f795f2a6..219b63c1e28c 100644
--- a/gcloud/datastore/test_query.py
+++ b/gcloud/datastore/test_query.py
@@ -71,6 +71,29 @@ def test_filter_w_known_operator(self):
         self.assertEqual(p_pb.property.name, 'firstname')
         self.assertEqual(p_pb.value.string_value, 'John')
 
+    def test_filter_w_known_operator_and_entity(self):
+        import operator
+        from gcloud.datastore.entity import Entity
+        query = self._makeOne()
+        other = Entity()
+        other['firstname'] = 'John'
+        other['lastname'] = 'Smith'
+        after = query.filter('other =', other)
+        self.assertFalse(after is query)
+        self.assertTrue(isinstance(after, self._getTargetClass()))
+        q_pb = after.to_protobuf()
+        self.assertEqual(q_pb.filter.composite_filter.operator, 1)  # AND
+        f_pb, = list(q_pb.filter.composite_filter.filter)
+        p_pb = f_pb.property_filter
+        self.assertEqual(p_pb.property.name, 'other')
+        other_pb = p_pb.value.entity_value
+        props = sorted(other_pb.property, key=operator.attrgetter('name'))
+        self.assertEqual(len(props), 2)
+        self.assertEqual(props[0].name, 'firstname')
+        self.assertEqual(props[0].value.string_value, 'John')
+        self.assertEqual(props[1].name, 'lastname')
+        self.assertEqual(props[1].value.string_value, 'Smith')
+
     def test_ancestor_w_non_key_non_list(self):
         query = self._makeOne()
         # XXX s.b. ValueError

From f5ee0d70c0bd3ad024f6b55ccef933f508cda2ad Mon Sep 17 00:00:00 2001
From: Tres Seaver <tseaver@palladion.com>
Date: Thu, 9 Oct 2014 15:48:06 -0400
Subject: [PATCH 05/10] Fix docstring param name.

Incorporates feedback from @dhermes.
---
 gcloud/datastore/helpers.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gcloud/datastore/helpers.py b/gcloud/datastore/helpers.py
index fc183088335a..1cdfae66c9f9 100644
--- a/gcloud/datastore/helpers.py
+++ b/gcloud/datastore/helpers.py
@@ -123,7 +123,7 @@ def set_protobuf_value(value_pb, val):
     this function handles them correctly.
 
     :type value_pb: :class:`gcloud.datastore.datastore_v1_pb2.Value`
-    :param value: The value protobuf to which the value is being assigned.
+    :param value_pb: The value protobuf to which the value is being assigned.
 
     :type val: `datetime.datetime`, bool, float, integer, string
                :class:`gcloud.datastore.key.Key`,

From 8fe416896a3dc483b6ccc6b8b00dc26d6929cf3c Mon Sep 17 00:00:00 2001
From: Tres Seaver <tseaver@palladion.com>
Date: Thu, 9 Oct 2014 15:51:19 -0400
Subject: [PATCH 06/10] Drop XXX comment.

Entities are not required to have datasets w/ active connections.
---
 gcloud/datastore/helpers.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gcloud/datastore/helpers.py b/gcloud/datastore/helpers.py
index 1cdfae66c9f9..e68f500c4143 100644
--- a/gcloud/datastore/helpers.py
+++ b/gcloud/datastore/helpers.py
@@ -107,7 +107,7 @@ def get_value_from_protobuf(pb):
         return pb.value.string_value
 
     elif pb.value.HasField('entity_value'):
-        return Entity.from_protobuf(pb.value.entity_value)  # XXX dataset?
+        return Entity.from_protobuf(pb.value.entity_value)
 
     else:
         return None

From 1679154e7170da2fb0e978380ac6a51852aa407a Mon Sep 17 00:00:00 2001
From: Tres Seaver <tseaver@palladion.com>
Date: Thu, 9 Oct 2014 16:28:46 -0400
Subject: [PATCH 07/10] Don't set default key on entity protobuf if entity has
 no key.

Incorporates feedback from @dhermes.
---
 gcloud/datastore/helpers.py      | 4 +---
 gcloud/datastore/test_helpers.py | 3 +--
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/gcloud/datastore/helpers.py b/gcloud/datastore/helpers.py
index e68f500c4143..b296c8286610 100644
--- a/gcloud/datastore/helpers.py
+++ b/gcloud/datastore/helpers.py
@@ -137,9 +137,7 @@ def set_protobuf_value(value_pb, val):
         e_pb = value_pb.entity_value
         e_pb.Clear()
         key = val.key()
-        if key is None:
-            e_pb.key.CopyFrom(Key().to_protobuf())
-        else:
+        if key is not None:
             e_pb.key.CopyFrom(key.to_protobuf())
         for k, v in val.items():
             p_pb = e_pb.property.add()
diff --git a/gcloud/datastore/test_helpers.py b/gcloud/datastore/test_helpers.py
index 2d4bb9a62086..9fff489df6c3 100644
--- a/gcloud/datastore/test_helpers.py
+++ b/gcloud/datastore/test_helpers.py
@@ -250,13 +250,12 @@ def test_unicode(self):
 
     def test_entity_empty_wo_key(self):
         from gcloud.datastore.entity import Entity
-        from gcloud.datastore.key import Key
 
         pb = self._makePB()
         entity = Entity()
         self._callFUT(pb, entity)
         value = pb.entity_value
-        self.assertEqual(value.key, Key().to_protobuf())
+        self.assertEqual(value.key.SerializeToString(), '')
         props = list(value.property)
         self.assertEqual(len(props), 0)
 

From 49f04b56c77348c7cf31c7213c5b45457b208fd1 Mon Sep 17 00:00:00 2001
From: Tres Seaver <tseaver@palladion.com>
Date: Thu, 9 Oct 2014 16:33:41 -0400
Subject: [PATCH 08/10] Rename helper functions with leading underscore.

Signal that they are *not* part of the gcloud-python API.
---
 gcloud/datastore/connection.py   |  2 +-
 gcloud/datastore/entity.py       |  2 +-
 gcloud/datastore/helpers.py      | 14 +++++++-------
 gcloud/datastore/query.py        |  2 +-
 gcloud/datastore/test_helpers.py | 16 ++++++++--------
 5 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/gcloud/datastore/connection.py b/gcloud/datastore/connection.py
index 7f97efa2141c..a43b3860807f 100644
--- a/gcloud/datastore/connection.py
+++ b/gcloud/datastore/connection.py
@@ -323,7 +323,7 @@ def save_entity(self, dataset_id, key_pb, properties):
             prop.name = name
 
             # Set the appropriate value.
-            helpers.set_protobuf_value(prop.value, value)
+            helpers._set_protobuf_value(prop.value, value)
 
         # If this is in a transaction, we should just return True. The
         # transaction will handle assigning any keys as necessary.
diff --git a/gcloud/datastore/entity.py b/gcloud/datastore/entity.py
index 06913fb3223b..be6ac340ffde 100644
--- a/gcloud/datastore/entity.py
+++ b/gcloud/datastore/entity.py
@@ -157,7 +157,7 @@ def from_protobuf(cls, pb, dataset=None):  # pylint: disable=invalid-name
         entity = cls.from_key(key)
 
         for property_pb in pb.property:
-            value = helpers.get_value_from_protobuf(property_pb)
+            value = helpers._get_value_from_protobuf(property_pb)
             entity[property_pb.name] = value
 
         return entity
diff --git a/gcloud/datastore/helpers.py b/gcloud/datastore/helpers.py
index b296c8286610..c9d9fd22bfd1 100644
--- a/gcloud/datastore/helpers.py
+++ b/gcloud/datastore/helpers.py
@@ -11,7 +11,7 @@
 INT_VALUE_CHECKER = Int64ValueChecker()
 
 
-def get_protobuf_attribute_and_value(val):
+def _get_protobuf_attribute_and_value(val):
     """Given a value, return the protobuf attribute name and proper value.
 
     The Protobuf API uses different attribute names
@@ -28,9 +28,9 @@ def get_protobuf_attribute_and_value(val):
 
     For example:
 
-    >>> get_protobuf_attribute_and_value(1234)
+    >>> _get_protobuf_attribute_and_value(1234)
     ('integer_value', 1234)
-    >>> get_protobuf_attribute_and_value('my_string')
+    >>> _get_protobuf_attribute_and_value('my_string')
     ('string_value', 'my_string')
 
     :type val: `datetime.datetime`, :class:`gcloud.datastore.key.Key`,
@@ -69,7 +69,7 @@ def get_protobuf_attribute_and_value(val):
     return name + '_value', value
 
 
-def get_value_from_protobuf(pb):
+def _get_value_from_protobuf(pb):
     """Given a protobuf for a Property, get the correct value.
 
     The Cloud Datastore Protobuf API returns a Property Protobuf
@@ -113,7 +113,7 @@ def get_value_from_protobuf(pb):
         return None
 
 
-def set_protobuf_value(value_pb, val):
+def _set_protobuf_value(value_pb, val):
     """Assign 'val' to the correct subfield of 'value_pb'.
 
     The Protobuf API uses different attribute names
@@ -130,7 +130,7 @@ def set_protobuf_value(value_pb, val):
                :class:`gcloud.datastore.entity.Entity`,
     :param val: The value to be assigned.
     """
-    attr, val = get_protobuf_attribute_and_value(val)
+    attr, val = _get_protobuf_attribute_and_value(val)
     if attr == 'key_value':
         value_pb.key_value.CopyFrom(val)
     elif attr == 'entity_value':
@@ -142,6 +142,6 @@ def set_protobuf_value(value_pb, val):
         for k, v in val.items():
             p_pb = e_pb.property.add()
             p_pb.name = k
-            set_protobuf_value(p_pb.value, v)
+            _set_protobuf_value(p_pb.value, v)
     else:  # scalar, just assign
         setattr(value_pb, attr, val)
diff --git a/gcloud/datastore/query.py b/gcloud/datastore/query.py
index 990c075652d1..2b867b71700f 100644
--- a/gcloud/datastore/query.py
+++ b/gcloud/datastore/query.py
@@ -131,7 +131,7 @@ def filter(self, expression, value):
         property_filter.operator = operator
 
         # Set the value to filter on based on the type.
-        helpers.set_protobuf_value(property_filter.value, value)
+        helpers._set_protobuf_value(property_filter.value, value)
         return clone
 
     def ancestor(self, ancestor):
diff --git a/gcloud/datastore/test_helpers.py b/gcloud/datastore/test_helpers.py
index 9fff489df6c3..5065a92b15ca 100644
--- a/gcloud/datastore/test_helpers.py
+++ b/gcloud/datastore/test_helpers.py
@@ -1,12 +1,12 @@
 import unittest2
 
 
-class Test_get_protobuf_attribute_and_value(unittest2.TestCase):
+class Test__get_protobuf_attribute_and_value(unittest2.TestCase):
 
     def _callFUT(self, val):
-        from gcloud.datastore.helpers import get_protobuf_attribute_and_value
+        from gcloud.datastore.helpers import _get_protobuf_attribute_and_value
 
-        return get_protobuf_attribute_and_value(val)
+        return _get_protobuf_attribute_and_value(val)
 
     def test_datetime_naive(self):
         import calendar
@@ -94,12 +94,12 @@ def test_object(self):
         self.assertRaises(ValueError, self._callFUT, object())
 
 
-class Test_get_value_from_protobuf(unittest2.TestCase):
+class Test__get_value_from_protobuf(unittest2.TestCase):
 
     def _callFUT(self, pb):
-        from gcloud.datastore.helpers import get_value_from_protobuf
+        from gcloud.datastore.helpers import _get_value_from_protobuf
 
-        return get_value_from_protobuf(pb)
+        return _get_value_from_protobuf(pb)
 
     def _makePB(self, attr_name, value):
         from gcloud.datastore.datastore_v1_pb2 import Property
@@ -176,9 +176,9 @@ def test_unknown(self):
 class Test_set_protobuf_value(unittest2.TestCase):
 
     def _callFUT(self, value_pb, val):
-        from gcloud.datastore.helpers import set_protobuf_value
+        from gcloud.datastore.helpers import _set_protobuf_value
 
-        return set_protobuf_value(value_pb, val)
+        return _set_protobuf_value(value_pb, val)
 
     def _makePB(self):
         from gcloud.datastore.datastore_v1_pb2 import Value

From 275f6bd42755776e5d754d35bca59b0dfc5dbf07 Mon Sep 17 00:00:00 2001
From: Tres Seaver <tseaver@palladion.com>
Date: Thu, 9 Oct 2014 16:35:05 -0400
Subject: [PATCH 09/10] Update doctring to indicate that helper functions are
 not API.

Note that these are functions, not methods.
---
 gcloud/datastore/helpers.py | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/gcloud/datastore/helpers.py b/gcloud/datastore/helpers.py
index c9d9fd22bfd1..792b3d494c42 100644
--- a/gcloud/datastore/helpers.py
+++ b/gcloud/datastore/helpers.py
@@ -1,4 +1,7 @@
-"""Helper methods for dealing with Cloud Datastore's Protobuf API."""
+"""Helper functions for dealing with Cloud Datastore's Protobuf API.
+
+These functions are *not* part of the API.
+"""
 import calendar
 from datetime import datetime, timedelta
 
@@ -16,7 +19,7 @@ def _get_protobuf_attribute_and_value(val):
 
     The Protobuf API uses different attribute names
     based on value types rather than inferring the type.
-    This method simply determines the proper attribute name
+    This function simply determines the proper attribute name
     based on the type of the value provided
     and returns the attribute name
     as well as a properly formatted value.
@@ -24,7 +27,7 @@ def _get_protobuf_attribute_and_value(val):
     Certain value types need to be coerced into a different type (such as a
     `datetime.datetime` into an integer timestamp, or a
     `gcloud.datastore.key.Key` into a Protobuf representation.
-    This method handles that for you.
+    This function handles that for you.
 
     For example:
 
@@ -74,7 +77,7 @@ def _get_value_from_protobuf(pb):
 
     The Cloud Datastore Protobuf API returns a Property Protobuf
     which has one value set and the rest blank.
-    This method retrieves the the one value provided.
+    This function retrieves the the one value provided.
 
     Some work is done to coerce the return value into a more useful type
     (particularly in the case of a timestamp value, or a key value).

From fef83a154d298fdb1bada1e05b2de9c62c5f2a01 Mon Sep 17 00:00:00 2001
From: Tres Seaver <tseaver@palladion.com>
Date: Thu, 9 Oct 2014 16:48:27 -0400
Subject: [PATCH 10/10] Rename datastore.helpers -> _helpers.

Further emphasize non-API nature.
---
 gcloud/datastore/{helpers.py => _helpers.py}           | 0
 gcloud/datastore/connection.py                         | 4 ++--
 gcloud/datastore/entity.py                             | 4 ++--
 gcloud/datastore/query.py                              | 4 ++--
 gcloud/datastore/{test_helpers.py => test__helpers.py} | 6 +++---
 5 files changed, 9 insertions(+), 9 deletions(-)
 rename gcloud/datastore/{helpers.py => _helpers.py} (100%)
 rename gcloud/datastore/{test_helpers.py => test__helpers.py} (97%)

diff --git a/gcloud/datastore/helpers.py b/gcloud/datastore/_helpers.py
similarity index 100%
rename from gcloud/datastore/helpers.py
rename to gcloud/datastore/_helpers.py
diff --git a/gcloud/datastore/connection.py b/gcloud/datastore/connection.py
index a43b3860807f..7015d205db45 100644
--- a/gcloud/datastore/connection.py
+++ b/gcloud/datastore/connection.py
@@ -1,6 +1,6 @@
 from gcloud import connection
 from gcloud.datastore import datastore_v1_pb2 as datastore_pb
-from gcloud.datastore import helpers
+from gcloud.datastore import _helpers
 from gcloud.datastore.dataset import Dataset
 
 
@@ -323,7 +323,7 @@ def save_entity(self, dataset_id, key_pb, properties):
             prop.name = name
 
             # Set the appropriate value.
-            helpers._set_protobuf_value(prop.value, value)
+            _helpers._set_protobuf_value(prop.value, value)
 
         # If this is in a transaction, we should just return True. The
         # transaction will handle assigning any keys as necessary.
diff --git a/gcloud/datastore/entity.py b/gcloud/datastore/entity.py
index be6ac340ffde..034e11f10212 100644
--- a/gcloud/datastore/entity.py
+++ b/gcloud/datastore/entity.py
@@ -151,13 +151,13 @@ def from_protobuf(cls, pb, dataset=None):  # pylint: disable=invalid-name
         """
 
         # This is here to avoid circular imports.
-        from gcloud.datastore import helpers
+        from gcloud.datastore import _helpers
 
         key = Key.from_protobuf(pb.key, dataset=dataset)
         entity = cls.from_key(key)
 
         for property_pb in pb.property:
-            value = helpers._get_value_from_protobuf(property_pb)
+            value = _helpers._get_value_from_protobuf(property_pb)
             entity[property_pb.name] = value
 
         return entity
diff --git a/gcloud/datastore/query.py b/gcloud/datastore/query.py
index 2b867b71700f..aeeb6e540d8e 100644
--- a/gcloud/datastore/query.py
+++ b/gcloud/datastore/query.py
@@ -2,7 +2,7 @@
 import copy
 
 from gcloud.datastore import datastore_v1_pb2 as datastore_pb
-from gcloud.datastore import helpers
+from gcloud.datastore import _helpers
 from gcloud.datastore.entity import Entity
 from gcloud.datastore.key import Key
 
@@ -131,7 +131,7 @@ def filter(self, expression, value):
         property_filter.operator = operator
 
         # Set the value to filter on based on the type.
-        helpers._set_protobuf_value(property_filter.value, value)
+        _helpers._set_protobuf_value(property_filter.value, value)
         return clone
 
     def ancestor(self, ancestor):
diff --git a/gcloud/datastore/test_helpers.py b/gcloud/datastore/test__helpers.py
similarity index 97%
rename from gcloud/datastore/test_helpers.py
rename to gcloud/datastore/test__helpers.py
index 5065a92b15ca..a31711fd9661 100644
--- a/gcloud/datastore/test_helpers.py
+++ b/gcloud/datastore/test__helpers.py
@@ -4,7 +4,7 @@
 class Test__get_protobuf_attribute_and_value(unittest2.TestCase):
 
     def _callFUT(self, val):
-        from gcloud.datastore.helpers import _get_protobuf_attribute_and_value
+        from gcloud.datastore._helpers import _get_protobuf_attribute_and_value
 
         return _get_protobuf_attribute_and_value(val)
 
@@ -97,7 +97,7 @@ def test_object(self):
 class Test__get_value_from_protobuf(unittest2.TestCase):
 
     def _callFUT(self, pb):
-        from gcloud.datastore.helpers import _get_value_from_protobuf
+        from gcloud.datastore._helpers import _get_value_from_protobuf
 
         return _get_value_from_protobuf(pb)
 
@@ -176,7 +176,7 @@ def test_unknown(self):
 class Test_set_protobuf_value(unittest2.TestCase):
 
     def _callFUT(self, value_pb, val):
-        from gcloud.datastore.helpers import _set_protobuf_value
+        from gcloud.datastore._helpers import _set_protobuf_value
 
         return _set_protobuf_value(value_pb, val)