internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: redis/redis-py

Issue:
Add support for EVALSHA_RO



Discussion:

Resolving patch:
diff --git a/redis/commands/core.py b/redis/commands/core.py
--- a/redis/commands/core.py
+++ b/redis/commands/core.py
@@ -3894,7 +3894,12 @@ def eval(self, script, numkeys, *keys_and_args):
         """
         return self.execute_command("EVAL", script, numkeys, *keys_and_args)
 
-    def evalsha(self, sha, numkeys, *keys_and_args):
+    def _evalsha(
+        self, command: str, sha: str, numkeys: int, *keys_and_args: list
+    ) -> str:
+        return self.execute_command(command, sha, numkeys, *keys_and_args)
+
+    def evalsha(self, sha: str, numkeys: int, *keys_and_args: list) -> str:
         """
         Use the ``sha`` to execute a Lua script already registered via EVAL
         or SCRIPT LOAD. Specify the ``numkeys`` the script will touch and the
@@ -3906,7 +3911,20 @@ def evalsha(self, sha, numkeys, *keys_and_args):
 
         For more information check  https://redis.io/commands/evalsha
         """
-        return self.execute_command("EVALSHA", sha, numkeys, *keys_and_args)
+        return self._evalsha("EVALSHA", sha, numkeys, *keys_and_args)
+
+    def evalsha_ro(self, sha: str, numkeys: int, *keys_and_args: list) -> str:
+        """
+        The read-only variant of the EVALSHA command
+
+        Use the ``sha`` to execute a read-only Lua script already registered via EVAL
+        or SCRIPT LOAD. Specify the ``numkeys`` the script will touch and the
+        key names and argument values in ``keys_and_args``. Returns the result
+        of the script.
+
+        For more information check  https://redis.io/commands/evalsha_ro
+        """
+        return self._evalsha("EVALSHA_RO", sha, numkeys, *keys_and_args)
 
     def script_exists(self, *args):
         """


Test patch:
diff --git a/tests/test_scripting.py b/tests/test_scripting.py
--- a/tests/test_scripting.py
+++ b/tests/test_scripting.py
@@ -1,5 +1,6 @@
 import pytest
 
+import redis
 from redis import exceptions
 from tests.conftest import skip_if_server_version_lt
 
@@ -66,6 +67,15 @@ def test_evalsha(self, r):
         # 2 * 3 == 6
         assert r.evalsha(sha, 1, "a", 3) == 6
 
+    # @skip_if_server_version_lt("7.0.0") turn on after redis 7 release
+    def test_evalsha_ro(self, unstable_r):
+        unstable_r.set("a", "b")
+        get_sha = unstable_r.script_load("return redis.call('GET', KEYS[1])")
+        del_sha = unstable_r.script_load("return redis.call('DEL', KEYS[1])")
+        assert unstable_r.evalsha_ro(get_sha, 1, "a") == b"b"
+        with pytest.raises(redis.ResponseError):
+            unstable_r.evalsha_ro(del_sha, 1, "a")
+
     def test_evalsha_script_not_loaded(self, r):
         r.set("a", 2)
         sha = r.script_load(multiply_script)
Repository: redis/redis-py

Issue:
run GraphCommands.explain raise TypeError

**Version**: 4.1.0

**Python Version**: 3.8

**Platform**: MacOS 12.0.1

**Description**: I'm using redisgraph:2.8.7 with redis-py. I met a error when I run explain. Error:
```python
graph.explain('match (n:person) where n.name = "ken" return n')
```
raise exception
```  
File "/lib/python3.8/site-packages/redis/commands/graph/commands.py", line 140, in explain
    return "\n".join(plan)
TypeError: sequence item 0: expected str instance, bytes found
```

```python
graph.execute_command('GRAPH.EXPLAIN', graph.name, 'match (n:person) where n.name = "ken" return n')
```
return
```python
[b'Results',
 b'    Project',
 b'        Filter',
 b'            Node By Label Scan | (n:person)']
```

The problem is the result that type is List[byte].
I guess the code should modify as this:
```python
    def explain(self, query, params=None):
        """
        Get the execution plan for given query,
        Returns an array of operations.
        For more information see `GRAPH.EXPLAIN <https://oss.redis.com/redisgraph/master/commands/#graphexplain>`_. # noqa

        Args:

        -------
        query:
            The query that will be executed.
        params: dict
            Query parameters.
        """
        if params is not None:
            query = self._build_params_header(params) + query

        plan = self.execute_command("GRAPH.EXPLAIN", self.name, query)
        return "\n".join([b.decode() for b in plan])
```
or
```python
    def explain(self, query, params=None):
        """
        Get the execution plan for given query,
        Returns an array of operations.
        For more information see `GRAPH.EXPLAIN <https://oss.redis.com/redisgraph/master/commands/#graphexplain>`_. # noqa

        Args:

        -------
        query:
            The query that will be executed.
        params: dict
            Query parameters.
        """
        if params is not None:
            query = self._build_params_header(params) + query

        plan = self.execute_command("GRAPH.EXPLAIN", self.name, query)
        return plan
```



Discussion:
@ChandlerBent When you constructed your redis connection how did you set decode_responses?

```
import redis
r = redis.Redis(decode_responses=True)
```

or

```
import redis
r = redis.Redis()
```

Can you try setting decode_responses=True if not already set?
@chayim Hi, Thank you.

I did not set `decode_responses=True`

But I think it should support `decode_responses=False`.

Resolving patch:
diff --git a/redis/commands/graph/commands.py b/redis/commands/graph/commands.py
--- a/redis/commands/graph/commands.py
+++ b/redis/commands/graph/commands.py
@@ -135,6 +135,8 @@ def explain(self, query, params=None):
             query = self._build_params_header(params) + query
 
         plan = self.execute_command("GRAPH.EXPLAIN", self.name, query)
+        if isinstance(plan[0], bytes):
+            plan = [b.decode() for b in plan]
         return "\n".join(plan)
 
     def bulk(self, **kwargs):


Test patch:
Repository: redis/redis-py

Issue:
AttributeError in UnixDomainSocketConnection starting v4.1.0
**Version**: v4.1.0

**Platform**: python 3.9

**Description**: 
I have a `ConnectionPool` with `connection_class=UnixDomainSocketConnection`.
In order to verify the socket is unreachable, we send a `ping` command and expect to get a `ConnectionError`, but starting v4.1.0 we get an `AttributeError` instead.

```
connection_pool = ConnectionPool(
                connection_class=redis.connection.UnixDomainSocketConnection, 
                max_connections = None,
                path=socket_path, 
                "password": password,
                "socket_timeout": socket_timeout,
                "encoding": "utf-8",
                "encoding_errors": "strict",
                "decode_responses": True,
                "retry_on_timeout": False,
)
rc = redis.Redis(connection_pool=connection_pool)
```
Then I terminate the process that is responsible for the above socket, and do: `rc.ping()` in order to know the socket is unreachable, from which I expect to get a `ConnectionError` (this is what we got from the `ping` in earlier versions).
But instead, in v4.1.0 (and v4.1.1) I get: `AttributeError: 'UnixDomainSocketConnection' object has no attribute 'host'`.

I investigated a little and seems like changing the `socket.error` to `OSError` in `connections.py` caused `ConnectionResetError` to get caught by an earlier `except` clause, where `self.host` is printed (and of course `UnixDomainSocketConnection` doesn't have one).
Thanks!


Discussion:

Resolving patch:
diff --git a/redis/connection.py b/redis/connection.py
--- a/redis/connection.py
+++ b/redis/connection.py
@@ -677,12 +677,19 @@ def _error_message(self, exception):
         # args for socket.error can either be (errno, "message")
         # or just "message"
         if len(exception.args) == 1:
-            return f"Error connecting to {self.host}:{self.port}. {exception.args[0]}."
+            try:
+                return f"Error connecting to {self.host}:{self.port}. \
+                        {exception.args[0]}."
+            except AttributeError:
+                return f"Connection Error: {exception.args[0]}"
         else:
-            return (
-                f"Error {exception.args[0]} connecting to "
-                f"{self.host}:{self.port}. {exception.args[1]}."
-            )
+            try:
+                return (
+                    f"Error {exception.args[0]} connecting to "
+                    f"{self.host}:{self.port}. {exception.args[1]}."
+                )
+            except AttributeError:
+                return f"Connection Error: {exception.args[0]}"
 
     def on_connect(self):
         "Initialize the connection, authenticate and select a database"


Test patch:
Repository: redis/redis-py

Issue:
Add support for missing command ACL DRYRUN



Discussion:

Resolving patch:
diff --git a/redis/commands/core.py b/redis/commands/core.py
--- a/redis/commands/core.py
+++ b/redis/commands/core.py
@@ -28,6 +28,14 @@ def acl_cat(self, category=None, **kwargs):
         pieces = [category] if category else []
         return self.execute_command("ACL CAT", *pieces, **kwargs)
 
+    def acl_dryrun(self, username, *args, **kwargs):
+        """
+        Simulate the execution of a given command by a given ``username``.
+
+        For more information check https://redis.io/commands/acl-dryrun
+        """
+        return self.execute_command("ACL DRYRUN", username, *args, **kwargs)
+
     def acl_deluser(self, *username, **kwargs):
         """
         Delete the ACL for the specified ``username``s


Test patch:
diff --git a/tests/test_commands.py b/tests/test_commands.py
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -87,6 +87,19 @@ def test_acl_cat_with_category(self, r):
         assert isinstance(commands, list)
         assert "get" in commands
 
+    @skip_if_server_version_lt("7.0.0")
+    def test_acl_dryrun(self, r):
+        username = "redis-py-user"
+        r.acl_setuser(
+            username,
+            keys=["*"],
+            commands=["+set"],
+        )
+        assert r.acl_dryrun(username, "set", "key", "value") == b"OK"
+        assert r.acl_dryrun(username, "get", "key").startswith(
+            b"This user has no permissions to run the"
+        )
+
     @skip_if_server_version_lt("6.0.0")
     @skip_if_redis_enterprise()
     def test_acl_deluser(self, r, request):
Repository: redis/redis-py

Issue:
Add support for CLUSTER ADDSLOTSRANGE



Discussion:

Resolving patch:
diff --git a/redis/client.py b/redis/client.py
--- a/redis/client.py
+++ b/redis/client.py
@@ -710,6 +710,7 @@ class AbstractRedis:
         "CLIENT GETREDIR": int,
         "CLIENT TRACKINGINFO": lambda r: list(map(str_if_bytes, r)),
         "CLUSTER ADDSLOTS": bool_ok,
+        "CLUSTER ADDSLOTSRANGE": bool_ok,
         "CLUSTER COUNT-FAILURE-REPORTS": lambda x: int(x),
         "CLUSTER COUNTKEYSINSLOT": lambda x: int(x),
         "CLUSTER DELSLOTS": bool_ok,
diff --git a/redis/cluster.py b/redis/cluster.py
--- a/redis/cluster.py
+++ b/redis/cluster.py
@@ -308,6 +308,7 @@ class RedisCluster(RedisClusterCommands):
 
     CLUSTER_COMMANDS_RESPONSE_CALLBACKS = {
         "CLUSTER ADDSLOTS": bool,
+        "CLUSTER ADDSLOTSRANGE": bool,
         "CLUSTER COUNT-FAILURE-REPORTS": int,
         "CLUSTER COUNTKEYSINSLOT": int,
         "CLUSTER DELSLOTS": bool,
diff --git a/redis/commands/cluster.py b/redis/commands/cluster.py
--- a/redis/commands/cluster.py
+++ b/redis/commands/cluster.py
@@ -244,6 +244,22 @@ def cluster_addslots(self, target_node, *slots):
             "CLUSTER ADDSLOTS", *slots, target_nodes=target_node
         )
 
+    def cluster_addslotsrange(self, target_node, *slots):
+        """
+        Similar to the CLUSTER ADDSLOTS command.
+        The difference between the two commands is that ADDSLOTS takes a list of slots
+        to assign to the node, while ADDSLOTSRANGE takes a list of slot ranges
+        (specified by start and end slots) to assign to the node.
+
+        :target_node: 'ClusterNode'
+            The node to execute the command on
+
+        For more information check https://redis.io/commands/cluster-addslotsrange
+        """
+        return self.execute_command(
+            "CLUSTER ADDSLOTSRANGE", *slots, target_nodes=target_node
+        )
+
     def cluster_countkeysinslot(self, slot_id):
         """
         Return the number of local keys in the specified hash slot


Test patch:
diff --git a/tests/test_cluster.py b/tests/test_cluster.py
--- a/tests/test_cluster.py
+++ b/tests/test_cluster.py
@@ -853,6 +853,12 @@ def test_cluster_addslots(self, r):
         mock_node_resp(node, "OK")
         assert r.cluster_addslots(node, 1, 2, 3) is True
 
+    @skip_if_server_version_lt("7.0.0")
+    def test_cluster_addslotsrange(self, r):
+        node = r.get_random_node()
+        mock_node_resp(node, "OK")
+        assert r.cluster_addslotsrange(node, 1, 5)
+
     def test_cluster_countkeysinslot(self, r):
         node = r.nodes_manager.get_node_from_slot(1)
         mock_node_resp(node, 2)
Repository: redis/redis-py

Issue:
Add support for CLUSTER DELSLOTSRANGE



Discussion:

Resolving patch:
diff --git a/redis/client.py b/redis/client.py
--- a/redis/client.py
+++ b/redis/client.py
@@ -713,6 +713,7 @@ class AbstractRedis:
         "CLUSTER COUNT-FAILURE-REPORTS": lambda x: int(x),
         "CLUSTER COUNTKEYSINSLOT": lambda x: int(x),
         "CLUSTER DELSLOTS": bool_ok,
+        "CLUSTER DELSLOTSRANGE": bool_ok,
         "CLUSTER FAILOVER": bool_ok,
         "CLUSTER FORGET": bool_ok,
         "CLUSTER INFO": parse_cluster_info,
diff --git a/redis/cluster.py b/redis/cluster.py
--- a/redis/cluster.py
+++ b/redis/cluster.py
@@ -299,6 +299,7 @@ class RedisCluster(RedisClusterCommands):
             [
                 "CLUSTER COUNTKEYSINSLOT",
                 "CLUSTER DELSLOTS",
+                "CLUSTER DELSLOTSRANGE",
                 "CLUSTER GETKEYSINSLOT",
                 "CLUSTER SETSLOT",
             ],
@@ -311,6 +312,7 @@ class RedisCluster(RedisClusterCommands):
         "CLUSTER COUNT-FAILURE-REPORTS": int,
         "CLUSTER COUNTKEYSINSLOT": int,
         "CLUSTER DELSLOTS": bool,
+        "CLUSTER DELSLOTSRANGE": bool,
         "CLUSTER FAILOVER": bool,
         "CLUSTER FORGET": bool,
         "CLUSTER GETKEYSINSLOT": list,
diff --git a/redis/commands/cluster.py b/redis/commands/cluster.py
--- a/redis/commands/cluster.py
+++ b/redis/commands/cluster.py
@@ -267,6 +267,17 @@ def cluster_delslots(self, *slots):
         """
         return [self.execute_command("CLUSTER DELSLOTS", slot) for slot in slots]
 
+    def cluster_delslotsrange(self, *slots):
+        """
+        Similar to the CLUSTER DELSLOTS command.
+        The difference is that CLUSTER DELSLOTS takes a list of hash slots to remove
+        from the node, while CLUSTER DELSLOTSRANGE takes a list of slot ranges to remove
+        from the node.
+
+        For more information check https://redis.io/commands/cluster-delslotsrange
+        """
+        return self.execute_command("CLUSTER DELSLOTSRANGE", *slots)
+
     def cluster_failover(self, target_node, option=None):
         """
         Forces a slave to perform a manual failover of its master


Test patch:
diff --git a/tests/test_cluster.py b/tests/test_cluster.py
--- a/tests/test_cluster.py
+++ b/tests/test_cluster.py
@@ -885,6 +885,13 @@ def test_cluster_delslots(self):
         assert node0.redis_connection.connection.read_response.called
         assert node1.redis_connection.connection.read_response.called
 
+    @skip_if_server_version_lt("7.0.0")
+    def test_cluster_delslotsrange(self, r):
+        node = r.get_random_node()
+        mock_node_resp(node, "OK")
+        r.cluster_addslots(node, 1, 2, 3, 4, 5)
+        assert r.cluster_delslotsrange(1, 5)
+
     def test_cluster_failover(self, r):
         node = r.get_random_node()
         mock_node_resp(node, "OK")
Repository: redis/redis-py

Issue:
Add support for CLUSTER LINKS



Discussion:

Resolving patch:
diff --git a/redis/commands/cluster.py b/redis/commands/cluster.py
--- a/redis/commands/cluster.py
+++ b/redis/commands/cluster.py
@@ -398,6 +398,18 @@ def cluster_slots(self, target_nodes=None):
         """
         return self.execute_command("CLUSTER SLOTS", target_nodes=target_nodes)
 
+    def cluster_links(self, target_node):
+        """
+        Each node in a Redis Cluster maintains a pair of long-lived TCP link with each
+        peer in the cluster: One for sending outbound messages towards the peer and one
+        for receiving inbound messages from the peer.
+
+        This command outputs information of all such peer links as an array.
+
+        For more information check https://redis.io/commands/cluster-links
+        """
+        return self.execute_command("CLUSTER LINKS", target_nodes=target_node)
+
     def readonly(self, target_nodes=None):
         """
         Enables read queries.


Test patch:
diff --git a/tests/test_cluster.py b/tests/test_cluster.py
--- a/tests/test_cluster.py
+++ b/tests/test_cluster.py
@@ -1016,6 +1016,17 @@ def test_cluster_replicas(self, r):
             == "r4xfga22229cf3c652b6fca0d09ff69f3e0d4d"
         )
 
+    @skip_if_server_version_lt("7.0.0")
+    def test_cluster_links(self, r):
+        node = r.get_random_node()
+        res = r.cluster_links(node)
+        links_to = sum(x.count("to") for x in res)
+        links_for = sum(x.count("from") for x in res)
+        assert links_to == links_for
+        print(res)
+        for i in range(0, len(res) - 1, 2):
+            assert res[i][3] == res[i + 1][3]
+
     def test_readonly(self):
         r = get_mocked_redis_client(host=default_host, port=default_port)
         mock_all_nodes_resp(r, "OK")
Repository: redis/redis-py

Issue:
graph commands explain raise TypeError again.
redis version: redis-4.1.4

#1848

This bug should fix in #1901.

Another pr #1993 overwrite it...

So the bug still unresolved


Discussion:

Resolving patch:
diff --git a/redis/commands/graph/commands.py b/redis/commands/graph/commands.py
--- a/redis/commands/graph/commands.py
+++ b/redis/commands/graph/commands.py
@@ -194,6 +194,8 @@ def execution_plan(self, query, params=None):
             query = self._build_params_header(params) + query
 
         plan = self.execute_command("GRAPH.EXPLAIN", self.name, query)
+        if isinstance(plan[0], bytes):
+            plan = [b.decode() for b in plan]
         return "\n".join(plan)
 
     def explain(self, query, params=None):
diff --git a/redis/commands/graph/execution_plan.py b/redis/commands/graph/execution_plan.py
--- a/redis/commands/graph/execution_plan.py
+++ b/redis/commands/graph/execution_plan.py
@@ -67,6 +67,9 @@ def __init__(self, plan):
         if not isinstance(plan, list):
             raise Exception("plan must be an array")
 
+        if isinstance(plan[0], bytes):
+            plan = [b.decode() for b in plan]
+
         self.plan = plan
         self.structured_plan = self._operation_tree()
 


Test patch:
Repository: redis/redis-py

Issue:
Add support for missing command CLUSTER MYID



Discussion:

Resolving patch:
diff --git a/redis/commands/cluster.py b/redis/commands/cluster.py
--- a/redis/commands/cluster.py
+++ b/redis/commands/cluster.py
@@ -275,6 +275,17 @@ class RedisClusterCommands(
         r.cluster_info(target_nodes=RedisCluster.ALL_NODES)
     """
 
+    def cluster_myid(self, target_node):
+        """
+        Returns the node’s id.
+
+        :target_node: 'ClusterNode'
+            The node to execute the command on
+
+        For more information check https://redis.io/commands/cluster-myid/
+        """
+        return self.execute_command("CLUSTER MYID", target_nodes=target_node)
+
     def cluster_addslots(self, target_node, *slots):
         """
         Assign new hash slots to receiving node. Sends to specified node.


Test patch:
diff --git a/tests/test_cluster.py b/tests/test_cluster.py
--- a/tests/test_cluster.py
+++ b/tests/test_cluster.py
@@ -840,6 +840,11 @@ def test_cluster_pubsub_numsub(self, r):
         channels = [(b"foo", 1), (b"bar", 2), (b"baz", 3)]
         assert r.pubsub_numsub("foo", "bar", "baz", target_nodes="all") == channels
 
+    def test_cluster_myid(self, r):
+        node = r.get_random_node()
+        myid = r.cluster_myid(node)
+        assert len(myid) == 40
+
     def test_cluster_slots(self, r):
         mock_all_nodes_resp(r, default_cluster_slots)
         cluster_slots = r.cluster_slots()
Repository: redis/redis-py

Issue:
Add support for missing command BITPOS - [BYTE|BIT] field



Discussion:

Resolving patch:
diff --git a/redis/commands/core.py b/redis/commands/core.py
--- a/redis/commands/core.py
+++ b/redis/commands/core.py
@@ -1363,6 +1363,7 @@ def bitcount(
         key: KeyT,
         start: Union[int, None] = None,
         end: Union[int, None] = None,
+        mode: Optional[str] = None,
     ) -> ResponseT:
         """
         Returns the count of set bits in the value of ``key``.  Optional
@@ -1376,6 +1377,8 @@ def bitcount(
             params.append(end)
         elif (start is not None and end is None) or (end is not None and start is None):
             raise DataError("Both start and end must be specified")
+        if mode is not None:
+            params.append(mode)
         return self.execute_command("BITCOUNT", *params)
 
     def bitfield(
@@ -1411,6 +1414,7 @@ def bitpos(
         bit: int,
         start: Union[int, None] = None,
         end: Union[int, None] = None,
+        mode: Optional[str] = None,
     ) -> ResponseT:
         """
         Return the position of the first bit set to 1 or 0 in a string.
@@ -1430,6 +1434,9 @@ def bitpos(
             params.append(end)
         elif start is None and end is not None:
             raise DataError("start argument is not set, " "when end is specified")
+
+        if mode is not None:
+            params.append(mode)
         return self.execute_command("BITPOS", *params)
 
     def copy(


Test patch:
diff --git a/tests/test_commands.py b/tests/test_commands.py
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -854,6 +854,15 @@ def test_bitcount(self, r):
         assert r.bitcount("a", -2, -1) == 2
         assert r.bitcount("a", 1, 1) == 1
 
+    @skip_if_server_version_lt("7.0.0")
+    def test_bitcount_mode(self, r):
+        r.set("mykey", "foobar")
+        assert r.bitcount("mykey") == 26
+        assert r.bitcount("mykey", 1, 1, "byte") == 6
+        assert r.bitcount("mykey", 5, 30, "bit") == 17
+        with pytest.raises(redis.ResponseError):
+            assert r.bitcount("mykey", 5, 30, "but")
+
     @pytest.mark.onlynoncluster
     @skip_if_server_version_lt("2.6.0")
     def test_bitop_not_empty_string(self, r):
@@ -926,6 +935,15 @@ def test_bitpos_wrong_arguments(self, r):
         with pytest.raises(exceptions.RedisError):
             r.bitpos(key, 7) == 12
 
+    @skip_if_server_version_lt("7.0.0")
+    def test_bitpos_mode(self, r):
+        r.set("mykey", b"\x00\xff\xf0")
+        assert r.bitpos("mykey", 1, 0) == 8
+        assert r.bitpos("mykey", 1, 2, -1, "byte") == 16
+        assert r.bitpos("mykey", 0, 7, 15, "bit") == 7
+        with pytest.raises(redis.ResponseError):
+            r.bitpos("mykey", 1, 7, 15, "bite")
+
     @pytest.mark.onlynoncluster
     @skip_if_server_version_lt("6.2.0")
     def test_copy(self, r):