From 91d8293d42da5d281ab98d631f72ecde8b767d88 Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Wed, 15 Jul 2026 07:49:42 -0700 Subject: [PATCH 1/2] docs: fix exception-handling examples and Lua.new option doctests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The README error-handling tour read `e.message`, but the wrapper leaves that field nil when it wraps a VM exception (since #384); the message is rendered lazily via `Exception.message/1`. Switch the example to the accessor and correct the illustrative frame — the formatter emits location-then-body, with no "Runtime Error" title line. Re-indent the three Lua.new/1 `## Options` doctests from 6 to 8 spaces so ExDoc renders them as code blocks inside the bullet list rather than plain continuation paragraphs, and drop the double-escaped \"x\" in favour of a single-quoted Lua string so it renders cleanly. Fix the marketing snippets: `Lua.eval/2` does not exist (only eval!/2), and the sandbox snippet read the nil `.message` field with the wrong expected text. --- README.md | 11 ++++++----- lib/lua.ex | 24 ++++++++++++------------ website/lib/website_web/snippets.ex | 12 ++++++------ 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 143d171d..7b2c15e0 100644 --- a/README.md +++ b/README.md @@ -69,14 +69,15 @@ rescue e.line # => 2 e.source # => "" (chunk name) - # e.message is a formatted, colorized frame (ANSI codes elided here): + # Render the message with Exception.message/1. The :message struct field + # is nil when the exception wraps a VM error — the frame is built lazily + # so ANSI color is gated on the terminal at output time. It's a formatted, + # colorized frame (ANSI codes elided here): # - # Lua runtime error: Runtime Error - # - # at :2: + # Lua runtime error: at :2: # # runtime error: something went wrong - e.message + Exception.message(e) end ``` diff --git a/lib/lua.ex b/lib/lua.ex index 465e4e88..057b9d20 100644 --- a/lib/lua.ex +++ b/lib/lua.ex @@ -96,10 +96,10 @@ defmodule Lua do bounds tail recursion too — including loops that PUC-Lua would run indefinitely. Leave the default `:infinity` if you rely on unbounded tail recursion. - iex> lua = Lua.new(max_call_depth: 10) - iex> {[false, message], _lua} = Lua.eval!(lua, "local function f() return f() end return pcall(f)") - iex> message =~ "stack overflow" - true + iex> lua = Lua.new(max_call_depth: 10) + iex> {[false, message], _lua} = Lua.eval!(lua, "local function f() return f() end return pcall(f)") + iex> message =~ "stack overflow" + true * `:max_string_bytes` - (default 256 MiB) ceiling for any single string the VM will build, whether via `..`, `string.rep`, or a `load` reader. An oversized result raises a catchable @@ -112,10 +112,10 @@ defmodule Lua do heap cap mid-allocation, where the kill depends on garbage-collection timing rather than a deterministic refusal. - iex> lua = Lua.new(max_string_bytes: 1024) - iex> {[false, message], _lua} = Lua.eval!(lua, "return pcall(string.rep, \\"x\\", 2048)") - iex> message =~ "resulting string too large" - true + iex> lua = Lua.new(max_string_bytes: 1024) + iex> {[false, message], _lua} = Lua.eval!(lua, "return pcall(string.rep, 'x', 2048)") + iex> message =~ "resulting string too large" + true * `:max_instructions` - (default `:infinity`) caps the number of VM instructions a single evaluation may execute. When a script exceeds this budget, a catchable @@ -125,10 +125,10 @@ defmodule Lua do back-edges and call boundaries, so the default `:infinity` path carries no per-instruction cost. The budget is fresh per top-level evaluation and recoverable via `pcall`. - iex> lua = Lua.new(max_instructions: 1000) - iex> {[false, message], _lua} = Lua.eval!(lua, "return pcall(function() while true do end end)") - iex> message =~ "instruction budget exceeded" - true + iex> lua = Lua.new(max_instructions: 1000) + iex> {[false, message], _lua} = Lua.eval!(lua, "return pcall(function() while true do end end)") + iex> message =~ "instruction budget exceeded" + true """ @spec new(keyword()) :: t() def new(opts \\ []) do diff --git a/website/lib/website_web/snippets.ex b/website/lib/website_web/snippets.ex index 23518747..e31fd91b 100644 --- a/website/lib/website_web/snippets.ex +++ b/website/lib/website_web/snippets.ex @@ -54,10 +54,10 @@ defmodule DemoWeb.Snippets do lua = Lua.new() - {:error, err} = - Lua.eval(lua, "return os.execute('rm -rf /')") - - # err.message =~ "attempted to call" + # Calling a sandboxed function raises. + Lua.eval!(lua, "return os.execute('rm -rf /')") + # ** (Lua.RuntimeException) Lua runtime error: + # os.execute(_) is sandboxed """ }, %{ @@ -75,7 +75,7 @@ defmodule DemoWeb.Snippets do lua = Lua.new() |> Lua.load_api(Agent.Tools) # The model emits Lua. You run it. Done. - {:ok, {results, _}} = Lua.eval(lua, llm_script) + {results, _lua} = Lua.eval!(lua, llm_script) """ } ] @@ -179,7 +179,7 @@ defmodule DemoWeb.Snippets do # The agent emits Lua. You run it. It can only # do what you exposed -- nothing else. - {:ok, {result, _lua}} = Lua.eval(lua, agent_script) + {result, _lua} = Lua.eval!(lua, agent_script) """ @tvlabs_remote_api """ From 4357eba2d0217467cfc9af2ba8acd5d1c4156d18 Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Wed, 15 Jul 2026 10:52:46 -0400 Subject: [PATCH 2/2] Apply suggestion from @davydog187 --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 7b2c15e0..658fd8b8 100644 --- a/README.md +++ b/README.md @@ -69,10 +69,6 @@ rescue e.line # => 2 e.source # => "" (chunk name) - # Render the message with Exception.message/1. The :message struct field - # is nil when the exception wraps a VM error — the frame is built lazily - # so ANSI color is gated on the terminal at output time. It's a formatted, - # colorized frame (ANSI codes elided here): # # Lua runtime error: at :2: #