# ICE Revision: $Id$
"""
Application class that implements pyFoamUpdateDictionary.py
"""
import sys
from os import path
from .PyFoamApplication import PyFoamApplication
from PyFoam.RunDictionary.ParsedParameterFile import ParsedParameterFile
from PyFoam.Basics.DataStructures import DictProxy,TupleProxy
from PyFoam.Error import error,warning
from .CommonParserOptions import CommonParserOptions
from PyFoam.Basics.TerminalFormatter import TerminalFormatter
from PyFoam.ThirdParty.six import print_
from PyFoam.ThirdParty.six.moves import input
f=TerminalFormatter()
f.getConfigFormat("source",shortName="src")
f.getConfigFormat("destination",shortName="dst")
f.getConfigFormat("difference",shortName="diff")
f.getConfigFormat("question",shortName="ask")
f.getConfigFormat("input")
[docs]class UpdateDictionary(PyFoamApplication,
CommonParserOptions):
def __init__(self,
args=None,
**kwargs):
description="""\
Takes two dictionary and modifies the second one after the example of
the first. If the dictionaries do not have the same name, it looks for
the destination file by searching the equivalent place in the
destination case
"""
PyFoamApplication.__init__(self,
args=args,
description=description,
usage="%prog [options] <source> <destination-case>",
nr=2,
changeVersion=False,
interspersed=True,
**kwargs)
[docs] def addOptions(self):
self.parser.add_option("--interactive",
action="store_true",
default=True,
dest="interactive",
help="Asks the user before applying changes")
self.parser.add_option("--batch",
action="store_false",
default=True,
dest="interactive",
help="Don't ask the user before applying changes")
self.parser.add_option("--clear-unused",
action="store_true",
default=False,
dest="clear",
help="Removes all the dictionary entries that are not in the source")
self.parser.add_option("--add-missing",
action="store_true",
default=False,
dest="add",
help="Add all the dictionary entries that are not in the destination")
self.parser.add_option("--append-lists",
action="store_true",
default=False,
dest="append",
help="Append to lists if they are shorter than the original")
self.parser.add_option("--shorten-lists",
action="store_true",
default=False,
dest="shorten",
help="Shortens lists if they are longer than the original")
self.parser.add_option("--all",
action="store_true",
default=False,
dest="all",
help="Do all the editing commands: clear, add, shorten and append")
self.parser.add_option("--test",
action="store_true",
default=False,
dest="test",
help="Does not write the file but only prints it to the screen")
self.parser.add_option("--not-equal",
action="store_true",
default=False,
dest="notequal",
help="Allow source and destination to have different names")
self.parser.add_option("--verbose",
action="store_true",
default=False,
dest="verbose",
help="Print every change that is being made")
self.parser.add_option("--min-recursion",
action="store",
default=0,
type="int",
dest="min",
help="Minimum depth of the recursive decent into dictionaries at which 'editing' should start (default: %default)")
self.parser.add_option("--max-recursion",
action="store",
default=100,
type="int",
dest="max",
help="Maximum depth of the recursive decent into dictionaries (default: %default)")
CommonParserOptions.addOptions(self)
[docs] def ask(self,*question):
if not self.opts.interactive:
return False
else:
print_(f.ask,"QUESTION:",end="")
for q in question:
print_(q,end="")
answer=None
while answer!="y" and answer!="n":
answer=input(f.reset+f.ask+" [Y]es or [N]no ? "+f.input).strip()[0].lower()
print_(f.reset,end="")
return answer=="y"
[docs] def workList(self,source,dest,depth):
if depth>self.opts.max:
if self.opts.verbose:
print_("- "*depth,"Recursion ended")
return
for i in range(min(len(source),len(dest))):
if type(source[i])==type(dest[i]) and type(source[i]) in [dict,DictProxy]:
if self.opts.verbose:
print_("- "*depth,"Entering dict nr.",i)
self.workDict(source[i],dest[i],depth+1)
if self.opts.verbose:
print_("- "*depth,"Leaving dict nr.",i)
elif type(source[i])==type(dest[i]) and type(source[i]) in [tuple,TupleProxy,list]:
if self.opts.verbose:
print_("- "*depth,"Entering tuple nr.",i)
self.workList(source[i],dest[i],depth+1)
if self.opts.verbose:
print_("- "*depth,"Leaving tuple nr.",i)
elif self.opts.interactive:
if source[i]!=dest[i]:
if self.ask("Replace for index",i,"the value",dest[i],"with the value",source[i]):
dest[i]=source[i]
if len(source)<len(dest) and self.opts.shorten:
if self.ask("Clip [",len(source),":] with the values ",dest[len(source):],"from the list"):
if self.opts.verbose:
print_("- "*depth,"Clipping",len(dest)-len(source),"entries starting with",len(source))
dest=dest[0:len(source)]
elif len(source)>len(dest) and self.opts.append:
if self.ask("Append [",len(dest),":] with the values ",source[len(dest):],"to the list"):
if self.opts.verbose:
print_("- "*depth,"Appending",len(source)-len(dest),"entries starting with",len(dest))
dest+=source[len(dest):]
[docs] def workDict(self,source,dest,depth):
if depth>self.opts.max:
if self.opts.verbose:
print_("- "*depth,"Recursion ended")
return
if depth>=self.opts.min:
doIt=True
else:
doIt=False
for name in source:
if name not in dest:
if self.opts.add and doIt:
if self.ask("Add the key",name,"with value",source[name]):
if self.opts.verbose:
print_("- "*depth,"Adding",name)
dest[name]=source[name]
elif type(source[name]) in [dict,DictProxy]:
if type(dest[name]) not in [dict,DictProxy]:
error("Entry",name,"is not a dictionary in destination (but in source)")
if self.opts.verbose:
print_("- "*depth,"Entering dict ",name)
self.workDict(source[name],dest[name],depth+1)
if self.opts.verbose:
print_("- "*depth,"Leaving dict ",name)
elif type(source[name])==type(dest[name]) and type(dest[name]) in [tuple,TupleProxy,list]:
if self.opts.verbose:
print_("- "*depth,"Entering tuple ",name)
self.workList(source[name],dest[name],depth+1)
if self.opts.verbose:
print_("- "*depth,"Leaving tuple ",name)
elif self.opts.interactive:
if source[name]!=dest[name]:
if self.ask("Replace for key",name,"the value",dest[name],"with the value",source[name]):
dest[name]=source[name]
else:
if self.opts.verbose:
print_("- "*depth,"Nothing done for",name)
if self.opts.clear and doIt:
weg=[]
for name in dest:
if name not in source:
weg.append(name)
for name in weg:
if self.ask("Remove the key",name,"with the value",dest[name]):
if self.opts.verbose:
print_("- "*depth,"Removing",name)
del dest[name]
[docs] def run(self):
sName=path.abspath(self.parser.getArgs()[0])
dName=path.abspath(self.parser.getArgs()[1])
if self.opts.all:
self.opts.append=True
self.opts.shorten=True
self.opts.add=True
self.opts.clear=True
try:
source=ParsedParameterFile(sName,
backup=False,
debug=self.opts.debugParser,
noBody=self.opts.noBody,
noHeader=self.opts.noHeader,
boundaryDict=self.opts.boundaryDict,
listDict=self.opts.listDict,
listDictWithHeader=self.opts.listDictWithHeader)
except IOError:
e = sys.exc_info()[1] # Needed because python 2.5 does not support 'as e'
self.error("Problem with file",sName,":",e)
if not self.opts.notequal and path.basename(sName)!=path.basename(dName):
found=False
parts=sName.split(path.sep)
for i in range(len(parts)):
tmp=path.join(*[dName]+parts[-(i+1):])
if path.exists(tmp):
found=True
dName=tmp
warning("Found",dName,"and using this")
break
if not found:
error("Could not find a file named",path.basename(sName),"in",dName)
if path.samefile(sName,dName):
error("Source",sName,"and destination",dName,"are the same")
try:
dest=ParsedParameterFile(dName,
backup=False,
debug=self.opts.debugParser,
noBody=self.opts.noBody,
noHeader=self.opts.noHeader,
boundaryDict=self.opts.boundaryDict,
listDict=self.opts.listDict,
listDictWithHeader=self.opts.listDictWithHeader)
except IOError:
e = sys.exc_info()[1] # Needed because python 2.5 does not support 'as e'
self.error("Problem with file",dName,":",e)
dCase=dest.getCaseDir()
if self.opts.interactive:
self.opts.verbose=True
if not self.opts.boundaryDict and not self.opts.listDict and not self.opts.listDictWithHeader:
self.workDict(source.content,dest.content,1)
else:
self.workList(source.content,dest.content,1)
if self.opts.test or self.opts.interactive:
print_(str(dest))
if not self.opts.test and self.ask("\n Write this file to disk"):
dest.writeFile()
if dCase!=None:
self.addToCaseLog(dCase,"Source",sName,"Destination:",dName)
# Should work with Python3 and Python2