From ed922735e2a5d92c6687944fc5d35bcd8fcdb2ac Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 17 Jun 2023 21:07:41 -0400 Subject: [PATCH] feat[list-commands]:implement `BLMOVE` --- docs/about/changelog.md | 15 +++++++++------ docs/redis-commands/Redis.md | 8 ++++---- fakeredis/commands_mixins/list_mixin.py | 16 ++++++++++++---- test/test_mixins/test_list_commands.py | 6 ++++++ 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/docs/about/changelog.md b/docs/about/changelog.md index 91d33a49..c0ad3b36 100644 --- a/docs/about/changelog.md +++ b/docs/about/changelog.md @@ -8,12 +8,15 @@ description: Change log of all fakeredis releases ### 🚀 Features - Implemented support for various stream groups commands: - - `XGROUP CREATE` #161, `XGROUP DESTROY` #164, `XGROUP SETID` #165, `XGROUP DELCONSUMER` #162, - `XGROUP CREATECONSUMER` #163, `XINFO GROUPS` #168, `XINFO CONSUMERS` #168, `XINFO STREAM` #169, `XREADGROUP` #171, - `XACK` #157, `XPENDING` #170 -- Implemented sorted set commands: - - `ZRANDMEMBER` #192, `ZDIFF` #187, `ZINTER` #189, `ZUNION` #194, `ZDIFFSTORE` #188, - `ZINTERCARD` #190, `ZRANGESTORE` #193 + - `XGROUP CREATE` #161, `XGROUP DESTROY` #164, `XGROUP SETID` #165, `XGROUP DELCONSUMER` #162, + `XGROUP CREATECONSUMER` #163, `XINFO GROUPS` #168, `XINFO CONSUMERS` #168, `XINFO STREAM` #169, `XREADGROUP` #171, + `XACK` #157, `XPENDING` #170 +- Implemented sorted set commands: + - `ZRANDMEMBER` #192, `ZDIFF` #187, `ZINTER` #189, `ZUNION` #194, `ZDIFFSTORE` #188, + `ZINTERCARD` #190, `ZRANGESTORE` #193 + +- Implemented list commands: + - `BLMOVE` #182, ### 🧰 Maintenance diff --git a/docs/redis-commands/Redis.md b/docs/redis-commands/Redis.md index 9d6e9858..dad5c503 100644 --- a/docs/redis-commands/Redis.md +++ b/docs/redis-commands/Redis.md @@ -656,6 +656,10 @@ Performs arbitrary read-only bitfield integer operations on strings. ## list commands +### [BLMOVE](https://redis.io/commands/blmove/) + +Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved. + ### [BLPOP](https://redis.io/commands/blpop/) Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped. @@ -732,10 +736,6 @@ Appends an element to a list only when the list exists. ### Unsupported list commands > To implement support for a command, see [here](../../guides/implement-command/) -#### [BLMOVE](https://redis.io/commands/blmove/) (not implemented) - -Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved. - #### [BLMPOP](https://redis.io/commands/blmpop/) (not implemented) Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped. diff --git a/fakeredis/commands_mixins/list_mixin.py b/fakeredis/commands_mixins/list_mixin.py index 9c0aea5f..4d035bbb 100644 --- a/fakeredis/commands_mixins/list_mixin.py +++ b/fakeredis/commands_mixins/list_mixin.py @@ -116,16 +116,24 @@ def linsert(self, key, where, pivot, value): def llen(self, key): return len(key.value) - @command((Key(list, None), Key(list), SimpleString, SimpleString)) - def lmove(self, first_list, second_list, src, dst): + def _lmove(self, first_list, second_list, src, dst, first_pass): if ((not casematch(src, b'left') and not casematch(src, b'right')) or (not casematch(dst, b'left') and not casematch(dst, b'right'))): raise SimpleError(msgs.SYNTAX_ERROR_MSG) - el = self.rpop(first_list) if src == b'RIGHT' else self.lpop(first_list) - self.lpush(second_list, el) if dst == b'LEFT' else self.rpush(second_list, el) + el = self.rpop(first_list) if casematch(src, b'RIGHT') else self.lpop(first_list) + self.lpush(second_list, el) if casematch(dst, b'LEFT') else self.rpush(second_list, el) return el + @command((Key(list, None), Key(list), SimpleString, SimpleString)) + def lmove(self, first_list, second_list, src, dst): + return self._lmove(first_list, second_list, src, dst, False) + + @command((Key(list, None), Key(list), SimpleString, SimpleString, Timeout)) + def blmove(self, first_list, second_list, src, dst, timeout): + return self._blocking( + timeout, functools.partial(self._lmove, first_list, second_list, src, dst)) + @command(fixed=(Key(),), repeat=(bytes,)) def lpop(self, key, *args): return _list_pop(lambda count: slice(None, count), key, *args) diff --git a/test/test_mixins/test_list_commands.py b/test/test_mixins/test_list_commands.py index 5f0da5e8..dc7917ff 100644 --- a/test/test_mixins/test_list_commands.py +++ b/test/test_mixins/test_list_commands.py @@ -603,6 +603,12 @@ def test_lmove(r: redis.Redis): assert r.lrem('bar', -1, 'two') == 1 +def test_blmove(r: redis.Redis): + r.rpush("a", "one", "two", "three", "four") + assert r.blmove("a", "b", 5) + assert r.blmove("a", "b", 1, "RIGHT", "LEFT") + + @pytest.mark.disconnected @testtools.fake_only def test_lmove_disconnected_raises_connection_error(r: redis.Redis):