configurationengine/source/cone/public/utils.py
changeset 3 e7e0ae78773e
parent 0 2e8eeb919028
child 5 d2c80f5cab53
equal deleted inserted replaced
2:87cfa131b535 3:e7e0ae78773e
    15 #
    15 #
    16 
    16 
    17 
    17 
    18 import os
    18 import os
    19 import re
    19 import re
       
    20 import posixpath
    20 import StringIO
    21 import StringIO
    21 import tokenize
    22 import tokenize
    22 import inspect
    23 import inspect
    23 import traceback
    24 import traceback
    24 import logging
    25 import logging
       
    26 import shlex
       
    27 from xml.parsers import expat
    25 import imghdr
    28 import imghdr
    26 from token import ENDMARKER, NAME, NUMBER, STRING
    29 from token import ENDMARKER, NAME, NUMBER, STRING
    27 import api
    30 from cone.public import exceptions
    28 import mimetypes
       
    29 import exceptions
       
    30 
    31 
    31 import _etree_wrapper
    32 import _etree_wrapper
    32 etree = _etree_wrapper.ElementTreeWrapper()
    33 etree = _etree_wrapper.ElementTreeWrapper()
    33 
    34 
    34 class resourceref(object):
    35 class resourceref(object):
    59             return '/' + ref
    60             return '/' + ref
    60         return ref
    61         return ref
    61     
    62     
    62     @classmethod
    63     @classmethod
    63     def remove_begin_slash(cls, ref):
    64     def remove_begin_slash(cls, ref):
    64         if ref.startswith('/'): 
    65         while ref.startswith('/'): 
    65             return ref.replace('/', '', 1)
    66             ref = ref.replace('/', '', 1)
    66         return ref
    67         return ref
    67 
    68     
    68     @classmethod
    69     @classmethod
    69     def remove_end(self, path, str):
    70     def remove_end(self, path, str):
    70         try:
    71         try:
    71             (ret, sep, rest) = path.partition(str)
    72             (ret, sep, rest) = path.partition(str)
    72             return ret
    73             return ret
    96         """
    97         """
    97         
    98         
    98         # Do not modify emtpy string at all
    99         # Do not modify emtpy string at all
    99         if not ref == '':
   100         if not ref == '':
   100             normref = os.path.normpath(ref)
   101             normref = os.path.normpath(ref)
   101             normref = normref.replace('\\','/').replace('"','').replace('//','/')
   102             normref = normref.replace('\\','/').replace('"','')
       
   103             normref = posixpath.normpath(normref)
       
   104             normref = normref.rstrip('\\/')
   102         else:
   105         else:
   103             normref = ref
   106             normref = ref
   104         return normref
   107         return normref
   105     
   108     
   106     @classmethod
   109     @classmethod
   191 
   194 
   192     @classmethod
   195     @classmethod
   193     def get_filename(cls, ref):
   196     def get_filename(cls, ref):
   194         """
   197         """
   195         get file name part from ref 
   198         get file name part from ref 
   196         1. get file extension
   199         1. get file name
   197         @return: a reference. E.g. (foo/test.confml) => confml
   200         @return: a reference. E.g. (foo/test.confml) => test.confml
   198         """
   201         """
   199         return ref.rsplit('/', 1)[-1]
   202         return ref.rsplit('/', 1)[-1]
   200 
   203 
   201     @classmethod
   204     @classmethod
   202     def get_path(cls, ref):
   205     def get_path(cls, ref):
   203         """
   206         """
   204         get file name part from ref 
   207         get path part from ref 
   205         1. get file extension
   208         1. get path from ref
   206         @return: a reference. E.g. (foo/test.confml) => confml
   209         @return: a reference. E.g. (foo/test.confml) => foo
   207         """
   210         """
   208         if len(ref.rsplit('/', 1)) == 2: 
   211         if len(ref.rsplit('/', 1)) == 2: 
   209             return ref.rsplit('/', 1)[0]
   212             return ref.rsplit('/', 1)[0]
   210         else:
   213         else:
   211             return ""
   214             return ""
   230         2. convert path delims to dots
   233         2. convert path delims to dots
   231         3. using double underscores for directory separation
   234         3. using double underscores for directory separation
   232         @return: a dotted reference. E.g. (foo/test.confml) => foo_test
   235         @return: a dotted reference. E.g. (foo/test.confml) => foo_test
   233         """
   236         """
   234         ref = ref.replace('/', '__')
   237         ref = ref.replace('/', '__')
       
   238         # Change the python comment character also as underscore so that the tokenizer 
       
   239         # does not leave anything out
       
   240         ref = ref.replace('#', '_')
   235         newref = ''
   241         newref = ''
   236         first_token = True
   242         first_token = True
   237         try:
   243         try:
   238             for toknum, tokval, spos, epos, _ in tokenize.generate_tokens(StringIO.StringIO(unicode(ref)).readline):
   244             for toknum, tokval, spos, epos, _ in tokenize.generate_tokens(StringIO.StringIO(unicode(ref)).readline):
   239                 if toknum == ENDMARKER:
   245                 if toknum == ENDMARKER:
   272         Convert a resource ref to to hash 32 bit integer
   278         Convert a resource ref to to hash 32 bit integer
   273         @return: 
   279         @return: 
   274         """
   280         """
   275         return "%s" % hex(hash(ref))
   281         return "%s" % hex(hash(ref))
   276 
   282 
       
   283     @classmethod
       
   284     def is_path(cls, ref):
       
   285         """
       
   286         returns true if the ref seems like a path
       
   287         @return: Boolean value [True|False]
       
   288         """
       
   289         if cls.get_ext(ref) or cls.get_path(ref):
       
   290             return True
       
   291         return False
       
   292 
   277 class dottedref(object):
   293 class dottedref(object):
   278     """
   294     """
   279     Class container for set of dotted reference related functions
   295     Class container for set of dotted reference related functions
   280     """
   296     """
   281     @classmethod
   297     @classmethod
   376                 elems.append(".*")
   392                 elems.append(".*")
   377             else:
   393             else:
   378                 elems.append(refelem.replace("*","[^\.]*"))
   394                 elems.append(refelem.replace("*","[^\.]*"))
   379         return "\\.".join(elems)+"$"
   395         return "\\.".join(elems)+"$"
   380     
   396     
       
   397     @classmethod
       
   398     def has_wildcard(cls, ref):
       
   399         """
       
   400         Tests if the ref has any wildcards '*' in it.
       
   401         @return: Boolean value. True when wildcards are found.
       
   402         """
       
   403         return ref.find('*') != -1
       
   404 
       
   405     @classmethod
       
   406     def get_static_ref(cls, ref):
       
   407         """
       
   408         Checks if the ref has any wildcards and return the non wildcard part of ref.
       
   409         @return: string.
       
   410         """
       
   411         retparts = []
       
   412         for part in cls.split_ref(ref):
       
   413             if cls.has_wildcard(part):
       
   414                 break
       
   415             else:
       
   416                 retparts.append(part)
       
   417         return ".".join(retparts)
       
   418 
   381 def extract_delimited_tokens(string, delimiters=('${', '}')):
   419 def extract_delimited_tokens(string, delimiters=('${', '}')):
   382     """
   420     """
   383     Return a list of all tokens delimited by the given strings in the given string.
   421     Return a list of all tokens delimited by the given strings in the given string.
   384     This function returns basically the first row of the result of
   422     This function returns basically the first row of the result of
   385     extract_delimited_token_tuples(), with duplicates removed.
   423     extract_delimited_token_tuples(), with duplicates removed.
   386     
   424     
   387     >>> dottedref.extract_refs("test1 ${my.ref1} test2 ${ my.ref1 } ${my.ref2}")
   425     >>> dottedref.extract_refs("test1 ${my.ref1} test2 ${ my.ref1 } ${my.ref2}")
   388     ['my.ref1', 'my.ref2']
   426     ['my.ref1', 'my.ref2']
   389     """
   427     """
   390     ref_tuples = extract_delimited_token_tuples(string, delimiters)
   428     ref_tuples = extract_delimited_token_tuples(string, delimiters)
   391     return distinct_array([ref for ref, raw_ref in ref_tuples])
   429     return distinct_array([u'%s' % ref for ref, raw_ref in ref_tuples])
   392 
   430 
   393 def extract_delimited_token_tuples(string, delimiters=('${', '}')):
   431 def extract_delimited_token_tuples(string, delimiters=('${', '}')):
   394     """
   432     """
   395     Extract a list of (token, raw_token) tuples from the given string.
   433     Extract a list of (token, raw_token) tuples from the given string.
   396     'token' is the the token extracted from the string and trimmed (surrounding
   434     'token' is the the token extracted from the string and trimmed (surrounding
   441     for entry in tokens.itervalues():
   479     for entry in tokens.itervalues():
   442         for raw_token in entry.raw_tokens:
   480         for raw_token in entry.raw_tokens:
   443             result = result.replace(raw_token, entry.value)
   481             result = result.replace(raw_token, entry.value)
   444     return result
   482     return result
   445 
   483 
   446 def expand_refs_by_default_view(string, default_view, delimiters=('${', '}'), default_value_for_missing=''):
   484 def expand_refs_by_default_view(string, default_view, delimiters=('${', '}'), default_value_for_missing='',
       
   485                                 catch_not_found=True):
   447     """
   486     """
   448     Convenience function for expanding the refs in a string using setting values.
   487     Convenience function for expanding the refs in a string using setting values.
   449     @param default_value_for_missing: The default value used if a setting for
   488     @param default_value_for_missing: The default value used if a setting for
   450         a reference cannot be found.
   489         a reference cannot be found. Has no effect if catch_not_found is False.
       
   490     @param catch_not_found: If True, the NotFound exception raised when a setting
       
   491         is not found is caught and the value of default_value_for_missing is inserted
       
   492         in its place.
   451     @return: The expanded string.
   493     @return: The expanded string.
   452     """
   494     """
   453     def expand(ref, index):
   495     def expand(ref, index):
   454         try:
   496         if catch_not_found:
       
   497             try:
       
   498                 return default_view.get_feature(ref).get_original_value()
       
   499             except exceptions.NotFound:
       
   500                 logging.getLogger('cone').error("Feature '%s' not found" % ref)
       
   501                 return default_value_for_missing
       
   502         else:
   455             return default_view.get_feature(ref).get_original_value()
   503             return default_view.get_feature(ref).get_original_value()
   456         except exceptions.NotFound:
       
   457             logging.getLogger('cone').error("Feature '%s' not found" % ref)
       
   458             return default_value_for_missing
       
   459     return expand_delimited_tokens(string, expand, delimiters)
   504     return expand_delimited_tokens(string, expand, delimiters)
   460 
   505 
   461 def distinct_array(arr):
   506 def distinct_array(arr):
   462     newarray = []
   507     newarray = []
   463     for val in arr:
   508     for val in arr:
   525     return retlist
   570     return retlist
   526 
   571 
   527 def is_list(elem):
   572 def is_list(elem):
   528     return isinstance(elem, list)
   573     return isinstance(elem, list)
   529 
   574 
       
   575 def is_float(value):
       
   576     """
       
   577     Test if the fiven value (which can be a string) is a floating point value. 
       
   578     """
       
   579     fvalue = float(value)
       
   580     ivalue = int(fvalue)
       
   581     
       
   582     return (fvalue - ivalue) != 0
       
   583 
   530 def get_class(modelinstance, classinstance):
   584 def get_class(modelinstance, classinstance):
   531     """
   585     """
   532     Get the actual model specific implementation class for a classinstance
   586     Get the actual model specific implementation class for a classinstance
   533     """
   587     """
   534     for attr in dir(modelinstance):
   588     for attr in dir(modelinstance):
   536         if inspect.isclass(modelclass): 
   590         if inspect.isclass(modelclass): 
   537             if issubclass(modelclass, classinstance):
   591             if issubclass(modelclass, classinstance):
   538                 return modelclass
   592                 return modelclass
   539     return classinstance
   593     return classinstance
   540 
   594 
   541 class DataMapRef(object):
   595 class OProperty(object):
   542     """
   596     """Based on the emulation of PyProperty_Type() in Objects/descrobject.c
   543     Utility class for handling map attributes in data section
   597     from http://infinitesque.net/articles/2005/enhancing%20Python%27s%20property.xhtml"""
   544     """
   598     def __init__(self, fget=None, fset=None, fdel=None, doc=None):
   545     @classmethod
   599         self.fget = fget
   546     def get_feature_ref(cls, map):
   600         self.fset = fset
   547         index = map.find("@")
   601         self.fdel = fdel
   548         if index != -1:
   602         self.__doc__ = doc
   549             parts = map.split("@")
   603  
   550             return parts[0][:-1]
   604     def __get__(self, obj, objtype=None):
   551         else:
   605         if obj is None:
   552             return None
   606             return self
   553         
   607         if self.fget is None:
   554     @classmethod
   608             raise AttributeError, "unreadable attribute"
   555     def get_key_value(cls, map):
   609         if self.fget.__name__ == '<lambda>' or not self.fget.__name__:
   556         index = map.find("@")
   610             return self.fget(obj)
   557         if index != -1:
   611         else:
   558             parts = map.split("@")
   612             return getattr(obj, self.fget.__name__)()
   559             key = parts[1][:-1]
   613  
   560             keys = key.split("=")
   614     def __set__(self, obj, value):
   561             value = keys[1].strip()
   615         if self.fset is None:
   562             return value[1:-1]
   616             raise AttributeError, "can't set attribute"
   563         else:
   617         if self.fset.__name__ == '<lambda>' or not self.fset.__name__:
   564             return None
   618             self.fset(obj, value)
   565         
   619         else:
       
   620             getattr(obj, self.fset.__name__)(value)
       
   621  
       
   622     def __delete__(self, obj):
       
   623         if self.fdel is None:
       
   624             raise AttributeError, "can't delete attribute"
       
   625         if self.fdel.__name__ == '<lambda>' or not self.fdel.__name__:
       
   626             self.fdel(obj)
       
   627         else:
       
   628             getattr(obj, self.fdel.__name__)()
   566 
   629 
   567 class xml(object):
   630 class xml(object):
   568     """
   631     """
   569     Class container for set of XML-related helper functions.
   632     Class container for set of XML-related helper functions.
   570     """
   633     """
   583             parts = xml_tag[1:].split('}')
   646             parts = xml_tag[1:].split('}')
   584             return (parts[0], parts[1])
   647             return (parts[0], parts[1])
   585         else:
   648         else:
   586             return (None, xml_tag)
   649             return (None, xml_tag)
   587 
   650 
       
   651     @classmethod
       
   652     def get_xml_root(cls, resource):
       
   653         """
       
   654         Get a (namespace, tag) tuple of the root element in the XML data
       
   655         read from the given resource.
       
   656         
       
   657         @param resource:  The resource from which to read data. Should be a
       
   658             file-like object (i.e. should have a read() method).
       
   659         @return: A (namespace, tag) tuple. Note that the namespace may
       
   660             be None.
       
   661         
       
   662         @raise exceptions.XmlParseError: The resource contains invalid XML data.
       
   663         """
       
   664         class RootElementFound(RuntimeError):
       
   665             def __init__(self, root_name):
       
   666                 self.root_name = root_name
       
   667         
       
   668         def handle_start(name, attrs):
       
   669             raise RootElementFound(name)
       
   670         
       
   671         p = expat.ParserCreate(namespace_separator=':')
       
   672         p.StartElementHandler = handle_start
       
   673         
       
   674         BUFSIZE = 128
       
   675         while True:
       
   676             data = resource.read(BUFSIZE)
       
   677             try:
       
   678                 p.Parse(data, len(data) < BUFSIZE)
       
   679             except RootElementFound, e:
       
   680                 parts = e.root_name.rsplit(':', 1)
       
   681                 if len(parts) > 1:
       
   682                     return parts[0], parts[1]
       
   683                 else:
       
   684                     return None, parts[0]
       
   685             except expat.ExpatError, e:
       
   686                 raise exceptions.XmlParseError(
       
   687                     "XML parse error on line %d: %s" % (e.lineno, e),
       
   688                     e.lineno, str(e))
       
   689 
       
   690 def update_dict(todict, fromdict):
       
   691     """
       
   692     Merges the elements of two dictionaries together.
       
   693     @param todict: the target dictionary where data is merged. 
       
   694     @param fromdict: the source dict where data is read 
       
   695     @return: the modified todict.  
       
   696     """
       
   697     for key in fromdict:
       
   698         todict.setdefault(key, []).extend(fromdict[key])
       
   699     return todict
       
   700 
   588 def log_exception(logger, msg, msg_level=logging.ERROR, traceback_level=logging.DEBUG):
   701 def log_exception(logger, msg, msg_level=logging.ERROR, traceback_level=logging.DEBUG):
   589     """
   702     """
   590     Log an exception so that the given message and the exception's
   703     Log an exception so that the given message and the exception's
   591     traceback are logged separately with the given log levels.
   704     traceback are logged separately with the given log levels.
   592     
   705     
   598     Note that this function should be only used in an exception handler.
   711     Note that this function should be only used in an exception handler.
   599     """
   712     """
   600     logger.log(msg_level, msg)
   713     logger.log(msg_level, msg)
   601     logger.log(traceback_level, traceback.format_exc())
   714     logger.log(traceback_level, traceback.format_exc())
   602 
   715 
   603 def make_content_info(resource, data):
   716 
   604     """
   717 def grep(string,list):
   605     Factory for ContentInfo
   718     """
   606     """
   719     Grep throught the items in the given list to find matching entries. 
   607     cnt_inf = None
   720     """
   608     
   721     expr = re.compile(string)
   609     if resource != None:
   722     return filter(expr.search,list)
   610         guessed_type = mimetypes.guess_type(resource.get_path())
   723 
   611         mimetype = None
   724 def grep_tuple(string,list):
   612         mimesubtype = None
   725     """
   613         
   726     Grep throught the items in the given list to find matching entries. 
   614         if guessed_type != None:
   727     @return: a list of tuples (index,text) 
   615             mimetype, mimesubtype = guessed_type[0].split('/') 
   728     """
   616         
   729     results = []
   617         if mimetype == 'image' and mimesubtype == 'x-ms-bmp':
   730     expr = re.compile(string)
   618             cnt_inf = api.BmpImageContentInfo(resource, data)
   731     for (index,text) in enumerate(list):
   619         else:
   732         match = expr.search(text)
   620             cnt_inf = api.ContentInfo(mimetype, mimesubtype)
   733         if match != None:
   621     return cnt_inf
   734             results.append((index,match.string))
       
   735     return results
       
   736 
       
   737 def grep_dict(string,list):
       
   738     """
       
   739     Grep throught the items in the given list to find matching entries. 
       
   740     @return: a dictionary with list index as key and matching text as value.
       
   741     """
       
   742     results = {}
       
   743     expr = re.compile(string)
       
   744     for (index,text) in enumerate(list):
       
   745         match = expr.search(text)
       
   746         if match != None:
       
   747             results[index]  = match.string
       
   748     return results
       
   749 
       
   750 def cmdsplit(s, comments=False, os_name='nt'):
       
   751     """
       
   752     Copy of shlex split method to allow parsing of command line parameters in operating system specific mode.
       
   753     
       
   754     """
       
   755     posix = True
       
   756     lex = shlex.shlex(s, posix=posix)
       
   757     lex.whitespace_split = True
       
   758     if not comments:
       
   759         lex.commenters = ''
       
   760     if os_name == 'nt':
       
   761         lex.escape = '^'
       
   762     return list(lex)
       
   763 
       
   764 
       
   765 import sys
       
   766 sys_version = "%d.%d" % (sys.version_info[0],sys.version_info[1])
       
   767 if sys_version >= "2.6":
       
   768     def relpath(path, start=os.curdir):
       
   769         return os.path.relpath(path, start)
       
   770 else:
       
   771     def relpath(path, start=os.curdir):
       
   772         """Return a relative version of a path"""
       
   773     
       
   774         if not path:
       
   775             raise ValueError("no path specified")
       
   776         start_list = os.path.abspath(start).split(os.sep)
       
   777         path_list = os.path.abspath(path).split(os.sep)
       
   778         if start_list[0].lower() != path_list[0].lower():
       
   779             unc_path, rest = os.path.splitunc(path)
       
   780             unc_start, rest = os.path.splitunc(start)
       
   781             if bool(unc_path) ^ bool(unc_start):
       
   782                 raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
       
   783                                                                     % (path, start))
       
   784             else:
       
   785                 raise ValueError("path is on drive %s, start on drive %s"
       
   786                                                     % (path_list[0], start_list[0]))
       
   787         # Work out how much of the filepath is shared by start and path.
       
   788         for i in range(min(len(start_list), len(path_list))):
       
   789             if start_list[i].lower() != path_list[i].lower():
       
   790                 break
       
   791         else:
       
   792             i += 1
       
   793     
       
   794         rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:]
       
   795         if not rel_list:
       
   796             return os.curdir
       
   797         return os.path.join(*rel_list)