From b2138ad9309b8fadc83ea032974fb8cde100318c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:44:55 +0000 Subject: [PATCH 1/9] Bump cryptography from 42.0.2 to 42.0.4 Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.2 to 42.0.4. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/42.0.2...42.0.4) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index ea57048..088711a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -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 From 83436057c5dd29c5837fe65a5a5b2b2a9957510b Mon Sep 17 00:00:00 2001 From: Pydes-boop <59734957+Pydes-boop@users.noreply.github.com> Date: Thu, 22 Feb 2024 00:52:25 +0100 Subject: [PATCH 2/9] updated to python3 __eq__ and __neq__ comparison operators - removed __cmp__ python2 operators - added __eq__ and __neq__ operations - updated testing suit according to default dict behaviour --- redis_dict.py | 36 ++++++++-------- tests.py | 111 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 104 insertions(+), 43 deletions(-) diff --git a/redis_dict.py b/redis_dict.py index bf312ef..c548c28 100644 --- a/redis_dict.py +++ b/redis_dict.py @@ -7,7 +7,6 @@ SENTINEL = object() - transform_type = Dict[str, Callable[[str], Any]] pre_transform_type = Dict[str, Callable[[Any], str]] @@ -234,7 +233,7 @@ 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. @@ -242,22 +241,26 @@ def __cmp__(self, other: Any) -> int: 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: """ @@ -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(): @@ -712,7 +715,8 @@ def multi_dict(self, key: str) -> Dict[str, Any]: if len(keys) == 0: return {} to_rm = keys[0].rfind(':') + 1 - return dict(zip([i[to_rm:] for i in keys], (self._transform(i) for i in self.redis.mget(keys) if i is not None))) + return dict( + zip([i[to_rm:] for i in keys], (self._transform(i) for i in self.redis.mget(keys) if i is not None))) def multi_del(self, key: str) -> int: """ diff --git a/tests.py b/tests.py index 14ffc0b..6d39cbb 100644 --- a/tests.py +++ b/tests.py @@ -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") @@ -1238,46 +1227,114 @@ def tearDown(self): self.r3.clear() self.r4.clear() - def test_equality(self): + 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 class TestRedisDictPreserveExpire(unittest.TestCase): From e63e9cd741dc2e28f3df8f73d98cdb8c6b18401c Mon Sep 17 00:00:00 2001 From: Pydes-boop <59734957+Pydes-boop@users.noreply.github.com> Date: Sun, 25 Feb 2024 21:57:21 +0100 Subject: [PATCH 3/9] added sequential __cmp__ test for readability --- redis_dict.py | 3 +-- tests.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/redis_dict.py b/redis_dict.py index c548c28..5aa41f8 100644 --- a/redis_dict.py +++ b/redis_dict.py @@ -715,8 +715,7 @@ def multi_dict(self, key: str) -> Dict[str, Any]: if len(keys) == 0: return {} to_rm = keys[0].rfind(':') + 1 - return dict( - zip([i[to_rm:] for i in keys], (self._transform(i) for i in self.redis.mget(keys) if i is not None))) + return dict(zip([i[to_rm:] for i in keys], (self._transform(i) for i in self.redis.mget(keys) if i is not None))) def multi_del(self, key: str) -> int: """ diff --git a/tests.py b/tests.py index 6d39cbb..a3fb3b4 100644 --- a/tests.py +++ b/tests.py @@ -1336,6 +1336,61 @@ 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 + assert d is not d2 + assert d is not rd + + # Testing for equality + assert d == d2 + assert d == rd + assert d.items() == d2.items() + assert d.items() == rd.items() + + d["foo1"] = "bar1" + + # Testing for inequality after modification in 'd' + assert d != d2 + assert d != rd + assert d.items() != d2.items() + assert d.items() != rd.items() + + # Modifying 'd2' and 'rd' + d2["foo1"] = "bar1" + rd["foo1"] = "bar1" + + # Testing for equality + assert d == d2 + assert d == rd + assert d.items() == d2.items() + assert d.items() == rd.items() + + d.clear() + d2.clear() + rd.clear() + + rd.update({"a": {}}) + d.update({"a": {}}) + d2.update({"a": {}}) + + # Testing for nested comparison + assert d == d2 + assert d == rd + assert d.items() == d2.items() + assert d.items() == rd.items() + + d.clear() + + # Testing for inequality after clear + assert d != d2 + assert d != rd + assert d.items() != d2.items() + assert d.items() != rd.items() + class TestRedisDictPreserveExpire(unittest.TestCase): @classmethod From 02fcbd82d1a70278d2d828325739981ca50d8085 Mon Sep 17 00:00:00 2001 From: Pydes-boop <59734957+Pydes-boop@users.noreply.github.com> Date: Mon, 26 Feb 2024 23:43:04 +0100 Subject: [PATCH 4/9] fixed sequential comparison test to use unit test comparison --- tests.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests.py b/tests.py index a3fb3b4..992b8f5 100644 --- a/tests.py +++ b/tests.py @@ -1342,32 +1342,32 @@ def test_sequential_comparison(self): rd = RedisDict(namespace="sequential_comparison") # Testing for identity - assert d is not d2 - assert d is not rd + self.assertTrue(d is not d2) + self.assertTrue(d is not rd) # Testing for equality - assert d == d2 - assert d == rd - assert d.items() == d2.items() - assert d.items() == rd.items() + 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' - assert d != d2 - assert d != rd - assert d.items() != d2.items() - assert d.items() != rd.items() + 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 - assert d == d2 - assert d == rd - assert d.items() == d2.items() - assert d.items() == rd.items() + 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() @@ -1378,18 +1378,18 @@ def test_sequential_comparison(self): d2.update({"a": {}}) # Testing for nested comparison - assert d == d2 - assert d == rd - assert d.items() == d2.items() - assert d.items() == rd.items() + 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 - assert d != d2 - assert d != rd - assert d.items() != d2.items() - assert d.items() != rd.items() + 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): From 7457d1b047d6cd83414c83eecfa5874031b2e54a Mon Sep 17 00:00:00 2001 From: Attumm Date: Tue, 27 Feb 2024 09:36:28 +0100 Subject: [PATCH 5/9] Added import to example --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 923ac47..5f43c1c 100644 --- a/README.md +++ b/README.md @@ -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" From bffb1543d7d6eef0cd3f4ec597c7bfac93f47d84 Mon Sep 17 00:00:00 2001 From: Attumm Date: Tue, 27 Feb 2024 09:37:07 +0100 Subject: [PATCH 6/9] bumped v2.5.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index efe5508..cb04139 100644 --- a/setup.py +++ b/setup.py @@ -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', From cf4105760366d90c013f33a245c6bb4a0bb67c72 Mon Sep 17 00:00:00 2001 From: Attumm Date: Tue, 27 Feb 2024 09:41:39 +0100 Subject: [PATCH 7/9] Added python 3.12 --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index cb04139..d108b35 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,7 @@ 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', ], ) From ab1bf62f2d9624d4d214707973ed824656cd15a6 Mon Sep 17 00:00:00 2001 From: Attumm Date: Tue, 27 Feb 2024 10:48:23 +0100 Subject: [PATCH 8/9] Changed delete residual data following test execution --- tests.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests.py b/tests.py index 992b8f5..3cbf7a9 100644 --- a/tests.py +++ b/tests.py @@ -1227,6 +1227,20 @@ def tearDown(self): self.r3.clear() self.r4.clear() + @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) @@ -1391,6 +1405,15 @@ def test_sequential_comparison(self): 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): @classmethod From f1762582f4fd25777b9640e6329840a4468c51d3 Mon Sep 17 00:00:00 2001 From: Attumm Date: Wed, 28 Feb 2024 10:15:03 +0100 Subject: [PATCH 9/9] Changed typing for compatibility with Python 3.9 --- redis_dict.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/redis_dict.py b/redis_dict.py index 5aa41f8..c1ef61f 100644 --- a/redis_dict.py +++ b/redis_dict.py @@ -642,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.