Utilities/__init__.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Utilities/__init__.py	Mon Dec 28 16:03:33 2009 +0000
@@ -0,0 +1,1232 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2003 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing various functions/classes needed everywhere within eric4. 
+"""
+
+import os
+import sys
+import re
+import fnmatch
+import glob
+from types import UnicodeType
+import random
+import base64
+
+import warnings
+warnings.filterwarnings("error", category=SyntaxWarning)
+
+from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF32
+
+from PyQt4.QtCore import QRegExp, QDir, QProcess, Qt, \
+    qVersion, PYQT_VERSION_STR
+from PyQt4.QtGui import QApplication
+from PyQt4.Qsci import QSCINTILLA_VERSION_STR, QsciScintilla
+
+from Globals import isWindowsPlatform   # import this method into the Utilities namespace
+
+from E4Gui.E4Application import e4App
+
+from UI.Info import Program, Version
+
+from eric4config import getConfig
+import Preferences
+
+configDir = None
+
+coding_regexps = [
+    (2, re.compile(r'''coding[:=]\s*([-\w_.]+)''')), 
+    (1, re.compile(r'''<\?xml.*\bencoding\s*=\s*['"]([-\w_.]+)['"]\?>''')), 
+]
+supportedCodecs = ['utf-8', 
+          'iso8859-1', 'iso8859-15', 'iso8859-2', 'iso8859-3', 
+          'iso8859-4', 'iso8859-5', 'iso8859-6', 'iso8859-7', 
+          'iso8859-8', 'iso8859-9', 'iso8859-10', 'iso8859-11', 
+          'iso8859-13', 'iso8859-14', 'iso8859-16', 'latin-1', 
+          'koi8-r', 'koi8-u', 'utf-16', 
+          'cp037', 'cp424', 'cp437', 'cp500', 'cp737', 'cp775', 
+          'cp850', 'cp852', 'cp855', 'cp856', 'cp857', 'cp860', 
+          'cp861', 'cp862', 'cp863', 'cp864', 'cp865', 'cp866', 
+          'cp874', 'cp875', 'cp932', 'cp949', 'cp950', 'cp1006', 
+          'cp1026', 'cp1140', 'cp1250', 'cp1251', 'cp1252', 
+          'cp1253', 'cp1254', 'cp1255', 'cp1256', 'cp1257', 
+          'cp1258', 
+          'ascii']
+
+class CodingError(Exception):
+    """
+    Class implementing an exception, which is raised, if a given coding is incorrect.
+    """
+    def __init__(self, coding):
+        """
+        Constructor
+        """
+        self.errorMessage = QApplication.translate("CodingError", 
+            "The coding '{0}' is wrong for the given text.").format(coding)
+        
+    def __repr__(self):
+        """
+        Private method returning a representation of the exception.
+        
+        @return string representing the error message
+        """
+        return unicode(self.errorMessage)
+        
+    def __str__(self):
+        """
+        Private method returning a string representation of the exception.
+        
+        @return string representing the error message
+        """
+        return str(self.errorMessage)
+    
+def get_coding(text):
+    """
+    Function to get the coding of a text.
+    
+    @param text text to inspect (string)
+    @return coding string
+    """
+    lines = text.splitlines()
+    for coding in coding_regexps:
+        coding_re = coding[1]
+        head = lines[:coding[0]]
+        for l in head:
+            m = coding_re.search(l)
+            if m:
+                return m.group(1).lower()
+    return None
+
+def decode(text):
+    """
+    Function to decode a text.
+    
+    @param text text to decode (string)
+    @return decoded text and encoding
+    """
+    try:
+        if text.startswith(BOM_UTF8):
+            # UTF-8 with BOM
+            return unicode(text[len(BOM_UTF8):], 'utf-8'), 'utf-8-bom'
+        elif text.startswith(BOM_UTF16):
+            # UTF-16 with BOM
+            return unicode(text[len(BOM_UTF16):], 'utf-16'), 'utf-16'
+        elif text.startswith(BOM_UTF32):
+            # UTF-32 with BOM
+            return unicode(text[len(BOM_UTF32):], 'utf-32'), 'utf-32'
+        coding = get_coding(text)
+        if coding:
+            return unicode(text, coding), coding
+    except (UnicodeError, LookupError):
+        pass
+    
+    guess = None
+    if Preferences.getEditor("AdvancedEncodingDetection"):
+        # Try the universal character encoding detector
+        try:
+            import ThirdParty.CharDet.chardet
+            guess = ThirdParty.CharDet.chardet.detect(text)
+            if guess and guess['confidence'] > 0.95 and guess['encoding'] is not None:
+                codec = guess['encoding'].lower()
+                return unicode(text, codec), '%s-guessed' % codec
+        except (UnicodeError, LookupError):
+            pass
+        except ImportError:
+            pass
+    
+    # Try default encoding
+    try:
+        codec = Preferences.getEditor("DefaultEncoding")
+        return unicode(text, codec), '%s-default' % codec
+    except (UnicodeError, LookupError):
+        pass
+    
+    # Assume UTF-8
+    try:
+        return unicode(text, 'utf-8'), 'utf-8-guessed'
+    except (UnicodeError, LookupError):
+        pass
+    
+    if Preferences.getEditor("AdvancedEncodingDetection"):
+        # Use the guessed one even if confifence level is low
+        if guess and guess['encoding'] is not None:
+            try:
+                codec = guess['encoding'].lower()
+                return unicode(text, codec), '%s-guessed' % codec
+            except (UnicodeError, LookupError):
+                pass
+    
+    # Assume Latin-1 (behaviour before 3.7.1)
+    return unicode(text, "latin-1"), 'latin-1-guessed'
+
+def encode(text, orig_coding):
+    """
+    Function to encode a text.
+    
+    @param text text to encode (string)
+    @param orig_coding type of the original coding (string)
+    @return encoded text and encoding
+    """
+    if orig_coding == 'utf-8-bom':
+        return BOM_UTF8 + text.encode("utf-8"), 'utf-8-bom'
+    
+    # Try declared coding spec
+    coding = get_coding(text)
+    if coding:
+        try:
+            return text.encode(coding), coding
+        except (UnicodeError, LookupError):
+            # Error: Declared encoding is incorrect
+            raise CodingError(coding)
+    
+    if orig_coding and orig_coding.endswith('-selected'):
+        coding = orig_coding.replace("-selected", "")
+        try:
+            return text.encode(coding), coding
+        except (UnicodeError, LookupError):
+            pass
+    if orig_coding and orig_coding.endswith('-default'):
+        coding = orig_coding.replace("-default", "")
+        try:
+            return text.encode(coding), coding
+        except (UnicodeError, LookupError):
+            pass
+    if orig_coding and orig_coding.endswith('-guessed'):
+        coding = orig_coding.replace("-guessed", "")
+        try:
+            return text.encode(coding), coding
+        except (UnicodeError, LookupError):
+            pass
+    
+    # Try configured default
+    try:
+        codec = Preferences.getEditor("DefaultEncoding")
+        return text.encode(codec), codec
+    except (UnicodeError, LookupError):
+        pass
+    
+    # Try saving as ASCII
+    try:
+        return text.encode('ascii'), 'ascii'
+    except UnicodeError:
+        pass
+    
+    # Save as UTF-8 without BOM
+    return text.encode('utf-8'), 'utf-8'
+    
+def toUnicode(s):
+    """
+    Public method to convert a string to unicode.
+    
+    Various codes are tried until one converts the string without an error.
+    If all codecs fail, the string is returned unaltered.
+    
+    @param s string to be converted (string)
+    @return converted string (unicode)
+    """
+    if type(s) is type(u""):
+        return s
+    
+    for codec in supportedCodecs:
+        try:
+            u = unicode(s, codec)
+            return u
+        except UnicodeError:
+            pass
+        except TypeError:
+            break
+    
+    # we didn't succeed
+    return s
+    
+_escape = re.compile(eval(r'u"[&<>\"\u0080-\uffff]"'))
+
+_escape_map = {
+    "&": "&amp;",
+    "<": "&lt;",
+    ">": "&gt;",
+    '"': "&quot;",
+}
+
+def escape_entities(m, map=_escape_map):
+    """
+    Function to encode html entities.
+    
+    @param m the match object
+    @param map the map of entities to encode
+    @return the converted text (string)
+    """
+    char = m.group()
+    text = map.get(char)
+    if text is None:
+        text = "&#%d;" % ord(char)
+    return text
+    
+def html_encode(text, pattern=_escape):
+    """
+    Function to correctly encode a text for html.
+    
+    @param text text to be encoded (string)
+    @param pattern search pattern for text to be encoded (string)
+    @return the encoded text (string)
+    """
+    if not text:
+        return ""
+    text = pattern.sub(escape_entities, text)
+    return text.encode("ascii")
+
+_uescape = re.compile(ur'[\u0080-\uffff]')
+
+def escape_uentities(m):
+    """
+    Function to encode html entities.
+    
+    @param m the match object
+    @return the converted text (string)
+    """
+    char = m.group()
+    text = "&#%d;" % ord(char)
+    return text
+    
+def html_uencode(text, pattern=_uescape):
+    """
+    Function to correctly encode a unicode text for html.
+    
+    @param text text to be encoded (string)
+    @param pattern search pattern for text to be encoded (string)
+    @return the encoded text (string)
+    """
+    if not text:
+        return ""
+    try:
+        if type(text) is not UnicodeType:
+            text = unicode(text, "utf-8")
+    except (ValueError,  LookupError):
+        pass
+    text = pattern.sub(escape_uentities, text)
+    return text.encode("ascii")
+
+def convertLineEnds(text, eol):
+    """
+    Function to convert the end of line characters.
+    
+    @param text text to be converted (string)
+    @param eol new eol setting (string)
+    @return text with converted eols (string)
+    """
+    if eol == '\r\n':
+        regexp = re.compile(r"""(\r(?!\n)|(?<!\r)\n)""")
+        return regexp.sub(lambda m, eol = '\r\n': eol, text)
+    elif eol == '\n':
+        regexp = re.compile(r"""(\r\n|\r)""")
+        return regexp.sub(lambda m, eol = '\n': eol, text)
+    elif eol == '\r':
+        regexp = re.compile(r"""(\r\n|\n)""")
+        return regexp.sub(lambda m, eol = '\r': eol, text)
+    else:
+        return text
+
+def linesep():
+    """
+    Function to return the lineseparator used by the editor.
+    
+    @return line separator used by the editor (string)
+    """
+    eolMode = Preferences.getEditor("EOLMode")
+    if eolMode == QsciScintilla.EolUnix:
+        return "\n"
+    elif eolMode == QsciScintilla.EolMac:
+        return "\r"
+    else:
+        return "\r\n"
+
+def toNativeSeparators(path):
+    """
+    Function returning a path, that is using native separator characters.
+    
+    @param path path to be converted (string)
+    @return path with converted separator characters (string)
+    """
+    return QDir.toNativeSeparators(path)
+    
+def fromNativeSeparators(path):
+    """
+    Function returning a path, that is using "/" separator characters.
+    
+    @param path path to be converted (string)
+    @return path with converted separator characters (string)
+    """
+    return QDir.fromNativeSeparators(path)
+    
+def normcasepath(path):
+    """
+    Function returning a path, that is normalized with respect to its case and references.
+    
+    @param path file path (string)
+    @return case normalized path (string)
+    """
+    return os.path.normcase(os.path.normpath(path))
+    
+def normabspath(path):
+    """
+    Function returning a normalized, absolute path.
+    
+    @param path file path (string)
+    @return absolute, normalized path (string)
+    """
+    return os.path.abspath(path)
+    
+def normcaseabspath(path):
+    """
+    Function returning an absolute path, that is normalized with respect to its case 
+    and references.
+    
+    @param path file path (string)
+    @return absolute, normalized path (string)
+    """
+    return os.path.normcase(os.path.abspath(path))
+    
+def normjoinpath(a, *p):
+    """
+    Function returning a normalized path of the joined parts passed into it.
+    
+    @param a first path to be joined (string)
+    @param p variable number of path parts to be joind (string)
+    @return normalized path (string)
+    """
+    return os.path.normpath(os.path.join(a, *p))
+    
+def normabsjoinpath(a, *p):
+    """
+    Function returning a normalized, absolute path of the joined parts passed into it.
+    
+    @param a first path to be joined (string)
+    @param p variable number of path parts to be joind (string)
+    @return absolute, normalized path (string)
+    """
+    return os.path.abspath(os.path.join(a, *p))
+    
+def relpath(path, start = os.path.curdir):
+    """
+    Return a relative version of a path.
+    
+    @param path path to make relative (string)
+    @param start path to make relative from (string)
+    """
+    if not path:
+        raise ValueError("no path specified")
+
+    start_list = os.path.abspath(start).split(os.path.sep)
+    path_list = os.path.abspath(path).split(os.path.sep)
+
+    # Work out how much of the filepath is shared by start and path.
+    i = len(os.path.commonprefix([start_list, path_list]))
+
+    rel_list = [os.path.pardir] * (len(start_list) - i) + path_list[i:]
+    if not rel_list:
+        return os.path.curdir
+    return os.path.join(*rel_list)
+
+def isinpath(file):
+    """
+    Function to check for an executable file.
+    
+    @param file filename of the executable to check (string)
+    @return flag to indicate, if the executable file is accessible
+        via the searchpath defined by the PATH environment variable.
+    """
+    if os.path.isabs(file):
+        return os.access(file, os.X_OK)
+    
+    path = getEnvironmentEntry('PATH')
+    
+    # environment variable not defined
+    if path is None:
+        return False
+    
+    dirs = path.split(os.pathsep)
+    for dir in dirs:
+        if os.access(os.path.join(dir, file), os.X_OK):
+            return True
+    
+    return False
+    
+def getExecutablePath(file):
+    """
+    Function to build the full path of an executable file from the environment.
+    
+    @param file filename of the executable to check (string)
+    @return full executable name, if the executable file is accessible
+        via the searchpath defined by the PATH environment variable, or an
+        empty string otherwise.
+    """
+    if os.path.isabs(file):
+        if os.access(file, os.X_OK):
+            return file
+        else:
+            return ""
+        
+    path = os.getenv('PATH')
+    
+    # environment variable not defined
+    if path is None:
+        return ""
+        
+    dirs = path.split(os.pathsep)
+    for dir in dirs:
+        exe = os.path.join(dir, file)
+        if os.access(exe, os.X_OK):
+            return exe
+            
+    return ""
+    
+def isExecutable(exe):
+    """
+    Function to check, if a file is executable.
+    
+    @param exe filename of the executable to check (string)
+    @return flag indicating executable status (boolean)
+    """
+    return os.access(exe, os.X_OK)
+    
+def samepath(f1, f2):
+    """
+    Function to compare two paths.
+    
+    @param f1 first path for the compare (string)
+    @param f2 second path for the compare (string)
+    @return flag indicating whether the two paths represent the
+        same path on disk.
+    """
+    if f1 is None or f2 is None:
+        return False
+    
+    if normcaseabspath(os.path.realpath(f1)) == normcaseabspath(os.path.realpath(f2)):
+        return True
+    
+    return False
+    
+try:
+    EXTSEP = os.extsep
+except AttributeError:
+    EXTSEP = "."
+
+def splitPath(name):
+    """
+    Function to split a pathname into a directory part and a file part.
+    
+    @param name path name (string)
+    @return a tuple of 2 strings (dirname, filename).
+    """
+    if os.path.isdir(name):
+        dn = os.path.abspath(name)
+        fn = "."
+    else:
+        dn, fn = os.path.split(name)
+    return (dn, fn)
+
+def joinext(prefix, ext):
+    """
+    Function to join a file extension to a path.
+    
+    The leading "." of ext is replaced by a platform specific extension
+    separator if necessary.
+    
+    @param prefix the basepart of the filename (string)
+    @param ext the extension part (string)
+    @return the complete filename (string)
+    """
+    if ext[0] != ".":
+        ext = ".%s" % ext # require leading separator, to match os.path.splitext
+    return prefix + EXTSEP + ext[1:]
+
+def compactPath(path, width, measure = len):
+    """
+    Function to return a compacted path fitting inside the given width.
+    
+    @param path path to be compacted (string)
+    @param width width for the compacted path (integer)
+    @param measure reference to a function used to measure the length of the string
+    @return compacted path (string)
+    """
+    if measure(path) <= width:
+        return path
+    
+    ellipsis = '...'
+    
+    head, tail = os.path.split(path)
+    while head:
+        path = os.path.join("%s%s" % (head, ellipsis), tail)
+        if measure(path) <= width:
+            return path
+        head = head[:-1]
+    path = os.path.join(ellipsis, tail)
+    if measure(path) <= width:
+        return path
+    while tail:
+        path = "%s%s" % (ellipsis, tail)
+        if measure(path) <= width:
+            return path
+        tail = tail[1:]
+    return ""
+    
+def direntries(path, filesonly=False, pattern=None, followsymlinks=True, checkStop=None):
+    """
+    Function returning a list of all files and directories.
+    
+    @param path root of the tree to check
+    @param filesonly flag indicating that only files are wanted
+    @param pattern a filename pattern to check against
+    @param followsymlinks flag indicating whether symbolic links
+            should be followed
+    @param checkStop function to be called to check for a stop
+    @return list of all files and directories in the tree rooted
+        at path. The names are expanded to start with path. 
+    """
+    if filesonly:
+        files = []
+    else:
+        files = [path]
+    try:
+        entries = os.listdir(path)
+        for entry in entries:
+            if checkStop and checkStop():
+                break
+            
+            if entry in ['CVS', '.svn', '_svn', '.hg', '.ropeproject']:
+                continue
+            
+            fentry = os.path.join(path, entry)
+            if pattern and \
+            not os.path.isdir(fentry) and \
+            not fnmatch.fnmatch(entry, pattern):
+                # entry doesn't fit the given pattern
+                continue
+                
+            if os.path.isdir(fentry):
+                if os.path.islink(fentry) and not followsymlinks:
+                    continue
+                files += direntries(fentry, filesonly, pattern, followsymlinks, checkStop)
+            else:
+                files.append(fentry)
+    except OSError:
+        pass
+    except UnicodeDecodeError:
+        pass
+    return files
+
+def getDirs(path, excludeDirs):
+    """
+    Function returning a list of all directories below path.
+    
+    @param path root of the tree to check
+    @param excludeDirs basename of directories to ignore
+    @return list of all directories found
+    """
+    try:
+        names = os.listdir(path)
+    except EnvironmentError:
+        return
+
+    dirs = []
+    for name in names:
+        if os.path.isdir(os.path.join(path, name)) and \
+          not os.path.islink(os.path.join(path, name)):
+            exclude = 0
+            for e in excludeDirs:
+                if name.split(os.sep,1)[0] == e:
+                    exclude = 1
+                    break
+            if not exclude:
+                dirs.append(os.path.join(path, name))
+
+    for name in dirs[:]:
+        if not os.path.islink(name):
+            dirs = dirs + getDirs(name, excludeDirs)
+
+    return dirs
+
+def getTestFileName(fn):
+    """
+    Function to build the filename of a unittest file.
+    
+    The filename for the unittest file is built by prepending
+    the string "test" to the filename passed into this function.
+    
+    @param fn filename basis to be used for the unittest filename (string)
+    @return filename of the corresponding unittest file (string)
+    """
+    dn, fn = os.path.split(fn)
+    return os.path.join(dn, "test%s" % fn)
+
+def parseOptionString(s):
+    """
+    Function used to convert an option string into a list of options.
+    
+    @param s option string (string or string)
+    @return list of options (list of strings)
+    """
+    rx = QRegExp(r"""\s([^\s]+|"[^"]+"|'[^']+')""")
+    s = re.sub(r"%[A-Z%]", _percentReplacementFunc, s)
+    return parseString(s, rx)
+    
+def parseEnvironmentString(s):
+    """
+    Function used to convert an environment string into a list of environment settings.
+    
+    @param s environment string (string)
+    @return list of environment settings (list of strings)
+    """
+    rx = QRegExp(r"""\s(\w+\+?=[^\s]+|\w+="[^"]+"|\w+='[^']+')""")
+    return parseString(s, rx)
+
+def parseString(s, rx):
+    """
+    Function used to convert a string into a list.
+    
+    @param s string to be parsed (string)
+    @param rx regex defining the parse pattern (QRegExp)
+    @return list of parsed data (list of strings)
+    """
+    olist = []
+    if not s.startswith(' '):
+        # prepare the  string to fit our pattern
+        s = ' ' + s
+        
+    pos = rx.indexIn(s)
+    while pos != -1:
+        cs = rx.cap(1)
+        if cs.startswith('"') or cs.startswith("'"):
+            cs = cs[1:-1]
+        olist.append(cs)
+        pos += rx.matchedLength()
+        pos = rx.indexIn(s, pos)
+        
+    return olist
+
+def _percentReplacementFunc(matchobj):
+    """
+    Protected function called for replacing % codes.
+    
+    @param matchobj matchobject for the code
+    @return replacement string
+    """
+    return getPercentReplacement(matchobj.group(0))
+    
+def getPercentReplacement(code):
+    """
+    Function to get the replacement for code.
+    
+    @param code code indicator (string)
+    @return replacement string (string)
+    """
+    if code in ["C", "%C"]:
+        # column of the cursor of the current editor
+        aw = e4App().getObject("ViewManager").activeWindow()
+        if aw is None:
+            column = -1
+        else:
+            column = aw.getCursorPosition()[1]
+        return "%d" % column
+    elif code in ["D", "%D"]:
+        # directory of active editor
+        aw = e4App().getObject("ViewManager").activeWindow()
+        if aw is None:
+            dn = "not_available"
+        else:
+            fn = aw.getFileName()
+            if fn is None:
+                dn = "not_available"
+            else:
+                dn = os.path.dirname(fn)
+        return dn
+    elif code in ["F", "%F"]:
+        # filename (complete) of active editor
+        aw = e4App().getObject("ViewManager").activeWindow()
+        if aw is None:
+            fn = "not_available"
+        else:
+            fn = aw.getFileName()
+            if fn is None:
+                fn = "not_available"
+        return fn
+    elif code in ["H", "%H"]:
+        # home directory
+        return getHomeDir()
+    elif code in ["L", "%L"]:
+        # line of the cursor of the current editor
+        aw = e4App().getObject("ViewManager").activeWindow()
+        if aw is None:
+            line = 0
+        else:
+            line = aw.getCursorPosition()[0] + 1
+        return "%d" % line
+    elif code in ["P", "%P"]:
+        # project path
+        projectPath = e4App().getObject("Project").getProjectPath()
+        if not projectPath:
+            projectPath = "not_available"
+        return projectPath
+    elif code in ["S", "%S"]:
+        # selected text of the current editor
+        aw = e4App().getObject("ViewManager").activeWindow()
+        if aw is None:
+            text = "not_available"
+        else:
+            text = aw.selectedText()
+        return text
+    elif code in ["U", "%U"]:
+        # username
+        un = getUserName()
+        if un is None:
+            return code
+        else:
+            return un
+    elif code in ["%", "%%"]:
+        # the percent sign
+        return "%"
+    else:
+        # unknown code, just return it
+        return code
+    
+def getPercentReplacementHelp():
+    """
+    Function to get the help text for the supported %-codes.
+    
+    @returns help text (string)
+    """
+    return QApplication.translate("Utilities", 
+        """<p>You may use %-codes as placeholders in the string."""
+        """ Supported codes are:"""
+        """<table>"""
+        """<tr><td>%C</td><td>column of the cursor of the current editor</td></tr>"""
+        """<tr><td>%D</td><td>directory of the current editor</td></tr>"""
+        """<tr><td>%F</td><td>filename of the current editor</td></tr>"""
+        """<tr><td>%H</td><td>home directory of the current user</td></tr>"""
+        """<tr><td>%L</td><td>line of the cursor of the current editor</td></tr>"""
+        """<tr><td>%P</td><td>path of the current project</td></tr>"""
+        """<tr><td>%S</td><td>selected text of the current editor</td></tr>"""
+        """<tr><td>%U</td><td>username of the current user</td></tr>"""
+        """<tr><td>%%</td><td>the percent sign</td></tr>"""
+        """</table>"""
+        """</p>""")
+
+def getUserName():
+    """
+    Function to get the user name.
+    
+    @return user name (string)
+    """
+    if isWindowsPlatform():
+        return win32_GetUserName()
+    else:
+        return posix_GetUserName()
+
+def getHomeDir():
+    """
+    Function to get a users home directory
+    
+    @return home directory (string)
+    """
+    return QDir.homePath()
+    
+def getPythonModulesDirectory():
+    """
+    Function to determine the path to Python's modules directory.
+    
+    @return path to the Python modules directory (string)
+    """
+    import distutils.sysconfig
+    return distutils.sysconfig.get_python_lib(True)
+    
+def getPythonLibPath():
+    """
+    Function to determine the path to Python's library.
+    
+    @return path to the Python library (string)
+    """
+    pyFullVers = sys.version.split()[0]
+
+    vl = re.findall("[0-9.]*", pyFullVers)[0].split(".")
+    major = vl[0]
+    minor = vl[1]
+
+    pyVers = major + "." + minor
+    pyVersNr = int(major) * 10 + int(minor)
+
+    if isWindowsPlatform():
+        libDir = sys.prefix + "\\Lib"
+    else:
+        try:
+            syslib = sys.lib
+        except AttributeError:
+            syslib = "lib"
+        libDir = sys.prefix + "/" + syslib + "/python" + pyVers
+        
+    return libDir
+    
+def getPythonVersion():
+    """
+    Function to get the Python version (major, minor) as an integer value.
+    
+    @return An integer representing major and minor version number (integer)
+    """
+    return sys.hexversion >> 16
+    
+def compile(file, codestring = ""):
+    """
+    Function to compile one Python source file to Python bytecode.
+    
+    @param file source filename (string)
+    @param codestring string containing the code to compile (string)
+    @return A tuple indicating status (1 = an error was found), the
+        filename, the linenumber, the code string and the error message
+        (boolean, string, string, string, string). The values are only
+        valid, if the status equals 1.
+    """
+    import __builtin__
+    if not codestring:
+        try:
+            f = open(file)
+            codestring, encoding = decode(f.read())
+            f.close()
+        except IOError:
+            return (0, None, None, None, None)
+
+    if type(codestring) == type(u""):
+        codestring = codestring.encode('utf-8')
+    codestring = codestring.replace("\r\n","\n")
+    codestring = codestring.replace("\r","\n")
+
+    if codestring and codestring[-1] != '\n':
+        codestring = codestring + '\n'
+    
+    try:
+        if type(file) == type(u""):
+            file = file.encode('utf-8')
+        
+        if file.endswith('.ptl'):
+            try:
+                import quixote.ptl_compile
+            except ImportError:
+                return (0, None, None, None, None)
+            template = quixote.ptl_compile.Template(codestring, file)
+            template.compile()
+            codeobject = template.code
+        else:
+            codeobject = __builtin__.compile(codestring, file, 'exec')
+    except SyntaxError, detail:
+        import traceback, re
+        lines = traceback.format_exception_only(SyntaxError, detail)
+        match = re.match('\s*File "(.+)", line (\d+)', 
+            lines[0].replace('<string>', '%s' % file))
+        if match is not None:
+            fn, line = match.group(1, 2)
+            if lines[1].startswith('SyntaxError:'):
+                code = ""
+                error = re.match('SyntaxError: (.+)', lines[1]).group(1)
+            else:
+                code = re.match('(.+)', lines[1]).group(1)
+                error = ""
+                for seLine in lines[2:]:
+                    if seLine.startswith('SyntaxError:'):
+                        error = re.match('SyntaxError: (.+)', seLine).group(1)
+        else:
+            fn = detail.filename
+            line = detail.lineno and detail.lineno or 1
+            code = ""
+            error = detail.msg
+        return (1, fn, line, code, error)
+    except ValueError, detail:
+        try:
+            fn = detail.filename
+            line = detail.lineno
+            error = detail.msg
+        except AttributeError:
+            fn = ""
+            line = 1
+            error = unicode(detail)
+        code = ""
+        return (1, fn, line, code, error)
+    except StandardError, detail:
+        try:
+            fn = detail.filename
+            line = detail.lineno
+            code = ""
+            error = detail.msg
+            return (1, fn, line, code, error)
+        except:         # this catchall is intentional
+            pass
+    
+    return (0, None, None, None, None)
+
+def getConfigDir():
+    """
+    Module function to get the name of the directory storing the config data.
+    
+    @return directory name of the config dir (string)
+    """
+    if configDir is not None and os.path.exists(configDir):
+        hp = configDir
+    else:
+        if isWindowsPlatform():
+            cdn = "_eric5"
+        else:
+            cdn = ".eric5"
+            
+        hp = QDir.homePath()
+        dn = QDir(hp)
+        dn.mkdir(cdn)
+        hp.append("/").append(cdn)
+    return toNativeSeparators(hp)
+
+def setConfigDir(d):
+    """
+    Module function to set the name of the directory storing the config data.
+    
+    @param d name of an existing directory (string)
+    """
+    global configDir
+    configDir = os.path.expanduser(d)
+
+################################################################################
+# functions for environment handling
+################################################################################
+
+def getEnvironmentEntry(key, default = None):
+    """
+    Module function to get an environment entry.
+    
+    @param key key of the requested environment entry (string)
+    @param default value to be returned, if the environment doesn't contain
+        the requested entry (string)
+    @return the requested entry or the default value, if the entry wasn't 
+        found (string or None)
+    """
+    filter = QRegExp("^%s[ \t]*=" % key)
+    if isWindowsPlatform():
+        filter.setCaseSensitivity(Qt.CaseInsensitive)
+    
+    entries = [e for e in QProcess.systemEnvironment() if filter.indexIn(e) != -1]
+    if not entries:
+        return default
+    
+    # if there are multiple entries, just consider the first one
+    ename, val = entries[0].split("=", 1)
+    return val.strip()
+
+def hasEnvironmentEntry(key):
+    """
+    Module function to check, if the environment contains an entry.
+    
+    @param key key of the requested environment entry (string)
+    @return flag indicating the presence of the requested entry (boolean)
+    """
+    filter = QRegExp("^%s[ \t]*=" % key)
+    if isWindowsPlatform():
+        filter.setCaseSensitivity(Qt.CaseInsensitive)
+    
+    entries = [e for e in QProcess.systemEnvironment() if filter.indexIn(e) != -1]
+    return len(entries) > 0
+
+################################################################################
+# Qt utility functions below
+################################################################################
+
+def generateQtToolName(toolname):
+    """
+    Module function to generate the executable name for a Qt tool like designer.
+    
+    @param toolname base name of the tool (string)
+    @return the Qt tool name without extension (string)
+    """
+    return "%s%s%s" % (Preferences.getQt("QtToolsPrefix4"), 
+                       toolname, 
+                       Preferences.getQt("QtToolsPostfix4")
+                      )
+
+def prepareQtMacBundle(toolname, version, args):
+    """
+    Module function for starting Qt tools that are Mac OS X bundles.
+
+    @param toolname  plain name of the tool (e.g. "designer") (string)
+    @param version indication for the requested version (Qt 4) (integer)
+    @param args    name of input file for tool, if any (list of strings)
+    @return command-name and args for QProcess (tuple)
+    """
+    if version == 4:
+        qtDir = Preferences.getQt("Qt4Dir")
+    else:
+        return ("", [])
+    
+    fullBundle = os.path.join(qtDir, 'bin',
+        generateQtToolName(toolname)) + ".app"
+
+    newArgs = []
+    newArgs.append("-a")
+    newArgs.append(fullBundle)
+    newArgs += args
+
+    return ("open", newArgs)
+
+################################################################################
+# Other utility functions below
+################################################################################
+
+def generateVersionInfo(linesep = '\n'):
+    """
+    Module function to generate a string with various version infos.
+    
+    @param linesep string to be used to separate lines (string)
+    @return string with version infos (string)
+    """
+    try:
+        import sipconfig
+        sip_version_str = sipconfig.Configuration().sip_version_str
+    except ImportError:
+        sip_version_str = "sip version not available"
+    
+    info =  "Version Numbers:%s  Python %s%s" % \
+        (linesep, sys.version.split()[0], linesep)
+    info += "  Qt %s%s  PyQt4 %s%s" % \
+        (str(qVersion()), linesep, str(PYQT_VERSION_STR), linesep)
+    info += "  sip %s%s  QScintilla %s%s" % \
+        (str(sip_version_str), linesep, str(QSCINTILLA_VERSION_STR), linesep)
+    info += "  %s %s%s" % \
+        (Program, Version, linesep * 2)
+    info += "Platform: %s%s%s%s" % \
+        (sys.platform, linesep, sys.version, linesep)
+    
+    return info
+
+def generatePluginsVersionInfo(linesep = '\n'):
+    """
+    Module function to generate a string with plugins version infos.
+    
+    @param linesep string to be used to separate lines (string)
+    @return string with plugins version infos (string)
+    """
+    infoStr = ""
+    app = e4App()
+    if app is not None:
+        try:
+            pm = app.getObject("PluginManager")
+            versions = {}
+            for info in pm.getPluginInfos():
+                versions[info[0]] = info[2]
+            
+            infoStr = "Plugins Version Numbers:%s" % linesep
+            for pluginName in sorted(versions.keys()):
+                infoStr += "  %s %s%s" % (pluginName, versions[pluginName], linesep)
+        except KeyError:
+            pass
+    
+    return infoStr
+
+def generateDistroInfo(linesep = '\n'):
+    """
+    Module function to generate a string with distribution infos.
+    
+    @param linesep string to be used to separate lines (string)
+    @return string with plugins version infos (string)
+    """
+    infoStr = ""
+    if sys.platform.startswith("linux"):
+        releaseList = glob.glob("/etc/*-release")
+        if releaseList:
+            infoStr = "Distribution Info:%s" % linesep
+            infoParas = []
+            for rfile in releaseList:
+                try:
+                    f = open(rfile, "r")
+                    lines = f.read().splitlines()
+                    f.close
+                except IOError:
+                    continue
+                
+                lines.insert(0, rfile)
+                infoParas.append('  ' + (linesep + '  ').join(lines))
+            infoStr += (linesep + linesep).join(infoParas)
+    
+    return infoStr
+
+################################################################################
+# password handling functions below
+################################################################################
+
+def pwEncode(pw):
+    """
+    Module function to encode a password.
+    
+    @param pw password to encode (string)
+    @return encoded password (string)
+    """
+    pop = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,;:-_!$?*+#"
+    marker = "CE4"
+    rpw = "".join(random.sample(pop, 32)) + pw + "".join(random.sample(pop, 32))
+    return marker + base64.b64encode(rpw)
+
+def pwDecode(epw):
+    """
+    Module function to decode a password.
+    
+    @param pw encoded password to decode (string)
+    @return decoded password (string)
+    """
+    if not epw.startswith("CE4"):
+        return epw  # it was not encoded using pwEncode
+    
+    return base64.b64decode(epw[3:])[32:-32]
+
+################################################################################
+# posix compatibility functions below
+################################################################################
+
+def posix_GetUserName():
+    """
+    Function to get the user name under Posix systems.
+    
+    @return user name (string)
+    """
+    try:
+        import pwd
+        return pwd.getpwuid(os.getuid())[0]
+    except ImportError:
+        try:
+            u = getEnvironmentEntry('USER')
+        except KeyError:
+            u = getEnvironmentEntry('user', None)
+        return u
+
+################################################################################
+# win32 compatibility functions below
+################################################################################
+
+def win32_Kill(pid):
+    """
+    Function to provide an os.kill equivalent for Win32.
+    
+    @param pid process id
+    """
+    import win32api
+    handle = win32api.OpenProcess(1, 0, pid)
+    return (0 != win32api.TerminateProcess(handle, 0))
+
+def win32_GetUserName():
+    """
+    Function to get the user name under Win32.
+    
+    @return user name (string)
+    """
+    try:
+        import win32api
+        return win32api.GetUserName()
+    except ImportError:
+        try:
+            u = getEnvironmentEntry('USERNAME')
+        except KeyError:
+            u = getEnvironmentEntry('username', None)
+        return u

eric ide

mercurial