class LuaRunner(object): def __init__(self, keyspace): self._runtime = LuaRuntime(unpack_returned_tuples=True) self._lua_table_type = type(self._runtime.table()) self._redis_obj = RedisLua(keyspace, self._runtime) def run(self, script, keys, argv): self._runtime.execute('KEYS = {%s}' % ', '.join(map(json.dumps, keys))) self._runtime.execute('ARGV = {%s}' % ', '.join(map(json.dumps, argv))) script_function = self._runtime.eval( 'function(redis) {} end'.format(script)) result = script_function(self._redis_obj) return self._convert_lua_types_to_redis_types(result) def _convert_lua_types_to_redis_types(self, result): def convert(value): """ str -> str true -> 1 false -> None number -> int table -> { if 'err' key is present, raise an error else if 'ok' key is present, return its value else convert to a list using the previous rules } Reference: https://github.com/antirez/redis/blob/5b4bec9d336655889641b134791dfdd2adc864cf/src/scripting.c#L273-L340 """ if isinstance(value, self._lua_table_type): if 'err' in value: raise RedisScriptError(value['err']) elif 'ok' in value: return value['ok'] else: return map(convert, value.values()) elif isinstance(value, (tuple, list, set)): return map(convert, value) elif value is True: return 1 elif value is False: return None elif isinstance(value, float): return int(value) else: # assuming string at this point return value return convert(result)
def _lua_render_template(luastr: str, kwargs=None): """ Renders a Lua template. """ def getter(obj, attr_name): if attr_name.startswith("_"): raise AttributeError("Not allowed to access attribute `{}` of `{}`" .format(attr_name, type(obj).__name__)) return attr_name def setter(obj, attr_name, value): raise AttributeError("Python object attribute setting is forbidden") # the attribute_handlers are probably enough to prevent access eval otherwise lua = LuaRuntime(register_eval=False, unpack_returned_tuples=True, attribute_handlers=(getter, setter)) # execute the sandbox preamble sandbox = lua.execute(sandbox_preamble) # call sandbox.run with `glob.sandbox, code` # and unpack the variables new = {} # HECK for key, val in kwargs.items(): new[key] = lua.table_from(val) _ = sandbox.run(luastr, lua.table_from(new)) if isinstance(_, bool): # idk return NO_RESULT called, result = _ if lupa.lua_type(result) == 'table': # dictify result = dictify_table_recursively(result) return str(result)