lua source as config file

classic Classic list List threaded Threaded
21 messages Options
12
Reply | Threaded
Open this post in threaded view
|

lua source as config file

gary ng
I am sure this must have come up before but failed to
search it through google etc.

at the moment, my relatively ugly solution is just
this :

dofile("my_config.lua")

where my_config.lua is like this :

my_config={ ... all sorts of embedded table ...}

This however pollute the namespace. What I want is to
do something like this(imaginary):

in my program:

local my_config=dofile("my_config.lua")

where the same table is created but not polluting the
name space.


 
____________________________________________________________________________________
Have a burning question?  
Go to www.Answers.yahoo.com and get answers from real people who know.

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Glenn Maynard
On Thu, Feb 08, 2007 at 12:22:50AM -0800, gary ng wrote:
> I am sure this must have come up before but failed to
> search it through google etc.
> 
> at the moment, my relatively ugly solution is just
> this :
> 
> dofile("my_config.lua")
> 
> where my_config.lua is like this :
> 
> my_config={ ... all sorts of embedded table ...}
> 
> This however pollute the namespace. What I want is to
> do something like this(imaginary):
> 
> in my program:
> 
> local my_config=dofile("my_config.lua")
> 
> where the same table is created but not polluting the
> name space.

That's not imaginary.  Your configuration file just returns
the table:

return {
	config = "var";
};

-- 
Glenn Maynard

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

gary ng
--- Glenn Maynard <[hidden email]> wrote:
> That's not imaginary.  Your configuration file just
> returns
> the table:
> 
> return {
> 	config = "var";
> };
> 
thanks.

thought about that too but the return makes it look
less like a config file.

Oh, I forgot to mention, my dofile approach is also a
bit dangerous too.



 
____________________________________________________________________________________
Do you Yahoo!?
Everyone is raving about the all-new Yahoo! Mail beta.
http://new.mail.yahoo.com

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Glenn Maynard
On Thu, Feb 08, 2007 at 12:34:54AM -0800, gary ng wrote:
> > That's not imaginary.  Your configuration file just
> > returns
> > the table:
> > 
> > return {
> > 	config = "var";
> > };
> > 
> thanks.
> 
> thought about that too but the return makes it look
> less like a config file.

"config = {" seems cosmetically the same as "return {".

(By the way, your mail client is wrapping at something extremely short,
around 60 columns, which is chopping up everyone's posts in your quotes.
Might want to turn that up to a more typical 72 or so.)

-- 
Glenn Maynard

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Mikko Sivulainen
In reply to this post by gary ng
That's not imaginary.  Your configuration file just
returns
the table:

return {
	config = "var";
};

thanks.

thought about that too but the return makes it look
less like a config file.

Oh, I forgot to mention, my dofile approach is also a
bit dangerous too.




What i usually do is something like:

---------
function read_settings(filename)
	local f=loadfile(filename)
	local t={}
	
	setfenv(f,t)
	f()
	return t
end

local settings=read_settings(filename)
--------








--
----------------------------------------------------------
Mikko Sivulainen
Senior Programmer
Bugbear Entertainment Ltd.
e-mail: [hidden email]
phone: +358-40-5272342

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Luiz Henrique de Figueiredo
In reply to this post by gary ng
> but the return makes it look less like a config file.

Drop the return and the table constructor all together and use a custom
dofile, one that adds "return {" at the beginning and "}" at the end.
This will allow (and restrict) config files to be a series assignments.
On the other hand, you'll need commas or semicolons after each assignments;
semicolons seem better in this case.
--lhf

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Glenn Maynard
On Thu, Feb 08, 2007 at 07:46:04AM -0200, Luiz Henrique de Figueiredo wrote:
> > but the return makes it look less like a config file.
> 
> Drop the return and the table constructor all together and use a custom
> dofile, one that adds "return {" at the beginning and "}" at the end.
> This will allow (and restrict) config files to be a series assignments.
> On the other hand, you'll need commas or semicolons after each assignments;
> semicolons seem better in this case.

It doesn't prevent non-config-file-like behavior, like
"x = function() ... end();", and by restricting the syntax and
preventing doing things naturally, it'll tend to encourage doing
things unnaturally.

It also makes it inconvenient to compile the files, which can be useful
(depending on the "config files").  I've witten scripts to process and
compile Lua scripts, and it's much nicer to be able to compile them in-
place.

The environment approach lets a Lua script be a real Lua script while
still allowing simple configuration files to just be a bunch of
assignments, though.  (I'd also __index it to the caller environment,
unless I really wanted a restricted environment.)

-- 
Glenn Maynard

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Luiz Henrique de Figueiredo
> It doesn't prevent non-config-file-like behavior, like
> "x = function() ... end();"

This may be config-file-like behavior, yes, who knows?
Perhaps the app allows the user to set callbacks.

> It also makes it inconvenient to compile the files, which can be useful
> (depending on the "config files").  I've witten scripts to process and
> compile Lua scripts, and it's much nicer to be able to compile them in-
> place.

(echo 'return {'; cat $*; echo '}') | luac -

But, yes, I'm pushing it for no reason :-)
--lhf

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Luiz Henrique de Figueiredo
In reply to this post by Luiz Henrique de Figueiredo
> Maybe something like this then ?

This code is slow because it concats the file line by line.
See http://www.lua.org/pil/11.6.html

A better solution is to read it all at once:

function getconfig( file ) 
  local f = assert(io.open( file ))
  local ret = "return {" .. f:read"*a" .. "}"
  f:close()
  return ret
end

But I mean something with load (untested):

function loadconfig(file)
	local state=0
	local f = assert(io.open( file ))
	load(function()
		if state==0 then
			state=1
			return "return {"
		elseif state==1 then
			local s=f:read()
			if s==nil then state=2 s=}" end
			return s
		else
			return nil
		end
	end)
end

--lhf

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Luiz Henrique de Figueiredo
> But I mean something with load (untested):

Oops:
	assert(load(function()
		...
	end))()

Still untested.
--lhf

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Glenn Maynard
In reply to this post by Luiz Henrique de Figueiredo
On Thu, Feb 08, 2007 at 08:58:07AM -0200, Luiz Henrique de Figueiredo wrote:
> > It doesn't prevent non-config-file-like behavior, like
> > "x = function() ... end();"
Oops.  This should have been "x = (function() ... end)();".
> 
> This may be config-file-like behavior, yes, who knows?
> Perhaps the app allows the user to set callbacks.

I meant that in response to:

> This will allow (and restrict) config files to be a series assignments.

meaning to say that while it does restrict the syntax, it doesn't
restrict the behavior, which just encourages uglier syntax to get
unrestricted syntax back.

> (echo 'return {'; cat $*; echo '}') | luac -
> 
> But, yes, I'm pushing it for no reason :-)

Then you have to special-case compiled Lua source in your app, to not
add this stuff at runtime.  :)

-- 
Glenn Maynard

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Luiz Henrique de Figueiredo
In reply to this post by Luiz Henrique de Figueiredo
> This is a bit tweaked.

Right. Let's just drop all these hacks. Lua is meant to be used as a config
language. It was designed for that. It already supports config files in
the form of series of assignments without ending commas or semicolons. It
supports a whole bunch more. If you need those assignments to go into a
table, setfenv after loadfile does it, as someone has already suggested.

Sorry about the noise. I just thought it was an opportunity to show how load
worked...
--lhf

Reply | Threaded
Open this post in threaded view
|

RE: lua source as config file

Andre Carregal
In reply to this post by gary ng
> Luiz Henrique de Figueiredo wrote:
> ...
> If you need those assignments to go into a table, setfenv after loadfile
> does it, as someone has already suggested.

Or just prepend

module("config")

to the file and then load it. Should be safe enough, no?

> Sorry about the noise. I just thought it was an opportunity 
> to show how load worked...

Ok, shutting down now. :o)

Andre


Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Alex Queiroz
Hallo,

On 2/8/07, Andre Carregal <[hidden email]> wrote:
> Luiz Henrique de Figueiredo wrote:
> ...
> If you need those assignments to go into a table, setfenv after loadfile
> does it, as someone has already suggested.

Or just prepend

module("config")

to the file and then load it. Should be safe enough, no?


    That's how I do it. I do not mind the global "config" table.

--
-alex
http://www.ventonegro.org/

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Zachary P. Landau-4
In reply to this post by Luiz Henrique de Figueiredo
I wish that there was a YAML lib for Lua ;)

There is, in fairly alpha form.  I wrote one for Syck[1] that is currently only
in Syck's repository[2].  It probably has issues,  I haven't visited it in a
while.

If anyone is looking for a small project, you might want to review the code and
see if you can spot any problems.

[1] http://whytheluckystiff.net/syck/
[2] http://code.whytheluckystiff.net/svn/syck/trunk/

--
Zachary P. Landau <[hidden email]>

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Petite Abeille
In reply to this post by Luiz Henrique de Figueiredo

On Feb 08, 2007, at 12:36, George Petsagourakis wrote:

There is no way to parse the following format using your proposed func:

Hmmm... as Mikko Sivulainen mentioned earlier... this is really straightforward using a function environment... perhaps even worth mentioning again :)

assuming a 'config.txt' with the same content as in your example...

--8<--
option1 = "ok"
option2 = true
option3 = 34
--8<--

function config( aPath )
        local aFile, aStatus = io.open( aPath, "rb" )
        local aContent = aFile:read( "*all" )
        local aChunk, aStatus = loadstring( aContent )
        local anEnvironment = {}

        setfenv( aChunk, anEnvironment )
        aChunk()

        return anEnvironment
end

local aConfig = config( "config.txt" )

for aKey, aValue in pairs( aConfig ) do
        print( aKey, aValue )
end

> option3 34
> option2 true
> option1 ok


Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Luiz Henrique de Figueiredo
>          local aFile, aStatus = io.open( aPath, "rb" )
>          local aContent = aFile:read( "*all" )
>          local aChunk, aStatus = loadstring( aContent )

No need to read the file into memory:

           local aChunk, aStatus = loadfile( aPath )

--lhf

Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Petite Abeille

On Feb 08, 2007, at 20:12, Luiz Henrique de Figueiredo wrote:

No need to read the file into memory:

I was wondering if it could be possible to write it as a one liner...

function config( aPath )
        local aChunck = setfenv( loadfile( aPath ), {} )

        return getfenv( aChunck, aChunck() )
end

Thoughts?


Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Flemming Madsen-3
In reply to this post by Mikko Sivulainen


What i usually do is something like:

---------
function read_settings(filename)
    local f=loadfile(filename)
    local t={}
setfenv(f,t)
    f()
    return t
end

local settings=read_settings(filename)
--------


Nice.

I was trying to elaborate on this a little bit to see if I could make sort of a mini DSL for
configuration of a syslogd replacement i am working on.

It should give me the possibility of defining a number of records with settable defaults.

I ended up with a config file like syntax like

#config

defaults [[ rotlog = 5 ]]

config [[
   name = "online.log"
   programs = { "pppd", "chat" }
]]

defaults [[ level = "error" ]]

config [[
   name    = "fatal"
   size    = 200 * k
   pattern = { "^[Ff]atal", "%W[Ff]atal" }
]]


config[[ ]] -- Catch all


The first shot was with table constructors as argument to "defaults" and "config", but inspired by this thread I thought i'd try and make the argument an ordinary lua chunk. The experience was a little mind boggling, trying to figure out how to keep everything in working order along with the required setfenv's. Anyway here is what i came up with, in case it is of use to anyone.

-- Define the default defaults, the resulting table & the sandbox
local logconfigs = {}
local cfg = {
  log_defaults = {
     dir        = "/var/log";
     name       = "messages";
     action     = { file=1 };
     size       = 100 * 1024;
     rotlog     = 1;
     level      = "info";
     facilities = "*";
     pattern    = {};
     programs   = {};
     shortfmt   = 1;
     dobreak    = 0;
  };
  k = 1024;
  m = 1024 * 1024;
  --
  logconfigs = logconfigs;
  loadstring = loadstring;
  setmetatable = setmetatable;
  setfenv = setfenv;
  table_insert = table.insert;
}

-- Cater for the defaults[[...]] section in the config file
function cfg.defaults(s)
  conf_defaults = {k = k, m = m}
  setmetatable(conf_defaults, {__index = log_defaults}) -- Route through
  local chunk = loadstring(s)
  setfenv(chunk, conf_defaults)
  chunk()
  defmeta = { __index = conf_defaults }
end
setfenv(cfg.defaults, cfg)
cfg.defaults'' -- Default is straight through to log_defaults

-- Cater for the config[[...]] sections in the config file
function cfg.config(s)
  local v = {}
  setmetatable(v, defmeta)
  local chunk = loadstring(s)
  setfenv(chunk, v)
  chunk()
  table_insert(logconfigs, v)
end
setfenv(cfg.config, cfg)

-- The sandboxed loader
function cfg.loadconfig(chunk)
  setfenv(chunk, cfg)
  chunk()
end


-- Load config file
cfg.loadconfig(assert(loadfile("/etc/syslogl.conf")))




Reply | Threaded
Open this post in threaded view
|

Re: lua source as config file

Doug Rogers-4
In reply to this post by Petite Abeille
PA wrote:
> function config( aPath )
>         local aChunck = setfenv( loadfile( aPath ), {} )
>         return getfenv( aChunck, aChunck() )
> end
> Thoughts?

I like this. The only drawback is that in operational code you will have
to catch any errors raised by loadfile(). It also makes config()
unusable after such an error...

It looks like setfenv() gets applied to config() when loadfile() fails,
returning nil. So setfenv() is applied to the wrong chunk. The manual
says that setfenv(0, t) sets the running thread's environment. It should
not apply if the first argument is nil.

Below is a transcript. Notice how getfenv(config) changes after the
syntax error. I believe this is bug in getfunc() in lbaselib.c (patch
and description in separate email).

rogers@rogers-~$ cat good.cfg
opt1=4
opt2=true
opt3='test string'
rogers@rogers-~$ cat bad.cfg
opt1=4
this is my song
-- not yours!
rogers@rogers-~$ lua
Lua 5.1.1  Copyright (C) 1994-2006 Lua.org, PUC-Rio
> function config( aPath )
>> local aChunck = setfenv( loadfile( aPath ), {} )
>> return getfenv( aChunck, aChunck() )
>> end
> s=config'good.cfg'
> for k,v in pairs(s) do print(k,v) end
opt3    test string
opt1    4
opt2    true
> print(s, config, getfenv(config), loadfile, _G)
table: 0x807efe8        function: 0x807f428     table: 0x806a450
function: 0x806b188     table: 0x806a450
> s=config'bad.cfg'
stdin:2: attempt to call global 'loadfile' (a nil value)
stack traceback:
        stdin:2: in function 'aChunck'
        stdin:3: in function 'config'
        stdin:1: in main chunk
        [C]: ?
> print(s, config, getfenv(config), loadfile, _G)
table: 0x807efe8        function: 0x807f428     table: 0x807e2e0
function: 0x806b188     table: 0x806a450
> s=config'good.cfg'
stdin:2: attempt to call global 'loadfile' (a nil value)
stack traceback:
        stdin:2: in function 'config'
        stdin:1: in main chunk
        [C]: ?
>

I've hacked the source quite a bit and I'm still not really sure why
this happens. It seems that lapi.c and lvm.c have the proper safeguards.
luaB_setfenv() checks lua_isnumber() on its first argument before
checking if it is zero.

If I do something clever like putting loadfile into the environment,
then it cannot find setfenv:

> t=getfenv(config)
> t.loadfile = loadfile
> s=config'/home/rogers/good.cfg'
stdin:2: attempt to call global 'setfenv' (a nil value)
stack traceback:
        stdin:2: in function 'config'
        stdin:1: in main chunk
        [C]: ?
>

It looks like getfunc() uses luaL_optint(L, 1, 1) when nil is at index
1, resulting in 1 for the level (default). I believe the code should
check for a numeric type first. I'll place a patch in a separate email.

Doug

-- 
Innovative Concepts, Inc. www.innocon.com 703-893-2007 x220

12