Repository: googleapis/google-cloud-python

Issue:
In datastore.query, figure out how to properly handle namespaces
From TODO:

``` python
# TODO: Figure out how to properly handle namespaces.
```

See [`datastore.query`](https://github.com/GoogleCloudPlatform/gcloud-python/blob/1f2aaf3606802f4f02eeb30e22e1082ef7f4594e/gcloud/datastore/query.py#L9) for relevant code and #146 for some context.

`git blame` output

```
dec0e7c6 (JJ Geewax 2014-01-28 15:15:40 -0500   9) # TODO: Figure out how to properly handle namespaces.
```

In datastore.query, figure out how to properly handle namespaces
From TODO:

``` python
# TODO: Figure out how to properly handle namespaces.
```

See [`datastore.query`](https://github.com/GoogleCloudPlatform/gcloud-python/blob/1f2aaf3606802f4f02eeb30e22e1082ef7f4594e/gcloud/datastore/query.py#L9) for relevant code and #146 for some context.

`git blame` output

```
dec0e7c6 (JJ Geewax 2014-01-28 15:15:40 -0500   9) # TODO: Figure out how to properly handle namespaces.
```



Discussion:

Resolving patch:
diff --git a/gcloud/datastore/query.py b/gcloud/datastore/query.py
--- a/gcloud/datastore/query.py
+++ b/gcloud/datastore/query.py
@@ -41,6 +41,9 @@ class Query(object):
 
     :type dataset: :class:`gcloud.datastore.dataset.Dataset`
     :param dataset: The dataset to query.
+
+    :type namespace: string or None
+    :param dataset: The namespace to which to restrict results.
     """
 
     OPERATORS = {
@@ -52,8 +55,9 @@ class Query(object):
     }
     """Mapping of operator strings and their protobuf equivalents."""
 
-    def __init__(self, kind=None, dataset=None):
+    def __init__(self, kind=None, dataset=None, namespace=None):
         self._dataset = dataset
+        self._namespace = namespace
         self._pb = datastore_pb.Query()
         self._cursor = None
 
@@ -66,11 +70,20 @@ def _clone(self):
         :rtype: :class:`gcloud.datastore.query.Query`
         :returns: a copy of 'self'.
         """
-        clone = self.__class__(dataset=self._dataset)
+        clone = self.__class__(dataset=self._dataset,
+                               namespace=self._namespace)
         clone._pb.CopyFrom(self._pb)
         clone._cursor = self._cursor
         return clone
 
+    def namespace(self):
+        """This query's namespace
+
+        :rtype: string or None
+        :returns: the namespace assigned to this query
+        """
+        return self._namespace
+
     def to_protobuf(self):
         """Convert :class:`Query` instance to :class:`.datastore_v1_pb2.Query`.
 
@@ -316,7 +329,10 @@ def fetch(self, limit=None):
             clone = self.limit(limit)
 
         query_results = self.dataset().connection().run_query(
-            query_pb=clone.to_protobuf(), dataset_id=self.dataset().id())
+            query_pb=clone.to_protobuf(),
+            dataset_id=self.dataset().id(),
+            namespace=self._namespace,
+            )
         entity_pbs, end_cursor = query_results[:2]
 
         self._cursor = end_cursor


Test patch:
diff --git a/gcloud/datastore/test_query.py b/gcloud/datastore/test_query.py
--- a/gcloud/datastore/test_query.py
+++ b/gcloud/datastore/test_query.py
@@ -8,25 +8,28 @@ def _getTargetClass(self):
 
         return Query
 
-    def _makeOne(self, kind=None, dataset=None):
-        return self._getTargetClass()(kind, dataset)
+    def _makeOne(self, kind=None, dataset=None, namespace=None):
+        return self._getTargetClass()(kind, dataset, namespace)
 
     def test_ctor_defaults(self):
-        query = self._makeOne()
+        query = self._getTargetClass()()
         self.assertEqual(query.dataset(), None)
         self.assertEqual(list(query.kind()), [])
         self.assertEqual(query.limit(), 0)
+        self.assertEqual(query.namespace(), None)
 
     def test_ctor_explicit(self):
         from gcloud.datastore.dataset import Dataset
 
         _DATASET = 'DATASET'
         _KIND = 'KIND'
+        _NAMESPACE = 'NAMESPACE'
         dataset = Dataset(_DATASET)
-        query = self._makeOne(_KIND, dataset)
+        query = self._makeOne(_KIND, dataset, _NAMESPACE)
         self.assertTrue(query.dataset() is dataset)
         kq_pb, = list(query.kind())
         self.assertEqual(kq_pb.name, _KIND)
+        self.assertEqual(query.namespace(), _NAMESPACE)
 
     def test__clone(self):
         from gcloud.datastore.dataset import Dataset
@@ -34,13 +37,15 @@ def test__clone(self):
         _DATASET = 'DATASET'
         _KIND = 'KIND'
         _CURSOR = 'DEADBEEF'
+        _NAMESPACE = 'NAMESPACE'
         dataset = Dataset(_DATASET)
-        query = self._makeOne(_KIND, dataset)
+        query = self._makeOne(_KIND, dataset, _NAMESPACE)
         query._cursor = _CURSOR
         clone = query._clone()
         self.assertFalse(clone is query)
         self.assertTrue(isinstance(clone, self._getTargetClass()))
         self.assertTrue(clone.dataset() is dataset)
+        self.assertEqual(clone.namespace(), _NAMESPACE)
         kq_pb, = list(clone.kind())
         self.assertEqual(kq_pb.name, _KIND)
         self.assertEqual(clone._cursor, _CURSOR)
@@ -101,7 +106,7 @@ def test_ancestor_w_non_key_non_list(self):
         query = self._makeOne()
         self.assertRaises(TypeError, query.ancestor, object())
 
-    def test_ancester_wo_existing_ancestor_query_w_key_and_propfilter(self):
+    def test_ancestor_wo_existing_ancestor_query_w_key_and_propfilter(self):
         from gcloud.datastore.key import Key
         _KIND = 'KIND'
         _ID = 123
@@ -121,7 +126,7 @@ def test_ancester_wo_existing_ancestor_query_w_key_and_propfilter(self):
         self.assertEqual(p_pb.property.name, '__key__')
         self.assertEqual(p_pb.value.key_value, key.to_protobuf())
 
-    def test_ancester_wo_existing_ancestor_query_w_key(self):
+    def test_ancestor_wo_existing_ancestor_query_w_key(self):
         from gcloud.datastore.key import Key
         _KIND = 'KIND'
         _ID = 123
@@ -137,7 +142,7 @@ def test_ancester_wo_existing_ancestor_query_w_key(self):
         self.assertEqual(p_pb.property.name, '__key__')
         self.assertEqual(p_pb.value.key_value, key.to_protobuf())
 
-    def test_ancester_wo_existing_ancestor_query_w_list(self):
+    def test_ancestor_wo_existing_ancestor_query_w_list(self):
         from gcloud.datastore.key import Key
         _KIND = 'KIND'
         _ID = 123
@@ -153,7 +158,7 @@ def test_ancester_wo_existing_ancestor_query_w_list(self):
         self.assertEqual(p_pb.property.name, '__key__')
         self.assertEqual(p_pb.value.key_value, key.to_protobuf())
 
-    def test_ancester_clears_existing_ancestor_query_w_only(self):
+    def test_ancestor_clears_existing_ancestor_query_w_only(self):
         _KIND = 'KIND'
         _ID = 123
         query = self._makeOne()
@@ -164,7 +169,7 @@ def test_ancester_clears_existing_ancestor_query_w_only(self):
         q_pb = after.to_protobuf()
         self.assertEqual(list(q_pb.filter.composite_filter.filter), [])
 
-    def test_ancester_clears_existing_ancestor_query_w_others(self):
+    def test_ancestor_clears_existing_ancestor_query_w_others(self):
         _KIND = 'KIND'
         _ID = 123
         _NAME = 'NAME'
@@ -257,6 +262,7 @@ def test_fetch_default_limit(self):
         expected_called_with = {
             'dataset_id': _DATASET,
             'query_pb': query.to_protobuf(),
+            'namespace': None,
         }
         self.assertEqual(connection._called_with, expected_called_with)
 
@@ -266,6 +272,7 @@ def test_fetch_explicit_limit(self):
         _DATASET = 'DATASET'
         _KIND = 'KIND'
         _ID = 123
+        _NAMESPACE = 'NAMESPACE'
         entity_pb = Entity()
         path_element = entity_pb.key.path_element.add()
         path_element.kind = _KIND
@@ -276,7 +283,7 @@ def test_fetch_explicit_limit(self):
         connection = _Connection(entity_pb)
         connection._cursor = _CURSOR
         dataset = _Dataset(_DATASET, connection)
-        query = self._makeOne(_KIND, dataset)
+        query = self._makeOne(_KIND, dataset, _NAMESPACE)
         limited = query.limit(13)
         entities = query.fetch(13)
         self.assertEqual(query._cursor, _CURSOR)
@@ -286,6 +293,7 @@ def test_fetch_explicit_limit(self):
         expected_called_with = {
             'dataset_id': _DATASET,
             'query_pb': limited.to_protobuf(),
+            'namespace': _NAMESPACE,
         }
         self.assertEqual(connection._called_with, expected_called_with)