Skip to content

Commit

Permalink
Merge pull request #888 from dhermes/fix-887
Browse files Browse the repository at this point in the history
Only allowing tuple or list for ctor inputs which assume a sequence
  • Loading branch information
dhermes committed May 27, 2015
2 parents 0dcb839 + aa2c138 commit 6bc06ce
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 4 deletions.
23 changes: 23 additions & 0 deletions gcloud/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,29 @@ def top(self):
return self._stack[-1]


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)


class _LazyProperty(object):
"""Descriptor for lazy loaded property.
Expand Down
6 changes: 5 additions & 1 deletion gcloud/datastore/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
"""Class for representing a single entity in the Cloud Datastore."""


from gcloud._helpers import _ensure_tuple_or_list


class Entity(dict):
"""Entities are akin to rows in a relational database
Expand Down Expand Up @@ -76,7 +79,8 @@ class Entity(dict):
def __init__(self, key=None, exclude_from_indexes=()):
super(Entity, self).__init__()
self.key = key
self._exclude_from_indexes = set(exclude_from_indexes)
self._exclude_from_indexes = set(_ensure_tuple_or_list(
'exclude_from_indexes', exclude_from_indexes))

def __eq__(self, other):
"""Compare two entities for equality.
Expand Down
7 changes: 4 additions & 3 deletions gcloud/datastore/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import base64

from gcloud._helpers import _ensure_tuple_or_list
from gcloud.datastore import _implicit_environ
from gcloud.datastore import _datastore_v1_pb2 as datastore_pb
from gcloud.datastore import helpers
Expand Down Expand Up @@ -90,9 +91,9 @@ 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 = _ensure_tuple_or_list('projection', projection)
self._order = _ensure_tuple_or_list('order', order)
self._group_by = _ensure_tuple_or_list('group_by', group_by)

@property
def dataset_id(self):
Expand Down
6 changes: 6 additions & 0 deletions gcloud/datastore/test_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ def test_ctor_explicit(self):
self.assertEqual(sorted(entity.exclude_from_indexes),
sorted(_EXCLUDE_FROM_INDEXES))

def test_ctor_bad_exclude_from_indexes(self):
BAD_EXCLUDE_FROM_INDEXES = object()
key = _Key()
self.assertRaises(TypeError, self._makeOne, key=key,
exclude_from_indexes=BAD_EXCLUDE_FROM_INDEXES)

def test___eq_____ne___w_non_entity(self):
from gcloud.datastore.key import Key
key = Key(_KIND, _ID, dataset_id=_DATASET_ID)
Expand Down
21 changes: 21 additions & 0 deletions gcloud/datastore/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
27 changes: 27 additions & 0 deletions gcloud/test__helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,33 @@ def test_it(self):
self.assertEqual(list(batches), [])


class Test__ensure_tuple_or_list(unittest2.TestCase):

def _callFUT(self, arg_name, tuple_or_list):
from gcloud._helpers import _ensure_tuple_or_list
return _ensure_tuple_or_list(arg_name, tuple_or_list)

def test_valid_tuple(self):
valid_tuple_or_list = ('a', 'b', 'c', 'd')
result = self._callFUT('ARGNAME', valid_tuple_or_list)
self.assertEqual(result, ['a', 'b', 'c', 'd'])

def test_valid_list(self):
valid_tuple_or_list = ['a', 'b', 'c', 'd']
result = self._callFUT('ARGNAME', valid_tuple_or_list)
self.assertEqual(result, valid_tuple_or_list)

def test_invalid(self):
invalid_tuple_or_list = object()
with self.assertRaises(TypeError):
self._callFUT('ARGNAME', invalid_tuple_or_list)

def test_invalid_iterable(self):
invalid_tuple_or_list = 'FOO'
with self.assertRaises(TypeError):
self._callFUT('ARGNAME', invalid_tuple_or_list)


class Test__LazyProperty(unittest2.TestCase):

def _getTargetClass(self):
Expand Down

0 comments on commit 6bc06ce

Please sign in to comment.