diff --git a/redis/client.py b/redis/client.py index d8d7a75ce0..e44f5abeb2 100755 --- a/redis/client.py +++ b/redis/client.py @@ -760,7 +760,6 @@ class AbstractRedis: "DEBUG OBJECT": parse_debug_object, "FUNCTION DELETE": bool_ok, "FUNCTION FLUSH": bool_ok, - "FUNCTION LOAD": bool_ok, "FUNCTION RESTORE": bool_ok, "GEOHASH": lambda r: list(map(str_if_bytes, r)), "GEOPOS": lambda r: list( diff --git a/redis/commands/core.py b/redis/commands/core.py index d1d465d7a8..8aa2dce629 100644 --- a/redis/commands/core.py +++ b/redis/commands/core.py @@ -5703,28 +5703,20 @@ class FunctionCommands: def function_load( self, - engine: str, - library: str, code: str, replace: Optional[bool] = False, - description: Optional[str] = None, ) -> Union[Awaitable[str], str]: """ Load a library to Redis. - :param engine: the name of the execution engine for the library - :param library: the unique name of the library - :param code: the source code - :param replace: changes the behavior to replace the library if a library called - ``library`` already exists - :param description: description to the library + :param code: the source code (must start with + Shebang statement that provides a metadata about the library) + :param replace: changes the behavior to overwrite the existing library + with the new contents. + Return the library name that was loaded. For more information see https://redis.io/commands/function-load """ - pieces = [engine, library] - if replace: - pieces.append("REPLACE") - if description is not None: - pieces.append(description) + pieces = ["REPLACE"] if replace else [] pieces.append(code) return self.execute_command("FUNCTION LOAD", *pieces) diff --git a/tests/test_function.py b/tests/test_function.py index 6f0a6ec1e1..e6c4bcd586 100644 --- a/tests/test_function.py +++ b/tests/test_function.py @@ -4,12 +4,16 @@ from .conftest import skip_if_server_version_lt -function = "redis.register_function('myfunc', function(keys, args) return args[1] end)" +engine = "lua" +lib = "mylib" +lib2 = "mylib2" +function = "redis.register_function{function_name='myfunc', callback=function(keys, \ + args) return args[1] end, flags={ 'no-writes' }}" function2 = "redis.register_function('hello', function() return 'Hello World' end)" -set_function = "redis.register_function('set', function(keys, args) \ - return redis.call('SET', keys[1], args[1]) end)" -get_function = "redis.register_function('get', function(keys, args) \ - return redis.call('GET', keys[1]) end)" +set_function = "redis.register_function('set', function(keys, args) return \ + redis.call('SET', keys[1], args[1]) end)" +get_function = "redis.register_function('get', function(keys, args) return \ + redis.call('GET', keys[1]) end)" @skip_if_server_version_lt("7.0.0") @@ -19,25 +23,28 @@ def reset_functions(self, r): r.function_flush() def test_function_load(self, r): - assert r.function_load("Lua", "mylib", function) - assert r.function_load("Lua", "mylib", function, replace=True) + print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + assert lib == r.function_load(f"#!{engine} name={lib} \n {function}") + assert lib == r.function_load( + f"#!{engine} name={lib} \n {function}", replace=True + ) with pytest.raises(ResponseError): - r.function_load("Lua", "mylib", function) + r.function_load(f"#!{engine} name={lib} \n {function}") with pytest.raises(ResponseError): - r.function_load("Lua", "mylib2", function) + r.function_load(f"#!{engine} name={lib2} \n {function}") def test_function_delete(self, r): - r.function_load("Lua", "mylib", set_function) + r.function_load(f"#!{engine} name={lib} \n {set_function}") with pytest.raises(ResponseError): - r.function_load("Lua", "mylib", set_function) + r.function_load(f"#!{engine} name={lib} \n {set_function}") assert r.fcall("set", 1, "foo", "bar") == "OK" assert r.function_delete("mylib") with pytest.raises(ResponseError): r.fcall("set", 1, "foo", "bar") - assert r.function_load("Lua", "mylib", set_function) + assert lib == r.function_load(f"#!{engine} name={lib} \n {set_function}") def test_function_flush(self, r): - r.function_load("Lua", "mylib", function) + r.function_load(f"#!{engine} name={lib} \n {function}") assert r.fcall("myfunc", 0, "hello") == "hello" assert r.function_flush() with pytest.raises(ResponseError): @@ -47,36 +54,35 @@ def test_function_flush(self, r): @pytest.mark.onlynoncluster def test_function_list(self, r): - r.function_load("Lua", "mylib", function) + r.function_load(f"#!{engine} name={lib} \n {function}") res = [ [ "library_name", "mylib", "engine", "LUA", - "description", - None, "functions", - [["name", "myfunc", "description", None]], + [["name", "myfunc", "description", None, "flags", ["no-writes"]]], ], ] assert r.function_list() == res assert r.function_list(library="*lib") == res - assert r.function_list(withcode=True)[0][9] == function + assert ( + r.function_list(withcode=True)[0][7] + == f"#!{engine} name={lib} \n {function}" + ) @pytest.mark.onlycluster def test_function_list_on_cluster(self, r): - r.function_load("Lua", "mylib", function) + r.function_load(f"#!{engine} name={lib} \n {function}") function_list = [ [ "library_name", "mylib", "engine", "LUA", - "description", - None, "functions", - [["name", "myfunc", "description", None]], + [["name", "myfunc", "description", None, "flags", ["no-writes"]]], ], ] primaries = r.get_primaries() @@ -86,25 +92,28 @@ def test_function_list_on_cluster(self, r): assert r.function_list() == res assert r.function_list(library="*lib") == res node = primaries[0].name - assert r.function_list(withcode=True)[node][0][9] == function + assert ( + r.function_list(withcode=True)[node][0][7] + == f"#!{engine} name={lib} \n {function}" + ) def test_fcall(self, r): - r.function_load("Lua", "mylib", set_function) - r.function_load("Lua", "mylib2", get_function) + r.function_load(f"#!{engine} name={lib} \n {set_function}") + r.function_load(f"#!{engine} name={lib2} \n {get_function}") assert r.fcall("set", 1, "foo", "bar") == "OK" assert r.fcall("get", 1, "foo") == "bar" with pytest.raises(ResponseError): r.fcall("myfunc", 0, "hello") def test_fcall_ro(self, r): - r.function_load("Lua", "mylib", function) + r.function_load(f"#!{engine} name={lib} \n {function}") assert r.fcall_ro("myfunc", 0, "hello") == "hello" - r.function_load("Lua", "mylib2", set_function) + r.function_load(f"#!{engine} name={lib2} \n {set_function}") with pytest.raises(ResponseError): r.fcall_ro("set", 1, "foo", "bar") def test_function_dump_restore(self, r): - r.function_load("Lua", "mylib", set_function) + r.function_load(f"#!{engine} name={lib} \n {set_function}") payload = r.function_dump() assert r.fcall("set", 1, "foo", "bar") == "OK" r.function_delete("mylib") @@ -112,7 +121,7 @@ def test_function_dump_restore(self, r): r.fcall("set", 1, "foo", "bar") assert r.function_restore(payload) assert r.fcall("set", 1, "foo", "bar") == "OK" - r.function_load("Lua", "mylib2", get_function) + r.function_load(f"#!{engine} name={lib2} \n {get_function}") assert r.fcall("get", 1, "foo") == "bar" r.function_delete("mylib") assert r.function_restore(payload, "FLUSH")