← Back to team overview

yade-dev team mailing list archive

[Branch ~yade-dev/yade/trunk] Rev 1871: 1. Refactor parameter table support (one reader class)

 

------------------------------------------------------------
revno: 1871
committer: Václav Šmilauer <eudoxos@xxxxxxxx>
branch nick: trunk
timestamp: Tue 2009-12-08 13:08:48 +0100
message:
  1. Refactor parameter table support (one reader class)
  2. Fix yade-multi, add detection of regular exit with log4cxx crash at exit
  3. Add --nice option to main.py
  4. Make O.exitNoBacktrace output either "Yade: normal exit." (magic for OK exit, detected by yade-multi for instance) or "Yade: error exit".
added:
  scripts/test/batch/
  scripts/test/batch/params.table
  scripts/test/batch/sim.py
renamed:
  gui/py/yade-multi => core/main/yade-multi.in
modified:
  core/SConscript
  core/main/main.py.in
  gui/SConscript
  py/system.py
  py/utils.py
  py/yadeWrapper/yadeWrapper.cpp
  core/main/yade-multi.in


--
lp:yade
https://code.launchpad.net/~yade-dev/yade/trunk

Your team Yade developers is subscribed to branch lp:yade.
To unsubscribe from this branch go to https://code.launchpad.net/~yade-dev/yade/trunk/+edit-subscription.
=== modified file 'core/SConscript'
--- core/SConscript	2009-12-05 10:45:14 +0000
+++ core/SConscript	2009-12-08 12:08:48 +0000
@@ -2,10 +2,14 @@
 Import('*')
 
 pyMain='$PREFIX/bin/yade$SUFFIX'
-target=env.ScanReplace('main/main.py.in')
-env.AlwaysBuild(target)
-env.InstallAs(pyMain,target)
+main=env.ScanReplace('main/main.py.in')
+multi=env.ScanReplace('main/yade-multi.in')
+env.AlwaysBuild(main)
+env.AlwaysBuild(multi)
+env.InstallAs(pyMain,main)
+env.InstallAs(pyMain+'-multi',multi)
 env.AddPostAction(pyMain,Chmod(pyMain,0755))
+env.AddPostAction(pyMain+'-multi',Chmod(pyMain+'-multi',0755))
 
 env.Install('$PREFIX/lib/yade$SUFFIX/py/yade',[
 	env.SharedLibrary('boot',['main/pyboot.cpp'],SHLIBPREFIX='',LIBS=env['LIBS']+['yade-support','core'])

=== modified file 'core/main/main.py.in'
--- core/main/main.py.in	2009-12-06 17:18:16 +0000
+++ core/main/main.py.in	2009-12-08 12:08:48 +0000
@@ -13,7 +13,8 @@
 import optparse
 par=optparse.OptionParser(usage='%prog [options] [ simulation.xml[.bz2] | script.py [script options]]',prog='Yade',version='%s (%s)'%(version,','.join(features)))
 par.add_option('-j','--threads',help='Number of OpenMP threads to run; defaults to number of cores.',dest='threads',type='int')
-par.add_option('-x',help='Exit when the script finishes',dest='exitAfter')
+par.add_option('--nice',help='Increase nice level (i.e. decrease priority) by given number.',dest='nice',type='int')
+par.add_option('-x',help='Exit when the script finishes',dest='exitAfter',action='store_true')
 if 'log4cxx' in features:
 	par.add_option('-v',help='Increase logging verbosity; first occurence sets default logging level to info, second to debug, third to trace.',action='count',dest='verbosity')
 if debug:
@@ -38,6 +39,8 @@
 
 if opts.threads:
 	os.environ['OMP_NUM_THREADS']=str(opts.threads)
+if opts.nice:
+	os.nice(opts.nice)
 if yade.config.debug and opts.noGdb:
 	yade.wrapper.Omega().disableGdb()
 if 'log4cxx' in yade.config.features and opts.verbosity:

=== renamed file 'gui/py/yade-multi' => 'core/main/yade-multi.in'
--- gui/py/yade-multi	2009-11-04 21:54:10 +0000
+++ core/main/yade-multi.in	2009-12-08 12:08:48 +0000
@@ -5,6 +5,14 @@
 
 import os, sys, thread, time, logging, pipes
 
+## replaced by scons automatically
+prefix,suffix='${PREFIX}' if not os.environ.has_key('YADE_PREFIX') else os.environ('YADE_PREFIX'),'${SUFFIX}'
+sys.path.append(os.path.join(prefix,'lib','yade'+suffix,'py'))
+executable=os.path.join(prefix,'bin','yade'+suffix)
+## we just need this ...
+import yade.utils, yade.config
+
+
 class JobInfo():
 	def __init__(self,num,id,command,log,nSlots):
 		self.started,self.finished,self.duration,self.exitStatus=None,None,None,None
@@ -116,6 +124,7 @@
 	job.started=time.time();
 	print '#%d (%s%s) started on %s'%(job.num,job.id,'' if job.nSlots==1 else '/%d'%job.nSlots,time.asctime())
 	job.exitStatus=os.system(job.command)
+	if job.exitStatus!=0 and len([l for l in open(job.log) if l.startswith('Yade: normal exit.')])>0: job.exitStatus=0
 	job.finished=time.time()
 	dt=job.finished-job.started;
 	job.duration=t2hhmmss(dt)
@@ -159,7 +168,7 @@
 parser.add_option('--global-log',dest='globalLog',help='Filename where to redirect output of yade-multi itself (as opposed to --log); if not specified (default), stdout/stderr are used',metavar='FILE')
 parser.add_option('-l','--lines',dest='lineList',help='Lines of TABLE to use, in the format 2,3-5,8,11-13 (default: all available lines in TABLE)',metavar='LIST')
 parser.add_option('--nice',dest='nice',type='int',help='Nice value of spawned jobs (default: 10)',default=10)
-parser.add_option('--executable',dest='executable',help='Name of the program to run (default: %s)'%sys.argv[0][:-6],default=sys.argv[0][:-6],metavar='FILE') ## strip the '-multi' extension
+parser.add_option('--executable',dest='executable',help='Name of the program to run (default: %s)'%executable,default=executable,metavar='FILE')
 parser.add_option('--gnuplot',dest='gnuplotOut',help='Gnuplot file where gnuplot from all jobs should be put together',default=None,metavar='FILE')
 parser.add_option('--dry-run',action='store_true',dest='dryRun',help='Do not actually run (useful for getting gnuplot only, for instance)',default=False)
 parser.add_option('--http-wait',action='store_true',dest='httpWait',help='Do not quit if still serving overview over http repeatedly',default=False)
@@ -180,18 +189,9 @@
 
 print "Will run `%s' on `%s' with nice value %d, output redirected to `%s', %d jobs at a time."%(executable,simul,nice,logFormat,maxJobs)
 
-ll=[re.sub('\s*#.*','',l) for l in ['']+open(table,'r').readlines()] # remove comments
-availableLines=[i for i in range(len(ll)) if not re.match(r'^\s*(#.*)?$',ll[i][:-1])]
-# first non-blank line are column headings
-headings=ll[availableLines[0]].split()
-availableLines=availableLines[1:]
-# read actual data
-values={}
-for l in availableLines:
-	val={}
-	for i in range(len(headings)):
-		val[i]=ll[l].split()[i]
-	values[l]=val
+reader=yade.utils.TableParamReader(table)
+params=reader.paramDict()
+availableLines=params.keys()
 
 print "Will use table `%s', with available lines"%(table),', '.join([str(i) for i in availableLines])+'.'
 
@@ -205,55 +205,30 @@
 		return ret
 	useLines0=numRange2List(lineList)
 	for l in useLines0:
-		if l not in availableLines: logging.warn('skipping unavailable line %d that was requested from the command line.'%l)
-		elif l==1: logging.warn("WARNING: skipping line 1 that should contain variable labels")
+		if l not in availableLines: logging.warn('Skipping unavailable line %d that was requested from the command line.'%l)
 		else: useLines+=[l]
 else: useLines=availableLines
-try:
-	idColumn=headings.index('description')
-	idStrings={}
-	for i in useLines: idStrings[i]=values[i][idColumn] # textual descripion of respective lines 
-	print "Will use lines ",', '.join([str(i)+' (%s)'%idStrings[i] for i in useLines])+'.'
-	#print idStrings
-except ValueError:
-	# try to get idStrings from columns with trailing !
-	bangCols=[i for i,h in enumerate(headings) if h[-1]=='!']
-	if len(bangCols)==0:
-		print 'WARN: no headings with trailing !, will use all of them for description'
-		bangCols=range(len(headings))
-	for i in range(len(headings)):
-		if headings[i][-1]=='!': headings[i]=headings[i][:-1]
-	idStrings={}
-	for i in useLines:
-		newIdBase=','.join( headings[col]+'='+('%g'%values[i][col] if isinstance(values[i][col],float) else str(values[i][col])) for col in bangCols)
-		newId=newIdBase; j=1
-		while newId in idStrings.values():
-			newId=newIdBase+'_%d_'%j; j+=1
-		idStrings[i]=newId.replace("'",'').replace('"','')
-	print "Will use lines ",', '.join([str(i) for i in useLines])+'.'
-
+print "Will use lines ",', '.join([str(i)+' (%s)'%params[i]['description'] for i in useLines])+'.'
 
 jobs=[]
 for i,l in enumerate(useLines):
 	logFile=logFormat.replace('%',str(l))
-	if idStrings: logFile=logFile.replace('@',idStrings[l])
-	else: logFile=logFile.replace('@',str(l))
-	logFile=logFile.replace('!','')
+	logFile=logFile.replace('@',params[l]['description'])
 	envVars=[]
 	nSlots=opts.defaultThreads
-	for col,head in enumerate(headings):
-		if head=='!EXEC': executable=values[l][col]
-		if head=='!OMP_NUM_THREADS':
-			nSlots=int(values[l][col]); maxCpu=getNumCores()
-			continue
-		if head[0]=='!': envVars+=['%s=%s'%(head[1:],values[l][col])]
+	for col in params[l].keys():
+		if col[0]!='!': continue
+		if col=='!OMP_NUM_THREADS':
+			nSlots=int(params[l][col]); maxCpu=getNumCores()
+		elif col=='!EXEC': executable=params[l][col]
+		else: envVars+=['%s=%s'%(head[1:],values[l][col])]
 	if nSlots>maxJobs:
 		if opts.forceThreads:
 			logging.info('Forcing job #%d to use only %d slots (max available) instead of %d requested'%(i,maxJobs,nSlots))
 			nSlots=maxJobs
 		else:
 			logging.warning('WARNING: job #%d will use %d slots but only %d are available'%(i,nSlots,maxJobs))
-	jobs.append(JobInfo(i,idStrings[l] if idStrings else '#'+str(i),'PARAM_TABLE=%s:%d OMP_NUM_THREADS=%d %s nice -n %d %s -N PythonUI -- -n -x %s > %s 2>&1'%(table,l,nSlots,' '.join(envVars),nice,executable,simul,pipes.quote(logFile)),logFile,nSlots))
+	jobs.append(JobInfo(i,params[l]['description'],'PARAM_TABLE=%s:%d DISPLAY= %s %s --threads=%d %s -x %s > %s 2>&1'%(table,l,' '.join(envVars),executable,int(nSlots),'--nice=%s'%nice if nice!=None else '',simul,pipes.quote(logFile)),logFile,nSlots))
 
 print "Job summary:"
 for job in jobs:
@@ -309,3 +284,5 @@
 	print "(continue serving http until no longer requested  as per --http-wait)"
 	while time.time()-httpLastServe<10:
 		time.sleep(1)
+
+yade.Omega().exitNoBacktrace()

=== modified file 'gui/SConscript'
--- gui/SConscript	2009-12-04 21:56:59 +0000
+++ gui/SConscript	2009-12-08 12:08:48 +0000
@@ -45,7 +45,7 @@
 	env.SharedLibrary('PythonUI',['py/PythonUI.cpp'],LIBS=env['LIBS']+linkPlugins([])),
 	env.File('PythonUI_rc.py','py'),
 ])
-env.InstallAs('$PREFIX/bin/yade$SUFFIX-multi',env.File('yade-multi','py'))
+# env.InstallAs('$PREFIX/bin/yade$SUFFIX-multi',env.File('yade-multi','py'))
 
 # these modules will not be imported directly (probably)
 # the rest was moved to py/ directory

=== modified file 'py/system.py'
--- py/system.py	2009-12-04 23:46:45 +0000
+++ py/system.py	2009-12-08 12:08:48 +0000
@@ -8,6 +8,7 @@
 from yade import wrapper
 from yade._customConverters import *
 from yade import runtime
+from yade import config
 O=wrapper.Omega()
 
 def childClasses(base):
@@ -93,15 +94,12 @@
 	return proxyNamespace
 
 def setExitHandlers():
-	"""Set exit handler to avoid gdb run if log4cxx crashes at exit.
-	Remove excepthook."""
-	#import sys
-	# python2.4 workaround (so that quit() works as it does in 2.5)
-	if not callable(__builtins__['quit']):
-		def _quit(): import sys; sys.exit(0)
-		__builtins__['quit']=_quit
+	"""Set exit handler to avoid gdb run if log4cxx crashes at exit."""
 	# avoid backtrace at regular exit, even if we crash
-	sys.exit=wrapper.Omega().exitNoBacktrace
+	if 'log4cxx' in config.features:
+		__builtins__['quit']=wrapper.Omega().exitNoBacktrace
+		sys.exit=wrapper.Omega().exitNoBacktrace
+	# this seems to be not needed anymore:
 	#sys.excepthook=sys.__excepthook__ # apport on ubuntu overrides this, we don't need it
 
 def runServers():

=== modified file 'py/utils.py'
--- py/utils.py	2009-12-06 17:18:16 +0000
+++ py/utils.py	2009-12-08 12:08:48 +0000
@@ -362,83 +362,6 @@
 	if tmpDir: shutil.rmtree(tmpDir)
 
 
-def readParamsFromTable(tableFileLine=None,noTableOk=False,unknownOk=False,**kw):
-	"""
-	Read parameters from a file and assign them to __builtin__ variables.
-
-	tableFile is a text file (with one value per blank-separated columns)
-	tableLine is number of line where to get the values from
-
-		The format of the file is as follows (commens starting with # and empty lines allowed)
-		
-		# commented lines allowed anywhere
-		name1 name2 … # first non-blank line are column headings
-		val1  val2  … # 1st parameter set
-		val2  val2  … # 2nd 
-		…
-
-	The name `description' is special and is assigned to Omega().tags['description']
-
-	assigns Omega().tags['params']="name1=val1,name2=val2,…"
-	
-	assigns Omega().tags['defaultParams']="unassignedName1=defaultValue1,…"
-
-	saves all parameters (default as well as settable) using saveVars('table')
-
-	return value is the number of assigned parameters.
-	"""
-	o=Omega()
-	tagsParams=[]
-	dictDefaults,dictParams={},{}
-	import os, __builtin__,re
-	if not tableFileLine and not os.environ.has_key('PARAM_TABLE'):
-		if not noTableOk: raise EnvironmentError("PARAM_TABLE is not defined in the environment")
-		o.tags['line']='l!'
-	else:
-		if not tableFileLine: tableFileLine=os.environ['PARAM_TABLE']
-		env=tableFileLine.split(':')
-		tableDesc=None
-		tableFile,tableLine=env[0],env[1]
-		if len(env)>2: tableDesc=env[3]
-		o.tags['line']='l'+tableLine
-		# the empty '#' line to make line number 1-based
-		ll=[l for l in ['#']+open(tableFile).readlines()]; values=ll[int(tableLine)][:-1].split('#')[0].split()
-		for i in range(0,len(values)):
-			lineNo=int(tableLine)-1
-			while values[i]=='=':
-				values[i]=ll[lineNo].split('#')[0].split()[i]
-				if lineNo==0: raise RuntimeError("the = specifier doesn't refer to a value at some previous line.")
-				lineNo-=1
-		names=None
-		for l in ll:
-			if not re.match(r'^\s*(#.*)?$',l): names=l.split(); break
-		if not names: raise RuntimeError("No non-blank line (colum headings) found.");
-		assert(len(names)==len(values))
-		if 'description' in names: O.tags['description']=values[names.index('description')]
-		else:
-			bangCols=[i for i,h in enumerate(names) if h[-1]=='!']
-			if len(bangCols)==0: bangCols=range(len(names))
-			for i in range(len(names)):
-				if names[i][-1]=='!': names[i]=names[i][:-1] # strip trailing !
-			O.tags['description']=','.join(names[col]+'='+('%g'%values[col] if isinstance(values[col],float) else str(values[col])) for col in bangCols).replace("'",'').replace('"','')
-		for i in range(len(names)):
-			if names[i]=='description': continue
-			if names[i] not in kw.keys():
-				if (not unknownOk) and names[i][0]!='!': raise NameError("Parameter `%s' has no default value assigned"%names[i])
-			else:
-				if values[i]!='*': kw.pop(names[i])
-				else: continue
-			if names[i][0]!='!':
-				exec('%s=%s'%(names[i],values[i])) in __builtins__; tagsParams+=['%s=%s'%(names[i],values[i])]; dictParams[names[i]]=values[i]
-	defaults=[]
-	for k in kw.keys():
-		exec("%s=%s"%(k,repr(kw[k]))) in __builtins__
-		defaults+=["%s=%s"%(k,kw[k])]; dictDefaults[k]=kw[k]
-	o.tags['defaultParams']=",".join(defaults)
-	o.tags['params']=",".join(tagsParams)
-	dictParams.update(dictDefaults); saveVars('table',**dictParams)
-	return len(tagsParams)
-
 def replaceCollider(colliderEngine):
 	"""Replaces collider (Collider) engine with the engine supplied. Raises error if no collider is in engines."""
 	colliderIdx=-1
@@ -522,3 +445,126 @@
 	_deprecatedUtilsFunction(func.__name__,'yade.import.gengeo')
 	import yade.ymport
 	return yade.ymport.gengeo(*args,**kw)
+
+
+class TableParamReader():
+	def __init__(self,file):
+		import re
+		"Setup the reader class, read data into memory."
+		# read file in memory, remove newlines and comments; the [''] makes lines 1-indexed
+		ll=[re.sub('\s*#.*','',l[:-1]) for l in ['']+open(file,'r').readlines()]
+		# usable lines are those that contain something else than just spaces
+		usableLines=[i for i in range(len(ll)) if not re.match(r'^\s*(#.*)?$',ll[i][:-1])]
+		headings=ll[usableLines[0]].split()
+		# use all values of which heading has ! after its name to build up the description string
+		# if there are none, use all columns
+		if not 'description' in headings:
+			bangHeads=[h[:-1] for h in headings if h[-1]=='!'] or headings
+			headings=[(h[:-1] if h[-1]=='!' else h) for h in headings]
+		usableLines=usableLines[1:] # and remove headinds from usableLines
+		values={}
+		for l in usableLines:
+			val={}
+			for i in range(len(headings)):
+				val[headings[i]]=ll[l].split()[i]
+			values[l]=val
+		lines=values.keys(); lines.sort()
+		# replace '=' by previous value of the parameter
+		for i,l in enumerate(lines):
+			for j in values[l].keys():
+				if values[l][j]=='=':
+					try:
+						values[l][j]=values[lines[i-1]][j]
+					except IndexError,KeyError:
+						raise RuntimeError("The = specifier on line %d refers to nonexistent value on previous line?"%l)
+		#import pprint; pprint.pprint(headings); pprint.pprint(values)
+		# add descriptions, but if they repeat, append line number as well
+		if not 'description' in headings:
+			descs=set()
+			for l in lines:
+				dd=','.join(head.replace('!','')+'='+('%g'%values[head] if isinstance(values[l][head],float) else str(values[l][head])) for head in bangHeads).replace("'",'').replace('"','')
+				if dd in descs: dd+='__line=%d__'%l
+				values[l]['description']=dd
+				descs.add(dd)
+		self.values=values
+				
+	def paramDict(self):
+		return self.values
+		
+if __name__=="__main__":
+	tryTable="""head1 important2! !OMP_NUM_THREADS! abcd
+	1 1.1 1.2 1.3
+	'a' 'b' 'c' 'd'  ### comment
+	
+	# empty line
+	1 = = g
+"""
+	file='/tmp/try-tbl.txt'
+	f=open(file,'w')
+	f.write(tryTable)
+	f.close()
+	from pprint import *
+	pprint(TableParamReader(file).paramDict())
+	
+def readParamsFromTable(tableFileLine=None,noTableOk=False,unknownOk=False,**kw):
+	"""
+	Read parameters from a file and assign them to __builtin__ variables.
+
+	tableFile is a text file (with one value per blank-separated columns)
+	tableLine is number of line where to get the values from
+
+		The format of the file is as follows (commens starting with # and empty lines allowed)
+		
+		# commented lines allowed anywhere
+		name1 name2 … # first non-blank line are column headings
+					# empty line is OK, with or without comment
+		val1  val2  … # 1st parameter set
+		val2  val2  … # 2nd 
+		…
+
+	The name `description' is special and is assigned to Omega().tags['description']
+
+	assigns Omega().tags['params']="name1=val1,name2=val2,…"
+	
+	assigns Omega().tags['defaultParams']="unassignedName1=defaultValue1,…"
+
+	saves all parameters (default as well as settable) using saveVars('table')
+
+	return value is the number of assigned parameters.
+	"""
+	tagsParams=[]
+	dictDefaults,dictParams={},{}
+	import os, __builtin__,re
+	if not tableFileLine and not os.environ.has_key('PARAM_TABLE'):
+		if not noTableOk: raise EnvironmentError("PARAM_TABLE is not defined in the environment")
+		o.tags['line']='l!'
+	else:
+		if not tableFileLine: tableFileLine=os.environ['PARAM_TABLE']
+		env=tableFileLine.split(':')
+		tableFile,tableLine=env[0],int(env[1])
+		allTab=TableParamReader(tableFile).paramDict()
+		if not allTab.has_key(tableLine): raise RuntimeError("Table %s doesn't contain valid line number %d"%(tableFile,tableLine))
+		vv=allTab[tableLine]
+		O.tags['line']='l%d'%tableLine
+		O.tags['description']=vv['description']
+		# assign values specified in the table to python vars
+		# !something cols are skipped, those are env vars we don't treat at all (they are contained in description, though)
+		for col in vv.keys():
+			if col=='description' or col[0]=='!': continue
+			if col not in kw.keys():
+				if (not unknownOk) and col[0]!='!': raise NameError("Parameter `%s' has no default value assigned"%names[i])
+			else:
+				continue # skipping unknown var
+			if vv[col]=='*': vv[col]=kw[col] # use default value for * in the table
+			else: kw.pop(col) # remove the var from kw, so that it contains only those that were default at the end of this loop
+			exec('%s=%s'%(col,vv[col])) in __builtins__; tagsParams+=['%s=%s'%(col,vv[col])]; dictParams[col]=vv[col]
+	# assign remaining (default) keys to python vars
+	defaults=[]
+	for k in kw.keys():
+		exec("%s=%s"%(k,repr(kw[k]))) in __builtins__
+		defaults+=["%s=%s"%(k,kw[k])]; dictDefaults[k]=kw[k]
+	O.tags['defaultParams']=",".join(defaults)
+	O.tags['params']=",".join(tagsParams)
+	dictParams.update(dictDefaults); saveVars('table',**dictParams)
+	return len(tagsParams)
+

=== modified file 'py/yadeWrapper/yadeWrapper.cpp'
--- py/yadeWrapper/yadeWrapper.cpp	2009-12-04 23:46:45 +0000
+++ py/yadeWrapper/yadeWrapper.cpp	2009-12-08 12:08:48 +0000
@@ -290,7 +290,8 @@
 		int len(){ return (int)rb->materials.size(); }
 };
 
-void termHandler(int sig){cerr<<"terminating..."<<endl; raise(SIGTERM);}
+void termHandlerNormal(int sig){cerr<<"Yade: normal exit."<<endl; raise(SIGTERM);}
+void termHandlerError(int sig){cerr<<"Yade: error exit."<<endl; raise(SIGTERM);}
 
 class pyOmega{
 	private:
@@ -536,7 +537,8 @@
 		signal(SIGABRT,SIG_DFL);
 	}
 	void exitNoBacktrace(int status=0){
-		signal(SIGSEGV,termHandler); /* unset the handler that runs gdb and prints backtrace */
+		if(status==0) signal(SIGSEGV,termHandlerNormal); /* unset the handler that runs gdb and prints backtrace */
+		else signal(SIGSEGV,termHandlerError);
 		exit(status);
 	}
 	void runEngine(const shared_ptr<Engine>& e){ e->action(OMEGA.getScene().get()); }
@@ -733,7 +735,7 @@
 		.add_property("bexSyncCount",&pyOmega::bexSyncCount_get,&pyOmega::bexSyncCount_set,"Counter for number of syncs in BexContainer, for profiling purposes.")
 		.add_property("numThreads",&pyOmega::numThreads_get /* ,&pyOmega::numThreads_set*/ ,"Get maximum number of threads openMP can use.")
 		.add_property("periodicCell",&pyOmega::periodicCell_get,&pyOmega::periodicCell_set, "Get/set periodic cell minimum and maximum (tuple of 2 Vector3's), or () for no periodicity.")
-		.def("exitNoBacktrace",&pyOmega::exitNoBacktrace,omega_exitNoBacktrace_overloads(python::args("status"),"Disable our SEGV handler and exit."))
+		.def("exitNoBacktrace",&pyOmega::exitNoBacktrace,omega_exitNoBacktrace_overloads("Disable SEGV handler and exit."))
 		.def("disableGdb",&pyOmega::disableGdb,"Revert SEGV and ABRT handlers to system defaults.")
 		#ifdef YADE_BOOST_SERIALIZATION
 			.def("saveXML",&pyOmega::saveXML,"[EXPERIMENTAL] function saving to XML file using boost::serialization.")

=== added directory 'scripts/test/batch'
=== added file 'scripts/test/batch/params.table'
--- scripts/test/batch/params.table	1970-01-01 00:00:00 +0000
+++ scripts/test/batch/params.table	2009-12-08 12:08:48 +0000
@@ -0,0 +1,9 @@
+!OMP_NUM_THREADS important! unimportant this!
+# comment
+	# comment and empty line
+
+3 51 'hummm' 3.1415 
+3 51 'paaah' =
+= 52 = * 
+= =  'not' =
+= 52 'yes' 2.71828

=== added file 'scripts/test/batch/sim.py'
--- scripts/test/batch/sim.py	1970-01-01 00:00:00 +0000
+++ scripts/test/batch/sim.py	2009-12-08 12:08:48 +0000
@@ -0,0 +1,17 @@
+from yade import *
+from yade import utils
+utils.readParamsFromTable(unknownOk=True,
+	important=6,
+	unimportant='foo',
+	this=-1,
+	notInTable='notInTable'
+)
+print O.tags['description']
+#print O.tags['params']
+#print O.tags['defaultParams']
+import time
+time.sleep(5)
+print 'finished'
+import sys
+sys.stdout.flush()
+sys.exit(0)