Add plugin handler traceback when started in debug mode (#13677)

This commit is contained in:
Edoardo Putti
2025-09-14 15:30:03 +02:00
committed by GitHub
parent 99c7d00b58
commit 8da7774905

View File

@@ -1,3 +1,17 @@
--[[--
Allows extending KOReader through plugins.
Plugins will be sourced from DEFAULT_PLUGIN_PATH. If set, extra_plugin_paths
is also used. Directories are considered plugins if the name matches
".+%.koplugin".
Running with debug turned on will log stacktraces for event handlers.
Plugins are controlled by the following settings.
- plugins_disabled
- extra_plugin_paths
]]
local dbg = require("dbg")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
local util = require("util")
@@ -55,22 +69,54 @@ local function getMenuTable(plugin)
return t
end
local function sandboxPluginEventHandlers(plugin)
for key, value in pairs(plugin) do
if key:sub(1, 2) == "on" and type(value) == "function" then
plugin[key] = function(self, ...)
local ok, re = pcall(value, self, ...)
if ok then
return re
else
logger.err("failed to call event handler", key, re)
return false
end
end
-- Event handlers defined by plugins are wrapped in a HandlerSandbox.
-- The purpose of the sandbox is to get meaningful stack-traces out of errors happening in plugins.
local HandlerSandbox = { mt = {} }
function HandlerSandbox.new(context, fname, f, module)
local t = {
context = context,
fname = fname,
f = f,
log_stacktrace = dbg.is_on,
}
return setmetatable(t, HandlerSandbox.mt)
end
function HandlerSandbox:call(module, ...)
-- NOTE the signature is (self, module, ...)
-- self refers to the HandlerSandbox instance but module refers to the
-- self parameter of the handlers
local ok, re
if self.log_stacktrace then
local traceback = function(err)
-- do not print 2 topmost entries in traceback. The first is this local function
-- and the second is the `call` method of HandlerSandbox.
logger.err("An error occurred while executing a handler:\n"..err.."\n"..debug.traceback(self.context.name..":"..self.fname, 2))
end
ok, re = xpcall(self.f, traceback, module, ...)
else
ok, re = pcall(self.f, module, ...)
if not ok then logger.err("An error occurred while executing handler "..self.context.name..":"..self.fname..":\n", re) end
end
-- NOTE backward compatibility with previous implementation
-- of handler wrapping that returned false on error
if ok then
return re
else
return false
end
end
HandlerSandbox.mt.__call = HandlerSandbox.call
local function sandboxPluginEventHandlers(plugin)
for key, value in pairs(plugin) do
if key:sub(1, 2) == "on" and type(value) == "function" then
plugin[key] = HandlerSandbox.new(plugin, key, value)
end
end
end
local PluginLoader = {
show_info = true,