Skip to content

Where Lua differs from Python

These are the gaps that matter. Most are closed for you; the rest are refused.

Truthiness is closed

Lua counts 0 and '' as true. Any condition that is not already a boolean is routed through a generated __truthy helper, so if count: means what it means in Python.

Missing values are closed

A Redis command with nothing to return hands Lua false, not nil. This is the classic trap: a hand-written == nil never matches, so the branch silently never runs. x is None compiles to a helper accepting both, which also takes x as an argument — so if redis.hget(k, f) is None: does not run the command twice.

Indexing is closed

Lua tables are 1-based. items[0] compiles to items[1]. Write Python indices and let the compiler shift them. Negative indices are refused, because Lua has no equivalent.

Assignment scope is closed

Python scopes a name to the whole function; Lua's local scopes it to the enclosing block. A name assigned inside an if and read after it is hoisted to the top of the script, so it does not silently read back nil.

+ is arithmetic, not concatenation

Use an f-string, which compiles to Lua's ...

and / or work only in conditions

In Python they return an operand, not a boolean, and that does not survive the truthiness difference. Use an if.

There is no continue

Lua 5.1 does not have one. Invert the condition and nest the rest of the body.

A loop variable does not outlive its loop

Unlike in Python.

A nil inside a returned table truncates the reply

Redis converts a returned array by walking it from the first element and stopping at the first nil, so the caller gets a shorter list rather than a null in the middle of one.

Which values are actually nil is the part worth being exact about. A command with nothing to return hands Lua false, and false converts to a null element without ending the array — return [1, redis.get(missing), 3] really does reach the caller as [1, None, 3]. What truncates is a genuine nil, and in practice that means a name that was not assigned on this path. The compiler warns where it can see one:

'first' is not assigned on every path to this return, and a nil in a returned
table truncates the reply there
  File "/srv/app/queue.py", line 31
    return [1, first, count]
               ^
  hint: Give it a value before the branch, so that every branch returns a
  table of the same shape.

and refuses a None written out in the table, since that one is never what anybody meant. Silence the warning with:

import warnings

from redis_lua_py import NilTruncationWarning

warnings.filterwarnings("ignore", category=NilTruncationWarning)

if your script really does mean to stop there.

Return values follow Redis' own conversion rules

True becomes 1, False and None become nil, floats are truncated to integers. Return a string, or cjson.encode(...), when you need one preserved exactly. See What the caller gets.