Depending if it errors are raised or not, pcall(function) may return:
Success: true and the return value[s] of the function.
Failure: false and the error.
In my case I'm calling a function to return a table, so in case of no errors I will get my data from the second return value, and in case of error I will print manage the error.
How can I do it with assert?
At first I wrote this:
local ret, data = pcall(the_function)
assert(ret, "Error: "..data)
-- use data from here on.
The problem is that the assert message is evaluated even in case of success, so when the call succeeds Lua complains about concatenating a string with a table.
This problem is due to the fact that I want to use assert and cite the error, but avoiding using something like if not ret then assert(false, "...") end.
Try this:
local ret, data = assert(pcall(the_function))
If you don't need to alter the error message from pcall lhf's suggestion is best.
Otherwise a solution is:
local ret, data = pcall( the_function )
assert( ret, type( data ) == 'string' and "Error: " .. data )
or this one, which is a cleaner approach:
local ret, data = pcall( the_function )
if not ret then error( "Error: " .. data ) end
this latter avoids completely to evaluate the error message expression if pcall doesn't give an error.
Related
I'm putting some code into a module so I can draw and maintain multiple copies. I'm getting this common error but I can't see why. I understand what it's saying to a basic level, but as I'm able to see a print out from the table being created, I don't understand why calling a function that module contains would throw this error.
I've read through all the answers on SO, but I'm still at a loss. I've tried printing out at various stages to see where the issue is, everything works as if I had created an instance of the module, but the error persists.
Code below is cleaned of extraneous stuff.
local orbitalCircle = include('lib/orbital_circle')
function init()
c1 = orbitalCircle.new(20, 42, 18, 1.7, 16, 62, 15, c1Sequence)
<-- at this point print code from the module's init function works
c1:doFunc(param) <-- this will call the error
The module:
local Orbital_Circle = {}
-- set up variables
local some Vars Are here
function Orbital_Circle.new(x, y, diameter, scale_factor, number_of_notes, beats_per_second, frames_per_second, sequence_data)
print("Orbital_Circle running")
end
function Orbital_Circle:doFunc(param)
self.var = param <-- I update a local var here
print("self.var") <-- I then print the updated number for sanity checking
end
return Orbital_Circle
I expect the var in my instance of this module to update and the functions code to run, but... no joy. I get the error.
Cheers.
I'm putting some code into a module so I can draw and maintain multiple copies.
I think there's a bit of a misunderstanding about how Lua modules work here. It's an easy mistake to make.
When you require a module in Lua, each subsequent require of the same file refers to the same code. So (eg) these two variables contain exactly the same code:
local orbitalCircle1 = require('lib/orbital_circle')
local orbitalCircle2 = require('lib/orbital_circle')
Which means that you can't use Lua modules by themselves to create OOP type objects as you are trying to do. Your new function must return something that can be used like an instance of a class, a unique table for each call:
local Orbital_Circle = {}
local shared_variable = 1
function Orbital_Circle.new(x, y)
-- create unique table
local obj = {}
-- access these from table/object methods with self.xxx
obj.x = x or 0
obj.y = y or 0
obj.var = "initial value"
-- now define functions with an explicit 'self' parameter...
function obj.doFunc(self, param)
self.var = self.var .. " " .. param
shared_variable = shared_variable + 1
end
-- ... or with the syntactic 'self' sugar, ':'
function obj:printVars()
print("self.var = " .. self.var)
print("shared_variable = " .. shared_variable)
print("self.x = " .. self.x)
end
return obj
end
return Orbital_Circle
You can also define the methods as local functions outside the new function that have self parameter and have a list of entries such as:
obj.anotherMethod = functionDeclaredAtTopOfFile
… to keep things tidier, if you like.
Your code is completely messed up.
<-- will cause an error for unexpected symbol.
c1 = orbitalCircle.new(20, 42, 18, 1.7, 16, 62, 15, c1Sequence)
will give you an error for indexing a global nil value c1 because orbitalCircle.new has no return value.
your init function is incomplete and you don't call it so the provided code does not do anything even if you fix the above errors.
The reported error is not caused by any line of code you provided here.
Code below is cleaned of extraneous stuff.
I'm afraid you removed too much.
The error message tells you that you're indexing local n, a nil value from within a local function that has been defined in n's scope.
This code for example:
local n
function test()
local b = n.a
end
test()
would result in the error message:
input:3: attempt to index a nil value (upvalue 'n')
n is an upvalue for test because it is a local variable defined outside the functions body, but not a global variable.
My code (psuedo)
function foo(cmd)
return load(cmd) --Note that this could cause an error, should 'cmd' be an invalid command
end
function moo()
return "moo"
end
function yoo(something)
something.do()
end
cmd = io.read() --Note that a syntax error will call an error in load. Therefore, I use pcall()
local result, error = pcall(cmd)
print(result)
This code looks okay, and works, but my problem is if I type in moo() then result will only show whether or not the command was executed without an error (If the command calls an error, error will have its value).
On another note, if I want to call yoo(), I won't get a return value from it, so I want pcall()'s true / false (or any alternate means other than pcall())
Is there an alternate way to call moo(), get a return value, and also be able to catch any errors?
NOTE: I couldn't find any try catch equivalent other then pcall / xpcall.
A bit outdated but still without proper answer...
What you are looking for is a combination of both load() and pcall()
Use load()to compile the entered string cmd into something that can be executed (function).
Use pcall() to execute the function returned by load()
Both functions can return error messages. Get syntax error description from load() and runtime error description from pcall()
function moo()
return "moo"
end
function yoo(something)
something.do_something()
end
cmd = io.read()
-- we got some command in the form of a string
-- to be able to execute it, we have to compile (load) it first
local cmd_fn, err = load("return "..cmd);
if not cmd_fn then
-- there was a syntax error
print("Syntax error: "..err);
else
-- we have now compiled cmd in the form of function cmd_fn(), so we can try to execute it
local ok, result_or_error = pcall(cmd_fn)
if ok then
-- the code was executed successfully, print the returned value
print("Result: "..tostring(result_or_error));
else
-- runtime error, print error description
print("Run error: "..result_or_error)
end
end
I have a very simple problem with a simple iterator.
Let's say that I design a function, files(), that iterates over all the files in a folder:
for file in files("/path/to/folder") do
print(file)
end
Now, this seems perfect, but there's a problem here: What if the the folder doesn't exist, or we don't have read permission to it?
How would we indicate such error?
One solution would be to have files() return nil, "no read permission" in this case. We'd then be able to wrap the call to files() inside assert():
for file in assert(files("/path/to/folder")) do
print(file)
end
This seemingly solves the problem. But this forces our users to always use assert(). What if the user doesn't care about errors? For this kind of users we'd want our files() to behave as if the folder is empty. But Lua --in case files() indicates error-- would try to call the returned nil and this will result in an error ("attempt to call a nil value").
So,
How can we design an iterator, files(), that would cater to both users that care about errors and users that don't?
If it's not possible, what alternative would you suggest?
First: Instead of returning nil + error message consider raising an error in the files function (using error). This way you can't forget the assert call, and you won't get the confusing "attempt to call a nil value" error.
You could pass an extra boolean parameter to files when you don't want to raise errors -- you should return an empty function (function() end) instead of calling error in this case.
A more general approach is the following:
-- an iterator that immediately stops a for loop
local function dummy_iter() end
-- catch errors and skip for loop in that case
function iterpcall( g, ... )
local ok, f, st, var = pcall( g, ... )
if ok then
return f, st, var
else
return dummy_iter
end
end
for file in iterpcall( files, "/path/to/folder" ) do
print( file )
for line in iterpcall( io.lines, file ) do -- works for other iterators as well
print( line )
end
end
The implementation of iterpcall above only handles errors raised in the iterator generator (files or io.lines), not in the iterator function (f) itself. You would have to wrap f in a closure with a pcall to do that.
There also question what you whant to do if you get error in the middle of iteration (e.g. access deny for subfolder with recurcive iteration). In this case assert does not help.
In this case I create 2 variant of iterators (inner and outer).
-- raise error
for file in files(...) do ... end
-- return error
files(...,function(file) ... end)
Or just create 2 different iterators.
I am sure I am missing something ridiculously simple, but I stared at this long enough to ask for your help. I am trying to write the simplest of all loggers, and when I'm trying to run it, it's not seeing the global variable (that I (think) I declare!). I am an "advanced" beginner in lua, so silly mistakes are not as common, but still do happen in my case. Please, tell me what I'm missing here!
Here's the logger code, and I'm testing it within the script....
local type, pairs, table, tostring, io, os, print = type, pairs, table, tostring, io, os, print
local file
LEVELS = {}
LEVELS ["ERROR"] = 0
LEVELS ["WARN"] = 1
LEVELS ["INFO"] = 2
LEVELS ["DEBUG"] = 3
print (LEVELS.WARN)
local level
function init (outputFile, debugLevel)
level = debugLevel
file = io.open(outputFile, "a")
local ret
if (file == nil) then ret = false else ret = true end
return ret
end
function warn (message)
if level ~= nil and level >= LEVELS.WARN and file ~= nil then
log(message)
end
end
function log (message)
local timestamp = os.date()
file:write(timestamp..' - '..message..'\n')
file:flush()
end
function deInit ()
file:close()
end
init ("testInsideLogger.lua", 3)
warn("HI")
deInit()
What I get in the output:
Print right after the declaration of LEVELS works, prints out 1.
But when I call the warn("HI") method, I get
attempt to index global 'LEVELS' (a nil value).
WHY???
Thank you so much for your help!
dofile("x/y/m.lua")
dofile("x/y/p.lua")
if m.lua fails due to some issue , p.lua will not run at all, please give me some resolution that i can run both files even if the first one fails and have logs for both
Try
function dofile(name)
local f,err=loadfile(name)
if f==nil then print(err) end
local ok,err=pcall(f)
if not ok then print(err) end
end
Use pcall to catch Lua errors, possibly like this:
local success, result = pcall(dofile, "foo.lua")
If success is false, the function failed and the error message will be in result. If success is true, the return values of dofile will be in result. You can add additional result variables. For example:
local success, result1, result2, result3 = pcall(dofile, "foo.lua")