Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 90d5063

Browse files
committed
Adding Key.__eq__ and __ne__ for comparison.
1 parent 12ac983 commit 90d5063

File tree

3 files changed

+129
-31
lines changed

3 files changed

+129
-31
lines changed

gcloud/datastore/key.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,17 @@ def parent(self):
335335

336336
return self._parent
337337

338+
def __eq__(self, other):
339+
if not isinstance(other, self.__class__):
340+
return False
341+
342+
return (self.namespace == other.namespace and
343+
self.path == other.path and
344+
_dataset_ids_equal(self.dataset_id, other.dataset_id))
345+
346+
def __ne__(self, other):
347+
return not self.__eq__(other)
348+
338349
def __repr__(self):
339350
return '<Key%s, dataset=%s>' % (self.path, self.dataset_id)
340351

@@ -347,6 +358,14 @@ def _validate_dataset_id(dataset_id, parent):
347358
348359
If ``dataset_id`` is unset, attempt to infer the ID from the environment.
349360
361+
:type dataset_id: string
362+
:param dataset_id: A dataset ID.
363+
364+
:type parent: :class:`gcloud.datastore.key.Key` or ``NoneType``
365+
:param parent: The parent of the key or ``None``.
366+
367+
:rtype: string
368+
:returns: The ``dataset_id`` passed in, or implied from the environment.
350369
:raises: :class:`ValueError` if ``dataset_id`` is ``None`` and no dataset
351370
can be inferred.
352371
"""
@@ -360,3 +379,55 @@ def _validate_dataset_id(dataset_id, parent):
360379
dataset_id = _implicit_environ.DATASET_ID
361380

362381
return dataset_id
382+
383+
384+
def _dataset_ids_equal(dataset_id1, dataset_id2):
385+
"""Compares two dataset IDs for fuzzy equality.
386+
387+
Each may be prefixed or unprefixed (but not null, since dataset ID
388+
is required on a key). The only allowed prefixes are 's~' and 'e~'.
389+
390+
Two identical prefixed match
391+
392+
>>> 's~foo' == 's~foo'
393+
>>> 'e~bar' == 'e~bar'
394+
395+
while non-identical prefixed don't
396+
397+
>>> 's~foo' != 's~bar'
398+
>>> 's~foo' != 'e~foo'
399+
400+
As for non-prefixed, they can match other non-prefixed or
401+
prefixed:
402+
403+
>>> 'foo' == 'foo'
404+
>>> 'foo' == 's~foo'
405+
>>> 'foo' == 'e~foo'
406+
>>> 'foo' != 'bar'
407+
>>> 'foo' != 's~bar'
408+
409+
(Ties are resolved since 'foo' can only be an alias for one of
410+
s~foo or e~foo in the backend.)
411+
412+
:type dataset_id1: string
413+
:param dataset_id1: A dataset ID.
414+
415+
:type dataset_id2: string
416+
:param dataset_id2: A dataset ID.
417+
418+
:rtype: boolean
419+
:returns: Boolean indicating if the IDs are the same.
420+
"""
421+
if dataset_id1 == dataset_id2:
422+
return True
423+
424+
if dataset_id1.startswith('s~') or dataset_id1.startswith('e~'):
425+
# If `dataset_id1` is prefixed and not matching, then the only way
426+
# they can match is if `dataset_id2` is unprefixed.
427+
return dataset_id1[2:] == dataset_id2
428+
elif dataset_id2.startswith('s~') or dataset_id2.startswith('e~'):
429+
# Here we know `dataset_id1` is unprefixed and `dataset_id2`
430+
# is prefixed.
431+
return dataset_id1 == dataset_id2[2:]
432+
433+
return False

gcloud/datastore/test_key.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ class TestKey(unittest2.TestCase):
2020
_DEFAULT_DATASET = 'DATASET'
2121

2222
def setUp(self):
23-
2423
from gcloud.datastore import _implicit_environ
2524
self._replaced_dataset_id = _implicit_environ.DATASET_ID
2625
_implicit_environ.DATASET_ID = None
@@ -291,3 +290,58 @@ def test_parent_multiple_calls(self):
291290
self.assertEqual(parent.path, _PARENT_PATH)
292291
new_parent = key.parent
293292
self.assertTrue(parent is new_parent)
293+
294+
def test___eq__bad_type(self):
295+
key = self._makeOne('KIND', dataset_id=self._DEFAULT_DATASET)
296+
self.assertNotEqual(key, None)
297+
self.assertNotEqual(key, object())
298+
self.assertNotEqual(None, key)
299+
300+
def test___eq__same_value(self):
301+
key1 = self._makeOne('KIND', dataset_id=self._DEFAULT_DATASET)
302+
key2 = self._makeOne('KIND', dataset_id=self._DEFAULT_DATASET)
303+
self.assertFalse(key1 is key2)
304+
self.assertEqual(key1, key2)
305+
306+
def test___eq__different_dataset(self):
307+
ALT_DATASET = 'DATASET-ALT'
308+
key1 = self._makeOne('KIND', dataset_id=self._DEFAULT_DATASET)
309+
key2 = self._makeOne('KIND', dataset_id=ALT_DATASET)
310+
311+
self.assertNotEqual(ALT_DATASET, self._DEFAULT_DATASET)
312+
self.assertNotEqual(key1, key2)
313+
314+
def test___eq__different_path(self):
315+
key1 = self._makeOne('KIND', 10, dataset_id=self._DEFAULT_DATASET)
316+
key2 = self._makeOne('KIND', dataset_id=self._DEFAULT_DATASET)
317+
self.assertNotEqual(key1, key2)
318+
319+
def test___eq__different_namespace(self):
320+
key1 = self._makeOne('KIND', dataset_id=self._DEFAULT_DATASET,
321+
namespace='NAMESPACE')
322+
key2 = self._makeOne('KIND', dataset_id=self._DEFAULT_DATASET)
323+
self.assertNotEqual(key1, key2)
324+
325+
326+
class Test__dataset_ids_equal(unittest2.TestCase):
327+
328+
def _callFUT(self, dataset_id1, dataset_id2):
329+
from gcloud.datastore.key import _dataset_ids_equal
330+
return _dataset_ids_equal(dataset_id1, dataset_id2)
331+
332+
def test_identical_prefixed(self):
333+
self.assertTrue(self._callFUT('s~foo', 's~foo'))
334+
self.assertTrue(self._callFUT('e~bar', 'e~bar'))
335+
336+
def test_different_prefixed(self):
337+
self.assertFalse(self._callFUT('s~foo', 's~bar'))
338+
self.assertFalse(self._callFUT('s~foo', 'e~foo'))
339+
340+
def test_all_unprefixed(self):
341+
self.assertTrue(self._callFUT('foo', 'foo'))
342+
self.assertFalse(self._callFUT('foo', 'bar'))
343+
344+
def test_unprefixed_with_prefixed(self):
345+
self.assertTrue(self._callFUT('foo', 's~foo'))
346+
self.assertTrue(self._callFUT('foo', 'e~foo'))
347+
self.assertFalse(self._callFUT('foo', 's~bar'))

regression/datastore.py

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,7 @@ def _generic_test_post(self, name=None, key_id=None):
8888
self.assertEqual(entity.key.id, key_id)
8989
retrieved_entity, = datastore.get([entity.key])
9090
# Check the keys are the same.
91-
self.assertEqual(retrieved_entity.key.path, entity.key.path)
92-
self.assertEqual(retrieved_entity.key.namespace, entity.key.namespace)
93-
self.assertTrue(_compare_dataset_ids(
94-
retrieved_entity.key.dataset_id, entity.key.dataset_id))
91+
self.assertEqual(retrieved_entity.key, entity.key)
9592

9693
# Check the data is the same.
9794
retrieved_dict = dict(retrieved_entity.items())
@@ -157,8 +154,7 @@ def test_save_key_self_reference(self):
157154

158155
stored_person = stored_persons[0]
159156
self.assertEqual(stored_person['fullName'], entity['fullName'])
160-
self.assertEqual(stored_person.key.path, key.path)
161-
self.assertEqual(stored_person.key.namespace, key.namespace)
157+
self.assertEqual(stored_person.key, key)
162158

163159

164160
class TestDatastoreQuery(TestDatastore):
@@ -263,14 +259,7 @@ def test_projection_query(self):
263259
{'name': 'Catelyn', 'family': 'Tully'})
264260

265261
# Check both Catelyn keys are the same.
266-
catelyn_stark_key = catelyn_stark_entity.key
267-
catelyn_tully_key = catelyn_tully_entity.key
268-
self.assertEqual(catelyn_stark_key.path, catelyn_tully_key.path)
269-
self.assertEqual(catelyn_stark_key.namespace,
270-
catelyn_tully_key.namespace)
271-
# Also check the dataset_id since both retrieved from datastore.
272-
self.assertEqual(catelyn_stark_key.dataset_id,
273-
catelyn_tully_key.dataset_id)
262+
self.assertEqual(catelyn_stark_entity.key, catelyn_tully_entity.key)
274263

275264
sansa_entity = entities[8]
276265
sansa_dict = dict(sansa_entity.items())
@@ -352,19 +341,3 @@ def test_transaction(self):
352341
retrieved_dict = dict(retrieved_entity.items())
353342
entity_dict = dict(entity.items())
354343
self.assertEqual(retrieved_dict, entity_dict)
355-
356-
357-
def _compare_dataset_ids(dataset_id1, dataset_id2):
358-
if dataset_id1 == dataset_id2:
359-
return True
360-
361-
if dataset_id1.startswith('s~') or dataset_id1.startswith('e~'):
362-
# If `dataset_id1` is prefixed and not matching, then the only way
363-
# they can match is if `dataset_id2` is unprefixed.
364-
return dataset_id1[2:] == dataset_id2
365-
elif dataset_id2.startswith('s~') or dataset_id2.startswith('e~'):
366-
# Here we know `dataset_id1` is unprefixed and `dataset_id2`
367-
# is prefixed.
368-
return dataset_id1 == dataset_id2[2:]
369-
370-
return False

0 commit comments

Comments
 (0)