Repository: redis/redis-py

Issue:
EVAL_RO, EVALSHA_RO doesn't get routed to replicas
Thanks for wanting to report an issue you've found in redis-py. Please delete this text and fill in the template below.  
It is of course not always possible to reduce your code to a small test case, but it's highly appreciated to have as much data as possible. Thank you!

**Version**: What redis-py and what redis version is the issue happening on?
redis-py 4.4.0, redis 7.0

**Platform**: What platform / version? (For example Python 3.5.1 on Windows 7 / Ubuntu 15.10 / Azure)
python 3.11.0 on Mac

**Description**: Description of your issue, stack traces from errors and code that reproduces the issue
Commands like `EVAL_RO` and `EVALSHA_RO` are always directed to the master, even though they can be routed to replicas as they are read-only commands

I suspect the cause to be READ_COMMANDS missing few read only commands
 https://github.com/redis/redis-py/blob/6219574b042a6596b150ca8248441198f01f8c87/redis/commands/cluster.py#L49-L93

Can i make a PR to add those commands or is there any other rationale that prevents adding EVAL_RO and EVALSHA_RO into READ_COMMANDS?


Discussion:
@zakaf The reason these aren't in the READ_COMMANDS list is purely historic - they came after the list, in the redis 7 release. We'd love a PR - especially if you're working on and validating the behaviour :D Since you offered.. I even assigned it to you!

Resolving patch:
diff --git a/redis/commands/cluster.py b/redis/commands/cluster.py
--- a/redis/commands/cluster.py
+++ b/redis/commands/cluster.py
@@ -52,6 +52,8 @@
     [
         "BITCOUNT",
         "BITPOS",
+        "EVAL_RO",
+        "EVALSHA_RO",
         "EXISTS",
         "GEODIST",
         "GEOHASH",
diff --git a/redis/commands/core.py b/redis/commands/core.py
--- a/redis/commands/core.py
+++ b/redis/commands/core.py
@@ -5139,7 +5139,7 @@ def eval_ro(
         """
         The read-only variant of the EVAL command
 
-        Execute the read-only Lue ``script`` specifying the ``numkeys`` the script
+        Execute the read-only Lua ``script`` specifying the ``numkeys`` the script
         will touch and the key names and argument values in ``keys_and_args``.
         Returns the result of the script.
 


Test patch:
diff --git a/tests/test_cluster.py b/tests/test_cluster.py
--- a/tests/test_cluster.py
+++ b/tests/test_cluster.py
@@ -561,7 +561,15 @@ def parse_response_mock_third(connection, *args, **options):
                 read_cluster.get("foo")
                 read_cluster.get("foo")
                 read_cluster.get("foo")
-                mocks["send_command"].assert_has_calls([call("READONLY")])
+                mocks["send_command"].assert_has_calls(
+                    [
+                        call("READONLY"),
+                        call("GET", "foo"),
+                        call("READONLY"),
+                        call("GET", "foo"),
+                        call("GET", "foo"),
+                    ]
+                )
 
     def test_keyslot(self, r):
         """
diff --git a/tests/test_scripting.py b/tests/test_scripting.py
--- a/tests/test_scripting.py
+++ b/tests/test_scripting.py
@@ -67,7 +67,6 @@ def test_eval_multiply(self, r):
 
     @skip_if_server_version_lt("7.0.0")
     @skip_if_redis_enterprise()
-    @pytest.mark.onlynoncluster
     def test_eval_ro(self, r):
         r.set("a", "b")
         assert r.eval_ro("return redis.call('GET', KEYS[1])", 1, "a") == b"b"
@@ -157,7 +156,6 @@ def test_evalsha(self, r):
 
     @skip_if_server_version_lt("7.0.0")
     @skip_if_redis_enterprise()
-    @pytest.mark.onlynoncluster
     def test_evalsha_ro(self, r):
         r.set("a", "b")
         get_sha = r.script_load("return redis.call('GET', KEYS[1])")