configurationengine/source/cone/public/utils.py
changeset 3 e7e0ae78773e
parent 0 2e8eeb919028
child 5 d2c80f5cab53
--- a/configurationengine/source/cone/public/utils.py	Fri Mar 12 08:30:17 2010 +0200
+++ b/configurationengine/source/cone/public/utils.py	Tue Aug 10 14:29:28 2010 +0300
@@ -17,16 +17,17 @@
 
 import os
 import re
+import posixpath
 import StringIO
 import tokenize
 import inspect
 import traceback
 import logging
+import shlex
+from xml.parsers import expat
 import imghdr
 from token import ENDMARKER, NAME, NUMBER, STRING
-import api
-import mimetypes
-import exceptions
+from cone.public import exceptions
 
 import _etree_wrapper
 etree = _etree_wrapper.ElementTreeWrapper()
@@ -61,10 +62,10 @@
     
     @classmethod
     def remove_begin_slash(cls, ref):
-        if ref.startswith('/'): 
-            return ref.replace('/', '', 1)
+        while ref.startswith('/'): 
+            ref = ref.replace('/', '', 1)
         return ref
-
+    
     @classmethod
     def remove_end(self, path, str):
         try:
@@ -98,7 +99,9 @@
         # Do not modify emtpy string at all
         if not ref == '':
             normref = os.path.normpath(ref)
-            normref = normref.replace('\\','/').replace('"','').replace('//','/')
+            normref = normref.replace('\\','/').replace('"','')
+            normref = posixpath.normpath(normref)
+            normref = normref.rstrip('\\/')
         else:
             normref = ref
         return normref
@@ -193,17 +196,17 @@
     def get_filename(cls, ref):
         """
         get file name part from ref 
-        1. get file extension
-        @return: a reference. E.g. (foo/test.confml) => confml
+        1. get file name
+        @return: a reference. E.g. (foo/test.confml) => test.confml
         """
         return ref.rsplit('/', 1)[-1]
 
     @classmethod
     def get_path(cls, ref):
         """
-        get file name part from ref 
-        1. get file extension
-        @return: a reference. E.g. (foo/test.confml) => confml
+        get path part from ref 
+        1. get path from ref
+        @return: a reference. E.g. (foo/test.confml) => foo
         """
         if len(ref.rsplit('/', 1)) == 2: 
             return ref.rsplit('/', 1)[0]
@@ -232,6 +235,9 @@
         @return: a dotted reference. E.g. (foo/test.confml) => foo_test
         """
         ref = ref.replace('/', '__')
+        # Change the python comment character also as underscore so that the tokenizer 
+        # does not leave anything out
+        ref = ref.replace('#', '_')
         newref = ''
         first_token = True
         try:
@@ -274,6 +280,16 @@
         """
         return "%s" % hex(hash(ref))
 
+    @classmethod
+    def is_path(cls, ref):
+        """
+        returns true if the ref seems like a path
+        @return: Boolean value [True|False]
+        """
+        if cls.get_ext(ref) or cls.get_path(ref):
+            return True
+        return False
+
 class dottedref(object):
     """
     Class container for set of dotted reference related functions
@@ -378,6 +394,28 @@
                 elems.append(refelem.replace("*","[^\.]*"))
         return "\\.".join(elems)+"$"
     
+    @classmethod
+    def has_wildcard(cls, ref):
+        """
+        Tests if the ref has any wildcards '*' in it.
+        @return: Boolean value. True when wildcards are found.
+        """
+        return ref.find('*') != -1
+
+    @classmethod
+    def get_static_ref(cls, ref):
+        """
+        Checks if the ref has any wildcards and return the non wildcard part of ref.
+        @return: string.
+        """
+        retparts = []
+        for part in cls.split_ref(ref):
+            if cls.has_wildcard(part):
+                break
+            else:
+                retparts.append(part)
+        return ".".join(retparts)
+
 def extract_delimited_tokens(string, delimiters=('${', '}')):
     """
     Return a list of all tokens delimited by the given strings in the given string.
@@ -388,7 +426,7 @@
     ['my.ref1', 'my.ref2']
     """
     ref_tuples = extract_delimited_token_tuples(string, delimiters)
-    return distinct_array([ref for ref, raw_ref in ref_tuples])
+    return distinct_array([u'%s' % ref for ref, raw_ref in ref_tuples])
 
 def extract_delimited_token_tuples(string, delimiters=('${', '}')):
     """
@@ -443,19 +481,26 @@
             result = result.replace(raw_token, entry.value)
     return result
 
-def expand_refs_by_default_view(string, default_view, delimiters=('${', '}'), default_value_for_missing=''):
+def expand_refs_by_default_view(string, default_view, delimiters=('${', '}'), default_value_for_missing='',
+                                catch_not_found=True):
     """
     Convenience function for expanding the refs in a string using setting values.
     @param default_value_for_missing: The default value used if a setting for
-        a reference cannot be found.
+        a reference cannot be found. Has no effect if catch_not_found is False.
+    @param catch_not_found: If True, the NotFound exception raised when a setting
+        is not found is caught and the value of default_value_for_missing is inserted
+        in its place.
     @return: The expanded string.
     """
     def expand(ref, index):
-        try:
+        if catch_not_found:
+            try:
+                return default_view.get_feature(ref).get_original_value()
+            except exceptions.NotFound:
+                logging.getLogger('cone').error("Feature '%s' not found" % ref)
+                return default_value_for_missing
+        else:
             return default_view.get_feature(ref).get_original_value()
-        except exceptions.NotFound:
-            logging.getLogger('cone').error("Feature '%s' not found" % ref)
-            return default_value_for_missing
     return expand_delimited_tokens(string, expand, delimiters)
 
 def distinct_array(arr):
@@ -527,6 +572,15 @@
 def is_list(elem):
     return isinstance(elem, list)
 
+def is_float(value):
+    """
+    Test if the fiven value (which can be a string) is a floating point value. 
+    """
+    fvalue = float(value)
+    ivalue = int(fvalue)
+    
+    return (fvalue - ivalue) != 0
+
 def get_class(modelinstance, classinstance):
     """
     Get the actual model specific implementation class for a classinstance
@@ -538,31 +592,40 @@
                 return modelclass
     return classinstance
 
-class DataMapRef(object):
-    """
-    Utility class for handling map attributes in data section
-    """
-    @classmethod
-    def get_feature_ref(cls, map):
-        index = map.find("@")
-        if index != -1:
-            parts = map.split("@")
-            return parts[0][:-1]
+class OProperty(object):
+    """Based on the emulation of PyProperty_Type() in Objects/descrobject.c
+    from http://infinitesque.net/articles/2005/enhancing%20Python%27s%20property.xhtml"""
+    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
+        self.fget = fget
+        self.fset = fset
+        self.fdel = fdel
+        self.__doc__ = doc
+ 
+    def __get__(self, obj, objtype=None):
+        if obj is None:
+            return self
+        if self.fget is None:
+            raise AttributeError, "unreadable attribute"
+        if self.fget.__name__ == '<lambda>' or not self.fget.__name__:
+            return self.fget(obj)
         else:
-            return None
-        
-    @classmethod
-    def get_key_value(cls, map):
-        index = map.find("@")
-        if index != -1:
-            parts = map.split("@")
-            key = parts[1][:-1]
-            keys = key.split("=")
-            value = keys[1].strip()
-            return value[1:-1]
+            return getattr(obj, self.fget.__name__)()
+ 
+    def __set__(self, obj, value):
+        if self.fset is None:
+            raise AttributeError, "can't set attribute"
+        if self.fset.__name__ == '<lambda>' or not self.fset.__name__:
+            self.fset(obj, value)
         else:
-            return None
-        
+            getattr(obj, self.fset.__name__)(value)
+ 
+    def __delete__(self, obj):
+        if self.fdel is None:
+            raise AttributeError, "can't delete attribute"
+        if self.fdel.__name__ == '<lambda>' or not self.fdel.__name__:
+            self.fdel(obj)
+        else:
+            getattr(obj, self.fdel.__name__)()
 
 class xml(object):
     """
@@ -585,6 +648,56 @@
         else:
             return (None, xml_tag)
 
+    @classmethod
+    def get_xml_root(cls, resource):
+        """
+        Get a (namespace, tag) tuple of the root element in the XML data
+        read from the given resource.
+        
+        @param resource:  The resource from which to read data. Should be a
+            file-like object (i.e. should have a read() method).
+        @return: A (namespace, tag) tuple. Note that the namespace may
+            be None.
+        
+        @raise exceptions.XmlParseError: The resource contains invalid XML data.
+        """
+        class RootElementFound(RuntimeError):
+            def __init__(self, root_name):
+                self.root_name = root_name
+        
+        def handle_start(name, attrs):
+            raise RootElementFound(name)
+        
+        p = expat.ParserCreate(namespace_separator=':')
+        p.StartElementHandler = handle_start
+        
+        BUFSIZE = 128
+        while True:
+            data = resource.read(BUFSIZE)
+            try:
+                p.Parse(data, len(data) < BUFSIZE)
+            except RootElementFound, e:
+                parts = e.root_name.rsplit(':', 1)
+                if len(parts) > 1:
+                    return parts[0], parts[1]
+                else:
+                    return None, parts[0]
+            except expat.ExpatError, e:
+                raise exceptions.XmlParseError(
+                    "XML parse error on line %d: %s" % (e.lineno, e),
+                    e.lineno, str(e))
+
+def update_dict(todict, fromdict):
+    """
+    Merges the elements of two dictionaries together.
+    @param todict: the target dictionary where data is merged. 
+    @param fromdict: the source dict where data is read 
+    @return: the modified todict.  
+    """
+    for key in fromdict:
+        todict.setdefault(key, []).extend(fromdict[key])
+    return todict
+
 def log_exception(logger, msg, msg_level=logging.ERROR, traceback_level=logging.DEBUG):
     """
     Log an exception so that the given message and the exception's
@@ -600,22 +713,85 @@
     logger.log(msg_level, msg)
     logger.log(traceback_level, traceback.format_exc())
 
-def make_content_info(resource, data):
+
+def grep(string,list):
+    """
+    Grep throught the items in the given list to find matching entries. 
+    """
+    expr = re.compile(string)
+    return filter(expr.search,list)
+
+def grep_tuple(string,list):
+    """
+    Grep throught the items in the given list to find matching entries. 
+    @return: a list of tuples (index,text) 
     """
-    Factory for ContentInfo
+    results = []
+    expr = re.compile(string)
+    for (index,text) in enumerate(list):
+        match = expr.search(text)
+        if match != None:
+            results.append((index,match.string))
+    return results
+
+def grep_dict(string,list):
     """
-    cnt_inf = None
+    Grep throught the items in the given list to find matching entries. 
+    @return: a dictionary with list index as key and matching text as value.
+    """
+    results = {}
+    expr = re.compile(string)
+    for (index,text) in enumerate(list):
+        match = expr.search(text)
+        if match != None:
+            results[index]  = match.string
+    return results
+
+def cmdsplit(s, comments=False, os_name='nt'):
+    """
+    Copy of shlex split method to allow parsing of command line parameters in operating system specific mode.
     
-    if resource != None:
-        guessed_type = mimetypes.guess_type(resource.get_path())
-        mimetype = None
-        mimesubtype = None
-        
-        if guessed_type != None:
-            mimetype, mimesubtype = guessed_type[0].split('/') 
-        
-        if mimetype == 'image' and mimesubtype == 'x-ms-bmp':
-            cnt_inf = api.BmpImageContentInfo(resource, data)
+    """
+    posix = True
+    lex = shlex.shlex(s, posix=posix)
+    lex.whitespace_split = True
+    if not comments:
+        lex.commenters = ''
+    if os_name == 'nt':
+        lex.escape = '^'
+    return list(lex)
+
+
+import sys
+sys_version = "%d.%d" % (sys.version_info[0],sys.version_info[1])
+if sys_version >= "2.6":
+    def relpath(path, start=os.curdir):
+        return os.path.relpath(path, start)
+else:
+    def relpath(path, start=os.curdir):
+        """Return a relative version of a path"""
+    
+        if not path:
+            raise ValueError("no path specified")
+        start_list = os.path.abspath(start).split(os.sep)
+        path_list = os.path.abspath(path).split(os.sep)
+        if start_list[0].lower() != path_list[0].lower():
+            unc_path, rest = os.path.splitunc(path)
+            unc_start, rest = os.path.splitunc(start)
+            if bool(unc_path) ^ bool(unc_start):
+                raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
+                                                                    % (path, start))
+            else:
+                raise ValueError("path is on drive %s, start on drive %s"
+                                                    % (path_list[0], start_list[0]))
+        # Work out how much of the filepath is shared by start and path.
+        for i in range(min(len(start_list), len(path_list))):
+            if start_list[i].lower() != path_list[i].lower():
+                break
         else:
-            cnt_inf = api.ContentInfo(mimetype, mimesubtype)
-    return cnt_inf
+            i += 1
+    
+        rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:]
+        if not rel_list:
+            return os.curdir
+        return os.path.join(*rel_list) 
\ No newline at end of file