diff options
Diffstat (limited to 'indra/lib')
-rw-r--r-- | indra/lib/python/indra/base/llsd.py | 142 | ||||
-rw-r--r-- | indra/lib/python/indra/util/fastest_elementtree.py | 29 |
2 files changed, 121 insertions, 50 deletions
diff --git a/indra/lib/python/indra/base/llsd.py b/indra/lib/python/indra/base/llsd.py index c841cdaf63..521b79c65a 100644 --- a/indra/lib/python/indra/base/llsd.py +++ b/indra/lib/python/indra/base/llsd.py @@ -4,7 +4,7 @@ $LicenseInfo:firstyear=2006&license=mit$ -Copyright (c) 2006-2007, Linden Research, Inc. +Copyright (c) 2006-2008, Linden Research, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -33,19 +33,26 @@ import time import types import re -from indra.util.fastest_elementtree import fromstring +from indra.util.fastest_elementtree import ElementTreeError, fromstring from indra.base import lluuid -int_regex = re.compile("[-+]?\d+") -real_regex = re.compile("[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?") -alpha_regex = re.compile("[a-zA-Z]+") -date_regex = re.compile("(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})T(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})(?P<second_float>\.\d{2})?Z") +try: + import cllsd +except ImportError: + cllsd = None + +int_regex = re.compile(r"[-+]?\d+") +real_regex = re.compile(r"[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?") +alpha_regex = re.compile(r"[a-zA-Z]+") +date_regex = re.compile(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})T" + r"(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})" + r"(?P<second_float>(\.\d+)?)Z") #date: d"YYYY-MM-DDTHH:MM:SS.FFZ" class LLSDParseError(Exception): pass -class LLSDSerializationError(Exception): +class LLSDSerializationError(TypeError): pass @@ -64,7 +71,7 @@ def format_datestr(v): """ Formats a datetime object into the string format shared by xml and notation serializations.""" second_str = "" if v.microsecond > 0: - seconds = v.second + float(v.microsecond) / 1000000 + seconds = v.second + float(v.microsecond) / 1e6 second_str = "%05.2f" % seconds else: second_str = "%02d" % v.second @@ -89,7 +96,7 @@ def parse_datestr(datestr): seconds_float = match.group('second_float') microsecond = 0 if seconds_float: - microsecond = int(seconds_float[1:]) * 10000 + microsecond = int(float('0' + seconds_float) * 1e6) return datetime.datetime(year, month, day, hour, minute, second, microsecond) @@ -116,7 +123,7 @@ def uuid_to_python(node): return lluuid.UUID(node.text) def str_to_python(node): - return unicode(node.text or '').encode('utf8', 'replace') + return node.text or '' def bin_to_python(node): return binary(base64.decodestring(node.text or '')) @@ -189,9 +196,13 @@ class LLSDXMLFormatter(object): if(contents is None or contents is ''): return "<%s />" % (name,) else: + if type(contents) is unicode: + contents = contents.encode('utf-8') return "<%s>%s</%s>" % (name, contents, name) def xml_esc(self, v): + if type(v) is unicode: + v = v.encode('utf-8') return v.replace('&', '&').replace('<', '<').replace('>', '>') def LLSD(self, v): @@ -237,9 +248,14 @@ class LLSDXMLFormatter(object): raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( t, something)) - def format(self, something): + def _format(self, something): return '<?xml version="1.0" ?>' + self.elt("llsd", self.generate(something)) + def format(self, something): + if cllsd: + return cllsd.llsd_to_xml(something) + return self._format(something) + _g_xml_formatter = None def format_xml(something): global _g_xml_formatter @@ -356,8 +372,10 @@ class LLSDNotationFormatter(object): def UUID(self, v): return "u%s" % v def BINARY(self, v): - raise LLSDSerializationError("binary notation not yet supported") + return 'b64"' + base64.encodestring(v) + '"' def STRING(self, v): + if isinstance(v, unicode): + v = v.encode('utf-8') return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'") def URI(self, v): return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"') @@ -366,16 +384,24 @@ class LLSDNotationFormatter(object): def ARRAY(self, v): return "[%s]" % ','.join([self.generate(item) for item in v]) def MAP(self, v): - return "{%s}" % ','.join(["'%s':%s" % (key.replace("\\", "\\\\").replace("'", "\\'"), self.generate(value)) + def fix(key): + if isinstance(key, unicode): + return key.encode('utf-8') + return key + return "{%s}" % ','.join(["'%s':%s" % (fix(key).replace("\\", "\\\\").replace("'", "\\'"), self.generate(value)) for key, value in v.items()]) def generate(self, something): t = type(something) - if self.type_map.has_key(t): - return self.type_map[t](something) + handler = self.type_map.get(t) + if handler: + return handler(something) else: - raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( - t, something)) + try: + return self.ARRAY(iter(something)) + except TypeError: + raise LLSDSerializationError( + "Cannot serialize unknown type: %s (%s)" % (t, something)) def format(self, something): return self.generate(something) @@ -479,7 +505,6 @@ class LLSDBinaryParser(object): raise LLSDParseError("invalid map key at byte %d." % ( self._index - 1,)) value = self._parse() - #print "kv:",key,value rv[key] = value count += 1 cc = self._buffer[self._index] @@ -636,11 +661,23 @@ class LLSDNotationParser(object): # 'd' = date in seconds since epoch return self._parse_date() elif cc == 'b': - raise LLSDParseError("binary notation not yet supported") + return self._parse_binary() else: raise LLSDParseError("invalid token at index %d: %d" % ( self._index - 1, ord(cc))) + def _parse_binary(self): + i = self._index + if self._buffer[i:i+2] == '64': + q = self._buffer[i+2] + e = self._buffer.find(q, i+3) + try: + return base64.decodestring(self._buffer[i+3:e]) + finally: + self._index = e + 1 + else: + raise LLSDParseError('random horrible binary format not supported') + def _parse_map(self): """ map: { string:object, string:object } """ rv = {} @@ -653,30 +690,23 @@ class LLSDNotationParser(object): if cc in ("'", '"', 's'): key = self._parse_string(cc) found_key = True - #print "key:",key elif cc.isspace() or cc == ',': cc = self._buffer[self._index] self._index += 1 else: raise LLSDParseError("invalid map key at byte %d." % ( self._index - 1,)) + elif cc.isspace() or cc == ':': + cc = self._buffer[self._index] + self._index += 1 + continue else: - if cc.isspace() or cc == ':': - #print "skipping whitespace '%s'" % cc - cc = self._buffer[self._index] - self._index += 1 - continue self._index += 1 value = self._parse() - #print "kv:",key,value rv[key] = value found_key = False cc = self._buffer[self._index] self._index += 1 - #if cc == '}': - # break - #cc = self._buffer[self._index] - #self._index += 1 return rv @@ -840,6 +870,14 @@ def format_binary(something): return '<?llsd/binary?>\n' + _format_binary_recurse(something) def _format_binary_recurse(something): + def _format_list(something): + array_builder = [] + array_builder.append('[' + struct.pack('!i', len(something))) + for item in something: + array_builder.append(_format_binary_recurse(item)) + array_builder.append(']') + return ''.join(array_builder) + if something is None: return '!' elif isinstance(something, LLSD): @@ -857,7 +895,10 @@ def _format_binary_recurse(something): return 'u' + something._bits elif isinstance(something, binary): return 'b' + struct.pack('!i', len(something)) + something - elif isinstance(something, (str, unicode)): + elif isinstance(something, str): + return 's' + struct.pack('!i', len(something)) + something + elif isinstance(something, unicode): + something = something.encode('utf-8') return 's' + struct.pack('!i', len(something)) + something elif isinstance(something, uri): return 'l' + struct.pack('!i', len(something)) + something @@ -865,35 +906,50 @@ def _format_binary_recurse(something): seconds_since_epoch = time.mktime(something.timetuple()) return 'd' + struct.pack('!d', seconds_since_epoch) elif isinstance(something, (list, tuple)): - array_builder = [] - array_builder.append('[' + struct.pack('!i', len(something))) - for item in something: - array_builder.append(_format_binary_recurse(item)) - array_builder.append(']') - return ''.join(array_builder) + return _format_list(something) elif isinstance(something, dict): map_builder = [] map_builder.append('{' + struct.pack('!i', len(something))) for key, value in something.items(): + if isinstance(key, unicode): + key = key.encode('utf-8') map_builder.append('k' + struct.pack('!i', len(key)) + key) map_builder.append(_format_binary_recurse(value)) map_builder.append('}') return ''.join(map_builder) else: - raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( - type(something), something)) + try: + return _format_list(list(something)) + except TypeError: + raise LLSDSerializationError( + "Cannot serialize unknown type: %s (%s)" % + (type(something), something)) + + +def parse_binary(something): + header = '<?llsd/binary?>\n' + if not something.startswith(header): + raise LLSDParseError('LLSD binary encoding header not found') + return LLSDBinaryParser().parse(something[len(header):]) + +def parse_xml(something): + try: + return to_python(fromstring(something)[0]) + except ElementTreeError, err: + raise LLSDParseError(*err.args) +def parse_notation(something): + return LLSDNotationParser().parse(something) def parse(something): try: if something.startswith('<?llsd/binary?>'): - just_binary = something.split('\n', 1)[1] - return LLSDBinaryParser().parse(just_binary) + return parse_binary(something) # This should be better. elif something.startswith('<'): - return to_python(fromstring(something)[0]) + return parse_xml(something) else: - return LLSDNotationParser().parse(something) + return parse_notation(something) except KeyError, e: raise Exception('LLSD could not be parsed: %s' % (e,)) diff --git a/indra/lib/python/indra/util/fastest_elementtree.py b/indra/lib/python/indra/util/fastest_elementtree.py index 228be49ed3..24701438f0 100644 --- a/indra/lib/python/indra/util/fastest_elementtree.py +++ b/indra/lib/python/indra/util/fastest_elementtree.py @@ -2,9 +2,9 @@ @file fastest_elementtree.py @brief Concealing some gnarly import logic in here. This should export the interface of elementtree. -$LicenseInfo:firstyear=2006&license=mit$ +$LicenseInfo:firstyear=2008&license=mit$ -Copyright (c) 2006-2007, Linden Research, Inc. +Copyright (c) 2008, Linden Research, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -26,25 +26,40 @@ THE SOFTWARE. $/LicenseInfo$ """ -# Using celementree might cause some unforeseen problems so here's a +# The parsing exception raised by the underlying library depends +# on the ElementTree implementation we're using, so we provide an +# alias here. +# +# Use ElementTreeError as the exception type for catching parsing +# errors. + + +# Using cElementTree might cause some unforeseen problems, so here's a # convenient off switch. + use_celementree = True try: if not use_celementree: raise ImportError() - from cElementTree import * ## This does not work under Windows + # Python 2.3 and 2.4. + from cElementTree import * + ElementTreeError = SyntaxError except ImportError: try: if not use_celementree: raise ImportError() - ## This is the name of cElementTree under python 2.5 + # Python 2.5 and above. from xml.etree.cElementTree import * + ElementTreeError = SyntaxError except ImportError: + # Pure Python code. try: - ## This is the old name of elementtree, for use with 2.3 + # Python 2.3 and 2.4. from elementtree.ElementTree import * except ImportError: - ## This is the name of elementtree under python 2.5 + # Python 2.5 and above. from xml.etree.ElementTree import * + # The pure Python ElementTree module uses Expat for parsing. + from xml.parsers.expat import ExpatError as ElementTreeError |