Repository: redis/redis-py

Issue:
Add support for COMMAND LIST



Discussion:
Hello @chayim. I would like to work on this issue. However, I have a question. How can I test this command? Since it returns all the commands I thought maybe I can get the number of commands and compare them. For instance, currently, I get 363 different commands. Assume I do this operation  ```len("command list") == 363``` and in the next version there is another command added. So, the command number is up to 364 and the test fails.

I could not think of a way to test this. Can you give me any advice?
@dogukanteber I think the right answer is to check that we receive a number of commands that's positive and high - as opposed to a specific number. The reason: redis will constantly change, but we'll keep trying to support as many versions as possible!

Ultimately we're trying to test each collection of possibilities. I think this means you need one function that that makes repeated calls and uses _r_ (the stock connection) and another that uses the _redismod_ client. Does that make sense?

Here's the direct link to the [new command](https://redis.io/commands/command-list).
CC: @dvora-h for visibility.

Resolving patch:
diff --git a/redis/commands/core.py b/redis/commands/core.py
--- a/redis/commands/core.py
+++ b/redis/commands/core.py
@@ -745,6 +745,34 @@ def command_info(self, **kwargs) -> None:
     def command_count(self, **kwargs) -> ResponseT:
         return self.execute_command("COMMAND COUNT", **kwargs)
 
+    def command_list(
+        self,
+        module: Optional[str] = None,
+        category: Optional[str] = None,
+        pattern: Optional[str] = None,
+    ) -> ResponseT:
+        """
+        Return an array of the server's command names.
+        You can use one of the following filters:
+        ``module``: get the commands that belong to the module
+        ``category``: get the commands in the ACL category
+        ``pattern``: get the commands that match the given pattern
+
+        For more information see https://redis.io/commands/command-list/
+        """
+        pieces = []
+        if module is not None:
+            pieces.extend(["MODULE", module])
+        if category is not None:
+            pieces.extend(["ACLCAT", category])
+        if pattern is not None:
+            pieces.extend(["PATTERN", pattern])
+
+        if pieces:
+            pieces.insert(0, "FILTERBY")
+
+        return self.execute_command("COMMAND LIST", *pieces)
+
     def command_getkeysandflags(self, *args: List[str]) -> List[Union[str, List[str]]]:
         """
         Returns array of keys from a full Redis command and their usage flags.


Test patch:
diff --git a/tests/test_commands.py b/tests/test_commands.py
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -4520,6 +4520,16 @@ def test_command_docs(self, r):
         with pytest.raises(NotImplementedError):
             r.command_docs("set")
 
+    @skip_if_server_version_lt("7.0.0")
+    @skip_if_redis_enterprise()
+    def test_command_list(self, r: redis.Redis):
+        assert len(r.command_list()) > 300
+        assert len(r.command_list(module="fakemod")) == 0
+        assert len(r.command_list(category="list")) > 15
+        assert "lpop" in r.command_list(pattern="l*")
+        with pytest.raises(redis.ResponseError):
+            r.command_list(category="list", pattern="l*")
+
     @pytest.mark.onlynoncluster
     @skip_if_server_version_lt("2.8.13")
     @skip_if_redis_enterprise()