# ICE Revision: $Id$
"""Basis for the handling of OpenFOAM-files
Transparently accepts gnuzipped files"""
import os,re
from os import path
from tempfile import mktemp
import gzip
from PyFoam.Basics.Utilities import Utilities
from PyFoam.Basics.LineReader import LineReader
from PyFoam.Error import warning,error
from PyFoam.ThirdParty.six import PY3
[docs]class FileBasis(Utilities):
""" Base class for the other OpenFOAM--file-classes"""
removedString="//PyFoamRemoved: "
"""Comment for lines that were overwritten by PyFoam-routines"""
addedString="//PyFoamAdded"
"""Comment for lines that were added by PyFoam-routines"""
def __init__(self,name,
createZipped=True,
useBinary=False):
""":param name: Name of the file. If the field is zipped the .gz is
appended. Alternatively it can be a filehandle
:param createZipped: if the file doesnot exist: should it be created
as a zipped file?"""
self.useBinary=useBinary
if hasattr(name,"read"):
self.name=None
self.exists=True
self.zipped=None
self.fh=name
else:
self.name = path.abspath(name)
self.exists = False
if path.exists(self.name):
self.exists = True
self.zipped=False
if path.splitext(self.name)[1]==".gz":
self.zipped=True
elif path.exists(self.name+".gz"):
warning(self.name+".gz","and",self.name,"existing - using the unzipped")
elif path.exists(self.name+".gz"):
self.zipped=True
self.exists = True
else:
self.zipped=createZipped
if path.splitext(self.name)[1]==".gz":
self.name=self.name[:-3]
self.fh=None
self.content=None
[docs] def realName(self):
"""The full filename with appended .gz (if zipped)"""
if self.name:
if self.zipped:
return self.name+".gz"
else:
return self.name
else:
return str(self.fh)
[docs] def baseName(self):
"""Returns the basic file name (without .gz)"""
if self.name:
return path.basename(self.name)
else:
return path.basename(self.fh.name)
[docs] def openFile(self,keepContent=False,mode="r"):
"""opens the file. To be overloaded by derived classes"""
if not keepContent:
self.content=None
if self.useBinary and mode.find("b")<0:
mode+="b"
elif mode.find("b")>=0:
self.useBinary=True
if self.name:
if self.zipped:
self.fh=gzip.open(self.name+".gz",mode)
else:
self.fh=open(self.name,mode)
else:
if mode!="r":
error("File-handle",str(self.fh),"can only be used with mode 'r'")
[docs] def closeFile(self):
""" closes the file"""
self.fh.close()
self.fh=None
[docs] def readFile(self):
""" read the whole File into memory"""
self.openFile()
txt=self.fh.read()
if PY3 and self.zipped:
txt=str(txt,"utf-8")
elif PY3 and self.useBinary:
txt=str(txt,encoding="latin-1")
self.content=self.parse(txt)
self.closeFile()
[docs] def writeFile(self,content=None):
""" write the whole File from memory
:param content: content that should replace the old content"""
if self.name:
if content!=None:
self.content=content
if self.content!=None:
self.openFile(keepContent=True,mode="w")
txt=str(self)
if bytes!=str and self.useBinary:
txt=bytes(txt,"utf-8")
self.fh.write(self.encode(txt))
self.closeFile()
else:
error("File-handle",str(self.fh),"can not be written")
[docs] def encode(self,txt):
"""Encode a string to byte if necessary (for Python3)"""
if PY3 and self.zipped and isinstance(txt,(str,)):
return bytes(txt,"utf-8")
else:
return txt
[docs] def writeFileAs(self,name):
""" Writes a copy of the file. Extends with .gz if the original
is zipped
:param name: Name under which the file is written"""
if self.name:
if path.abspath(self.name)==path.abspath(name):
warning(name,"and",self.name,"seem to be the same. Nothing done")
return
erase=False
if self.content==None:
erase=True
self.readFile()
tmp=self.name
fh=self.fh
self.name=name
self.writeFile()
self.name=tmp
self.fh=fh
if erase:
self.content=None
[docs] def parse(self,cnt):
""" Parse a string that is to be the content, to be overriden
by the sub-classes"""
return cnt
def __str__(self):
"""Build a string from self.content, to be overriden by sub-classes"""
return self.content
def __enter__(self):
"""Making the 'with'-statement happy"""
return self
def __exit__(self,typ,value,traceback):
"""Making the 'with'-statement happy"""
if self.fh!=None:
self.closeFile()
[docs] def makeTemp(self):
"""creates a temporary file"""
if self.name:
fName=self.name
else:
fName=self.fh.name
fn=mktemp(dir=path.dirname(fName))
if self.zipped:
fh=gzip.open(fn,"w")
else:
fh=open(fn,"w")
return fh,fn
[docs] def writeEncoded(self,out,txt):
"""Convert the text to 'bytes' is we encounter a zipped file"""
if PY3:
if type(out) is gzip.GzipFile:
txt=bytes(txt,"utf-8")
out.write(txt)
[docs] def goTo(self,l,s,out=None,echoLast=False,stop=None):
"""Read lines until a token is found
:param l: a LineReader object
:param s: the string to look for
:param out: filehandle to echo the lines to
:param stop: pattern that indicates that exp will never be found (only passed through to goMatch)
:param echoLast: echo the line with the string"""
exp=re.compile("( |^)"+s+"( |$)")
self.goMatch(l,exp,out=out,stop=stop)
if out!=None and echoLast:
self.writeEncoded(out,l.line+"\n")
[docs] def goMatch(self,l,exp,out=None,stop=None):
"""Read lines until a regular expression is matched
:param l: a LineReader object
:param exp: the expression to look for
:param out: filehandle to echo the lines to
:param stop: pattern that indicates that exp will never be found
:return: match-object if exp is found, the line if stop is found and None if the end of the file is reached"""
while l.read(self.fh):
m=exp.match(l.line)
if m!=None:
return m
elif stop!=None:
if stop.match(l.line):
return l.line
if out!=None:
self.writeEncoded(out,l.line+"\n")
return None
[docs] def copyRest(self,l,out):
"""Copy the rest of the file
:param l: a LineReader object
:param out: filehandle to echo the lines to"""
while l.read(self.fh):
self.writeEncoded(out,l.line+"\n")
[docs] def purgeFile(self):
"""Undo all the manipulations done by PyFOAM
Goes through the file and removes all lines that were added"""
if not self.name:
error("File-handle",str(self.fh),"can not be purged")
rmExp= re.compile("^"+self.removedString+"(.*)$")
addExp=re.compile("^(.*)"+self.addedString+"$")
l=LineReader()
self.openFile()
(fh,fn)=self.makeTemp()
while l.read(self.fh):
toPrint=l.line
m=addExp.match(l.line)
if m!=None:
continue
m=rmExp.match(l.line)
if m!=None:
toPrint=m.group(1)
self.writeEncoded(fh,toPrint+"\n")
self.closeFile()
fh.close()
os.rename(fn,self.name)
[docs] def getCaseDir(self):
"""Return the path to the case of this file (if any valid case is found).
Else return None"""
if self.name:
fName=self.name
else:
fName=self.fh.name
from .SolutionDirectory import NoTouchSolutionDirectory
caseDir=None
comp=path.split(fName)[0]
while len(comp)>1:
if NoTouchSolutionDirectory(comp).isValid():
caseDir=comp
break
comp=path.split(comp)[0]
return caseDir
[docs]class CleanCharactersFile(FileBasis):
"""Read file and remove characters from the content"""
def __init__(self,name,charsToRemove):
"""@param charsToRemove: string with characters that should be removed"""
FileBasis.__init__(self,name)
self.chars=charsToRemove
self.readFile()
[docs] def parse(self,cnt):
for c in self.chars:
cnt=cnt.replace(c,"")
return cnt
[docs]class FileBasisBackup(FileBasis):
"""A file with a backup-copy"""
counter={}
def __init__(self,name,
backup=False,
createZipped=True,
useBinary=False):
""":param name: The name of the parameter file
:type name: str
:param backup: create a backup-copy of the file
:type backup: boolean"""
if hasattr(name,"read"):
if backup:
warning(str(name),"is a file-handle. No backup possible")
backup=False
FileBasis.__init__(self,
name,
createZipped=createZipped,
useBinary=useBinary)
if backup:
self.backupName=self.name+".backup"
try:
FileBasisBackup.counter[self.name]+=1
except KeyError:
FileBasisBackup.counter[self.name]=1
if self.zipped:
self.copyfile(self.name+".gz",self.backupName+".gz")
else:
self.copyfile(self.name,self.backupName)
else:
self.backupName=None
[docs] def restore(self):
"""if a backup-copy was made the file is restored from this"""
if self.backupName is not None:
FileBasisBackup.counter[self.name] -= 1
if FileBasisBackup.counter[self.name] == 0:
if self.zipped:
bkpName = self.backupName + ".gz"
fileName = self.name + ".gz"
else:
bkpName = self.backupName
fileName = self.name
self.copyfile(bkpName, fileName)
self.remove(bkpName)
del FileBasisBackup.counter[self.name]
[docs]def exists(name):
f = FileBasis(name)
return f.exists
# Should work with Python3 and Python2