Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
* Node: Added binary variant to string commands ([#2183](https://github.com/valkey-io/valkey-glide/pull/2183))
* Node: Added binary variant to stream commands ([#2200](https://github.com/valkey-io/valkey-glide/pull/2200), [#2222](https://github.com/valkey-io/valkey-glide/pull/2222))
* Python, Node, Java: change BITCOUNT end param to optional ([#2248](https://github.com/valkey-io/valkey-glide/pull/2248))

* Python: Add Script commands ([#2208](https://github.com/valkey-io/valkey-glide/pull/2208))

#### Breaking Changes
* Node: (Refactor) Convert classes to types ([#2005](https://github.com/valkey-io/valkey-glide/pull/2005))
Expand Down
2 changes: 1 addition & 1 deletion glide-core/src/client/value_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1207,7 +1207,7 @@ pub(crate) fn expected_type_for_cmd(cmd: &Cmd) -> Option<ExpectedReturnType> {
| b"MSETNX"
| b"XGROUP DESTROY"
| b"XGROUP CREATECONSUMER" => Some(ExpectedReturnType::Boolean),
b"SMISMEMBER" => Some(ExpectedReturnType::ArrayOfBools),
b"SMISMEMBER" | b"SCRIPT EXISTS" => Some(ExpectedReturnType::ArrayOfBools),
b"SMEMBERS" | b"SINTER" | b"SDIFF" | b"SUNION" => Some(ExpectedReturnType::Set),
b"ZSCORE" | b"GEODIST" => Some(ExpectedReturnType::DoubleOrNull),
b"ZMSCORE" => Some(ExpectedReturnType::ArrayOfDoubleOrNull),
Expand Down
3 changes: 3 additions & 0 deletions glide-core/src/protobuf/command_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ enum RequestType {
PubSubNumSub = 212;
PubSubSChannels = 213;
PubSubSNumSub = 214;
ScriptExists = 215;
ScriptFlush = 216;
ScriptKill = 217;
}

message Command {
Expand Down
9 changes: 9 additions & 0 deletions glide-core/src/request_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ pub enum RequestType {
PubSubNumSub = 212,
PubSubSChannels = 213,
PubSubSNumSub = 214,
ScriptExists = 215,
ScriptFlush = 216,
ScriptKill = 217,
}

fn get_two_word_command(first: &str, second: &str) -> Cmd {
Expand Down Expand Up @@ -449,6 +452,9 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::PubSubNumPat => RequestType::PubSubNumPat,
ProtobufRequestType::PubSubSChannels => RequestType::PubSubSChannels,
ProtobufRequestType::PubSubSNumSub => RequestType::PubSubSNumSub,
ProtobufRequestType::ScriptExists => RequestType::ScriptExists,
ProtobufRequestType::ScriptFlush => RequestType::ScriptFlush,
ProtobufRequestType::ScriptKill => RequestType::ScriptKill,
}
}
}
Expand Down Expand Up @@ -673,6 +679,9 @@ impl RequestType {
RequestType::PubSubNumPat => Some(get_two_word_command("PUBSUB", "NUMPAT")),
RequestType::PubSubSChannels => Some(get_two_word_command("PUBSUB", "SHARDCHANNELS")),
RequestType::PubSubSNumSub => Some(get_two_word_command("PUBSUB", "SHARDNUMSUB")),
RequestType::ScriptExists => Some(get_two_word_command("SCRIPT", "EXISTS")),
RequestType::ScriptFlush => Some(get_two_word_command("SCRIPT", "FLUSH")),
RequestType::ScriptKill => Some(get_two_word_command("SCRIPT", "KILL")),
}
}
}
143 changes: 142 additions & 1 deletion python/python/glide/async_commands/cluster_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from glide.protobuf.command_request_pb2 import RequestType
from glide.routes import Route

from ..glide import ClusterScanCursor
from ..glide import ClusterScanCursor, Script


class ClusterCommands(CoreCommands):
Expand Down Expand Up @@ -1272,3 +1272,144 @@ async def scan(
List[Union[ClusterScanCursor, List[bytes]]],
await self._cluster_scan(cursor, match, count, type),
)

async def script_exists(
self, sha1s: List[TEncodable], route: Optional[Route] = None
) -> TClusterResponse[List[bool]]:
"""
Check existence of scripts in the script cache by their SHA1 digest.

See https://valkey.io/commands/script-exists for more details.

Args:
sha1s (List[TEncodable]): List of SHA1 digests of the scripts to check.
route (Optional[Route]): The command will be routed to all primary nodes, unless `route` is provided, in which
case the client will route the command to the nodes defined by `route`. Defaults to None.

Returns:
TClusterResponse[List[bool]]: A list of boolean values indicating the existence of each script.

Examples:
>>> lua_script = Script("return { KEYS[1], ARGV[1] }")
>>> await client.script_exists([lua_script.get_hash(), "sha1_digest2"])
[True, False]
"""
return cast(
TClusterResponse[List[bool]],
await self._execute_command(RequestType.ScriptExists, sha1s, route),
)

async def script_flush(
self, mode: Optional[FlushMode] = None, route: Optional[Route] = None
) -> TOK:
"""
Flush the Lua scripts cache.

See https://valkey.io/commands/script-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 automatically to all nodes, unless `route` is provided, in which
case the client will route the command to the nodes defined by `route`. Defaults to None.

Returns:
TOK: A simple `OK` response.

Examples:
>>> await client.script_flush()
"OK"

>>> await client.script_flush(FlushMode.ASYNC)
"OK"
"""

return cast(
TOK,
await self._execute_command(
RequestType.ScriptFlush, [mode.value] if mode else [], route
),
)

async def script_kill(self, route: Optional[Route] = None) -> TOK:
"""
Kill the currently executing Lua script, assuming no write operation was yet performed by the script.
The command is routed to all nodes, and aggregates the response to a single array.

See https://valkey.io/commands/script-kill for more details.

Returns:
TOK: A simple `OK` response.
route (Optional[Route]): The command will be routed automatically to all nodes, unless `route` is provided, in which
case the client will route the command to the nodes defined by `route`. Defaults to None.

Examples:
>>> await client.script_kill()
"OK"
"""
return cast(TOK, await self._execute_command(RequestType.ScriptKill, [], route))

async def invoke_script(
self,
script: Script,
keys: Optional[List[TEncodable]] = None,
args: Optional[List[TEncodable]] = None,
) -> TClusterResponse[TResult]:
"""
Invokes a Lua script with its keys and arguments.
This method simplifies the process of invoking scripts on a server by using an object that represents a Lua script.
The script loading, argument preparation, and execution will all be handled internally.
If the script has not already been loaded, it will be loaded automatically using the `SCRIPT LOAD` command.
After that, it will be invoked using the `EVALSHA` command.

When in cluster mode, `key`s must map to the same hash slot.

See https://valkey.io/commands/script-load/ and https://valkey.io/commands/evalsha/ for more details.

Args:
script (Script): The Lua script to execute.
keys (Optional[List[TEncodable]]): The keys that are used in the script. To ensure the correct execution of
the script, all names of keys that a script accesses must be explicitly provided as `keys`.
args (Optional[List[TEncodable]]): The non-key arguments for the script.

Returns:
TResult: a value that depends on the script that was executed.

Examples:
>>> lua_script = Script("return { KEYS[1], ARGV[1] }")
>>> await invoke_script(lua_script, keys=["foo"], args=["bar"] );
[b"foo", b"bar"]
"""
return await self._execute_script(script.get_hash(), keys, args)

async def invoke_script_route(
self,
script: Script,
args: Optional[List[TEncodable]] = None,
route: Optional[Route] = None,
) -> TClusterResponse[TResult]:
"""
Invokes a Lua script with its arguments and route.
This method simplifies the process of invoking scripts on a server by using an object that represents a Lua script.
The script loading, argument preparation, and execution will all be handled internally.
If the script has not already been loaded, it will be loaded automatically using the `SCRIPT LOAD` command.
After that, it will be invoked using the `EVALSHA` command.

See https://valkey.io/commands/script-load/ and https://valkey.io/commands/evalsha/ for more details.

Args:
script (Script): The Lua script to execute.
args (Optional[List[TEncodable]]): The non-key arguments for the script.
route (Optional[Route]): The command will be routed automatically to a random node, unless `route` is provided, in which
case the client will route the command to the nodes defined by `route`. Defaults to None.

Returns:
TResult: a value that depends on the script that was executed.

Examples:
>>> lua_script = Script("return { ARGV[1] }")
>>> await invoke_script(lua_script, args=["bar"], route=AllPrimaries());
[b"bar"]
"""
return await self._execute_script(
script.get_hash(), keys=None, args=args, route=route
)
32 changes: 1 addition & 31 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
from glide.protobuf.command_request_pb2 import RequestType
from glide.routes import Route

from ..glide import ClusterScanCursor, Script
from ..glide import ClusterScanCursor


class ConditionalChange(Enum):
Expand Down Expand Up @@ -5393,36 +5393,6 @@ async def zintercard(
await self._execute_command(RequestType.ZInterCard, args),
)

async def invoke_script(
self,
script: Script,
keys: Optional[List[TEncodable]] = None,
args: Optional[List[TEncodable]] = None,
) -> TResult:
"""
Invokes a Lua script with its keys and arguments.
This method simplifies the process of invoking scripts on a the server by using an object that represents a Lua script.
The script loading, argument preparation, and execution will all be handled internally.
If the script has not already been loaded, it will be loaded automatically using the `SCRIPT LOAD` command.
After that, it will be invoked using the `EVALSHA` command.

See https://valkey.io/commands/script-load/ and https://valkey.io/commands/evalsha/ for more details.

Args:
script (Script): The Lua script to execute.
keys (Optional[List[TEncodable]]): The keys that are used in the script.
args (Optional[List[TEncodable]]): The arguments for the script.

Returns:
TResult: a value that depends on the script that was executed.

Examples:
>>> lua_script = Script("return { KEYS[1], ARGV[1] }")
>>> await invoke_script(lua_script, keys=["foo"], args=["bar"] );
[b"foo", b"bar"]
"""
return await self._execute_script(script.get_hash(), keys, args)

async def pfadd(self, key: TEncodable, elements: List[TEncodable]) -> int:
"""
Adds all elements to the HyperLogLog data structure stored at the specified `key`.
Expand Down
94 changes: 94 additions & 0 deletions python/python/glide/async_commands/standalone_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
)
from glide.protobuf.command_request_pb2 import RequestType

from ..glide import ClusterScanCursor, Script


class StandaloneCommands(CoreCommands):
async def custom_command(self, command_args: List[TEncodable]) -> TResult:
Expand Down Expand Up @@ -1027,3 +1029,95 @@ async def scan(
List[Union[bytes, List[bytes]]],
await self._execute_command(RequestType.Scan, args),
)

async def script_exists(self, sha1s: List[TEncodable]) -> List[bool]:
"""
Check existence of scripts in the script cache by their SHA1 digest.

See https://valkey.io/commands/script-exists for more details.

Args:
sha1s (List[TEncodable]): List of SHA1 digests of the scripts to check.

Returns:
List[bool]: A list of boolean values indicating the existence of each script.

Examples:
>>> await client.script_exists(["sha1_digest1", "sha1_digest2"])
[True, False]
"""
return cast(
List[bool], await self._execute_command(RequestType.ScriptExists, sha1s)
)

async def script_flush(self, mode: Optional[FlushMode] = None) -> TOK:
"""
Flush the Lua scripts cache.

See https://valkey.io/commands/script-flush for more details.

Args:
mode (Optional[FlushMode]): The flushing mode, could be either `SYNC` or `ASYNC`.

Returns:
TOK: A simple `OK` response.

Examples:
>>> await client.script_flush()
"OK"

>>> await client.script_flush(FlushMode.ASYNC)
"OK"
"""

return cast(
TOK,
await self._execute_command(
RequestType.ScriptFlush, [mode.value] if mode else []
),
)

async def script_kill(self) -> TOK:
"""
Kill the currently executing Lua script, assuming no write operation was yet performed by the script.

See https://valkey.io/commands/script-kill for more details.

Returns:
TOK: A simple `OK` response.

Examples:
>>> await client.script_kill()
"OK"
"""
return cast(TOK, await self._execute_command(RequestType.ScriptKill, []))

async def invoke_script(
self,
script: Script,
keys: Optional[List[TEncodable]] = None,
args: Optional[List[TEncodable]] = None,
) -> TResult:
"""
Invokes a Lua script with its keys and arguments.
This method simplifies the process of invoking scripts on a the server by using an object that represents a Lua script.
The script loading, argument preparation, and execution will all be handled internally.
If the script has not already been loaded, it will be loaded automatically using the `SCRIPT LOAD` command.
After that, it will be invoked using the `EVALSHA` command.

See https://valkey.io/commands/script-load/ and https://valkey.io/commands/evalsha/ for more details.

Args:
script (Script): The Lua script to execute.
keys (Optional[List[TEncodable]]): The keys that are used in the script.
args (Optional[List[TEncodable]]): The arguments for the script.

Returns:
TResult: a value that depends on the script that was executed.

Examples:
>>> lua_script = Script("return { KEYS[1], ARGV[1] }")
>>> await invoke_script(lua_script, keys=["foo"], args=["bar"] );
[b"foo", b"bar"]
"""
return await self._execute_script(script.get_hash(), keys, args)
Loading