00001 """\
00002 @file llsd.py
00003 @brief Types as well as parsing and formatting functions for handling LLSD.
00004
00005 $LicenseInfo:firstyear=2006&license=mit$
00006
00007 Copyright (c) 2006-2008, Linden Research, Inc.
00008
00009 Permission is hereby granted, free of charge, to any person obtaining a copy
00010 of this software and associated documentation files (the "Software"), to deal
00011 in the Software without restriction, including without limitation the rights
00012 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00013 copies of the Software, and to permit persons to whom the Software is
00014 furnished to do so, subject to the following conditions:
00015
00016 The above copyright notice and this permission notice shall be included in
00017 all copies or substantial portions of the Software.
00018
00019 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00020 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00021 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00022 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00023 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00024 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00025 THE SOFTWARE.
00026 $/LicenseInfo$
00027 """
00028
00029 import datetime
00030 import base64
00031 import struct
00032 import time
00033 import types
00034 import re
00035
00036 from indra.util.fastest_elementtree import fromstring
00037 from indra.base import lluuid
00038
00039 int_regex = re.compile("[-+]?\d+")
00040 real_regex = re.compile("[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?")
00041 alpha_regex = re.compile("[a-zA-Z]+")
00042 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")
00043
00044
00045 class LLSDParseError(Exception):
00046 pass
00047
00048 class LLSDSerializationError(Exception):
00049 pass
00050
00051
00052 class binary(str):
00053 pass
00054
00055 class uri(str):
00056 pass
00057
00058
00059 BOOL_TRUE = ('1', '1.0', 'true')
00060 BOOL_FALSE = ('0', '0.0', 'false', '')
00061
00062
00063 def format_datestr(v):
00064 """ Formats a datetime object into the string format shared by xml and notation serializations."""
00065 second_str = ""
00066 if v.microsecond > 0:
00067 seconds = v.second + float(v.microsecond) / 1000000
00068 second_str = "%05.2f" % seconds
00069 else:
00070 second_str = "%d" % v.second
00071 return '%s%sZ' % (v.strftime('%Y-%m-%dT%H:%M:'), second_str)
00072
00073
00074 def parse_datestr(datestr):
00075 """Parses a datetime object from the string format shared by xml and notation serializations."""
00076 if datestr == "":
00077 return datetime.datetime(1970, 1, 1)
00078
00079 match = re.match(date_regex, datestr)
00080 if not match:
00081 raise LLSDParseError("invalid date string '%s'." % datestr)
00082
00083 year = int(match.group('year'))
00084 month = int(match.group('month'))
00085 day = int(match.group('day'))
00086 hour = int(match.group('hour'))
00087 minute = int(match.group('minute'))
00088 second = int(match.group('second'))
00089 seconds_float = match.group('second_float')
00090 microsecond = 0
00091 if seconds_float:
00092 microsecond = int(seconds_float[1:]) * 10000
00093 return datetime.datetime(year, month, day, hour, minute, second, microsecond)
00094
00095
00096 def bool_to_python(node):
00097 val = node.text or ''
00098 if val in BOOL_TRUE:
00099 return True
00100 else:
00101 return False
00102
00103 def int_to_python(node):
00104 val = node.text or ''
00105 if not val.strip():
00106 return 0
00107 return int(val)
00108
00109 def real_to_python(node):
00110 val = node.text or ''
00111 if not val.strip():
00112 return 0.0
00113 return float(val)
00114
00115 def uuid_to_python(node):
00116 return lluuid.UUID(node.text)
00117
00118 def str_to_python(node):
00119 return unicode(node.text or '').encode('utf8', 'replace')
00120
00121 def bin_to_python(node):
00122 return binary(base64.decodestring(node.text or ''))
00123
00124 def date_to_python(node):
00125 val = node.text or ''
00126 if not val:
00127 val = "1970-01-01T00:00:00Z"
00128 return parse_datestr(val)
00129
00130 def uri_to_python(node):
00131 val = node.text or ''
00132 if not val:
00133 return None
00134 return uri(val)
00135
00136 def map_to_python(node):
00137 result = {}
00138 for index in range(len(node))[::2]:
00139 result[node[index].text] = to_python(node[index+1])
00140 return result
00141
00142 def array_to_python(node):
00143 return [to_python(child) for child in node]
00144
00145
00146 NODE_HANDLERS = dict(
00147 undef=lambda x: None,
00148 boolean=bool_to_python,
00149 integer=int_to_python,
00150 real=real_to_python,
00151 uuid=uuid_to_python,
00152 string=str_to_python,
00153 binary=bin_to_python,
00154 date=date_to_python,
00155 uri=uri_to_python,
00156 map=map_to_python,
00157 array=array_to_python,
00158 )
00159
00160 def to_python(node):
00161 return NODE_HANDLERS[node.tag](node)
00162
00163 class Nothing(object):
00164 pass
00165
00166
00167 class LLSDXMLFormatter(object):
00168 def __init__(self):
00169 self.type_map = {
00170 type(None) : self.UNDEF,
00171 bool : self.BOOLEAN,
00172 int : self.INTEGER,
00173 long : self.INTEGER,
00174 float : self.REAL,
00175 lluuid.UUID : self.UUID,
00176 binary : self.BINARY,
00177 str : self.STRING,
00178 unicode : self.STRING,
00179 uri : self.URI,
00180 datetime.datetime : self.DATE,
00181 list : self.ARRAY,
00182 tuple : self.ARRAY,
00183 types.GeneratorType : self.ARRAY,
00184 dict : self.MAP,
00185 LLSD : self.LLSD
00186 }
00187
00188 def elt(self, name, contents=None):
00189 if(contents is None or contents is ''):
00190 return "<%s />" % (name,)
00191 else:
00192 return "<%s>%s</%s>" % (name, contents, name)
00193
00194 def xml_esc(self, v):
00195 return v.replace('&', '&').replace('<', '<').replace('>', '>')
00196
00197 def LLSD(self, v):
00198 return self.generate(v.thing)
00199 def UNDEF(self, v):
00200 return self.elt('undef')
00201 def BOOLEAN(self, v):
00202 if v:
00203 return self.elt('boolean', 'true')
00204 else:
00205 return self.elt('boolean', 'false')
00206 def INTEGER(self, v):
00207 return self.elt('integer', v)
00208 def REAL(self, v):
00209 return self.elt('real', v)
00210 def UUID(self, v):
00211 if(v.isNull()):
00212 return self.elt('uuid')
00213 else:
00214 return self.elt('uuid', v)
00215 def BINARY(self, v):
00216 return self.elt('binary', base64.encodestring(v))
00217 def STRING(self, v):
00218 return self.elt('string', self.xml_esc(v))
00219 def URI(self, v):
00220 return self.elt('uri', self.xml_esc(str(v)))
00221 def DATE(self, v):
00222 return self.elt('date', format_datestr(v))
00223 def ARRAY(self, v):
00224 return self.elt('array', ''.join([self.generate(item) for item in v]))
00225 def MAP(self, v):
00226 return self.elt(
00227 'map',
00228 ''.join(["%s%s" % (self.elt('key', key), self.generate(value))
00229 for key, value in v.items()]))
00230
00231 typeof = type
00232 def generate(self, something):
00233 t = self.typeof(something)
00234 if self.type_map.has_key(t):
00235 return self.type_map[t](something)
00236 else:
00237 raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % (
00238 t, something))
00239
00240 def format(self, something):
00241 return '<?xml version="1.0" ?>' + self.elt("llsd", self.generate(something))
00242
00243 def format_xml(something):
00244 return LLSDXMLFormatter().format(something)
00245
00246 class LLSDNotationFormatter(object):
00247 def __init__(self):
00248 self.type_map = {
00249 type(None) : self.UNDEF,
00250 bool : self.BOOLEAN,
00251 int : self.INTEGER,
00252 long : self.INTEGER,
00253 float : self.REAL,
00254 lluuid.UUID : self.UUID,
00255 binary : self.BINARY,
00256 str : self.STRING,
00257 unicode : self.STRING,
00258 uri : self.URI,
00259 datetime.datetime : self.DATE,
00260 list : self.ARRAY,
00261 tuple : self.ARRAY,
00262 types.GeneratorType : self.ARRAY,
00263 dict : self.MAP,
00264 LLSD : self.LLSD
00265 }
00266
00267 def LLSD(self, v):
00268 return self.generate(v.thing)
00269 def UNDEF(self, v):
00270 return '!'
00271 def BOOLEAN(self, v):
00272 if v:
00273 return 'true'
00274 else:
00275 return 'false'
00276 def INTEGER(self, v):
00277 return "i%s" % v
00278 def REAL(self, v):
00279 return "r%s" % v
00280 def UUID(self, v):
00281 return "u%s" % v
00282 def BINARY(self, v):
00283 raise LLSDSerializationError("binary notation not yet supported")
00284 def STRING(self, v):
00285 return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'")
00286 def URI(self, v):
00287 return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"')
00288 def DATE(self, v):
00289 return 'd"%s"' % format_datestr(v)
00290 def ARRAY(self, v):
00291 return "[%s]" % ','.join([self.generate(item) for item in v])
00292 def MAP(self, v):
00293 return "{%s}" % ','.join(["'%s':%s" % (key.replace("\\", "\\\\").replace("'", "\\'"), self.generate(value))
00294 for key, value in v.items()])
00295
00296 def generate(self, something):
00297 t = type(something)
00298 if self.type_map.has_key(t):
00299 return self.type_map[t](something)
00300 else:
00301 raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % (
00302 t, something))
00303
00304 def format(self, something):
00305 return self.generate(something)
00306
00307 def format_notation(something):
00308 return LLSDNotationFormatter().format(something)
00309
00310 def _hex_as_nybble(hex):
00311 if (hex >= '0') and (hex <= '9'):
00312 return ord(hex) - ord('0')
00313 elif (hex >= 'a') and (hex <='f'):
00314 return 10 + ord(hex) - ord('a')
00315 elif (hex >= 'A') and (hex <='F'):
00316 return 10 + ord(hex) - ord('A');
00317
00318 class LLSDBinaryParser(object):
00319 def __init__(self):
00320 pass
00321
00322 def parse(self, buffer, ignore_binary = False):
00323 """
00324 This is the basic public interface for parsing.
00325
00326 @param buffer the binary data to parse in an indexable sequence.
00327 @param ignore_binary parser throws away data in llsd binary nodes.
00328 @return returns a python object.
00329 """
00330 self._buffer = buffer
00331 self._index = 0
00332 self._keep_binary = not ignore_binary
00333 return self._parse()
00334
00335 def _parse(self):
00336 cc = self._buffer[self._index]
00337 self._index += 1
00338 if cc == '{':
00339 return self._parse_map()
00340 elif cc == '[':
00341 return self._parse_array()
00342 elif cc == '!':
00343 return None
00344 elif cc == '0':
00345 return False
00346 elif cc == '1':
00347 return True
00348 elif cc == 'i':
00349
00350 idx = self._index
00351 self._index += 4
00352 return struct.unpack("!i", self._buffer[idx:idx+4])[0]
00353 elif cc == ('r'):
00354
00355 idx = self._index
00356 self._index += 8
00357 return struct.unpack("!d", self._buffer[idx:idx+8])[0]
00358 elif cc == 'u':
00359
00360 idx = self._index
00361 self._index += 16
00362 return lluuid.uuid_bits_to_uuid(self._buffer[idx:idx+16])
00363 elif cc == 's':
00364
00365 return self._parse_string()
00366 elif cc in ("'", '"'):
00367
00368 return self._parse_string_delim(cc)
00369 elif cc == 'l':
00370
00371 return uri(self._parse_string())
00372 elif cc == ('d'):
00373
00374 idx = self._index
00375 self._index += 8
00376 seconds = struct.unpack("!d", self._buffer[idx:idx+8])[0]
00377 return datetime.datetime.fromtimestamp(seconds)
00378 elif cc == 'b':
00379 binary = self._parse_string()
00380 if self._keep_binary:
00381 return binary
00382
00383
00384 return None
00385 else:
00386 raise LLSDParseError("invalid binary token at byte %d: %d" % (
00387 self._index - 1, ord(cc)))
00388
00389 def _parse_map(self):
00390 rv = {}
00391 size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
00392 self._index += 4
00393 count = 0
00394 cc = self._buffer[self._index]
00395 self._index += 1
00396 key = ''
00397 while (cc != '}') and (count < size):
00398 if cc == 'k':
00399 key = self._parse_string()
00400 elif cc in ("'", '"'):
00401 key = self._parse_string_delim(cc)
00402 else:
00403 raise LLSDParseError("invalid map key at byte %d." % (
00404 self._index - 1,))
00405 value = self._parse()
00406
00407 rv[key] = value
00408 count += 1
00409 cc = self._buffer[self._index]
00410 self._index += 1
00411 if cc != '}':
00412 raise LLSDParseError("invalid map close token at byte %d." % (
00413 self._index,))
00414 return rv
00415
00416 def _parse_array(self):
00417 rv = []
00418 size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
00419 self._index += 4
00420 count = 0
00421 cc = self._buffer[self._index]
00422 while (cc != ']') and (count < size):
00423 rv.append(self._parse())
00424 count += 1
00425 cc = self._buffer[self._index]
00426 if cc != ']':
00427 raise LLSDParseError("invalid array close token at byte %d." % (
00428 self._index,))
00429 self._index += 1
00430 return rv
00431
00432 def _parse_string(self):
00433 size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0]
00434 self._index += 4
00435 rv = self._buffer[self._index:self._index+size]
00436 self._index += size
00437 return rv
00438
00439 def _parse_string_delim(self, delim):
00440 list = []
00441 found_escape = False
00442 found_hex = False
00443 found_digit = False
00444 byte = 0
00445 while True:
00446 cc = self._buffer[self._index]
00447 self._index += 1
00448 if found_escape:
00449 if found_hex:
00450 if found_digit:
00451 found_escape = False
00452 found_hex = False
00453 found_digit = False
00454 byte <<= 4
00455 byte |= _hex_as_nybble(cc)
00456 list.append(chr(byte))
00457 byte = 0
00458 else:
00459 found_digit = True
00460 byte = _hex_as_nybble(cc)
00461 elif cc == 'x':
00462 found_hex = True
00463 else:
00464 if cc == 'a':
00465 list.append('\a')
00466 elif cc == 'b':
00467 list.append('\b')
00468 elif cc == 'f':
00469 list.append('\f')
00470 elif cc == 'n':
00471 list.append('\n')
00472 elif cc == 'r':
00473 list.append('\r')
00474 elif cc == 't':
00475 list.append('\t')
00476 elif cc == 'v':
00477 list.append('\v')
00478 else:
00479 list.append(cc)
00480 found_escape = False
00481 elif cc == '\\':
00482 found_escape = True
00483 elif cc == delim:
00484 break
00485 else:
00486 list.append(cc)
00487 return ''.join(list)
00488
00489 class LLSDNotationParser(object):
00490 """ Parse LLSD notation:
00491 map: { string:object, string:object }
00492 array: [ object, object, object ]
00493 undef: !
00494 boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE
00495 integer: i####
00496 real: r####
00497 uuid: u####
00498 string: "g\'day" | 'have a "nice" day' | s(size)"raw data"
00499 uri: l"escaped"
00500 date: d"YYYY-MM-DDTHH:MM:SS.FFZ"
00501 binary: b##"ff3120ab1" | b(size)"raw data"
00502 """
00503 def __init__(self):
00504 pass
00505
00506 def parse(self, buffer, ignore_binary = False):
00507 """
00508 This is the basic public interface for parsing.
00509
00510 @param buffer the notation string to parse.
00511 @param ignore_binary parser throws away data in llsd binary nodes.
00512 @return returns a python object.
00513 """
00514 if buffer == "":
00515 return False
00516
00517 self._buffer = buffer
00518 self._index = 0
00519 return self._parse()
00520
00521 def _parse(self):
00522 cc = self._buffer[self._index]
00523 self._index += 1
00524 if cc == '{':
00525 return self._parse_map()
00526 elif cc == '[':
00527 return self._parse_array()
00528 elif cc == '!':
00529 return None
00530 elif cc == '0':
00531 return False
00532 elif cc == '1':
00533 return True
00534 elif cc in ('F', 'f'):
00535 self._skip_alpha()
00536 return False
00537 elif cc in ('T', 't'):
00538 self._skip_alpha()
00539 return True
00540 elif cc == 'i':
00541
00542 return self._parse_integer()
00543 elif cc == ('r'):
00544
00545 return self._parse_real()
00546 elif cc == 'u':
00547
00548 return self._parse_uuid()
00549 elif cc in ("'", '"', 's'):
00550 return self._parse_string(cc)
00551 elif cc == 'l':
00552
00553 delim = self._buffer[self._index]
00554 self._index += 1
00555 val = uri(self._parse_string(delim))
00556 if len(val) == 0:
00557 return None
00558 return val
00559 elif cc == ('d'):
00560
00561 return self._parse_date()
00562 elif cc == 'b':
00563 raise LLSDParseError("binary notation not yet supported")
00564 else:
00565 raise LLSDParseError("invalid token at index %d: %d" % (
00566 self._index - 1, ord(cc)))
00567
00568 def _parse_map(self):
00569 """ map: { string:object, string:object } """
00570 rv = {}
00571 cc = self._buffer[self._index]
00572 self._index += 1
00573 key = ''
00574 found_key = False
00575 while (cc != '}'):
00576 if not found_key:
00577 if cc in ("'", '"', 's'):
00578 key = self._parse_string(cc)
00579 found_key = True
00580
00581 elif cc.isspace() or cc == ',':
00582 cc = self._buffer[self._index]
00583 self._index += 1
00584 else:
00585 raise LLSDParseError("invalid map key at byte %d." % (
00586 self._index - 1,))
00587 else:
00588 if cc.isspace() or cc == ':':
00589
00590 cc = self._buffer[self._index]
00591 self._index += 1
00592 continue
00593 self._index += 1
00594 value = self._parse()
00595
00596 rv[key] = value
00597 found_key = False
00598 cc = self._buffer[self._index]
00599 self._index += 1
00600
00601
00602
00603
00604
00605 return rv
00606
00607 def _parse_array(self):
00608 """ array: [ object, object, object ] """
00609 rv = []
00610 cc = self._buffer[self._index]
00611 while (cc != ']'):
00612 if cc.isspace() or cc == ',':
00613 self._index += 1
00614 cc = self._buffer[self._index]
00615 continue
00616 rv.append(self._parse())
00617 cc = self._buffer[self._index]
00618
00619 if cc != ']':
00620 raise LLSDParseError("invalid array close token at index %d." % (
00621 self._index,))
00622 self._index += 1
00623 return rv
00624
00625 def _parse_uuid(self):
00626 match = re.match(lluuid.UUID.uuid_regex, self._buffer[self._index:])
00627 if not match:
00628 raise LLSDParseError("invalid uuid token at index %d." % self._index)
00629
00630 (start, end) = match.span()
00631 start += self._index
00632 end += self._index
00633 self._index = end
00634 return lluuid.UUID(self._buffer[start:end])
00635
00636 def _skip_alpha(self):
00637 match = re.match(alpha_regex, self._buffer[self._index:])
00638 if match:
00639 self._index += match.end()
00640
00641 def _parse_date(self):
00642 delim = self._buffer[self._index]
00643 self._index += 1
00644 datestr = self._parse_string(delim)
00645 return parse_datestr(datestr)
00646
00647 def _parse_real(self):
00648 match = re.match(real_regex, self._buffer[self._index:])
00649 if not match:
00650 raise LLSDParseError("invalid real token at index %d." % self._index)
00651
00652 (start, end) = match.span()
00653 start += self._index
00654 end += self._index
00655 self._index = end
00656 return float( self._buffer[start:end] )
00657
00658 def _parse_integer(self):
00659 match = re.match(int_regex, self._buffer[self._index:])
00660 if not match:
00661 raise LLSDParseError("invalid integer token at index %d." % self._index)
00662
00663 (start, end) = match.span()
00664 start += self._index
00665 end += self._index
00666 self._index = end
00667 return int( self._buffer[start:end] )
00668
00669 def _parse_string(self, delim):
00670 """ string: "g\'day" | 'have a "nice" day' | s(size)"raw data" """
00671 rv = ""
00672
00673 if delim in ("'", '"'):
00674 rv = self._parse_string_delim(delim)
00675 elif delim == 's':
00676 rv = self._parse_string_raw()
00677 else:
00678 raise LLSDParseError("invalid string token at index %d." % self._index)
00679
00680 return rv
00681
00682
00683 def _parse_string_delim(self, delim):
00684 """ string: "g'day 'un" | 'have a "nice" day' """
00685 list = []
00686 found_escape = False
00687 found_hex = False
00688 found_digit = False
00689 byte = 0
00690 while True:
00691 cc = self._buffer[self._index]
00692 self._index += 1
00693 if found_escape:
00694 if found_hex:
00695 if found_digit:
00696 found_escape = False
00697 found_hex = False
00698 found_digit = False
00699 byte <<= 4
00700 byte |= _hex_as_nybble(cc)
00701 list.append(chr(byte))
00702 byte = 0
00703 else:
00704 found_digit = True
00705 byte = _hex_as_nybble(cc)
00706 elif cc == 'x':
00707 found_hex = True
00708 else:
00709 if cc == 'a':
00710 list.append('\a')
00711 elif cc == 'b':
00712 list.append('\b')
00713 elif cc == 'f':
00714 list.append('\f')
00715 elif cc == 'n':
00716 list.append('\n')
00717 elif cc == 'r':
00718 list.append('\r')
00719 elif cc == 't':
00720 list.append('\t')
00721 elif cc == 'v':
00722 list.append('\v')
00723 else:
00724 list.append(cc)
00725 found_escape = False
00726 elif cc == '\\':
00727 found_escape = True
00728 elif cc == delim:
00729 break
00730 else:
00731 list.append(cc)
00732 return ''.join(list)
00733
00734 def _parse_string_raw(self):
00735 """ string: s(size)"raw data" """
00736
00737 cc = self._buffer[self._index]
00738 self._index += 1
00739 if cc != '(':
00740 raise LLSDParseError("invalid string token at index %d." % self._index)
00741
00742 rparen = self._buffer.find(')', self._index)
00743 if rparen == -1:
00744 raise LLSDParseError("invalid string token at index %d." % self._index)
00745
00746 size = int(self._buffer[self._index:rparen])
00747
00748 self._index = rparen + 1
00749 delim = self._buffer[self._index]
00750 self._index += 1
00751 if delim not in ("'", '"'):
00752 raise LLSDParseError("invalid string token at index %d." % self._index)
00753
00754 rv = self._buffer[self._index:(self._index + size)]
00755 self._index += size
00756 cc = self._buffer[self._index]
00757 self._index += 1
00758 if cc != delim:
00759 raise LLSDParseError("invalid string token at index %d." % self._index)
00760
00761 return rv
00762
00763 def format_binary(something):
00764 return '<?llsd/binary?>\n' + _format_binary_recurse(something)
00765
00766 def _format_binary_recurse(something):
00767 if something is None:
00768 return '!'
00769 elif isinstance(something, LLSD):
00770 return _format_binary_recurse(something.thing)
00771 elif isinstance(something, bool):
00772 if something:
00773 return '1'
00774 else:
00775 return '0'
00776 elif isinstance(something, (int, long)):
00777 return 'i' + struct.pack('!i', something)
00778 elif isinstance(something, float):
00779 return 'r' + struct.pack('!d', something)
00780 elif isinstance(something, lluuid.UUID):
00781 return 'u' + something._bits
00782 elif isinstance(something, binary):
00783 return 'b' + struct.pack('!i', len(something)) + something
00784 elif isinstance(something, (str, unicode)):
00785 return 's' + struct.pack('!i', len(something)) + something
00786 elif isinstance(something, uri):
00787 return 'l' + struct.pack('!i', len(something)) + something
00788 elif isinstance(something, datetime.datetime):
00789 seconds_since_epoch = time.mktime(something.timetuple())
00790 return 'd' + struct.pack('!d', seconds_since_epoch)
00791 elif isinstance(something, (list, tuple)):
00792 array_builder = []
00793 array_builder.append('[' + struct.pack('!i', len(something)))
00794 for item in something:
00795 array_builder.append(_format_binary_recurse(item))
00796 array_builder.append(']')
00797 return ''.join(array_builder)
00798 elif isinstance(something, dict):
00799 map_builder = []
00800 map_builder.append('{' + struct.pack('!i', len(something)))
00801 for key, value in something.items():
00802 map_builder.append('k' + struct.pack('!i', len(key)) + key)
00803 map_builder.append(_format_binary_recurse(value))
00804 map_builder.append('}')
00805 return ''.join(map_builder)
00806 else:
00807 raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % (
00808 type(something), something))
00809
00810
00811 def parse(something):
00812 try:
00813 if something.startswith('<?llsd/binary?>'):
00814 just_binary = something.split('\n', 1)[1]
00815 return LLSDBinaryParser().parse(just_binary)
00816
00817 elif something.startswith('<'):
00818 return to_python(fromstring(something)[0])
00819 else:
00820 return LLSDNotationParser().parse(something)
00821 except KeyError, e:
00822 raise Exception('LLSD could not be parsed: %s' % (e,))
00823
00824 class LLSD(object):
00825 def __init__(self, thing=None):
00826 self.thing = thing
00827
00828 def __str__(self):
00829 return self.toXML(self.thing)
00830
00831 parse = staticmethod(parse)
00832 toXML = staticmethod(format_xml)
00833 toBinary = staticmethod(format_binary)
00834 toNotation = staticmethod(format_notation)
00835
00836
00837 undef = LLSD(None)
00838
00839
00840 try:
00841 from mulib import stacked, mu
00842 stacked.NoProducer()
00843 mu.safe_load(None)
00844 except:
00845
00846 pass
00847 else:
00848 mu.add_parser(parse, 'application/llsd+xml')
00849 mu.add_parser(parse, 'application/llsd+binary')
00850
00851 def llsd_convert_xml(llsd_stuff, request):
00852 request.write(format_xml(llsd_stuff))
00853
00854 def llsd_convert_binary(llsd_stuff, request):
00855 request.write(format_binary(llsd_stuff))
00856
00857 for typ in [LLSD, dict, list, tuple, str, int, float, bool, unicode, type(None)]:
00858 stacked.add_producer(typ, llsd_convert_xml, 'application/llsd+xml')
00859 stacked.add_producer(typ, llsd_convert_xml, 'application/xml')
00860 stacked.add_producer(typ, llsd_convert_xml, 'text/xml')
00861
00862 stacked.add_producer(typ, llsd_convert_binary, 'application/llsd+binary')
00863
00864 stacked.add_producer(LLSD, llsd_convert_xml, '*/*')