Merge pull request #712 from psychon/protected_calls
Protected calls (gears.protected_call)
This commit is contained in:
commit
f874b0ad2e
|
@ -20,13 +20,13 @@ local beautiful = require("beautiful")
|
||||||
local dpi = require("beautiful").xresources.apply_dpi
|
local dpi = require("beautiful").xresources.apply_dpi
|
||||||
local object = require("gears.object")
|
local object = require("gears.object")
|
||||||
local surface = require("gears.surface")
|
local surface = require("gears.surface")
|
||||||
|
local protected_call = require("gears.protected_call")
|
||||||
local cairo = require("lgi").cairo
|
local cairo = require("lgi").cairo
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
local tonumber = tonumber
|
local tonumber = tonumber
|
||||||
local string = string
|
local string = string
|
||||||
local ipairs = ipairs
|
local ipairs = ipairs
|
||||||
local pairs = pairs
|
local pairs = pairs
|
||||||
local pcall = pcall
|
|
||||||
local print = print
|
local print = print
|
||||||
local table = table
|
local table = table
|
||||||
local type = type
|
local type = type
|
||||||
|
@ -374,12 +374,8 @@ function menu:add(args, index)
|
||||||
local theme = load_theme(args.theme or {}, self.theme)
|
local theme = load_theme(args.theme or {}, self.theme)
|
||||||
args.theme = theme
|
args.theme = theme
|
||||||
args.new = args.new or menu.entry
|
args.new = args.new or menu.entry
|
||||||
local success, item = pcall(args.new, self, args)
|
local item = protected_call(args.new, self, args)
|
||||||
if not success then
|
if (not item) or (not item.widget) then
|
||||||
print("Error while creating menu entry: " .. item)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not item.widget then
|
|
||||||
print("Error while checking menu entry: no property widget found.")
|
print("Error while checking menu entry: no property widget found.")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,6 +24,7 @@ local lgi = require("lgi")
|
||||||
local Gio = lgi.Gio
|
local Gio = lgi.Gio
|
||||||
local GLib = lgi.GLib
|
local GLib = lgi.GLib
|
||||||
local util = require("awful.util")
|
local util = require("awful.util")
|
||||||
|
local protected_call = require("gears.protected_call")
|
||||||
|
|
||||||
local spawn = {}
|
local spawn = {}
|
||||||
|
|
||||||
|
@ -205,10 +206,7 @@ function spawn.read_lines(input_stream, line_callback, done_callback, close)
|
||||||
stream:close()
|
stream:close()
|
||||||
end
|
end
|
||||||
if done_callback then
|
if done_callback then
|
||||||
xpcall(done_callback, function(err)
|
protected_call(done_callback)
|
||||||
print(debug.traceback("Error while calling done_callback:"
|
|
||||||
.. tostring(err), 2))
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local start_read, finish_read
|
local start_read, finish_read
|
||||||
|
@ -226,14 +224,9 @@ function spawn.read_lines(input_stream, line_callback, done_callback, close)
|
||||||
done()
|
done()
|
||||||
else
|
else
|
||||||
-- Read a line
|
-- Read a line
|
||||||
xpcall(function()
|
-- This needs tostring() for older lgi versions which returned
|
||||||
-- This needs tostring() for older lgi versions which returned
|
-- "GLib.Bytes" instead of Lua strings (I guess)
|
||||||
-- "GLib.Bytes" instead of Lua strings (I guess)
|
protected_call(line_callback, tostring(line))
|
||||||
line_callback(tostring(line))
|
|
||||||
end, function(err)
|
|
||||||
print(debug.traceback("Error while calling line_callback: "
|
|
||||||
.. tostring(err), 2))
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Read the next line
|
-- Read the next line
|
||||||
start_read()
|
start_read()
|
||||||
|
|
|
@ -18,6 +18,7 @@ local lgi = require("lgi")
|
||||||
local Pango = lgi.Pango
|
local Pango = lgi.Pango
|
||||||
local PangoCairo = lgi.PangoCairo
|
local PangoCairo = lgi.PangoCairo
|
||||||
local gears_debug = require("gears.debug")
|
local gears_debug = require("gears.debug")
|
||||||
|
local protected_call = require("gears.protected_call")
|
||||||
|
|
||||||
local xresources = require("beautiful.xresources")
|
local xresources = require("beautiful.xresources")
|
||||||
|
|
||||||
|
@ -109,7 +110,6 @@ end
|
||||||
-- containing all the theme values.
|
-- containing all the theme values.
|
||||||
function beautiful.init(config)
|
function beautiful.init(config)
|
||||||
if config then
|
if config then
|
||||||
local success
|
|
||||||
local homedir = os.getenv("HOME")
|
local homedir = os.getenv("HOME")
|
||||||
|
|
||||||
-- If config is the path to the theme file,
|
-- If config is the path to the theme file,
|
||||||
|
@ -118,16 +118,12 @@ function beautiful.init(config)
|
||||||
if type(config) == 'string' then
|
if type(config) == 'string' then
|
||||||
-- Expand the '~' $HOME shortcut
|
-- Expand the '~' $HOME shortcut
|
||||||
config = config:gsub("^~/", homedir .. "/")
|
config = config:gsub("^~/", homedir .. "/")
|
||||||
success, theme = xpcall(function() return dofile(config) end,
|
theme = protected_call(dofile, config)
|
||||||
debug.traceback)
|
|
||||||
elseif type(config) == 'table' then
|
elseif type(config) == 'table' then
|
||||||
success = true
|
|
||||||
theme = config
|
theme = config
|
||||||
end
|
end
|
||||||
|
|
||||||
if not success then
|
if theme then
|
||||||
return gears_debug.print_error("beautiful: error loading theme file " .. theme)
|
|
||||||
elseif theme then
|
|
||||||
-- expand '~'
|
-- expand '~'
|
||||||
if homedir then
|
if homedir then
|
||||||
for k, v in pairs(theme) do
|
for k, v in pairs(theme) do
|
||||||
|
|
|
@ -62,13 +62,13 @@ function debug.dump(data, tag, depth)
|
||||||
print(debug.dump_return(data, tag, depth))
|
print(debug.dump_return(data, tag, depth))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Print an warning message
|
--- Print an warning message
|
||||||
-- @tparam string message The warning message to print
|
-- @tparam string message The warning message to print
|
||||||
function debug.print_warning(message)
|
function debug.print_warning(message)
|
||||||
io.stderr:write(os.date("%Y-%m-%d %T W: ") .. tostring(message) .. "\n")
|
io.stderr:write(os.date("%Y-%m-%d %T W: ") .. tostring(message) .. "\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Print an error message
|
--- Print an error message
|
||||||
-- @tparam string message The error message to print
|
-- @tparam string message The error message to print
|
||||||
function debug.print_error(message)
|
function debug.print_error(message)
|
||||||
io.stderr:write(os.date("%Y-%m-%d %T E: ") .. tostring(message) .. "\n")
|
io.stderr:write(os.date("%Y-%m-%d %T E: ") .. tostring(message) .. "\n")
|
||||||
|
|
|
@ -17,6 +17,7 @@ return
|
||||||
cache = require("gears.cache");
|
cache = require("gears.cache");
|
||||||
matrix = require("gears.matrix");
|
matrix = require("gears.matrix");
|
||||||
shape = require("gears.shape");
|
shape = require("gears.shape");
|
||||||
|
protected_call = require("gears.protected_call");
|
||||||
}
|
}
|
||||||
|
|
||||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- @author Uli Schlachter
|
||||||
|
-- @copyright 2016 Uli Schlachter
|
||||||
|
-- @release @AWESOME_VERSION@
|
||||||
|
-- @module gears.protected_call
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local gdebug = require("gears.debug")
|
||||||
|
local tostring = tostring
|
||||||
|
local traceback = debug.traceback
|
||||||
|
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
||||||
|
local xpcall = xpcall
|
||||||
|
|
||||||
|
local protected_call = {}
|
||||||
|
|
||||||
|
local function error_handler(err)
|
||||||
|
gdebug.print_error(traceback("Error during a protected call: " .. tostring(err)))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handle_result(success, ...)
|
||||||
|
if success then
|
||||||
|
return ...
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local do_pcall
|
||||||
|
if _VERSION <= "Lua 5.1" then
|
||||||
|
-- Lua 5.1 doesn't support arguments in xpcall :-(
|
||||||
|
do_pcall = function(func, ...)
|
||||||
|
local args = { ... }
|
||||||
|
return handle_result(xpcall(function()
|
||||||
|
return func(unpack(args))
|
||||||
|
end, error_handler))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
do_pcall = function(func, ...)
|
||||||
|
return handle_result(xpcall(func, error_handler, ...))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Call a function in protected mode and handle error-reporting.
|
||||||
|
-- If the function call succeeds, all results of the function are returned.
|
||||||
|
-- Otherwise, an error message is printed and nothing is returned.
|
||||||
|
-- @tparam function func The function to call
|
||||||
|
-- @param ... Arguments to the function
|
||||||
|
-- @return The result of the given function, or nothing if an error occurred.
|
||||||
|
function protected_call.call(func, ...)
|
||||||
|
return do_pcall(func, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
local pcall_mt = {}
|
||||||
|
function pcall_mt:__call(...)
|
||||||
|
return do_pcall(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable(protected_call, pcall_mt)
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
|
@ -17,6 +17,7 @@ local traceback = debug.traceback
|
||||||
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
||||||
local glib = require("lgi").GLib
|
local glib = require("lgi").GLib
|
||||||
local object = require("gears.object")
|
local object = require("gears.object")
|
||||||
|
local protected_call = require("gears.protected_call")
|
||||||
|
|
||||||
--- Timer objects. This type of object is useful when triggering events repeatedly.
|
--- Timer objects. This type of object is useful when triggering events repeatedly.
|
||||||
-- The timer will emit the "timeout" signal every N seconds, N being the timeout
|
-- The timer will emit the "timeout" signal every N seconds, N being the timeout
|
||||||
|
@ -43,11 +44,7 @@ function timer:start()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self.data.source_id = glib.timeout_add(glib.PRIORITY_DEFAULT, self.data.timeout * 1000, function()
|
self.data.source_id = glib.timeout_add(glib.PRIORITY_DEFAULT, self.data.timeout * 1000, function()
|
||||||
xpcall(function()
|
protected_call(self.emit_signal, self, "timeout")
|
||||||
self:emit_signal("timeout")
|
|
||||||
end, function(err)
|
|
||||||
print(debug.traceback("Error during executing timeout handler: "..tostring(err)))
|
|
||||||
end)
|
|
||||||
return true
|
return true
|
||||||
end)
|
end)
|
||||||
self:emit_signal("start")
|
self:emit_signal("start")
|
||||||
|
@ -126,10 +123,8 @@ end
|
||||||
function timer.start_new(timeout, callback)
|
function timer.start_new(timeout, callback)
|
||||||
local t = timer.new({ timeout = timeout })
|
local t = timer.new({ timeout = timeout })
|
||||||
t:connect_signal("timeout", function()
|
t:connect_signal("timeout", function()
|
||||||
local success, cont = xpcall(callback, function(err)
|
local cont = protected_call(callback)
|
||||||
print(debug.traceback("Error during executing timeout handler: "..tostring(err), 2))
|
if not cont then
|
||||||
end)
|
|
||||||
if not success or not cont then
|
|
||||||
t:stop()
|
t:stop()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
@ -160,11 +155,7 @@ end
|
||||||
local delayed_calls = {}
|
local delayed_calls = {}
|
||||||
capi.awesome.connect_signal("refresh", function()
|
capi.awesome.connect_signal("refresh", function()
|
||||||
for _, callback in ipairs(delayed_calls) do
|
for _, callback in ipairs(delayed_calls) do
|
||||||
xpcall(function()
|
protected_call(unpack(callback))
|
||||||
callback[1](unpack(callback, 2))
|
|
||||||
end, function(err)
|
|
||||||
print(debug.traceback("Error during delayed call: "..tostring(err), 2))
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
delayed_calls = {}
|
delayed_calls = {}
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
local matrix = require("gears.matrix")
|
local matrix = require("gears.matrix")
|
||||||
|
local protected_call = require("gears.protected_call")
|
||||||
local cairo = require("lgi").cairo
|
local cairo = require("lgi").cairo
|
||||||
local base = require("wibox.widget.base")
|
local base = require("wibox.widget.base")
|
||||||
local no_parent = base.no_parent_I_know_what_I_am_doing
|
local no_parent = base.no_parent_I_know_what_I_am_doing
|
||||||
|
@ -274,17 +275,10 @@ function hierarchy:draw(context, cr)
|
||||||
local opacity = widget.opacity
|
local opacity = widget.opacity
|
||||||
local function call(func, extra_arg1, extra_arg2)
|
local function call(func, extra_arg1, extra_arg2)
|
||||||
if not func then return end
|
if not func then return end
|
||||||
local function error_function(err)
|
|
||||||
print(debug.traceback("Error while drawing widget: " .. tostring(err), 2))
|
|
||||||
end
|
|
||||||
if not extra_arg2 then
|
if not extra_arg2 then
|
||||||
xpcall(function()
|
protected_call(func, widget, context, cr, self:get_size())
|
||||||
func(widget, context, cr, self:get_size())
|
|
||||||
end, error_function)
|
|
||||||
else
|
else
|
||||||
xpcall(function()
|
protected_call(func, widget, context, extra_arg1, extra_arg2, cr, self:get_size())
|
||||||
func(widget, context, extra_arg1, extra_arg2, cr, self:get_size())
|
|
||||||
end, error_function)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
local object = require("gears.object")
|
local object = require("gears.object")
|
||||||
local cache = require("gears.cache")
|
local cache = require("gears.cache")
|
||||||
local matrix = require("gears.matrix")
|
local matrix = require("gears.matrix")
|
||||||
|
local protected_call = require("gears.protected_call")
|
||||||
local util = require("awful.util")
|
local util = require("awful.util")
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
local pairs = pairs
|
local pairs = pairs
|
||||||
|
@ -144,7 +145,7 @@ local widget_dependencies = setmetatable({}, { __mode = "kv" })
|
||||||
local function get_cache(widget, kind)
|
local function get_cache(widget, kind)
|
||||||
if not widget._widget_caches[kind] then
|
if not widget._widget_caches[kind] then
|
||||||
widget._widget_caches[kind] = cache.new(function(...)
|
widget._widget_caches[kind] = cache.new(function(...)
|
||||||
return widget[kind](widget, ...)
|
return protected_call(widget[kind], widget, ...)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
return widget._widget_caches[kind]
|
return widget._widget_caches[kind]
|
||||||
|
@ -222,9 +223,9 @@ function base.fit_widget(parent, context, widget, width, height)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Apply forced size
|
-- Apply forced size and handle nil's
|
||||||
w = widget._forced_width or w
|
w = widget._forced_width or w or 0
|
||||||
h = widget._forced_height or h
|
h = widget._forced_height or h or 0
|
||||||
|
|
||||||
-- Also sanitize the output.
|
-- Also sanitize the output.
|
||||||
w = math.max(0, math.min(w, width))
|
w = math.max(0, math.min(w, width))
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- @author Uli Schlachter
|
||||||
|
-- @copyright 2016 Uli Schlachter
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local gdebug = require("gears.debug")
|
||||||
|
local protected_call = require("gears.protected_call")
|
||||||
|
|
||||||
|
describe("gears.protected_call", function()
|
||||||
|
-- Stop the error reporting during tests
|
||||||
|
local orig_print_error = gdebug.print_error
|
||||||
|
local errors
|
||||||
|
before_each(function()
|
||||||
|
errors = {}
|
||||||
|
gdebug.print_error = function(err)
|
||||||
|
table.insert(errors, err)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
after_each(function()
|
||||||
|
gdebug.print_error = orig_print_error
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Call with arguments and result", function()
|
||||||
|
local called = false
|
||||||
|
local function f(...)
|
||||||
|
called = true
|
||||||
|
assert.is_same({ ... }, { 1, "second" })
|
||||||
|
return "first", 2
|
||||||
|
end
|
||||||
|
local results = { protected_call(f, 1, "second") }
|
||||||
|
assert.is_true(called)
|
||||||
|
assert.is_same({ "first", 2 }, results)
|
||||||
|
assert.is_same(errors, {})
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("Call with error", function()
|
||||||
|
assert.is_same({}, { protected_call(error, "I was called") })
|
||||||
|
assert.is_same(#errors, 1)
|
||||||
|
assert.is_truthy(string.find(errors[1], "I was called"))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
Loading…
Reference in New Issue