Source code for PyFoam.Basics.Utilities

#  ICE Revision: $Id$
""" Utility functions

Can be used via a class or as functions"""

import sys
from PyFoam.ThirdParty.six import print_
from PyFoam.Error import warning,error
import subprocess
import os,fnmatch

if sys.version_info<(2,6):
    from popen2 import popen4
else:
    from subprocess import Popen,PIPE,STDOUT
from os import listdir,path,remove as removeFile

import re

try:
    import shutil
except ImportError:
    # this is an old python-version without it. We'll try to work around it
    pass

[docs]class Utilities(object): """Class with utility methods Can be inherited without side effects by classes that need these methods""" def __init__(self): pass
[docs] def execute(self, cmd, debug=False, workdir=None, echo=None, outfile=None, getReturnCode=False): """Execute the command cmd. If specified change the working directory Currently no error-handling is done :return: A list with all the output-lines of the execution""" if debug: print_(cmd) oldDir=None if workdir: oldDir=os.getcwd() os.chdir(workdir) if type(cmd)==list: fpath=cmd[0] else: fpath=cmd.split(" ")[0] # Check if the file is there. Then we assume that this is a script if os.path.exists(fpath): # Script seems to be unexecutable if not os.access(fpath, os.X_OK): error("The script file",fpath,"is not executable") if sys.version_info<(2,6): raus,rein = popen4(cmd) else: p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True, universal_newlines=True) (rein,raus)=(p.stdin,p.stdout) if (echo is not None) or (outfile is not None): tmp=[] while p.poll()==None: l=raus.readline() if echo is not None: print_(echo,l,end="") if outfile is not None: outfile.write(l) else: tmp.append(l) else: tmp=raus.readlines() # line=raus.readline() # while line!="": # print line # line=raus.readline() if oldDir: os.chdir(oldDir) if getReturnCode: return p.returncode,tmp else: return tmp
[docs] def remove(self,f): """Remove a file if it exists.""" if path.exists(f): removeFile(f)
[docs] def rmtree(self,dst,ignore_errors=False): """Encapsulates the shutil rmtree and provides an alternative for old Python-version""" try: if path.isdir(dst): shutil.rmtree(dst,ignore_errors=ignore_errors) elif path.exists(dst): os.remove(dst) except NameError: self.execute("rm -rf "+dst)
[docs] def copytree(self,src,dst, symlinks=False,force=False): """Encapsulates the shutil copytree and provides an alternative for old Python-version""" if force and path.exists(dst): if path.isdir(dst): self.rmtree(dst) else: os.remove(dst) try: if path.isdir(dst): dst=path.join(dst,path.basename(path.abspath(src))) if path.islink(src) and symlinks: os.symlink(path.realpath(src),dst) elif path.isdir(src): shutil.copytree(src,dst, symlinks=symlinks) else: self.copyfile(src,dst) except NameError: cpOptions="-R" if not symlinks: cpOptions+=" -L" self.execute("cp "+cpOptions+" "+src+" "+dst)
[docs] def copyfile(self,src,dst): """Encapsulates the shutil copyfile and provides an alternative for old Python-version""" try: if path.isdir(dst): dst=path.join(dst,path.basename(path.abspath(src))) shutil.copyfile(src,dst) shutil.copymode(src,dst) except NameError: self.execute("cp "+src+" "+dst)
[docs] def writeDictionaryHeader(self,f): """Writes a dummy header so OpenFOAM accepts the file as a dictionary :param f: The file to write to :type f: file""" f.write(""" // * * * * * * * * * // FoamFile { version 0.5; format ascii; root "ROOT"; case "CASE"; class dictionary; object nix; } """)
excludeNames=["^.svn$" , "~$"]
[docs] def findFileInDir(self,dName,fName): """Find file in a directory (search recursively) :param dName: name of the directory :param fName: name of the file to look force :return: the complete path. Directory and path joined if nothing is found""" def lookFor(dName,fName): if path.exists(path.join(dName,fName)): return path.join(dName,fName) else: for f in listdir(dName): if path.isdir(path.join(dName,f)): result=lookFor(path.join(dName,f),fName) if result: return result return None result=lookFor(dName,fName) if result: return result else: return path.join(dName,fName)
[docs] def listDirectory(self,d): """Lists the files in a directory, but excludes certain names and files with certain endings :param d: The directory to list :return: List of the found files and directories""" result=[] excludes=[re.compile(e) for e in self.excludeNames] for n in listdir(d): ok=True for e in excludes: if e.search(n): ok=False break if ok: result.append(n) return result
[docs] def which(self,progname): """Get the full path. Return None if not found""" try: return shutil.which(progname) except AttributeError: # shutil has no which pipe = subprocess.Popen('which '+progname, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (fullname, errout) = pipe.communicate(input=input) stat = pipe.returncode if stat: warning("which can not find a match for",progname) return None else: return fullname
[docs] def find(self,pattern, path,directoriesToo=True): """Find all files whose names match :param pattern: glob-style pattern :param path: path under which this files are to be searched :param directoriesToo: also match directories?""" result = [] for root, dirs, files in os.walk(path): for name in files: if fnmatch.fnmatch(name, pattern): result.append(os.path.join(root, name)) if directoriesToo: for name in dirs: if fnmatch.fnmatch(name, pattern): result.append(os.path.join(root, name)) return result
[docs] def humanReadableSize(self,num): """Lifted from http://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size Gets a number in bytes and returns a human readable string""" for x in ['bytes','KB','MB','GB']: if num < 1024.0 and num > -1024.0: return "%3.1f%s" % (num, x) num /= 1024.0 return "%3.1f%s" % (num, 'TB')
[docs] def humanReadableDuration(self,num): """Creates a string that prints a duration in an easily readable form. Easily readable means that the two highest non-zero values will be printed""" intervals=[60*60*24*365, 60*60*24, 60*60, 60] names=["%dy", "%dd", "%dh", "%dmin", "%fs"] vals=[] for i in intervals: if num<i: vals.append(0) else: v=int(num/i) num-=v*i vals.append(v) result=None for v,s in zip(vals,names): if v>0: if result is None: result=s%v else: result+=" "+s%v break if result is None: return " 0s" else: return result
[docs] def diskUsage(self,fpath): """Calculate the disk space used at the specified path in bytes""" try: return int( subprocess.Popen( ["du","-sb",fpath], stdout=subprocess.PIPE, stderr=open(os.devnull,"w") ).communicate()[0].split()[0]) except IndexError: # assume that this du does not support -b return int( subprocess.Popen( ["du","-sk",fpath], stdout=subprocess.PIPE ).communicate()[0].split()[0])*1024
[docs]def diskUsage(fpath): """Calls the method of the same name from the Utilites class""" return Utilities().diskUsage(fpath)
[docs]def humanReadableSize(num): """Calls the method of the same name from the Utilites class""" return Utilities().humanReadableSize(num)
[docs]def humanReadableDuration(num): """Calls the method of the same name from the Utilites class""" return Utilities().humanReadableDuration(num)
[docs]def which(prog): """Calls the method of the same name from the Utilites class""" return Utilities().which(prog)
[docs]def execute(cmd, debug=False, workdir=None, echo=None, outfile=None, getReturnCode=False): """Calls the method of the same name from the Utilites class""" return Utilities().execute(cmd, debug=debug, workdir=workdir, echo=echo, outfile=outfile, getReturnCode=getReturnCode)
[docs]def writeDictionaryHeader(f): """Calls the method of the same name from the Utilites class""" Utilities().writeDictionaryHeader(f)
[docs]def listDirectory(d): """Calls the method of the same name from the Utilites class""" return Utilities().listDirectory(d)
[docs]def findFileInDir(d,f): """Calls the method of the same name from the Utilites class""" return Utilities().findFileInDir(d,f)
[docs]def rmtree(path,ignore_errors=False): """Calls the method of the same name from the Utilites class""" return Utilities().rmtree(path,ignore_errors=ignore_errors)
[docs]def copytree(src,dest,symlinks=False,force=False): """Calls the method of the same name from the Utilites class""" return Utilities().copytree(src,dest,symlinks=symlinks,force=force)
[docs]def remove(f): """Calls the method of the same name from the Utilites class""" return Utilities().remove(f)
[docs]def copyfile(src,dest): """Calls the method of the same name from the Utilites class""" return Utilities().copyfile(src,dest)
[docs]def find(pattern,path,directoriesToo=True): """Calls the method of the same name from the Utilites class""" return Utilities().find(pattern,path,directoriesToo=directoriesToo)
# Should work with Python3 and Python2