From 9059d90b43e328a74814d60a3e3e37b2dffdf141 Mon Sep 17 00:00:00 2001 From: amirreza Date: Thu, 12 Dec 2024 11:25:37 +0330 Subject: [PATCH 01/22] added hdel_many method --- django_valkey/async_cache/cache.py | 1 + django_valkey/async_cache/client/default.py | 13 +++++++++++++ django_valkey/base.py | 8 ++++++++ django_valkey/base_client.py | 11 +++++++++++ 4 files changed, 33 insertions(+) diff --git a/django_valkey/async_cache/cache.py b/django_valkey/async_cache/cache.py index ce68ece..e5fb034 100644 --- a/django_valkey/async_cache/cache.py +++ b/django_valkey/async_cache/cache.py @@ -67,6 +67,7 @@ class AsyncValkeyCache(BaseValkeyCache[AsyncDefaultClient, AValkey]): hset = BaseValkeyCache.ahset hdel = BaseValkeyCache.ahdel + hdel_many = BaseValkeyCache.ahdel_many hlen = BaseValkeyCache.ahlen diff --git a/django_valkey/async_cache/client/default.py b/django_valkey/async_cache/client/default.py index 5d6b657..9dffed9 100644 --- a/django_valkey/async_cache/client/default.py +++ b/django_valkey/async_cache/client/default.py @@ -1089,6 +1089,19 @@ async def hdel( ahdel = hdel + async def hdel_many( + self, + name: str, + keys: list, + version: int | None = None, + client: AValkey | Any | None = None, + ) -> int: + client = await self._get_client(write=True, client=client) + nkeys = [await self.make_key(key) for key in keys] + return await client.hdel(name, *nkeys) + + ahdel_many = hdel_many + async def hlen(self, name: str, client: AValkey | Any | None = None) -> int: """ Return the number of items in hash name. diff --git a/django_valkey/base.py b/django_valkey/base.py index 00493e2..f631707 100644 --- a/django_valkey/base.py +++ b/django_valkey/base.py @@ -356,6 +356,14 @@ def hdel(self, *args, **kwargs) -> int: async def ahdel(self, *args, **kwargs) -> int: return await self.client.hdel(*args, **kwargs) + @omit_exception + def hdel_many(self, *args, **kwargs) -> int: + return self.client.hdel_many(*args, **kwargs) + + @omit_exception + async def ahdel_many(self, *args, **kwargs) -> int: + return await self.client.ahdel_many(*args, **kwargs) + @omit_exception def hlen(self, *args, **kwargs) -> int: return self.client.hlen(*args, **kwargs) diff --git a/django_valkey/base_client.py b/django_valkey/base_client.py index b26c2c0..1cfbb37 100644 --- a/django_valkey/base_client.py +++ b/django_valkey/base_client.py @@ -1190,6 +1190,17 @@ def hdel( nkey = self.make_key(key, version=version) return client.hdel(name, nkey) + def hdel_many( + self, + name: str, + keys: list, + version: int | None = None, + client: Backend | Any | None = None, + ) -> int: + client = self._get_client(write=True, client=client) + nkeys = [self.make_key(key) for key in keys] + return client.hdel(name, *nkeys) + def hlen( self, name: str, From c89e52c53b4368d02c4a313292657825e17552dc Mon Sep 17 00:00:00 2001 From: amirreza Date: Thu, 12 Dec 2024 11:25:49 +0330 Subject: [PATCH 02/22] test hdel_many --- tests/test_backend.py | 12 ++++++++++++ tests/tests_async/test_backend.py | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index 071d75a..3353ce7 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -861,6 +861,18 @@ def test_hdel(self, cache: ValkeyCache): assert not cache.hexists("foo_hash2", "foo1") assert cache.hexists("foo_hash2", "foo2") + def test_hdel_many(self, cache: ValkeyCache): + if isinstance(cache.client, ShardClient): + pytest.skip("ShardClient doesn't support get_client") + cache.hset("foo_hash3", "foo1", "bar1") + cache.hset("foo_hash3", "foo2", "bar2") + assert cache.hlen("foo_hash3") == 2 + deleted_count = cache.hdel_many("foo_hash3", ["foo1", "foo2"]) + assert deleted_count == 2 + assert cache.hlen("foo_hash3") == 0 + assert not cache.hexists("foo_hash3", "foo1") + assert not cache.hexists("foo_hash3", "foo2") + def test_hlen(self, cache: ValkeyCache): if isinstance(cache.client, ShardClient): pytest.skip("ShardClient doesn't support get_client") diff --git a/tests/tests_async/test_backend.py b/tests/tests_async/test_backend.py index d0239f9..111ad0f 100644 --- a/tests/tests_async/test_backend.py +++ b/tests/tests_async/test_backend.py @@ -882,6 +882,16 @@ async def test_hdel(self, cache: AsyncValkeyCache): assert not await cache.ahexists("foo_hash2", "foo1") assert await cache.ahexists("foo_hash2", "foo2") + async def test_hdel_many(self, cache: AsyncValkeyCache): + await cache.ahset("foo_hash3", "foo1", "bar1") + await cache.ahset("foo_hash3", "foo2", "bar2") + assert await cache.ahlen("foo_hash3") == 2 + deleted_count = await cache.ahdel_many("foo_hash3", ["foo1", "foo2"]) + assert deleted_count == 2 + assert await cache.ahlen("foo_hash3") == 0 + assert not await cache.ahexists("foo_hash3", "foo1") + assert not await cache.ahexists("foo_hash3", "foo2") + async def test_hlen(self, cache: AsyncValkeyCache): # if isinstance(cache.client, ShardClient): # pytest.skip("ShardClient doesn't support get_client") From c8775e3c701867223c481eddb98d98eac7a57cd2 Mon Sep 17 00:00:00 2001 From: amirreza Date: Thu, 12 Dec 2024 11:45:25 +0330 Subject: [PATCH 03/22] added hget method --- django_valkey/async_cache/cache.py | 2 ++ django_valkey/async_cache/client/default.py | 19 +++++++++++++++++++ django_valkey/base.py | 8 ++++++++ django_valkey/base_client.py | 17 +++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/django_valkey/async_cache/cache.py b/django_valkey/async_cache/cache.py index e5fb034..0f1228a 100644 --- a/django_valkey/async_cache/cache.py +++ b/django_valkey/async_cache/cache.py @@ -69,6 +69,8 @@ class AsyncValkeyCache(BaseValkeyCache[AsyncDefaultClient, AValkey]): hdel = BaseValkeyCache.ahdel hdel_many = BaseValkeyCache.ahdel_many + hget = BaseValkeyCache.ahget + hlen = BaseValkeyCache.ahlen hkeys = BaseValkeyCache.ahkeys diff --git a/django_valkey/async_cache/client/default.py b/django_valkey/async_cache/client/default.py index 9dffed9..b05dc8b 100644 --- a/django_valkey/async_cache/client/default.py +++ b/django_valkey/async_cache/client/default.py @@ -1102,6 +1102,25 @@ async def hdel_many( ahdel_many = hdel_many + async def hget( + self, + name: str, + key: str, + version: int | None = None, + client: AValkey | Any | None = None, + ) -> str | None: + client = await self._get_client(write=False, client=client) + key = await self.make_key(key, version=version) + try: + value = await client.hget(name, key) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + if value is None: + return None + return await self.decode(value) + + ahget = hget + async def hlen(self, name: str, client: AValkey | Any | None = None) -> int: """ Return the number of items in hash name. diff --git a/django_valkey/base.py b/django_valkey/base.py index f631707..d6e911e 100644 --- a/django_valkey/base.py +++ b/django_valkey/base.py @@ -364,6 +364,14 @@ def hdel_many(self, *args, **kwargs) -> int: async def ahdel_many(self, *args, **kwargs) -> int: return await self.client.ahdel_many(*args, **kwargs) + @omit_exception + def hget(self, *args, **kwargs) -> bytes | None: + return self.client.hget(*args, **kwargs) + + @omit_exception + async def ahget(self, *args, **kwargs) -> str | None: + return await self.client.ahget(*args, **kwargs) + @omit_exception def hlen(self, *args, **kwargs) -> int: return self.client.hlen(*args, **kwargs) diff --git a/django_valkey/base_client.py b/django_valkey/base_client.py index 1cfbb37..eb71bd1 100644 --- a/django_valkey/base_client.py +++ b/django_valkey/base_client.py @@ -1201,6 +1201,23 @@ def hdel_many( nkeys = [self.make_key(key) for key in keys] return client.hdel(name, *nkeys) + def hget( + self, + name: str, + key: str, + version: int | None = None, + client: Backend | Any | None = None, + ) -> str | None: + client = self._get_client(write=False, client=client) + key = self.make_key(key, version=version) + try: + value = client.hget(name, key) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + if value is None: + return None + return self.decode(value) + def hlen( self, name: str, From 962c299d0d2ad4184f6dc513af252aaa7ad8e9fa Mon Sep 17 00:00:00 2001 From: amirreza Date: Thu, 12 Dec 2024 11:45:38 +0330 Subject: [PATCH 04/22] test hget --- tests/test_backend.py | 9 +++++++++ tests/tests_async/test_backend.py | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index 3353ce7..9e71d5b 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -873,6 +873,15 @@ def test_hdel_many(self, cache: ValkeyCache): assert not cache.hexists("foo_hash3", "foo1") assert not cache.hexists("foo_hash3", "foo2") + def test_hget(self, cache: ValkeyCache): + if isinstance(cache.client, ShardClient): + pytest.skip("ShardClient doesn't support get_client") + cache.hset("foo_hash1", "foo1", "bar1") + result = cache.hget("foo_hash1", "foo1") + assert result == "bar1" + result = cache.hget("foo_hash1", "foo2") + assert result is None + def test_hlen(self, cache: ValkeyCache): if isinstance(cache.client, ShardClient): pytest.skip("ShardClient doesn't support get_client") diff --git a/tests/tests_async/test_backend.py b/tests/tests_async/test_backend.py index 111ad0f..341b3ed 100644 --- a/tests/tests_async/test_backend.py +++ b/tests/tests_async/test_backend.py @@ -892,6 +892,13 @@ async def test_hdel_many(self, cache: AsyncValkeyCache): assert not await cache.ahexists("foo_hash3", "foo1") assert not await cache.ahexists("foo_hash3", "foo2") + async def test_hget(self, cache: AsyncValkeyCache): + await cache.ahset("foo_hash1", "foo1", "bar1") + result = await cache.ahget("foo_hash1", "foo1") + assert result == "bar1" + result = await cache.ahget("foo_hash1", "foo2") + assert result is None + async def test_hlen(self, cache: AsyncValkeyCache): # if isinstance(cache.client, ShardClient): # pytest.skip("ShardClient doesn't support get_client") From 915364a35604e7c2aac7a134a2189fe213dfc837 Mon Sep 17 00:00:00 2001 From: amirreza Date: Thu, 12 Dec 2024 12:27:52 +0330 Subject: [PATCH 05/22] added hgetall method --- django_valkey/async_cache/cache.py | 1 + django_valkey/async_cache/client/default.py | 16 ++++++++++++++++ django_valkey/base.py | 8 ++++++++ django_valkey/base_client.py | 14 ++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/django_valkey/async_cache/cache.py b/django_valkey/async_cache/cache.py index 0f1228a..05e896b 100644 --- a/django_valkey/async_cache/cache.py +++ b/django_valkey/async_cache/cache.py @@ -70,6 +70,7 @@ class AsyncValkeyCache(BaseValkeyCache[AsyncDefaultClient, AValkey]): hdel_many = BaseValkeyCache.ahdel_many hget = BaseValkeyCache.ahget + hgetall = BaseValkeyCache.ahgetall hlen = BaseValkeyCache.ahlen diff --git a/django_valkey/async_cache/client/default.py b/django_valkey/async_cache/client/default.py index b05dc8b..d976ed7 100644 --- a/django_valkey/async_cache/client/default.py +++ b/django_valkey/async_cache/client/default.py @@ -1121,6 +1121,22 @@ async def hget( ahget = hget + async def hgetall( + self, name: str, client: AValkey | Any | None = None + ) -> dict[str, str] | dict: + client = await self._get_client(write=False, client=client) + try: + _values = await client.hgetall(name) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + + values = {} + for key, value in _values.items(): + values[key.decode()] = await self.decode(value) + return values + + ahgetall = hgetall + async def hlen(self, name: str, client: AValkey | Any | None = None) -> int: """ Return the number of items in hash name. diff --git a/django_valkey/base.py b/django_valkey/base.py index d6e911e..ddf4abc 100644 --- a/django_valkey/base.py +++ b/django_valkey/base.py @@ -372,6 +372,14 @@ def hget(self, *args, **kwargs) -> bytes | None: async def ahget(self, *args, **kwargs) -> str | None: return await self.client.ahget(*args, **kwargs) + @omit_exception + def hgetall(self, *args, **kwargs) -> dict: + return self.client.hgetall(*args, **kwargs) + + @omit_exception + async def ahgetall(self, *args, **kwargs) -> dict: + return await self.client.ahgetall(*args, **kwargs) + @omit_exception def hlen(self, *args, **kwargs) -> int: return self.client.hlen(*args, **kwargs) diff --git a/django_valkey/base_client.py b/django_valkey/base_client.py index eb71bd1..7dd8776 100644 --- a/django_valkey/base_client.py +++ b/django_valkey/base_client.py @@ -1218,6 +1218,20 @@ def hget( return None return self.decode(value) + def hgetall( + self, name: str, client: Backend | Any | None = None + ) -> dict[str, str] | dict: + client = self._get_client(write=False, client=client) + try: + _values = client.hgetall(name) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + values = {} + for key, value in _values.items(): + values[key.decode()] = self.decode(value) + + return values + def hlen( self, name: str, From 98588aceb9639e6811381905583ed2411d077270 Mon Sep 17 00:00:00 2001 From: amirreza Date: Thu, 12 Dec 2024 12:28:01 +0330 Subject: [PATCH 06/22] test hgetall --- tests/test_backend.py | 11 +++++++++++ tests/tests_async/test_backend.py | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index 9e71d5b..7fca6b5 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -882,6 +882,17 @@ def test_hget(self, cache: ValkeyCache): result = cache.hget("foo_hash1", "foo2") assert result is None + def test_hgetall(self, cache: ValkeyCache): + if isinstance(cache.client, ShardClient): + pytest.skip("ShardClient doesn't support get_client") + + cache.hset("foo_hash1", "foo1", "bar1") + cache.hset("foo_hash1", "foo2", "bar2") + result = cache.hgetall("foo_hash1") + assert result == {":1:foo1": "bar1", ":1:foo2": "bar2"} + result = cache.hgetall("foo_hash2") + assert result == {} + def test_hlen(self, cache: ValkeyCache): if isinstance(cache.client, ShardClient): pytest.skip("ShardClient doesn't support get_client") diff --git a/tests/tests_async/test_backend.py b/tests/tests_async/test_backend.py index 341b3ed..45abbe7 100644 --- a/tests/tests_async/test_backend.py +++ b/tests/tests_async/test_backend.py @@ -899,6 +899,14 @@ async def test_hget(self, cache: AsyncValkeyCache): result = await cache.ahget("foo_hash1", "foo2") assert result is None + async def test_hgetall(self, cache: AsyncValkeyCache): + await cache.ahset("foo_hash1", "foo1", "bar1") + await cache.ahset("foo_hash1", "foo2", "bar2") + result = await cache.ahgetall("foo_hash1") + assert result == {":1:foo1": "bar1", ":1:foo2": "bar2"} + result = await cache.ahgetall("foo_hash2") + assert result == {} + async def test_hlen(self, cache: AsyncValkeyCache): # if isinstance(cache.client, ShardClient): # pytest.skip("ShardClient doesn't support get_client") From 404b17695eda4e2c5f75ff47d1c22e5fd7637655 Mon Sep 17 00:00:00 2001 From: amirreza Date: Sat, 14 Dec 2024 07:02:28 +0330 Subject: [PATCH 07/22] added hsetnx method --- django_valkey/async_cache/cache.py | 1 + django_valkey/async_cache/client/default.py | 15 +++++++++++++++ django_valkey/base.py | 8 ++++++++ django_valkey/base_client.py | 13 +++++++++++++ 4 files changed, 37 insertions(+) diff --git a/django_valkey/async_cache/cache.py b/django_valkey/async_cache/cache.py index 05e896b..feb4b01 100644 --- a/django_valkey/async_cache/cache.py +++ b/django_valkey/async_cache/cache.py @@ -65,6 +65,7 @@ class AsyncValkeyCache(BaseValkeyCache[AsyncDefaultClient, AValkey]): sunionstore = BaseValkeyCache.asunionstore hset = BaseValkeyCache.ahset + hsetnx = BaseValkeyCache.ahsetnx hdel = BaseValkeyCache.ahdel hdel_many = BaseValkeyCache.ahdel_many diff --git a/django_valkey/async_cache/client/default.py b/django_valkey/async_cache/client/default.py index d976ed7..791b66e 100644 --- a/django_valkey/async_cache/client/default.py +++ b/django_valkey/async_cache/client/default.py @@ -1072,6 +1072,21 @@ async def hset( ahset = hset + async def hsetnx( + self, + name: str, + key, + value, + version: int | None = None, + client: AValkey | Any | None = None, + ) -> int: + client = await self._get_client(write=True, client=client) + nkey = await self.make_key(key, version=version) + nvalue = await self.encode(value) + return await client.hsetnx(name, nkey, nvalue) + + ahsetnx = hsetnx + async def hdel( self, name: str, diff --git a/django_valkey/base.py b/django_valkey/base.py index ddf4abc..fb358a8 100644 --- a/django_valkey/base.py +++ b/django_valkey/base.py @@ -348,6 +348,14 @@ def hset(self, *args, **kwargs) -> int: async def ahset(self, *args, **kwargs) -> int: return await self.client.hset(*args, **kwargs) + @omit_exception + def hsetnx(self, *args, **kwargs) -> int: + return self.client.hsetnx(*args, **kwargs) + + @omit_exception + async def ahsetnx(self, *args, **kwargs) -> int: + return await self.client.hsetnx(*args, **kwargs) + @omit_exception def hdel(self, *args, **kwargs) -> int: return self.client.hdel(*args, **kwargs) diff --git a/django_valkey/base_client.py b/django_valkey/base_client.py index 7dd8776..f63e6ee 100644 --- a/django_valkey/base_client.py +++ b/django_valkey/base_client.py @@ -1175,6 +1175,19 @@ def hset( nvalue = self.encode(value) return client.hset(name, nkey, nvalue) + def hsetnx( + self, + name: str, + key: KeyT, + value: EncodableT, + version: int | None = None, + client: Backend | Any | None = None, + ) -> int: + client = self._get_client(write=True, client=client) + nkey = self.make_key(key, version=version) + nvalue = self.encode(value) + return client.hsetnx(name, nkey, nvalue) + def hdel( self, name: str, From 7a44e5b010a5c63c3db8e090f7c5495a95db1f0a Mon Sep 17 00:00:00 2001 From: amirreza Date: Sat, 14 Dec 2024 07:02:52 +0330 Subject: [PATCH 08/22] test hsetnx --- tests/test_backend.py | 13 +++++++++++++ tests/tests_async/test_backend.py | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index 7fca6b5..b66a97a 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -849,6 +849,19 @@ def test_hset(self, cache: ValkeyCache): assert cache.hexists("foo_hash1", "foo1") assert cache.hexists("foo_hash1", "foo2") + def test_hsetnx(self, cache: ValkeyCache): + if isinstance(cache.client, ShardClient): + pytest.skip("ShardClient doesn't support get_client") + + res = cache.hsetnx("bar_hash1", "foo1", "baz1") + assert res == 1 + result = cache.hget("bar_hash1", "foo1") + assert result == "baz1" + res = cache.hsetnx("bar_hash1", "foo1", "baz2") + assert res == 0 + result = cache.hget("bar_hash1", "foo1") + assert result == "baz1" + def test_hdel(self, cache: ValkeyCache): if isinstance(cache.client, ShardClient): pytest.skip("ShardClient doesn't support get_client") diff --git a/tests/tests_async/test_backend.py b/tests/tests_async/test_backend.py index 45abbe7..95f60f6 100644 --- a/tests/tests_async/test_backend.py +++ b/tests/tests_async/test_backend.py @@ -870,6 +870,16 @@ async def test_hset(self, cache: AsyncValkeyCache): assert await cache.ahexists("foo_hash1", "foo1") assert await cache.ahexists("foo_hash1", "foo2") + async def test_hsetnx(self, cache: AsyncValkeyCache): + res = await cache.ahsetnx("bar_hash1", "foo1", "baz1") + assert res == 1 + result = await cache.ahget("bar_hash1", "foo1") + assert result == "baz1" + res = await cache.ahsetnx("bar_hash1", "foo1", "baz2") + assert res == 0 + result = await cache.ahget("bar_hash1", "foo1") + assert result == "baz1" + async def test_hdel(self, cache: AsyncValkeyCache): # if isinstance(cache.client, ShardClient): # pytest.skip("ShardClient doesn't support get_client") From 4abfd03fc7563eaecdcd7f825192e9badcb33833 Mon Sep 17 00:00:00 2001 From: amirreza Date: Sat, 14 Dec 2024 07:04:05 +0330 Subject: [PATCH 09/22] added hincrby method --- django_valkey/async_cache/cache.py | 2 ++ django_valkey/async_cache/client/default.py | 18 ++++++++++++++++++ django_valkey/base.py | 8 ++++++++ django_valkey/base_client.py | 16 ++++++++++++++++ 4 files changed, 44 insertions(+) diff --git a/django_valkey/async_cache/cache.py b/django_valkey/async_cache/cache.py index feb4b01..4e804e4 100644 --- a/django_valkey/async_cache/cache.py +++ b/django_valkey/async_cache/cache.py @@ -73,6 +73,8 @@ class AsyncValkeyCache(BaseValkeyCache[AsyncDefaultClient, AValkey]): hget = BaseValkeyCache.ahget hgetall = BaseValkeyCache.ahgetall + hincrby = BaseValkeyCache.ahincrby + hlen = BaseValkeyCache.ahlen hkeys = BaseValkeyCache.ahkeys diff --git a/django_valkey/async_cache/client/default.py b/django_valkey/async_cache/client/default.py index 791b66e..bc36530 100644 --- a/django_valkey/async_cache/client/default.py +++ b/django_valkey/async_cache/client/default.py @@ -1152,6 +1152,24 @@ async def hgetall( ahgetall = hgetall + async def hincrby( + self, + name: str, + key: str, + amount: int = 1, + version: int | None = None, + client: AValkey | Any | None = None, + ) -> int: + client = await self._get_client(write=True, client=client) + nkey = await self.make_key(key, version=version) + try: + value = await client.hincrby(name, nkey, amount) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + return value + + ahincrby = hincrby + async def hlen(self, name: str, client: AValkey | Any | None = None) -> int: """ Return the number of items in hash name. diff --git a/django_valkey/base.py b/django_valkey/base.py index fb358a8..455dcb1 100644 --- a/django_valkey/base.py +++ b/django_valkey/base.py @@ -388,6 +388,14 @@ def hgetall(self, *args, **kwargs) -> dict: async def ahgetall(self, *args, **kwargs) -> dict: return await self.client.ahgetall(*args, **kwargs) + @omit_exception + def hincrby(self, *args, **kwargs) -> int: + return self.client.hincrby(*args, **kwargs) + + @omit_exception + async def ahincrby(self, *args, **kwargs) -> int: + return await self.client.ahincrby(*args, **kwargs) + @omit_exception def hlen(self, *args, **kwargs) -> int: return self.client.hlen(*args, **kwargs) diff --git a/django_valkey/base_client.py b/django_valkey/base_client.py index f63e6ee..575b0a7 100644 --- a/django_valkey/base_client.py +++ b/django_valkey/base_client.py @@ -1245,6 +1245,22 @@ def hgetall( return values + def hincrby( + self, + name: str, + key: str, + amount: int = 1, + version: int | None = None, + client: Backend | Any | None = None, + ) -> int: + client = self._get_client(write=True, client=client) + nkey = self.make_key(key, version=version) + try: + value = client.hincrby(name, nkey, amount) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + return value + def hlen( self, name: str, From 566ddf668bb2012f4c1f968588828ac15d867230 Mon Sep 17 00:00:00 2001 From: amirreza Date: Sat, 14 Dec 2024 07:04:56 +0330 Subject: [PATCH 10/22] test hincrby --- tests/test_backend.py | 10 ++++++++++ tests/tests_async/test_backend.py | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index b66a97a..acef6f9 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -906,6 +906,16 @@ def test_hgetall(self, cache: ValkeyCache): result = cache.hgetall("foo_hash2") assert result == {} + def test_hincrby(self, cache: ValkeyCache): + if isinstance(cache.client, ShardClient): + pytest.skip("ShardClient doesn't support get_client") + + cache.hset("foo_hash1", "foo1", 1) + assert cache.hget("foo_hash1", "foo1") == 1 + result = cache.hincrby("foo_hash1", "foo1", amount=2) + assert cache.hget("foo_hash1", "foo1") == 3 + assert result == 3 + def test_hlen(self, cache: ValkeyCache): if isinstance(cache.client, ShardClient): pytest.skip("ShardClient doesn't support get_client") diff --git a/tests/tests_async/test_backend.py b/tests/tests_async/test_backend.py index 95f60f6..71ba6ea 100644 --- a/tests/tests_async/test_backend.py +++ b/tests/tests_async/test_backend.py @@ -917,6 +917,13 @@ async def test_hgetall(self, cache: AsyncValkeyCache): result = await cache.ahgetall("foo_hash2") assert result == {} + async def test_hincrby(self, cache: AsyncValkeyCache): + await cache.ahset("foo_hash1", "foo1", 1) + assert await cache.ahget("foo_hash1", "foo1") == 1 + result = await cache.ahincrby("foo_hash1", "foo1", amount=2) + assert await cache.ahget("foo_hash1", "foo1") == 3 + assert result == 3 + async def test_hlen(self, cache: AsyncValkeyCache): # if isinstance(cache.client, ShardClient): # pytest.skip("ShardClient doesn't support get_client") From 2fee3564b58be66d8eb1fd53b7131c76d5a7bfeb Mon Sep 17 00:00:00 2001 From: amirreza Date: Sat, 14 Dec 2024 07:23:12 +0330 Subject: [PATCH 11/22] added hmget method --- django_valkey/async_cache/cache.py | 1 + django_valkey/async_cache/client/default.py | 19 +++++++++++++++++++ django_valkey/base.py | 8 ++++++++ django_valkey/base_client.py | 17 +++++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/django_valkey/async_cache/cache.py b/django_valkey/async_cache/cache.py index 4e804e4..79c929f 100644 --- a/django_valkey/async_cache/cache.py +++ b/django_valkey/async_cache/cache.py @@ -72,6 +72,7 @@ class AsyncValkeyCache(BaseValkeyCache[AsyncDefaultClient, AValkey]): hget = BaseValkeyCache.ahget hgetall = BaseValkeyCache.ahgetall + hmget = BaseValkeyCache.ahmget hincrby = BaseValkeyCache.ahincrby diff --git a/django_valkey/async_cache/client/default.py b/django_valkey/async_cache/client/default.py index bc36530..b979ed6 100644 --- a/django_valkey/async_cache/client/default.py +++ b/django_valkey/async_cache/client/default.py @@ -1152,6 +1152,25 @@ async def hgetall( ahgetall = hgetall + async def hmget( + self, + name: str, + keys: list, + version: int | None = None, + client: AValkey | Any | None = None, + ) -> list: + client = await self._get_client(write=False, client=client) + nkeys = [await self.make_key(key, version=version) for key in keys] + try: + values = await client.hmget(name, nkeys) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + + values = [await self.decode(val) for val in values] + return values + + ahmget = hmget + async def hincrby( self, name: str, diff --git a/django_valkey/base.py b/django_valkey/base.py index 455dcb1..29a9b7d 100644 --- a/django_valkey/base.py +++ b/django_valkey/base.py @@ -388,6 +388,14 @@ def hgetall(self, *args, **kwargs) -> dict: async def ahgetall(self, *args, **kwargs) -> dict: return await self.client.ahgetall(*args, **kwargs) + @omit_exception + def hmget(self, *args, **kwargs) -> dict: + return self.client.hmget(*args, **kwargs) + + @omit_exception + async def ahmget(self, *args, **kwargs) -> dict: + return await self.client.ahmget(*args, **kwargs) + @omit_exception def hincrby(self, *args, **kwargs) -> int: return self.client.hincrby(*args, **kwargs) diff --git a/django_valkey/base_client.py b/django_valkey/base_client.py index 575b0a7..3d811e6 100644 --- a/django_valkey/base_client.py +++ b/django_valkey/base_client.py @@ -1245,6 +1245,23 @@ def hgetall( return values + def hmget( + self, + name: str, + keys: list, + version: int | None = None, + client: Backend | Any | None = None, + ) -> list: + client = self._get_client(write=False, client=client) + nkeys = [self.make_key(key, version=version) for key in keys] + try: + values = client.hmget(name, nkeys) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + + values = [self.decode(val) for val in values] + return values + def hincrby( self, name: str, From 592108274ac4d1aeb1083851e0bf603a5dba34dc Mon Sep 17 00:00:00 2001 From: amirreza Date: Sat, 14 Dec 2024 07:23:28 +0330 Subject: [PATCH 12/22] test hmget --- tests/test_backend.py | 10 ++++++++++ tests/tests_async/test_backend.py | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index acef6f9..c8c1a66 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -906,6 +906,16 @@ def test_hgetall(self, cache: ValkeyCache): result = cache.hgetall("foo_hash2") assert result == {} + def test_hmget(self, cache: ValkeyCache): + if isinstance(cache.client, ShardClient): + pytest.skip("ShardClient doesn't support get_client") + + cache.hset("hash1", "foo1", "bar1") + cache.hset("hash1", "foo2", "bar2") + + result = cache.hmget("hash1", ["foo1", "foo2"]) + assert result == ["bar1", "bar2"] + def test_hincrby(self, cache: ValkeyCache): if isinstance(cache.client, ShardClient): pytest.skip("ShardClient doesn't support get_client") diff --git a/tests/tests_async/test_backend.py b/tests/tests_async/test_backend.py index 71ba6ea..c0f7096 100644 --- a/tests/tests_async/test_backend.py +++ b/tests/tests_async/test_backend.py @@ -917,6 +917,13 @@ async def test_hgetall(self, cache: AsyncValkeyCache): result = await cache.ahgetall("foo_hash2") assert result == {} + async def test_hmget(self, cache: AsyncValkeyCache): + await cache.ahset("hash1", "foo1", "bar1") + await cache.ahset("hash1", "foo2", "bar2") + + result = await cache.ahmget("hash1", ["foo1", "foo2"]) + assert result == ["bar1", "bar2"] + async def test_hincrby(self, cache: AsyncValkeyCache): await cache.ahset("foo_hash1", "foo1", 1) assert await cache.ahget("foo_hash1", "foo1") == 1 From 0a59e07d8b7b41d9f5f743c447b6c8f0d50736d6 Mon Sep 17 00:00:00 2001 From: amirreza Date: Sat, 14 Dec 2024 07:45:35 +0330 Subject: [PATCH 13/22] added hvals method --- django_valkey/async_cache/cache.py | 2 ++ django_valkey/async_cache/client/default.py | 9 +++++++++ django_valkey/base.py | 8 ++++++++ django_valkey/base_client.py | 7 +++++++ 4 files changed, 26 insertions(+) diff --git a/django_valkey/async_cache/cache.py b/django_valkey/async_cache/cache.py index 79c929f..34cd812 100644 --- a/django_valkey/async_cache/cache.py +++ b/django_valkey/async_cache/cache.py @@ -82,6 +82,8 @@ class AsyncValkeyCache(BaseValkeyCache[AsyncDefaultClient, AValkey]): hexists = BaseValkeyCache.ahexists + hvals = BaseValkeyCache.ahvals + @omit_exception async def set(self, *args, **kwargs): return await self.client.aset(*args, **kwargs) diff --git a/django_valkey/async_cache/client/default.py b/django_valkey/async_cache/client/default.py index b979ed6..b667829 100644 --- a/django_valkey/async_cache/client/default.py +++ b/django_valkey/async_cache/client/default.py @@ -1223,3 +1223,12 @@ async def hexists( return await client.hexists(name, nkey) ahexists = hexists + + async def hvals(self, name: str, client: AValkey | Any | None = None) -> list: + client = await self._get_client(write=False, client=client) + try: + return [await self.decode(val) for val in await client.hvals(name)] + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + + ahvals = hvals diff --git a/django_valkey/base.py b/django_valkey/base.py index 29a9b7d..45a37f4 100644 --- a/django_valkey/base.py +++ b/django_valkey/base.py @@ -427,3 +427,11 @@ def hexists(self, *args, **kwargs) -> bool: @omit_exception async def ahexists(self, *args, **kwargs) -> bool: return await self.client.ahexists(*args, **kwargs) + + @omit_exception + def hvals(self, *args, **kwargs) -> list: + return self.client.hvals(*args, **kwargs) + + @omit_exception + async def ahvals(self, *args, **kwargs) -> list: + return await self.client.ahvals(*args, **kwargs) diff --git a/django_valkey/base_client.py b/django_valkey/base_client.py index 3d811e6..b0a29b2 100644 --- a/django_valkey/base_client.py +++ b/django_valkey/base_client.py @@ -1316,3 +1316,10 @@ def hexists( client = self._get_client(write=False, client=client) nkey = self.make_key(key, version=version) return client.hexists(name, nkey) + + def hvals(self, name: str, client: Backend | Any | None = None) -> list: + client = self._get_client(write=False, client=client) + try: + return [self.decode(val) for val in client.hvals(name)] + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e From 246a653bd450764ee09fa1d64a6b194eac7fe245 Mon Sep 17 00:00:00 2001 From: amirreza Date: Sat, 14 Dec 2024 07:46:57 +0330 Subject: [PATCH 14/22] test hvals --- tests/test_backend.py | 9 +++++++++ tests/tests_async/test_backend.py | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index c8c1a66..8cb5ec6 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -953,6 +953,15 @@ def test_hexists(self, cache: ValkeyCache): assert cache.hexists("foo_hash5", "foo1") assert not cache.hexists("foo_hash5", "foo") + def test_hvals(self, cache: ValkeyCache): + if isinstance(cache.client, ShardClient): + pytest.skip("ShardClient doesn't support get_client") + + cache.hset("foo_hash6", "foo1", "bar1") + cache.hset("foo_hash6", "foo2", "bar2") + result = cache.hvals("foo_hash6") + assert result == ["bar1", "bar2"] + def test_sadd(self, cache: ValkeyCache): assert cache.sadd("foo", "bar") == 1 assert cache.smembers("foo") == {"bar"} diff --git a/tests/tests_async/test_backend.py b/tests/tests_async/test_backend.py index c0f7096..3a45de8 100644 --- a/tests/tests_async/test_backend.py +++ b/tests/tests_async/test_backend.py @@ -958,6 +958,12 @@ async def test_hexists(self, cache: AsyncValkeyCache): assert await cache.ahexists("foo_hash5", "foo1") assert not await cache.ahexists("foo_hash5", "foo") + async def test_hvals(self, cache: AsyncValkeyCache): + await cache.ahset("foo_hash6", "foo1", "bar1") + await cache.ahset("foo_hash6", "foo2", "bar2") + result = await cache.ahvals("foo_hash6") + assert result == ["bar1", "bar2"] + async def test_sadd(self, cache: AsyncValkeyCache): assert await cache.asadd("foo", "bar") == 1 assert await cache.asmembers("foo") == {"bar"} From bb9cad7c4feff5ceeba15a074059aad40b1299ee Mon Sep 17 00:00:00 2001 From: amirreza Date: Sat, 14 Dec 2024 07:59:46 +0330 Subject: [PATCH 15/22] added hstrlen method --- django_valkey/async_cache/cache.py | 2 ++ django_valkey/async_cache/client/default.py | 16 ++++++++++++++++ django_valkey/base.py | 8 ++++++++ django_valkey/base_client.py | 14 ++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/django_valkey/async_cache/cache.py b/django_valkey/async_cache/cache.py index 34cd812..a79473c 100644 --- a/django_valkey/async_cache/cache.py +++ b/django_valkey/async_cache/cache.py @@ -84,6 +84,8 @@ class AsyncValkeyCache(BaseValkeyCache[AsyncDefaultClient, AValkey]): hvals = BaseValkeyCache.ahvals + hstrlen = BaseValkeyCache.ahstrlen + @omit_exception async def set(self, *args, **kwargs): return await self.client.aset(*args, **kwargs) diff --git a/django_valkey/async_cache/client/default.py b/django_valkey/async_cache/client/default.py index b667829..8641f27 100644 --- a/django_valkey/async_cache/client/default.py +++ b/django_valkey/async_cache/client/default.py @@ -1232,3 +1232,19 @@ async def hvals(self, name: str, client: AValkey | Any | None = None) -> list: raise ConnectionInterrupted(connection=client) from e ahvals = hvals + + async def hstrlen( + self, + name: str, + key: KeyT, + version: int | None = None, + client: AValkey | Any | None = None, + ) -> int: + client = await self._get_client(write=False, client=client) + nkey = await self.make_key(key, version=version) + try: + return await client.hstrlen(name, nkey) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + + ahstrlen = hstrlen diff --git a/django_valkey/base.py b/django_valkey/base.py index 45a37f4..76d4023 100644 --- a/django_valkey/base.py +++ b/django_valkey/base.py @@ -435,3 +435,11 @@ def hvals(self, *args, **kwargs) -> list: @omit_exception async def ahvals(self, *args, **kwargs) -> list: return await self.client.ahvals(*args, **kwargs) + + @omit_exception + def hstrlen(self, *args, **kwargs) -> int: + return self.client.hstrlen(*args, **kwargs) + + @omit_exception + async def ahstrlen(self, *args, **kwargs) -> int: + return await self.client.ahstrlen(*args, **kwargs) diff --git a/django_valkey/base_client.py b/django_valkey/base_client.py index b0a29b2..b4b1462 100644 --- a/django_valkey/base_client.py +++ b/django_valkey/base_client.py @@ -1323,3 +1323,17 @@ def hvals(self, name: str, client: Backend | Any | None = None) -> list: return [self.decode(val) for val in client.hvals(name)] except _main_exceptions as e: raise ConnectionInterrupted(connection=client) from e + + def hstrlen( + self, + name: str, + key: KeyT, + version: int | None = None, + client: Backend | Any | None = None, + ) -> int: + client = self._get_client(write=False, client=client) + nkey = self.make_key(key, version=version) + try: + return client.hstrlen(name, nkey) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e From a31fb5b23de22f2345985a14216174c2bc075970 Mon Sep 17 00:00:00 2001 From: amirreza Date: Sun, 15 Dec 2024 12:22:39 +0330 Subject: [PATCH 16/22] test hstrlen --- tests/test_backend.py | 12 ++++++++++++ tests/tests_async/test_backend.py | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index 8cb5ec6..35e14a1 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -14,6 +14,7 @@ from django_valkey.cache import ValkeyCache from django_valkey.client import ShardClient, herd +from django_valkey.compressors.identity import IdentityCompressor from django_valkey.serializers.json import JSONSerializer from django_valkey.serializers.msgpack import MSGPackSerializer from django_valkey.serializers.pickle import PickleSerializer @@ -962,6 +963,17 @@ def test_hvals(self, cache: ValkeyCache): result = cache.hvals("foo_hash6") assert result == ["bar1", "bar2"] + def test_hstrlen(self, cache: ValkeyCache): + if isinstance(cache.client, ShardClient): + pytest.skip("ShardClient doesn't support get_client") + if not isinstance(cache.client._serializer, PickleSerializer): + pytest.skip("other serializers give different results") + if not isinstance(cache.client._compressor, IdentityCompressor): + pytest.skip("other compressors give different results") + + cache.hset("hash7", "bar", "baz") + assert cache.hstrlen("hash7", "bar") == 18 + def test_sadd(self, cache: ValkeyCache): assert cache.sadd("foo", "bar") == 1 assert cache.smembers("foo") == {"bar"} diff --git a/tests/tests_async/test_backend.py b/tests/tests_async/test_backend.py index 3a45de8..ac3ea93 100644 --- a/tests/tests_async/test_backend.py +++ b/tests/tests_async/test_backend.py @@ -16,6 +16,8 @@ from django_valkey.async_cache.cache import AsyncValkeyCache from django_valkey.async_cache.client import AsyncHerdClient +from django_valkey.compressors.identity import IdentityCompressor +from django_valkey.serializers.pickle import PickleSerializer from django_valkey.serializers.json import JSONSerializer from django_valkey.serializers.msgpack import MSGPackSerializer @@ -964,6 +966,14 @@ async def test_hvals(self, cache: AsyncValkeyCache): result = await cache.ahvals("foo_hash6") assert result == ["bar1", "bar2"] + async def test_hstrlen(self, cache: AsyncValkeyCache): + if not isinstance(cache.client._serializer, PickleSerializer): + pytest.skip("other serializers give different results") + if not isinstance(cache.client._compressor, IdentityCompressor): + pytest.skip("other compressors give different results") + await cache.hset("hash7", "bar", "baz") + assert await cache.ahstrlen("hash7", "bar") == 18 + async def test_sadd(self, cache: AsyncValkeyCache): assert await cache.asadd("foo", "bar") == 1 assert await cache.asmembers("foo") == {"bar"} From c176fee3c0f458d5fe05a57d55c0c29cdeb0c555 Mon Sep 17 00:00:00 2001 From: amirreza Date: Sun, 15 Dec 2024 15:23:06 +0330 Subject: [PATCH 17/22] add mapping and items parameters to hset --- django_valkey/async_cache/client/default.py | 26 +++++++++++++++------ django_valkey/base_client.py | 23 ++++++++++++++---- tests/test_backend.py | 11 +++++++++ tests/tests_async/test_backend.py | 9 +++++++ 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/django_valkey/async_cache/client/default.py b/django_valkey/async_cache/client/default.py index 8641f27..0292645 100644 --- a/django_valkey/async_cache/client/default.py +++ b/django_valkey/async_cache/client/default.py @@ -1054,8 +1054,10 @@ async def touch( async def hset( self, name: str, - key, - value, + key=None, + value=None, + mapping: dict | None = None, + items: list | None = None, version: int | None = None, client: AValkey | Any | None = None, ) -> int: @@ -1064,11 +1066,21 @@ async def hset( Returns the number of fields added to the hash. """ client = await self._get_client(write=True, client=client) - - nkey = await self.make_key(key, version) - nvalue = await self.encode(value) - - return await client.hset(name, nkey, nvalue) + if key and value: + key = await self.make_key(key, version=version) + value = await self.encode(value) + if mapping: + mapping = { + await self.make_key(key): await self.encode(value) + for key, value in mapping.items() + } + if items: + items = [ + await (self.encode if index & 1 else self.make_key)(item) + for index, item in enumerate(items) + ] + + return await client.hset(name, key, value, mapping=mapping, items=items) ahset = hset diff --git a/django_valkey/base_client.py b/django_valkey/base_client.py index b4b1462..5f7644f 100644 --- a/django_valkey/base_client.py +++ b/django_valkey/base_client.py @@ -1161,8 +1161,10 @@ def touch( def hset( self, name: str, - key: KeyT, - value: EncodableT, + key: KeyT | None = None, + value: EncodableT | None = None, + mapping: dict | None = None, + items: list | None = None, version: int | None = None, client: Backend | Any | None = None, ) -> int: @@ -1171,9 +1173,20 @@ def hset( Returns the number of fields added to the hash. """ client = self._get_client(write=True, client=client) - nkey = self.make_key(key, version=version) - nvalue = self.encode(value) - return client.hset(name, nkey, nvalue) + if key and value: + key = self.make_key(key, version=version) + value = self.encode(value) + if mapping: + mapping = { + self.make_key(key): self.encode(value) for key, value in mapping.items() + } + if items: + items = [ + (self.encode if index & 1 else self.make_key)(item) + for index, item in enumerate(items) + ] + + return client.hset(name, key, value, mapping=mapping, items=items) def hsetnx( self, diff --git a/tests/test_backend.py b/tests/test_backend.py index 35e14a1..8981975 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -850,6 +850,17 @@ def test_hset(self, cache: ValkeyCache): assert cache.hexists("foo_hash1", "foo1") assert cache.hexists("foo_hash1", "foo2") + def test_hset_parameters(self, cache: ValkeyCache): + if isinstance(cache.client, ShardClient): + pytest.skip("ShardClient doesn't support get_client") + cache.hset("hash1", mapping={"foo1": "bar1", "baz1": "biz1"}) + cache.hset("hash1", items=["foo2", "bar2", "baz2", "biz2"]) + assert cache.hlen("hash1") == 4 + assert cache.hexists("hash1", "foo1") + assert cache.hexists("hash1", "foo2") + assert cache.hget("hash1", "foo1") == "bar1" + assert cache.hget("hash1", "baz2") == "biz2" + def test_hsetnx(self, cache: ValkeyCache): if isinstance(cache.client, ShardClient): pytest.skip("ShardClient doesn't support get_client") diff --git a/tests/tests_async/test_backend.py b/tests/tests_async/test_backend.py index ac3ea93..99d9de1 100644 --- a/tests/tests_async/test_backend.py +++ b/tests/tests_async/test_backend.py @@ -872,6 +872,15 @@ async def test_hset(self, cache: AsyncValkeyCache): assert await cache.ahexists("foo_hash1", "foo1") assert await cache.ahexists("foo_hash1", "foo2") + async def test_hset_parameters(self, cache: AsyncValkeyCache): + await cache.ahset("hash1", mapping={"foo1": "bar1", "baz1": "biz1"}) + await cache.ahset("hash1", items=["foo2", "bar2", "baz2", "biz2"]) + assert await cache.ahlen("hash1") == 4 + assert await cache.ahexists("hash1", "foo1") + assert await cache.ahexists("hash1", "foo2") + assert await cache.ahget("hash1", "foo1") == "bar1" + assert await cache.ahget("hash1", "baz2") == "biz2" + async def test_hsetnx(self, cache: AsyncValkeyCache): res = await cache.ahsetnx("bar_hash1", "foo1", "baz1") assert res == 1 From 086860cedd25a27de4de39007f5019ecd025a216 Mon Sep 17 00:00:00 2001 From: amirreza Date: Sun, 15 Dec 2024 15:25:00 +0330 Subject: [PATCH 18/22] added hrandfield method --- django_valkey/async_cache/cache.py | 2 ++ django_valkey/async_cache/client/default.py | 32 +++++++++++++++++++++ django_valkey/base.py | 8 ++++++ django_valkey/base_client.py | 24 ++++++++++++++++ 4 files changed, 66 insertions(+) diff --git a/django_valkey/async_cache/cache.py b/django_valkey/async_cache/cache.py index a79473c..cbca896 100644 --- a/django_valkey/async_cache/cache.py +++ b/django_valkey/async_cache/cache.py @@ -86,6 +86,8 @@ class AsyncValkeyCache(BaseValkeyCache[AsyncDefaultClient, AValkey]): hstrlen = BaseValkeyCache.ahstrlen + hrandfield = BaseValkeyCache.ahrandfield + @omit_exception async def set(self, *args, **kwargs): return await self.client.aset(*args, **kwargs) diff --git a/django_valkey/async_cache/client/default.py b/django_valkey/async_cache/client/default.py index 0292645..843e372 100644 --- a/django_valkey/async_cache/client/default.py +++ b/django_valkey/async_cache/client/default.py @@ -1260,3 +1260,35 @@ async def hstrlen( raise ConnectionInterrupted(connection=client) from e ahstrlen = hstrlen + + async def hrandfield( + self, + name: str, + count: int | None = None, + withvalues: bool = False, + client: AValkey | None = None, + ) -> str | list | None: + client = await self._get_client(write=False, client=client) + try: + result = await client.hrandfield( + key=name, count=count, withvalues=withvalues + ) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + + if not result: + return None + elif count and withvalues: + return [ + ( + await self.decode(val) + if index & 1 + else self.reverse_key(val.decode()) + ) + for index, val in enumerate(result) + ] + elif count: + return [self.reverse_key(val.decode()) for val in result] + return self.reverse_key(result.decode()) + + ahrandfield = hrandfield diff --git a/django_valkey/base.py b/django_valkey/base.py index 76d4023..b4bfaa1 100644 --- a/django_valkey/base.py +++ b/django_valkey/base.py @@ -443,3 +443,11 @@ def hstrlen(self, *args, **kwargs) -> int: @omit_exception async def ahstrlen(self, *args, **kwargs) -> int: return await self.client.ahstrlen(*args, **kwargs) + + @omit_exception + def hrandfield(self, *args, **kwargs) -> str | list: + return self.client.hrandfield(*args, **kwargs) + + @omit_exception + async def ahrandfield(self, *args, **kwargs) -> str | list: + return await self.client.ahrandfield(*args, **kwargs) diff --git a/django_valkey/base_client.py b/django_valkey/base_client.py index 5f7644f..3b947d6 100644 --- a/django_valkey/base_client.py +++ b/django_valkey/base_client.py @@ -1350,3 +1350,27 @@ def hstrlen( return client.hstrlen(name, nkey) except _main_exceptions as e: raise ConnectionInterrupted(connection=client) from e + + def hrandfield( + self, + name: str, + count: int | None = None, + withvalues: bool = False, + client: Backend | None = None, + ) -> str | list | None: + client = self._get_client(write=False, client=client) + try: + result = client.hrandfield(key=name, count=count, withvalues=withvalues) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + + if not result: + return None + elif count and withvalues: + return [ + (self.decode(val) if index & 1 else self.reverse_key(val.decode())) + for index, val in enumerate(result) + ] + elif count: + return [self.reverse_key(val.decode()) for val in result] + return self.reverse_key(result.decode()) From 633516e86aa766069e7c4ab9f356d713eecea00b Mon Sep 17 00:00:00 2001 From: amirreza Date: Sun, 15 Dec 2024 15:25:16 +0330 Subject: [PATCH 19/22] test hrandfield --- tests/test_backend.py | 14 ++++++++++++++ tests/tests_async/test_backend.py | 11 +++++++++++ 2 files changed, 25 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index 8981975..bc5325b 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -985,6 +985,20 @@ def test_hstrlen(self, cache: ValkeyCache): cache.hset("hash7", "bar", "baz") assert cache.hstrlen("hash7", "bar") == 18 + def test_hrandfield(self, cache: ValkeyCache): + if isinstance(cache.client, ShardClient): + pytest.skip("ShardClient doesn't support get_client") + + cache.hset("hash8", mapping={"foo": "bar", "baz": "biz"}) + res = cache.hrandfield("hash8") + assert res in ("foo", "baz") + + res = set(cache.hrandfield("hash8", count=2)) + assert res == {"foo", "baz"} + + res = set(cache.hrandfield("hash8", count=2, withvalues=True)) + assert res == {"foo", "bar", "baz", "biz"} + def test_sadd(self, cache: ValkeyCache): assert cache.sadd("foo", "bar") == 1 assert cache.smembers("foo") == {"bar"} diff --git a/tests/tests_async/test_backend.py b/tests/tests_async/test_backend.py index 99d9de1..63dec8d 100644 --- a/tests/tests_async/test_backend.py +++ b/tests/tests_async/test_backend.py @@ -983,6 +983,17 @@ async def test_hstrlen(self, cache: AsyncValkeyCache): await cache.hset("hash7", "bar", "baz") assert await cache.ahstrlen("hash7", "bar") == 18 + async def test_hrandfield(self, cache: AsyncValkeyCache): + await cache.ahset("hash8", mapping={"foo": "bar", "baz": "biz"}) + res = await cache.ahrandfield("hash8") + assert res in ("foo", "baz") + + res = set(await cache.ahrandfield("hash8", count=2)) + assert res == {"foo", "baz"} + + res = set(await cache.ahrandfield("hash8", count=2, withvalues=True)) + assert res == {"foo", "bar", "baz", "biz"} + async def test_sadd(self, cache: AsyncValkeyCache): assert await cache.asadd("foo", "bar") == 1 assert await cache.asmembers("foo") == {"bar"} From ea5fd5191a0da5b73323c21b08fd52c90af2dd6c Mon Sep 17 00:00:00 2001 From: amirreza Date: Tue, 17 Dec 2024 13:48:07 +0330 Subject: [PATCH 20/22] added hincrbyfloat method --- django_valkey/async_cache/cache.py | 1 + django_valkey/async_cache/client/default.py | 18 ++++++++++++++++++ django_valkey/base.py | 8 ++++++++ django_valkey/base_client.py | 16 ++++++++++++++++ 4 files changed, 43 insertions(+) diff --git a/django_valkey/async_cache/cache.py b/django_valkey/async_cache/cache.py index cbca896..da7e851 100644 --- a/django_valkey/async_cache/cache.py +++ b/django_valkey/async_cache/cache.py @@ -75,6 +75,7 @@ class AsyncValkeyCache(BaseValkeyCache[AsyncDefaultClient, AValkey]): hmget = BaseValkeyCache.ahmget hincrby = BaseValkeyCache.ahincrby + hincrbyfloat = BaseValkeyCache.ahincrbyfloat hlen = BaseValkeyCache.ahlen diff --git a/django_valkey/async_cache/client/default.py b/django_valkey/async_cache/client/default.py index 843e372..765a1d3 100644 --- a/django_valkey/async_cache/client/default.py +++ b/django_valkey/async_cache/client/default.py @@ -1201,6 +1201,24 @@ async def hincrby( ahincrby = hincrby + async def hincrbyfloat( + self, + name: str, + key: str, + amount: float = 1.0, + version: int | None = None, + client: AValkey | Any | None = None, + ) -> float: + client = await self._get_client(write=True, client=client) + nkey = await self.make_key(key, version=version) + try: + value = await client.hincrbyfloat(name, nkey, amount) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + return value + + ahincrbyfloat = hincrbyfloat + async def hlen(self, name: str, client: AValkey | Any | None = None) -> int: """ Return the number of items in hash name. diff --git a/django_valkey/base.py b/django_valkey/base.py index b4bfaa1..2d7af42 100644 --- a/django_valkey/base.py +++ b/django_valkey/base.py @@ -404,6 +404,14 @@ def hincrby(self, *args, **kwargs) -> int: async def ahincrby(self, *args, **kwargs) -> int: return await self.client.ahincrby(*args, **kwargs) + @omit_exception + def hincrbyfloat(self, *args, **kwargs) -> float: + return self.client.hincrbyfloat(*args, **kwargs) + + @omit_exception + async def ahincrbyfloat(self, *args, **kwargs) -> float: + return await self.client.ahincrbyfloat(*args, **kwargs) + @omit_exception def hlen(self, *args, **kwargs) -> int: return self.client.hlen(*args, **kwargs) diff --git a/django_valkey/base_client.py b/django_valkey/base_client.py index 3b947d6..ad3514b 100644 --- a/django_valkey/base_client.py +++ b/django_valkey/base_client.py @@ -1291,6 +1291,22 @@ def hincrby( raise ConnectionInterrupted(connection=client) from e return value + def hincrbyfloat( + self, + name: str, + key: str, + amount: float = 1.0, + version: int | None = None, + client: Backend | Any | None = None, + ) -> float: + client = self._get_client(write=True, client=client) + nkey = self.make_key(key, version=version) + try: + value = client.hincrbyfloat(name, nkey, amount) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + return value + def hlen( self, name: str, From f61f3f9d7b8c577e3a6055bf198798fb4882fbd8 Mon Sep 17 00:00:00 2001 From: amirreza Date: Tue, 17 Dec 2024 13:48:49 +0330 Subject: [PATCH 21/22] test hincrbyfloat --- tests/test_backend.py | 10 ++++++++++ tests/tests_async/test_backend.py | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/tests/test_backend.py b/tests/test_backend.py index bc5325b..965decb 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -938,6 +938,16 @@ def test_hincrby(self, cache: ValkeyCache): assert cache.hget("foo_hash1", "foo1") == 3 assert result == 3 + def test_hincrbyfloat(self, cache: ValkeyCache): + if isinstance(cache.client, ShardClient): + pytest.skip("ShardClient doesn't support get_client") + + cache.hset("foo_hash1", "foo1", 1.0) + assert cache.hget("foo_hash1", "foo1") == 1.0 + result = cache.hincrbyfloat("foo_hash1", "foo1", amount=2.2) + assert cache.hget("foo_hash1", "foo1") == 3.2 + assert result == 3.2 + def test_hlen(self, cache: ValkeyCache): if isinstance(cache.client, ShardClient): pytest.skip("ShardClient doesn't support get_client") diff --git a/tests/tests_async/test_backend.py b/tests/tests_async/test_backend.py index 63dec8d..41523aa 100644 --- a/tests/tests_async/test_backend.py +++ b/tests/tests_async/test_backend.py @@ -942,6 +942,13 @@ async def test_hincrby(self, cache: AsyncValkeyCache): assert await cache.ahget("foo_hash1", "foo1") == 3 assert result == 3 + async def test_hincrbyfloat(self, cache: AsyncValkeyCache): + await cache.ahset("foo_hash1", "foo1", 1.0) + assert await cache.ahget("foo_hash1", "foo1") == 1.0 + result = await cache.ahincrbyfloat("foo_hash1", "foo1", amount=2.2) + assert await cache.ahget("foo_hash1", "foo1") == 3.2 + assert result == 3.2 + async def test_hlen(self, cache: AsyncValkeyCache): # if isinstance(cache.client, ShardClient): # pytest.skip("ShardClient doesn't support get_client") From c295f5f09c94176101306888b5af03cdca8a2d30 Mon Sep 17 00:00:00 2001 From: amirreza Date: Tue, 17 Dec 2024 14:04:54 +0330 Subject: [PATCH 22/22] added documentations and change log for hash methods --- CHANGES.rst | 10 ++++++++++ docs/source/migration.rst | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e6f16f4..0973cc5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,13 @@ +Version 1.0.0 +------------- + +Breaking change: +- floats are no longer serialized when passing to valkey (in theory shouldn't break anything) + +New features: +- Added the missing hash methods, now all hash methods are supported except for ``hmset`` which is deprecated. +- hset: added ``mapping`` and ``items`` parameters for bulk operations. + Version 0.1.8 ------------- diff --git a/docs/source/migration.rst b/docs/source/migration.rst index 8c532d3..963a33d 100644 --- a/docs/source/migration.rst +++ b/docs/source/migration.rst @@ -11,6 +11,10 @@ Install django-valkey As explained in :doc:`installation` you can easily install django-valkey. this project can easily live alongside django-redis so you don't need to delete that if you don't want to. +Different behaviour +################### + +in django-redis floats are serialized when being passed to redis, as of 1.0.0 django-valkey does not serialize floats to support atomic float operations, in theory this doesn't break anything since the return value does not change. Different configuration ####################### @@ -47,3 +51,8 @@ More options Although the above steps are completely enough to get you going, if you want you can now easily customize your compression behaviour. Check out :doc:`configure/compressors` for complete explanation. + +More methods +############ + +as of 1.0.0 every hash operation is supported. \ No newline at end of file