Quickstart¶
Define a script¶
Define scripts at module level. They compile once, when the module is imported.
from redis_lua_py import Key, redis, script
@script
def rate_limit(key: Key, limit: int, ttl: int) -> int:
current = redis.incr(key)
if current == 1:
redis.expire(key, ttl)
if current > limit:
return -1
return limit - current
The body is never executed by Python. It is read as source at import, compiled
to Lua, and sent to Redis with EVALSHA.
A script defined inside a function recompiles on every call, and one defined
through exec has no source to read and is refused.
Call it¶
from redis import Redis
from limits import rate_limit
client = Redis()
remaining = rate_limit(client, key="user:42", limit=10, ttl=60)
Importing the client as from redis import Redis leaves the name redis free
for the script namespace, so the two never collide. If you want the client
module itself, see
When the client is imported too.
Scripts accept positional or keyword arguments; keyword is clearer at the call site and is what the errors suggest.
Read the Lua¶
Nothing is hidden:
-- rate_limit
-- Generated by redis-lua-py from limits.py:5. Do not edit.
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local ttl = tonumber(ARGV[2])
local current = redis.call('INCR', key)
if current == 1 then
redis.call('EXPIRE', key, ttl)
end
if current > limit then
return -1
end
return limit - current
More on the header, the SHA, and why the path is repo-relative in What it compiles to.
Test it¶
fakeredis runs the real Lua in process, so a behavioural test needs no server:
import fakeredis
from limits import rate_limit
def test_rate_limit_refuses_past_the_limit():
client = fakeredis.FakeRedis()
assert rate_limit(client, key="u:42", limit=2, ttl=60) == 1
assert rate_limit(client, key="u:42", limit=2, ttl=60) == 0
assert rate_limit(client, key="u:42", limit=2, ttl=60) == -1
See Testing your scripts for golden-Lua snapshots as well.
Next¶
- Keys and arguments — why
Keyis not cosmetic - The supported subset — what a body may contain
- Where Lua differs from Python — the gaps that matter