diff --git a/CHANGELOG.md b/CHANGELOG.md index d51505f903..146267524d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ * Python: Added FUNCTION LOAD command ([#1699](https://github.com/aws/glide-for-redis/pull/1699)) * Python: Added XPENDING command ([#1704](https://github.com/aws/glide-for-redis/pull/1704)) * Python: Added RANDOMKEY command ([#1701](https://github.com/aws/glide-for-redis/pull/1701)) +* Python: Added FUNCTION FLUSH command ([#1700](https://github.com/aws/glide-for-redis/pull/1700)) ### Breaking Changes * Node: Update XREAD to return a Map of Map ([#1494](https://github.com/aws/glide-for-redis/pull/1494)) diff --git a/python/python/glide/async_commands/cluster_commands.py b/python/python/glide/async_commands/cluster_commands.py index f45a13839f..290478d3cc 100644 --- a/python/python/glide/async_commands/cluster_commands.py +++ b/python/python/glide/async_commands/cluster_commands.py @@ -353,6 +353,37 @@ async def function_load( ), ) + async def function_flush( + self, mode: Optional[FlushMode] = None, route: Optional[Route] = None + ) -> TOK: + """ + Deletes all function libraries. + + See https://valkey.io/docs/latest/commands/function-flush/ for more details. + + Args: + mode (Optional[FlushMode]): The flushing mode, could be either `SYNC` or `ASYNC`. + route (Optional[Route]): The command will be routed to all primaries, unless `route` is provided, + in which case the client will route the command to the nodes defined by `route`. + + Returns: + TOK: A simple `OK`. + + Examples: + >>> await client.function_flush(FlushMode.SYNC) + "OK" + + Since: Redis 7.0.0. + """ + return cast( + TOK, + await self._execute_command( + RequestType.FunctionFlush, + [mode.value] if mode else [], + route, + ), + ) + async def time(self, route: Optional[Route] = None) -> TClusterResponse[List[str]]: """ Returns the server time. diff --git a/python/python/glide/async_commands/standalone_commands.py b/python/python/glide/async_commands/standalone_commands.py index b182249b17..2cb15a3621 100644 --- a/python/python/glide/async_commands/standalone_commands.py +++ b/python/python/glide/async_commands/standalone_commands.py @@ -262,6 +262,32 @@ async def function_load(self, library_code: str, replace: bool = False) -> str: ), ) + async def function_flush(self, mode: Optional[FlushMode] = None) -> TOK: + """ + Deletes all function libraries. + + See https://valkey.io/docs/latest/commands/function-flush/ for more details. + + Args: + mode (Optional[FlushMode]): The flushing mode, could be either `SYNC` or `ASYNC`. + + Returns: + TOK: A simple `OK`. + + Examples: + >>> await client.function_flush(FlushMode.SYNC) + "OK" + + Since: Redis 7.0.0. + """ + return cast( + TOK, + await self._execute_command( + RequestType.FunctionFlush, + [mode.value] if mode else [], + ), + ) + async def time(self) -> List[str]: """ Returns the server time. diff --git a/python/python/glide/async_commands/transaction.py b/python/python/glide/async_commands/transaction.py index 112af1725a..402232a55b 100644 --- a/python/python/glide/async_commands/transaction.py +++ b/python/python/glide/async_commands/transaction.py @@ -1801,6 +1801,27 @@ def function_load( ["REPLACE", library_code] if replace else [library_code], ) + def function_flush( + self: TTransaction, mode: Optional[FlushMode] = None + ) -> TTransaction: + """ + Deletes all function libraries. + + See https://valkey.io/docs/latest/commands/function-flush/ for more details. + + Args: + mode (Optional[FlushMode]): The flushing mode, could be either `SYNC` or `ASYNC`. + + Commands response: + TOK: A simple `OK`. + + Since: Redis 7.0.0. + """ + return self.append_command( + RequestType.FunctionFlush, + [mode.value] if mode else [], + ) + def xadd( self: TTransaction, key: str, diff --git a/python/python/tests/test_async_client.py b/python/python/tests/test_async_client.py index bf8ac7168a..123f941510 100644 --- a/python/python/tests/test_async_client.py +++ b/python/python/tests/test_async_client.py @@ -6731,6 +6731,66 @@ async def test_function_load_cluster_with_route( assert await redis_client.function_load(new_code, True, route) == lib_name + @pytest.mark.parametrize("cluster_mode", [True, False]) + @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) + async def test_function_flush(self, redis_client: TGlideClient): + min_version = "7.0.0" + if await check_if_server_version_lt(redis_client, min_version): + pytest.skip(f"Redis version required >= {min_version}") + + lib_name = f"mylib1C{get_random_string(5)}" + func_name = f"myfunc1c{get_random_string(5)}" + code = generate_lua_lib_code(lib_name, {func_name: "return args[1]"}, True) + + # Load the function + assert await redis_client.function_load(code) == lib_name + + # TODO: Ensure the function exists with FUNCTION LIST + + # Flush functions + assert await redis_client.function_flush(FlushMode.SYNC) == OK + assert await redis_client.function_flush(FlushMode.ASYNC) == OK + + # TODO: Ensure the function is no longer present with FUNCTION LIST + + # Attempt to re-load library without overwriting to ensure FLUSH was effective + assert await redis_client.function_load(code) == lib_name + + # Clean up by flushing functions again + await redis_client.function_flush() + + @pytest.mark.parametrize("cluster_mode", [True]) + @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) + @pytest.mark.parametrize("single_route", [True, False]) + async def test_function_flush_with_routing( + self, redis_client: GlideClusterClient, single_route: bool + ): + min_version = "7.0.0" + if await check_if_server_version_lt(redis_client, min_version): + pytest.skip(f"Redis version required >= {min_version}") + + lib_name = f"mylib1C{get_random_string(5)}" + func_name = f"myfunc1c{get_random_string(5)}" + code = generate_lua_lib_code(lib_name, {func_name: "return args[1]"}, True) + route = SlotKeyRoute(SlotType.PRIMARY, "1") if single_route else AllPrimaries() + + # Load the function + assert await redis_client.function_load(code, False, route) == lib_name + + # TODO: Ensure the function exists with FUNCTION LIST + + # Flush functions + assert await redis_client.function_flush(FlushMode.SYNC, route) == OK + assert await redis_client.function_flush(FlushMode.ASYNC, route) == OK + + # TODO: Ensure the function is no longer present with FUNCTION LIST + + # Attempt to re-load library without overwriting to ensure FLUSH was effective + assert await redis_client.function_load(code, False, route) == lib_name + + # Clean up by flushing functions again + assert await redis_client.function_flush(route=route) == OK + @pytest.mark.parametrize("cluster_mode", [True, False]) @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) async def test_srandmember(self, redis_client: TGlideClient): diff --git a/python/python/tests/test_transaction.py b/python/python/tests/test_transaction.py index 971ac43f99..18552c5735 100644 --- a/python/python/tests/test_transaction.py +++ b/python/python/tests/test_transaction.py @@ -102,6 +102,12 @@ async def transaction_test( args.append(lib_name) transaction.function_load(code, True) args.append(lib_name) + transaction.function_flush() + args.append(OK) + transaction.function_flush(FlushMode.ASYNC) + args.append(OK) + transaction.function_flush(FlushMode.SYNC) + args.append(OK) transaction.dbsize() args.append(0)