Skip to content

Commit b2b699b

Browse files
committed
update tests & docs & add invoke_script cluster mode variant
Signed-off-by: TJ Zhang <tj.zhang@improving.com>
1 parent 6feffdb commit b2b699b

File tree

5 files changed

+68
-19
lines changed

5 files changed

+68
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
* Node: Added XGROUP SETID command ([#2135]((https://github.com/valkey-io/valkey-glide/pull/2135))
103103
* Node: Added binary variant to string commands ([#2183](https://github.com/valkey-io/valkey-glide/pull/2183))
104104
* Node: Added binary variant to stream commands ([#2200](https://github.com/valkey-io/valkey-glide/pull/2200))
105+
* Python: Add Script commands ([#2208](https://github.com/valkey-io/valkey-glide/pull/2208))
105106

106107
#### Breaking Changes
107108
* Node: (Refactor) Convert classes to types ([#2005](https://github.com/valkey-io/valkey-glide/pull/2005))

python/python/glide/async_commands/cluster_commands.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from glide.protobuf.command_request_pb2 import RequestType
2626
from glide.routes import Route
2727

28-
from ..glide import ClusterScanCursor
28+
from ..glide import ClusterScanCursor, Script
2929

3030

3131
class ClusterCommands(CoreCommands):
@@ -1283,7 +1283,7 @@ async def script_exists(
12831283
12841284
Args:
12851285
sha1s (List[TEncodable]): List of SHA1 digests of the scripts to check.
1286-
route (Optional[Route]): The command will be routed automatically based on the passed command's default request policy, unless `route` is provided, in which
1286+
route (Optional[Route]): The command will be routed to all primaries, unless `route` is provided, in which
12871287
case the client will route the command to the nodes defined by `route`. Defaults to None.
12881288
12891289
Returns:
@@ -1308,8 +1308,8 @@ async def script_flush(
13081308
13091309
Args:
13101310
mode (Optional[FlushMode]): The flushing mode, could be either `SYNC` or `ASYNC`.
1311-
route (Optional[Route]): The command will be routed automatically based on the passed command's default request policy, unless `route` is provided, in which
1312-
case the client will route the command to the nodes defined by `route`. Defaults to None.
1311+
route (Optional[Route]): The command will be routed automatically to all nodes, unless `route` is provided, in which
1312+
case the client will route the command to the nodes defined by `route`. Defaults to None.
13131313
13141314
Returns:
13151315
TOK: A simple `OK` response.
@@ -1338,11 +1338,44 @@ async def script_kill(self, route: Optional[Route] = None) -> TClusterResponse[T
13381338
13391339
Returns:
13401340
TOK: A simple `OK` response.
1341-
route (Optional[Route]): The command will be routed automatically based on the passed command's default request policy, unless `route` is provided, in which
1342-
case the client will route the command to the nodes defined by `route`. Defaults to None.
1341+
route (Optional[Route]): The command will be routed automatically to all nodes, unless `route` is provided, in which
1342+
case the client will route the command to the nodes defined by `route`. Defaults to None.
13431343
13441344
Examples:
13451345
>>> await client.script_kill()
13461346
"OK"
13471347
"""
13481348
return cast(TOK, await self._execute_command(RequestType.ScriptKill, [], route))
1349+
1350+
async def invoke_script(
1351+
self,
1352+
script: Script,
1353+
keys: Optional[List[TEncodable]] = None,
1354+
args: Optional[List[TEncodable]] = None,
1355+
route: Optional[Route] = None,
1356+
) -> TClusterResponse[TResult]:
1357+
"""
1358+
Invokes a Lua script with its keys and arguments.
1359+
This method simplifies the process of invoking scripts on a server by using an object that represents a Lua script.
1360+
The script loading, argument preparation, and execution will all be handled internally.
1361+
If the script has not already been loaded, it will be loaded automatically using the `SCRIPT LOAD` command.
1362+
After that, it will be invoked using the `EVALSHA` command.
1363+
1364+
See https://valkey.io/commands/script-load/ and https://valkey.io/commands/evalsha/ for more details.
1365+
1366+
Args:
1367+
script (Script): The Lua script to execute.
1368+
keys (Optional[List[TEncodable]]): The keys that are used in the script.
1369+
args (Optional[List[TEncodable]]): The arguments for the script.
1370+
route (Optional[Route]): The command will be routed automatically to all nodes, unless `route` is provided, in which
1371+
case the client will route the command to the nodes defined by `route`. Defaults to None.
1372+
1373+
Returns:
1374+
TResult: a value that depends on the script that was executed.
1375+
1376+
Examples:
1377+
>>> lua_script = Script("return { KEYS[1], ARGV[1] }")
1378+
>>> await invoke_script(lua_script, keys=["foo"], args=["bar"] );
1379+
[b"foo", b"bar"]
1380+
"""
1381+
return await self._execute_script(script.get_hash(), keys, args, route)

python/python/glide/async_commands/standalone_commands.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1078,7 +1078,6 @@ async def script_flush(self, mode: Optional[FlushMode] = None) -> TOK:
10781078
async def script_kill(self) -> TOK:
10791079
"""
10801080
Kill the currently executing Lua script, assuming no write operation was yet performed by the script.
1081-
The command is routed to all nodes, and aggregates the response to a single array.
10821081
10831082
See https://valkey.io/commands/script-kill for more details.
10841083

python/python/tests/test_async_client.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10298,32 +10298,40 @@ async def test_script_flush(self, glide_client: TGlideClient):
1029810298
assert await glide_client.script_exists([script.get_hash()]) == [False]
1029910299

1030010300
@pytest.mark.parametrize("cluster_mode", [True, False])
10301+
@pytest.mark.parametrize("single_route", [True, False])
1030110302
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
1030210303
async def test_script_kill(
10303-
self, request, cluster_mode, protocol, glide_client: TGlideClient
10304+
self,
10305+
request,
10306+
cluster_mode,
10307+
protocol,
10308+
glide_client: TGlideClient,
10309+
single_route: bool,
1030410310
):
10311+
is_cluster = isinstance(glide_client, GlideClusterClient)
10312+
route = SlotKeyRoute(SlotType.PRIMARY, "1") if single_route else AllPrimaries()
10313+
1030510314
# Verify that script_kill raises an error when no script is running
1030610315
with pytest.raises(RequestError) as e:
1030710316
await glide_client.script_kill()
1030810317
assert "No scripts in execution right now" in str(e)
1030910318

1031010319
# Create a long-running script
1031110320
long_script = Script(create_long_running_lua_script(10))
10312-
# long_script = Script("while true do end")
1031310321

1031410322
# Create a second client to run the script
1031510323
test_client = await create_client(
1031610324
request, cluster_mode=cluster_mode, protocol=protocol, timeout=30000
1031710325
)
1031810326

10319-
# Create a second client to kill the script
10320-
test_client2 = await create_client(
10321-
request, cluster_mode=cluster_mode, protocol=protocol, timeout=15000
10322-
)
10323-
1032410327
async def run_long_script():
1032510328
with pytest.raises(RequestError) as e:
10326-
await test_client.invoke_script(long_script)
10329+
if is_cluster:
10330+
await cast(GlideClusterClient, test_client).invoke_script(
10331+
long_script, route=route
10332+
)
10333+
else:
10334+
await test_client.invoke_script(long_script)
1032710335
assert "Script killed by user" in str(e)
1032810336

1032910337
async def wait_and_kill_script():
@@ -10332,7 +10340,12 @@ async def wait_and_kill_script():
1033210340
while timeout <= 5:
1033310341
# keep trying to kill until we get an "OK"
1033410342
try:
10335-
result = await glide_client.script_kill()
10343+
if is_cluster:
10344+
result = await cast(
10345+
GlideClusterClient, glide_client
10346+
).script_kill(route=route)
10347+
else:
10348+
result = await glide_client.script_kill()
1033610349
# we expect to get success
1033710350
assert result == "OK"
1033810351
break
@@ -10350,11 +10363,13 @@ async def wait_and_kill_script():
1035010363

1035110364
# Verify that script_kill raises an error when no script is running
1035210365
with pytest.raises(RequestError) as e:
10353-
await glide_client.script_kill()
10366+
if is_cluster:
10367+
await cast(GlideClusterClient, glide_client).script_kill(route=route)
10368+
else:
10369+
await glide_client.script_kill()
1035410370
assert "No scripts in execution right now" in str(e)
1035510371

1035610372
await test_client.close()
10357-
await test_client2.close()
1035810373

1035910374
@pytest.mark.parametrize("cluster_mode", [True, False])
1036010375
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])

python/python/tests/utils/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@ def create_lua_lib_with_long_running_function(
266266

267267
def create_long_running_lua_script(timeout: int) -> str:
268268
"""
269-
Create a lua script
269+
Create a lua script which runs an endless loop up to timeout sec.
270+
Execution takes at least 5 sec regardless of the timeout configured.
270271
"""
271272
script = (
272273
" local started = tonumber(redis.pcall('time')[1])\n"

0 commit comments

Comments
 (0)