Skip to content

Commit

Permalink
Merge pull request #45 from Attumm/v2.5.0
Browse files Browse the repository at this point in the history
V2.5.0
  • Loading branch information
Attumm authored Feb 28, 2024
2 parents d20c7a6 + f176258 commit fef0da4
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 45 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ with dic.pipeline():
You can use RedisDict for distributed computing by starting multiple RedisDict instances on different servers or instances that have access to the same Redis instance:
```python
# On server 1
from redis_dict import RedisDict

dic = RedisDict(namespace="example")
dic["foo"] = "bar"

Expand Down
37 changes: 21 additions & 16 deletions redis_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

SENTINEL = object()


transform_type = Dict[str, Callable[[str], Any]]
pre_transform_type = Dict[str, Callable[[Any], str]]

Expand Down Expand Up @@ -234,30 +233,34 @@ def add_type(self, k: str, v: Callable[[str], Any]) -> None:
"""
self.transform[k] = v

def __cmp__(self, other: Any) -> int:
def __eq__(self, other: Any) -> bool:
"""
Compare the current RedisDict with another object.
Args:
other (Any): The object to compare with.
Returns:
int: 1 if equal, -1 otherwise.
Note:
TODO add the following methods
__lt__(self, other)
__le__(self, other)
__eq__(self, other)
__ne__(self, other)
__gt__(self, other)
__ge__(self, other)
bool: True if equal, False otherwise
"""
if len(self) != len(other):
return -1
return False
for key, value in self.iteritems():
if value != other.get(key, SENTINEL):
return -1
return 1
return False
return True

def __ne__(self, other: Any) -> bool:
"""
Compare the current RedisDict with another object.
Args:
other (Any): The object to compare with.
Returns:
bool: False if equal, True otherwise
"""
return not self.__eq__(other)

def __getitem__(self, item: str) -> Any:
"""
Expand Down Expand Up @@ -427,7 +430,7 @@ def keys(self) -> List[str]:

def iteritems(self) -> Iterator[Tuple[str, Any]]:
"""
Note: for pythone2 str is needed
Note: for python2 str is needed
"""
to_rm = len(self.namespace) + 1
for item in self._scan_keys():
Expand Down Expand Up @@ -639,8 +642,10 @@ def chain_del(self, iterable: List[str]) -> None:
"""
return self.__delitem__(':'.join(iterable))

# def expire_at(self, sec_epoch: int | timedelta) -> Iterator[None]:
# compatibility with Python 3.9 typing
@contextmanager
def expire_at(self, sec_epoch: int | timedelta) -> Iterator[None]:
def expire_at(self, sec_epoch: Union[int, timedelta]) -> Iterator[None]:
"""
Context manager to set the expiration time for keys in the RedisDict.
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
long_description=long_description,
long_description_content_type='text/markdown',

version='2.4.1',
version='2.5.0',
py_modules=['redis_dict'],
install_requires=['redis',],
license='MIT',
Expand All @@ -41,6 +41,7 @@
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
],
)

2 changes: 1 addition & 1 deletion test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
attrs==22.2.0
cffi==1.15.1
coverage==5.5
cryptography==42.0.2
cryptography==42.0.4
exceptiongroup==1.1.1
future==0.18.3
hypothesis==6.70.1
Expand Down
189 changes: 162 additions & 27 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1206,18 +1206,7 @@ def test_injection_attack_rename(self):
self.assertEqual(self.r['foo3'], 'bar3')


@unittest.skip
class TestRedisDictComparison(unittest.TestCase):
""" Should be added for python3, then this unit test should pass.
TODO: add the following methods
__lt__(self, other)
__le__(self, other)
__eq__(self, other)
__ne__(self, other)
__gt__(self, other)
__ge__(self, other)
"""

def setUp(self):
self.r1 = RedisDict(namespace="test1")
self.r2 = RedisDict(namespace="test2")
Expand All @@ -1238,46 +1227,192 @@ def tearDown(self):
self.r3.clear()
self.r4.clear()

def test_equality(self):
@classmethod
def clear_test_namespace(cls):
names_spaces = [
"test1", "test2", "test3", "test4",
"sequential_comparison", "test_empty",
"test_nested_empty"
]
for namespace in names_spaces:
RedisDict(namespace).clear()

@classmethod
def tearDownClass(cls):
cls.clear_test_namespace()

def test_eq(self):
self.assertTrue(self.r1 == self.r2)
self.assertFalse(self.r1 == self.r3)
self.assertFalse(self.r1 == self.r4)

self.assertTrue(self.r2 == self.r1)
self.assertFalse(self.r2 == self.r3)
self.assertFalse(self.r2 == self.r4)

self.assertFalse(self.r3 == self.r4)

def test_inequality(self):
def test_eq_with_redis_dict(self):
self.assertEqual(self.r1, self.r2)

def test_eq_with_dict(self):
self.assertEqual(self.r1, self.d1)

def test_eq_empty(self):
empty_r = RedisDict(namespace="test_empty")
self.assertEqual(empty_r, {})
empty_r.clear()

def test_eq_nested_empty(self):
nested_empty_r = RedisDict(namespace="test_nested_empty")
nested_empty_r.update({"a": {}})
nested_empty_d = {"a": {}}
self.assertEqual(nested_empty_r, nested_empty_d)
nested_empty_r.clear()

def test_neq(self):
self.assertFalse(self.r1 != self.r2)
self.assertTrue(self.r1 != self.r3)
self.assertTrue(self.r1 != self.r4)

self.assertFalse(self.r2 != self.r1)
self.assertTrue(self.r2 != self.r3)
self.assertTrue(self.r2 != self.r4)

self.assertTrue(self.r3 != self.r4)

def test_equal_redis_dict(self):
self.assertEqual(self.r1, self.r2)
def test_neq_with_redis_dict(self):
self.assertNotEqual(self.r1, self.r3)
self.assertNotEqual(self.r1, self.r4)

def test_equal_with_dict(self):
self.assertEqual(self.r1, self.d1)
def test_neq_with_dict(self):
self.assertNotEqual(self.r1, self.d2)

def test_empty_equal(self):
empty_r = RedisDict(namespace="test_empty") # TODO make sure it's deleted
self.assertEqual(empty_r, {})
def test_neq_empty(self):
empty_r = RedisDict(namespace="test_empty")
self.assertNotEqual(self.r1, {})
self.assertNotEqual(empty_r, self.d1)
empty_r.clear()

def test_nested_empty_equal(self):
nested_empty_r = RedisDict(namespace="test_nested_empty") # TODO make sure it's deleted
def test_neq_nested_empty(self):
nested_empty_r = RedisDict(namespace="test_nested_empty")
nested_empty_r.update({"a": {}})
nested_empty_d = {"a": {}}
self.assertEqual(nested_empty_r, nested_empty_d)
self.assertNotEqual(self.r1, nested_empty_d)
self.assertNotEqual(nested_empty_r, self.d1)
nested_empty_r.clear()

def test_is_comparison(self):
self.assertTrue(self.r1 is self.r1)
self.assertFalse(self.r1 is self.r2)
self.assertTrue(self.r2 is self.r2)
self.assertFalse(self.r2 is self.r3)

def test_is_comparison_with_dict(self):
self.assertFalse(self.r1 is self.d1)
self.assertFalse(self.d2 is self.d1)

def test_is_not_comparison(self):
self.assertFalse(self.r1 is not self.r1)
self.assertTrue(self.r1 is not self.r2)
self.assertFalse(self.r2 is not self.r2)
self.assertTrue(self.r2 is not self.r3)

def test_is_not_comparison_with_dict(self):
self.assertTrue(self.r1 is not self.d1)
self.assertTrue(self.d2 is not self.d1)

def test_lt(self):
with self.assertRaises(TypeError):
self.r1 < self.r2

def test_lt_with_different_type(self):
with self.assertRaises(TypeError):
self.r1 < self.d1

def test_le(self):
with self.assertRaises(TypeError):
self.r1 <= self.r2

def test_le_with_different_type(self):
with self.assertRaises(TypeError):
self.r1 <= self.d1

def test_ge(self):
with self.assertRaises(TypeError):
self.r1 >= self.r2

def test_ge_with_different_type(self):
with self.assertRaises(TypeError):
self.r1 >= self.d1

def test_gt(self):
with self.assertRaises(TypeError):
self.r1 > self.r2

def test_gt_with_different_type(self):
with self.assertRaises(TypeError):
self.r1 > self.d1

def test_sequential_comparison(self):
d = {}
d2 = {}
rd = RedisDict(namespace="sequential_comparison")

# Testing for identity
self.assertTrue(d is not d2)
self.assertTrue(d is not rd)

# Testing for equality
self.assertTrue(d == d2)
self.assertTrue(d == rd)
self.assertTrue(d.items() == d2.items())
self.assertTrue(list(d.items()) == rd.items())

d["foo1"] = "bar1"

# Testing for inequality after modification in 'd'
self.assertTrue(d != d2)
self.assertTrue(d != rd)
self.assertTrue(d.items() != d2.items())
self.assertTrue(list(d.items()) != rd.items())

# Modifying 'd2' and 'rd'
d2["foo1"] = "bar1"
rd["foo1"] = "bar1"

# Testing for equality
self.assertTrue(d == d2)
self.assertTrue(d == rd)
self.assertTrue(d.items() == d2.items())
self.assertTrue(list(d.items()) == rd.items())

d.clear()
d2.clear()
rd.clear()

rd.update({"a": {}})
d.update({"a": {}})
d2.update({"a": {}})

# Testing for nested comparison
self.assertTrue(d == d2)
self.assertTrue(d == rd)
self.assertTrue(d.items() == d2.items())
self.assertTrue(list(d.items()) == rd.items())

d.clear()

# Testing for inequality after clear
self.assertTrue(d != d2)
self.assertTrue(d != rd)
self.assertTrue(d.items() != d2.items())
self.assertTrue(list(d.items()) != rd.items())

d2.clear()
rd.clear()

# Testing for equality after clear
self.assertTrue(d == d2)
self.assertTrue(d == rd)
self.assertTrue(d.items() == d2.items())
self.assertTrue(list(d.items()) == rd.items())


class TestRedisDictPreserveExpire(unittest.TestCase):
Expand Down

0 comments on commit fef0da4

Please sign in to comment.