releasing/blocks/cclient/blocks/python/utils.py
changeset 632 934f9131337b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/releasing/blocks/cclient/blocks/python/utils.py	Thu Sep 02 15:02:14 2010 +0800
@@ -0,0 +1,424 @@
+#
+# Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description:
+# Various utility functions
+#
+
+import timeit
+import platform
+import logging
+import logging.handlers
+import os
+import sys
+import stat
+import shutil
+import atexit
+import urllib2
+import urlparse
+import fnmatch
+import tempfile
+import itertools
+
+from generalexceptions import GenericError
+
+URL_SCHEMES = ("http", "https", "ftp", "ftps", "file")
+
+loglevel = logging.DEBUG
+
+def platformIsWindows():
+    return platform.system() == "Windows"
+
+class StopWatch(object): # pragma: no cover
+    ''' Measure elapsed time '''
+    def __init__(self, start=True):
+        self.start_time = None
+        self.stopped = False
+        if start:
+            self.start()
+
+    def start(self):
+        self.stopped = False
+        self.start_time = timeit.default_timer()
+
+    def stop(self):
+        if not self.stopped:
+            self.stopped = self.elapsed()
+        return self.stopped
+
+    def elapsed(self):
+        if self.stopped:
+            return self.stopped
+
+        if self.start_time:
+            elapsed = timeit.default_timer() - self.start_time
+        else:
+            elapsed = None
+        return elapsed
+
+def getErrorFunction(errortext):
+    def errorFunction(*args, **kwargs):
+        exit("Error: %s" % errortext)
+    return errorFunction
+
+def getRealPath(origpath):
+    realpath = origpath
+    if platformIsWindows():
+        try:
+            import win32file
+        except ImportError: # pragma: no cover
+            logging.warning("Pywin32 extension not available. Subst support disabled.")
+            return realpath
+
+        (drive, relpath) = os.path.splitdrive(origpath)
+        dev = win32file.QueryDosDevice(drive)
+        if dev.startswith("\\??\\"):
+            dev = dev[4:-2]
+            realpath = os.path.join(dev, relpath[1:])
+            logging.debug("Directory '%s' is on substed drive %s => %s. Real path is %s", origpath, drive, dev, realpath)
+    return realpath
+
+def getMetaPath():
+    altPath = os.environ.get("BLOCKS_METADATA")
+    if altPath:
+        blocksPath = altPath
+    else:
+        homeEnv = "APPDATA" if platformIsWindows() else "HOME"
+        home = os.environ.get(homeEnv)
+        if home is None:
+            raise GenericError("Could not get home directory from environment variable %s.\n"
+                               "Please define BLOCKS_METADATA environment variable to set blocks home dir." % homeEnv)
+        blocksPath = os.path.join(home, "Blocks" if platformIsWindows() else ".blocks")
+    blocksPath = os.path.normcase(blocksPath)
+    if not os.path.isdir(blocksPath):
+        os.mkdir(blocksPath) # pragma: no cover
+    return blocksPath
+
+def addSearchPath(path):
+    envPath = os.environ.get("PATH", "")
+    if path not in envPath:
+        os.environ["PATH"] = os.pathsep.join([path, envPath])
+
+def pathsUnique(path1, path2):
+    path1 = addPathSep(os.path.normcase(path1))
+    path2 = addPathSep(os.path.normcase(path2))
+    return not (path1.startswith(path2) or path2.startswith(path1))
+
+def pathInside(root, path, equalInside=True):
+    root = addPathSep(os.path.normcase(root))
+    path = addPathSep(os.path.normcase(path))
+    if not equalInside and path == root:
+        return False
+    return path.startswith(root)
+
+def removeStart(text, remove):
+    ''' Case-insensitive removing of text in start of string '''
+    removeCount = len(text) - len(text.lower().replace(remove.lower(), "", 1))
+    return text[removeCount:]
+
+def setReadOnly(path, read_only=True): # pragma: no cover
+    os.chmod(path, stat.S_IREAD if read_only else stat.S_IWRITE)
+
+def forceDelete(path): # pragma: no cover
+    ''' Deletes read-only files '''
+    os.chmod(path, stat.S_IWRITE)
+    os.remove(path)
+
+def __readOnlyDelete(func, path, exc): # pragma: no cover
+    os.chmod(path, stat.S_IWRITE)
+    func(path)
+
+def superDelete(path): # pragma: no cover
+    ''' Deletes both files and directories even if read-only '''
+    if os.path.isfile(path):
+        forceDelete(path)
+    elif os.path.isdir(path):
+        shutil.rmtree(path, onerror=__readOnlyDelete)
+
+DETAILED_LOG_FORMAT = '%(asctime)s.%(msecs)d - %(levelname)s: %(message)s'
+DETAILED_LOG_TIMEFORMAT = "%d.%m.%Y %H:%M:%S"
+DEFAULT_LOG_FORMAT = '%(levelname)s: %(message)s'
+
+def getConsoleLogFormat():
+    if loglevel <= logging.DEBUG:
+        return (DETAILED_LOG_FORMAT, DETAILED_LOG_TIMEFORMAT)
+    else:
+        return (DEFAULT_LOG_FORMAT, None)
+
+def setupLogging(version, verbose, path=None):
+    global loglevel
+
+    if not os.path.isabs(path):
+        path = os.path.join(getMetaPath(), path)
+
+    logging.DEBUG2 = logging.DEBUG - 1
+    logging.addLevelName(logging.DEBUG2, "DEBUG2")
+    verbosityToLoglevel = {-2: logging.CRITICAL,
+                           -1: logging.ERROR,
+                            0: logging.WARNING,
+                            1: logging.INFO,
+                            2: logging.DEBUG,
+                            3: logging.DEBUG2}
+    minVerbosity = min(verbosityToLoglevel.keys())
+    maxVerbosity = max(verbosityToLoglevel.keys())
+    verbose = min(max(verbose, minVerbosity), maxVerbosity)
+    loglevel = verbosityToLoglevel[verbose]
+
+    logger = logging.getLogger()
+    logger.setLevel(logging.NOTSET)
+
+    console = logging.StreamHandler()
+    console.setLevel(loglevel)
+    formatter = logging.Formatter(*getConsoleLogFormat())
+    console.setFormatter(formatter)
+    logger.addHandler(console)
+
+    fileHandler = logging.handlers.RotatingFileHandler(path, maxBytes=500000, backupCount=1)
+    if __debug__:
+        fileHandler.setLevel(loglevel if loglevel < logging.DEBUG else logging.DEBUG)
+    else:
+        fileHandler.setLevel(loglevel)
+    formatter = logging.Formatter(DETAILED_LOG_FORMAT, DETAILED_LOG_TIMEFORMAT)
+    fileHandler.setFormatter(formatter)
+    logger.addHandler(fileHandler)
+
+    cpu_count = "Unknown"
+    try:
+        import multiprocessing
+        cpu_count = multiprocessing.cpu_count()
+    except ImportError: # pragma: no cover
+        pass
+
+    logging.debug("%s started (PID %s) [OS: %s | Python: %s | CPU: %s | CPU Count: %s]",
+        version,
+        os.getpid(),
+        platform.platform(),
+        platform.python_version(),
+        platform.processor(),
+        cpu_count)
+    stopwatch = StopWatch()
+
+    @atexit.register
+    def runatexit():
+        # Fix to make coverage work. logging was none
+        if logging: # pragma: no cover
+            logging.info("Stopped. Run time: %.3fs", stopwatch.stop())
+
+    return loglevel
+
+def addPathSep(path):
+    return addSuffix(path, os.sep)
+
+def addSuffix(text, suffix):
+    return text if text.endswith(suffix) else text + suffix
+
+def relativePath(path, root):
+    '''
+    Returns relative path if path is absolute
+    Keeps casing in the path, but on windows matching of path and root are done
+    in lower case
+    '''
+    if os.path.isabs(path):
+        root = os.path.abspath(root)
+        if pathInside(root, path):
+            path = os.path.normpath(path)
+            root = os.path.normpath(root)
+            root = addPathSep(root)
+            # On windows we don't care about path case
+            if platformIsWindows():
+                path = removeStart(path, root)
+            else:
+                path = path.replace(root, "", 1)
+    return path
+
+def removeFilesRecursive(path, glob):
+    for name in getFilesRecursive(path, glob):
+        os.remove(name)
+
+def getFilesRecursive(path, glob=None):
+    ''' Get list of all files recursively from a path '''
+    files = [os.path.join(root, name) for root, _, files in os.walk(path) for name in files]
+    if glob:
+        files = fnmatch.filter(files, glob)
+    return files
+
+def getFileLines(path):
+    lines = []
+    if path:
+        with open(path) as f:
+            lines = [line.strip() for line in f.readlines()]
+    return lines
+
+def warnIfFileNotFound(path):
+    if not os.path.isfile(path):
+        if os.path.isdir(path):
+            logging.warning("No such file: %s. Directory found instead.", path)
+        else:
+            logging.warning("No such file: %s", path)
+
+def createFile(path):
+    open(path, "a+").close()
+
+def createDir(path):
+    '''
+    Create directory if it doesn't exist.
+    Ignores errors.
+    '''
+    try:
+        os.makedirs(path)
+    except OSError:
+        pass
+
+def getInstallDir():
+    return os.path.normpath(os.path.join(os.path.dirname(__file__), ".."))
+
+def copyFileData(src, dst, size=None, blocksize=32*1024):
+    ''' Copy data from source file to destination file '''
+    bytesread = 0
+    while size is None or bytesread < size:
+        if size and (bytesread + blocksize) >= size:
+            blocksize = size - bytesread
+        buf = src.read(blocksize)
+        bytesread += blocksize
+        if not buf:
+            break
+        dst.write(buf)
+
+def fileobjsEqual(fobj1, fobj2):
+    for line in fobj1:
+        if line != fobj2.readline():
+            return False
+    return True
+
+def getUtilDir():
+    binDir = os.path.join(getInstallDir(), "utils")
+    binDir = binDir.replace("\\", "/")
+    return binDir
+
+def initSearchPath():
+    addSearchPath(getUtilDir())
+
+def urlretrieve(url, path):
+    urlFile = openUrl(url)
+    try:
+        with open(path, "wb") as f:
+            copyFileData(urlFile, f)
+    finally:
+        urlFile.close()
+
+def openUrl(url, timeout=10):
+    ''' If URL cannot be opened raises IOError '''
+    errorText = "Problem on fetching '%s'" % url
+    try:
+        scheme = validateURL(url)
+    except ValueError, ex:
+        raise IOError("%s: %s" % (errorText, ex))
+
+    try:
+        urlFile = urllib2.urlopen(url, timeout=timeout)
+    except IOError, ex:
+        if hasattr(ex, "reason"):
+            problem = "Local file cannot be found" if scheme == "file" else "Server cannot be reached"
+            exc = IOError("%s: %s.\nReason: %s" % (errorText, problem, ex.reason))
+            if scheme == "file":
+                exc.errno = 404
+        elif hasattr(ex, "code"):
+            exc = IOError("%s: %s" % (errorText, ex))
+            exc.errno = ex.code
+        raise exc
+
+    return urlFile
+
+def validateURL(url):
+    ''' Raises ValueError exception if invalid URL '''
+    scheme = urlparse.urlparse(url).scheme
+    if scheme not in URL_SCHEMES:
+        raise ValueError("Invalid URL '%s' with scheme '%s': Supported URL schemes: %s" % (url, scheme, ", ".join(URL_SCHEMES)))
+    return scheme
+
+def error(text, critical=False, noOutput=False, retCode=1):
+    if critical:
+        logging.error(text, exc_info=True) # pragma: no cover
+    else:
+        logging.debug(text, exc_info=True)
+    if loglevel == logging.DEBUG or critical:
+        sys.exit(retCode) # pragma: no cover
+    else:
+        if not noOutput:
+            print >> sys.stderr, text
+        sys.exit(retCode)
+
+def getVersion(infoModule):
+    info = __import__(infoModule)
+    if info.VERSION_PRE_RELEASE > 0:
+        pre_release = info.VERSION_PRE_RELEASE_ID + str(info.VERSION_PRE_RELEASE)
+    else:
+        pre_release = "" # pragma: no cover
+    return "%%prog %d.%d.%d%s (%s)" % (info.VERSION_MAJOR,
+                                       info.VERSION_MINOR,
+                                       info.VERSION_REVISION,
+                                       pre_release,
+                                       info.VERSION_DATE or "dev")
+
+def atomicFileCreate(path, data):
+    tmpfile = tempfile.NamedTemporaryFile(bufsize=0, delete=False)
+    try:
+        tmpfile.write(data)
+        tmpfile.flush()
+        os.fsync(tmpfile.fileno())
+        tmpfile.close()
+        os.rename(tmpfile.name, path)
+    except Exception: # cleanup
+        tmpfile.close()
+        os.remove(tmpfile.name)
+        raise
+
+def uniqueName(prefix, usedNames):
+    for idnum in itertools.count(1):
+        name = "%s%d" % (prefix, idnum)
+        if name not in usedNames:
+            break
+    return name
+
+def isFile(name, ext):
+    ''' Checks if there is a file having extension ext with given name '''
+    return name.lower().endswith(ext) and os.path.isfile(name)
+
+def argsToStr(*args):
+    ''' Takes at least one string or iterable containing strings and quotes all that have spaces and returns string '''
+    argList = listify(*args)
+    for i, arg in enumerate(argList):
+        if " " in arg:
+            argList[i] = '"%s"' % arg
+    return " ".join(argList)
+
+def listify(*args):
+    retList = []
+    for arg in args:
+        if hasattr(arg, "__iter__"):
+            retList.extend(list(arg))
+        else:
+            retList.append(arg)
+    return retList
+
+def toBoolean(var):
+    if isinstance(var, bool):
+        return var
+    return {"yes": True, "true": True, "1": True, "enable": True,
+            "no": False, "false": False, "0": False, "disable": False}.get(var.lower() if var else None)
+
+def test():
+    print relativePath(r"c:\users\work\vc\blocks\blocks\python\data.py", ".")
+
+if __name__ == "__main__":
+    test()
\ No newline at end of file