From 301378ff8a5df112b4dc23c72b31807cb83f70c1 Mon Sep 17 00:00:00 2001 From: Danny Hermes Date: Wed, 20 May 2015 16:35:07 -0700 Subject: [PATCH] Ensuring query constructor args are correct types. Fixes #887. --- gcloud/datastore/query.py | 29 ++++++++++++++++++++++++++--- gcloud/datastore/test_query.py | 21 +++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/gcloud/datastore/query.py b/gcloud/datastore/query.py index 893580bc59ca..b14650538619 100644 --- a/gcloud/datastore/query.py +++ b/gcloud/datastore/query.py @@ -90,9 +90,32 @@ def __init__(self, self._namespace = namespace self._ancestor = ancestor self._filters = list(filters) - self._projection = list(projection) - self._order = list(order) - self._group_by = list(group_by) + self._projection = self._ensure_tuple_or_list('projection', projection) + self._order = self._ensure_tuple_or_list('order', order) + self._group_by = self._ensure_tuple_or_list('group_by', group_by) + + @staticmethod + def _ensure_tuple_or_list(arg_name, tuple_or_list): + """Ensures an input is a tuple or list. + + This effectively reduces the iterable types allowed to a very short + whitelist: list and tuple. + + :type arg_name: string + :param arg_name: Name of argument to use in error message. + + :type tuple_or_list: sequence of string + :param tuple_or_list: Sequence to be verified. + + :rtype: list of string + :returns: The ``tuple_or_list`` passed in cast to a ``list``. + :raises: class:`TypeError` if the ``tuple_or_list`` is not a tuple or + list. + """ + if not isinstance(tuple_or_list, (tuple, list)): + raise TypeError('Expected %s to be a tuple or list. ' + 'Received %r' % (arg_name, tuple_or_list)) + return list(tuple_or_list) @property def dataset_id(self): diff --git a/gcloud/datastore/test_query.py b/gcloud/datastore/test_query.py index 5296509d44aa..3ffee7f5d75d 100644 --- a/gcloud/datastore/test_query.py +++ b/gcloud/datastore/test_query.py @@ -79,6 +79,27 @@ def test_ctor_explicit(self): self.assertEqual(query.order, ORDER) self.assertEqual(query.group_by, GROUP_BY) + def test_ctor_bad_projection(self): + _DATASET = 'DATASET' + _KIND = 'KIND' + BAD_PROJECTION = object() + self.assertRaises(TypeError, self._makeOne, _KIND, _DATASET, + projection=BAD_PROJECTION) + + def test_ctor_bad_order(self): + _DATASET = 'DATASET' + _KIND = 'KIND' + BAD_ORDER = object() + self.assertRaises(TypeError, self._makeOne, _KIND, _DATASET, + order=BAD_ORDER) + + def test_ctor_bad_group_by(self): + _DATASET = 'DATASET' + _KIND = 'KIND' + BAD_GROUP_BY = object() + self.assertRaises(TypeError, self._makeOne, _KIND, _DATASET, + group_by=BAD_GROUP_BY) + def test_namespace_setter_w_non_string(self): _DATASET = 'DATASET' query = self._makeOne(dataset_id=_DATASET)