Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only allowing tuple or list for ctor inputs which assume a sequence #888

Merged
merged 2 commits into from
May 27, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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