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

Don't allow RedisDeques to equal Python lists... #574

Merged
merged 2 commits into from
Dec 31, 2021
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
2 changes: 1 addition & 1 deletion pottery/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@


__title__: Final[str] = 'pottery'
__version__: Final[str] = '2.3.3'
__version__: Final[str] = '2.3.4'
__description__: Final[str] = __doc__.split(sep='\n\n', maxsplit=1)[0]
__url__: Final[str] = 'https://github.com/brainix/pottery'
__author__: Final[str] = 'Rajiv Bakulesh Shah'
Expand Down
4 changes: 3 additions & 1 deletion pottery/deque.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@
class RedisDeque(RedisList, collections.deque): # type: ignore
'Redis-backed container compatible with collections.deque.'

# Method overrides:
# Overrides:

_ALLOWED_TO_EQUAL = collections.deque # type: ignore

def __init__(self,
iterable: Iterable[JSONTypes] = tuple(),
Expand Down
6 changes: 4 additions & 2 deletions pottery/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:
class RedisList(Base, collections.abc.MutableSequence):
'Redis-backed container compatible with Python lists.'

_ALLOWED_TO_EQUAL = list

def __slice_to_indices(self, slice_or_index: Union[slice, int]) -> range:
if isinstance(slice_or_index, slice):
start, stop, step = cast(slice, slice_or_index).indices(len(self))
Expand Down Expand Up @@ -261,13 +263,13 @@ def __eq__(self, other: Any) -> bool:
if self._same_redis(other) and self.key == other.key:
return True

if type(other) not in {list, self.__class__}:
if type(other) not in {self.__class__, self._ALLOWED_TO_EQUAL}:
return False
with self._watch(other):
if len(self) != len(other):
return False

# other is a mutable sequence too, and self and other are the same
# other is a list or RedisList too, and self and other are the same
# length. Make Python lists out of self and other, and compare
# those lists.
warnings.warn(
Expand Down
43 changes: 22 additions & 21 deletions tests/test_deque.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# --------------------------------------------------------------------------- #


import collections
import itertools
import unittest.mock

Expand All @@ -31,34 +32,34 @@ class DequeTests(TestCase):

def test_basic_usage(self):
d = RedisDeque('ghi', redis=self.redis)
assert d == ['g', 'h', 'i']
assert d == collections.deque(['g', 'h', 'i'])

d.append('j')
d.appendleft('f')
assert d == ['f', 'g', 'h', 'i', 'j']
assert d == collections.deque(['f', 'g', 'h', 'i', 'j'])

assert d.pop() == 'j'
assert d.popleft() == 'f'
assert d == ['g', 'h', 'i']
assert d == collections.deque(['g', 'h', 'i'])
assert d[0] == 'g'
assert d[-1] == 'i'

assert list(reversed(d)) == ['i', 'h', 'g']
assert 'h' in d
d.extend('jkl')
assert d == ['g', 'h', 'i', 'j', 'k', 'l']
assert d == collections.deque(['g', 'h', 'i', 'j', 'k', 'l'])
d.rotate(1)
assert d == ['l', 'g', 'h', 'i', 'j', 'k']
assert d == collections.deque(['l', 'g', 'h', 'i', 'j', 'k'])
d.rotate(-1)
assert d == ['g', 'h', 'i', 'j', 'k', 'l']
assert d == collections.deque(['g', 'h', 'i', 'j', 'k', 'l'])

assert RedisDeque(reversed(d), redis=self.redis) == ['l', 'k', 'j', 'i', 'h', 'g']
assert RedisDeque(reversed(d), redis=self.redis) == collections.deque(['l', 'k', 'j', 'i', 'h', 'g'])
d.clear()
with self.assertRaises(IndexError):
d.pop()

d.extendleft('abc')
assert d == ['c', 'b', 'a']
assert d == collections.deque(['c', 'b', 'a'])

def test_init_with_wrong_type_maxlen(self):
with unittest.mock.patch.object(Base, '__del__') as delete, \
Expand All @@ -68,10 +69,10 @@ def test_init_with_wrong_type_maxlen(self):

def test_init_with_maxlen(self):
d = RedisDeque([1, 2, 3, 4, 5, 6], redis=self.redis, maxlen=3)
assert d == [4, 5, 6]
assert d == collections.deque([4, 5, 6])

d = RedisDeque([1, 2, 3, 4, 5, 6], redis=self.redis, maxlen=0)
assert d == []
assert d == collections.deque()

def test_persistent_deque_bigger_than_maxlen(self):
d1 = RedisDeque('ghi', redis=self.redis)
Expand All @@ -87,26 +88,26 @@ def test_maxlen_not_writable(self):
def test_insert_into_full(self):
d = RedisDeque('gh', redis=self.redis, maxlen=3)
d.insert(len(d), 'i')
assert d == ['g', 'h', 'i']
assert d == collections.deque(['g', 'h', 'i'])

with self.assertRaises(IndexError):
d.insert(len(d), 'j')

def test_append_trims_when_full(self):
d = RedisDeque('gh', redis=self.redis, maxlen=3)
d.append('i')
assert d == ['g', 'h', 'i']
assert d == collections.deque(['g', 'h', 'i'])
d.append('j')
assert d == ['h', 'i', 'j']
assert d == collections.deque(['h', 'i', 'j'])
d.appendleft('g')
assert d == ['g', 'h', 'i']
assert d == collections.deque(['g', 'h', 'i'])

def test_extend(self):
d = RedisDeque('ghi', redis=self.redis, maxlen=4)
d.extend('jkl')
assert d == ['i', 'j', 'k', 'l']
assert d == collections.deque(['i', 'j', 'k', 'l'])
d.extendleft('hg')
assert d == ['g', 'h', 'i', 'j']
assert d == collections.deque(['g', 'h', 'i', 'j'])

def test_popleft_from_empty(self):
d = RedisDeque(redis=self.redis)
Expand All @@ -124,29 +125,29 @@ def test_rotate_zero_steps(self):
'Rotating 0 steps is a no-op'
d = RedisDeque(('g', 'h', 'i', 'j', 'k', 'l'), redis=self.redis)
d.rotate(0)
assert d == ['g', 'h', 'i', 'j', 'k', 'l']
assert d == collections.deque(['g', 'h', 'i', 'j', 'k', 'l'])

def test_rotate_empty_deque(self):
'Rotating an empty RedisDeque is a no-op'
d = RedisDeque(redis=self.redis)
d.rotate(2)
assert d == []
assert d == collections.deque()

def test_rotate_right(self):
'A positive number rotates a RedisDeque right'
# I got this example from here:
# https://pymotw.com/2/collections/deque.html#rotating
d = RedisDeque(range(10), redis=self.redis)
d.rotate(2)
assert d == [8, 9, 0, 1, 2, 3, 4, 5, 6, 7]
assert d == collections.deque([8, 9, 0, 1, 2, 3, 4, 5, 6, 7])

def test_rotate_left(self):
'A negative number rotates a RedisDeque left'
# I got this example from here:
# https://pymotw.com/2/collections/deque.html#rotating
d = RedisDeque(range(10), redis=self.redis)
d.rotate(-2)
assert d == [2, 3, 4, 5, 6, 7, 8, 9, 0, 1]
assert d == collections.deque([2, 3, 4, 5, 6, 7, 8, 9, 0, 1])

def test_moving_average(self):
'Test RedisDeque-based moving average'
Expand Down Expand Up @@ -177,7 +178,7 @@ def test_delete_nth(self):
e = d.popleft()
d.rotate(3)
assert e == 'j'
assert d == ['g', 'h', 'i', 'k', 'l']
assert d == collections.deque(['g', 'h', 'i', 'k', 'l'])

def test_truthiness(self):
d = RedisDeque('ghi', redis=self.redis)
Expand Down