diff -r 87cfa131b535 -r e7e0ae78773e configurationengine/source/cone/public/utils.py --- 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__ == '' 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__ == '' 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__ == '' 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