Skip to content

Commit b17dd7e

Browse files
Make ShardRange.OuterBound a proper singleton
ShardRange.OuterBound had the singleton-like property of all instances of the same class being equal, but did not prevent multiple instances being created. Instead, ShardRange.MIN and ShardRange.MAX are used to hold single instances of each outer bound. The ShardRange.OuterBound equality property was achieved by overriding the default __eq__ method, but as a consequence the type was not hashable, preventing it being used in lists or as a dict key without also overriding the default __hash__ method. This patch makes ShardRangeOuterBound a proper singleton, removing the need to override __eq__ or __hash__. Change-Id: I21292e7991e93834b35cda6f5daea4c552a8e999
1 parent 3a41cbe commit b17dd7e

File tree

2 files changed

+45
-21
lines changed

2 files changed

+45
-21
lines changed

swift/common/utils.py

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4970,6 +4970,29 @@ def md5(string=b'', usedforsecurity=True):
49704970
return hashlib.md5(string) # nosec
49714971

49724972

4973+
class ShardRangeOuterBound(object):
4974+
"""
4975+
A custom singleton type used for the outer bounds of ShardRanges.
4976+
"""
4977+
_singleton = None
4978+
4979+
def __new__(cls):
4980+
if cls._singleton is None:
4981+
cls._singleton = super(ShardRangeOuterBound, cls).__new__(cls)
4982+
return cls._singleton
4983+
4984+
def __str__(self):
4985+
return ''
4986+
4987+
def __repr__(self):
4988+
return type(self).__name__
4989+
4990+
def __bool__(self):
4991+
return False
4992+
4993+
__nonzero__ = __bool__
4994+
4995+
49734996
class ShardRange(object):
49744997
"""
49754998
A ShardRange encapsulates sharding state related to a container including
@@ -5034,31 +5057,15 @@ class ShardRange(object):
50345057
SHRUNK: 'shrunk'}
50355058
STATES_BY_NAME = dict((v, k) for k, v in STATES.items())
50365059

5037-
class OuterBound(object):
5038-
def __eq__(self, other):
5039-
return isinstance(other, type(self))
5040-
5041-
def __ne__(self, other):
5042-
return not self.__eq__(other)
5043-
5044-
def __str__(self):
5045-
return ''
5046-
5047-
def __repr__(self):
5048-
return type(self).__name__
5049-
5050-
def __bool__(self):
5051-
return False
5052-
5053-
__nonzero__ = __bool__
5054-
50555060
@functools.total_ordering
5056-
class MaxBound(OuterBound):
5061+
class MaxBound(ShardRangeOuterBound):
5062+
# singleton for maximum bound
50575063
def __ge__(self, other):
50585064
return True
50595065

50605066
@functools.total_ordering
5061-
class MinBound(OuterBound):
5067+
class MinBound(ShardRangeOuterBound):
5068+
# singleton for minimum bound
50625069
def __le__(self, other):
50635070
return True
50645071

@@ -5107,7 +5114,7 @@ def _encode(cls, value):
51075114
return value
51085115

51095116
def _encode_bound(self, bound):
5110-
if isinstance(bound, ShardRange.OuterBound):
5117+
if isinstance(bound, ShardRangeOuterBound):
51115118
return bound
51125119
if not (isinstance(bound, six.text_type) or
51135120
isinstance(bound, six.binary_type)):

test/unit/common/test_utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7750,6 +7750,10 @@ def test_min_max_bounds(self):
77507750
self.assertFalse(utils.ShardRange.MAX != utils.ShardRange.MAX)
77517751
self.assertTrue(
77527752
utils.ShardRange.MaxBound() == utils.ShardRange.MaxBound())
7753+
self.assertTrue(
7754+
utils.ShardRange.MaxBound() is utils.ShardRange.MaxBound())
7755+
self.assertTrue(
7756+
utils.ShardRange.MaxBound() is utils.ShardRange.MAX)
77537757
self.assertFalse(
77547758
utils.ShardRange.MaxBound() != utils.ShardRange.MaxBound())
77557759

@@ -7772,19 +7776,32 @@ def test_min_max_bounds(self):
77727776
self.assertFalse(utils.ShardRange.MIN != utils.ShardRange.MIN)
77737777
self.assertTrue(
77747778
utils.ShardRange.MinBound() == utils.ShardRange.MinBound())
7779+
self.assertTrue(
7780+
utils.ShardRange.MinBound() is utils.ShardRange.MinBound())
7781+
self.assertTrue(
7782+
utils.ShardRange.MinBound() is utils.ShardRange.MIN)
77757783
self.assertFalse(
77767784
utils.ShardRange.MinBound() != utils.ShardRange.MinBound())
77777785

77787786
self.assertFalse(utils.ShardRange.MAX == utils.ShardRange.MIN)
77797787
self.assertFalse(utils.ShardRange.MIN == utils.ShardRange.MAX)
77807788
self.assertTrue(utils.ShardRange.MAX != utils.ShardRange.MIN)
77817789
self.assertTrue(utils.ShardRange.MIN != utils.ShardRange.MAX)
7790+
self.assertFalse(utils.ShardRange.MAX is utils.ShardRange.MIN)
77827791

77837792
self.assertEqual(utils.ShardRange.MAX,
77847793
max(utils.ShardRange.MIN, utils.ShardRange.MAX))
77857794
self.assertEqual(utils.ShardRange.MIN,
77867795
min(utils.ShardRange.MIN, utils.ShardRange.MAX))
77877796

7797+
# check the outer bounds are hashable
7798+
hashmap = {utils.ShardRange.MIN: 'min',
7799+
utils.ShardRange.MAX: 'max'}
7800+
self.assertEqual(hashmap[utils.ShardRange.MIN], 'min')
7801+
self.assertEqual(hashmap[utils.ShardRange.MinBound()], 'min')
7802+
self.assertEqual(hashmap[utils.ShardRange.MAX], 'max')
7803+
self.assertEqual(hashmap[utils.ShardRange.MaxBound()], 'max')
7804+
77887805
def test_shard_range_initialisation(self):
77897806
def assert_initialisation_ok(params, expected):
77907807
pr = utils.ShardRange(**params)

0 commit comments

Comments
 (0)