import base64 import datetime try: import xml.etree.ElementTree as et except ImportError: import cElementTree as et from simplegeneric import generic from wsme.rest import RestProtocol from wsme.exc import * import wsme.types import re time_re = re.compile(r'(?P[0-2][0-9]):(?P[0-5][0-9]):(?P[0-6][0-9])') @generic def toxml(datatype, key, value): """ A generic converter from python to xml elements. If a non-complex user specific type is to be used in the api, a specific toxml should be added:: from wsme.protocol.restxml import toxml myspecialtype = object() @toxml.when_object(myspecialtype) def myspecialtype_toxml(datatype, key, value): el = et.Element(key) if value is None: el.set('nil', 'true') else: el.text = str(value) return el """ el = et.Element(key) if value is None: el.set('nil', 'true') else: if wsme.types.iscomplex(datatype): for key, attrdef in datatype._wsme_attributes: el.append(toxml(attrdef.datatype, key, getattr(value, key))) else: el.text = unicode(value) return el @generic def fromxml(datatype, element): """ A generic converter from xml elements to python datatype. If a non-complex user specific type is to be used in the api, a specific fromxml should be added:: from wsme.protocol.restxml import fromxml class MySpecialType(object): pass @fromxml.when_object(MySpecialType) def myspecialtype_fromxml(datatype, element): if element.get('nil', False): return None return MySpecialType(element.text) """ if element.get('nil', False): return None if wsme.types.iscomplex(datatype): obj = datatype() for key, attrdef in datatype._wsme_attributes: sub = element.find(key) if sub is not None: setattr(obj, key, fromxml(attrdef.datatype, sub)) return obj return datatype(element.text) @toxml.when_type(list) def array_toxml(datatype, key, value): el = et.Element(key) if value is None: el.set('nil', 'true') else: for item in value: el.append(toxml(datatype[0], 'item', item)) return el @toxml.when_object(bool) def bool_toxml(datatype, key, value): el = et.Element(key) if value is None: el.set('nil', 'true') else: el.text = value and 'true' or 'false' return el @toxml.when_object(datetime.date) def date_toxml(datatype, key, value): el = et.Element(key) if value is None: el.set('nil', 'true') else: el.text = value.isoformat() return el @toxml.when_object(datetime.datetime) def datetime_toxml(datatype, key, value): el = et.Element(key) if value is None: el.set('nil', 'true') else: el.text = value.isoformat() return el @toxml.when_object(wsme.types.binary) def binary_toxml(datatype, key, value): el = et.Element(key) if value is None: el.set('nil', 'true') else: el.text = base64.encodestring(value) return el @fromxml.when_type(list) def array_fromxml(datatype, element): if element.get('nil') == 'true': return None return [fromxml(datatype[0], item) for item in element.findall('item')] @fromxml.when_object(datetime.date) def date_fromxml(datatype, element): return datetime.datetime.strptime(element.text, '%Y-%m-%d').date() @fromxml.when_object(datetime.time) def time_fromxml(datatype, element): m = time_re.match(element.text) if m: return datetime.time( int(m.group('h')), int(m.group('m')), int(m.group('s'))) @fromxml.when_object(datetime.datetime) def datetime_fromxml(datatype, element): return datetime.datetime.strptime(element.text, '%Y-%m-%dT%H:%M:%S') @fromxml.when_object(wsme.types.binary) def binary_fromxml(datatype, element): return base64.decodestring(element.text) class RestXmlProtocol(RestProtocol): """ REST+XML protocol. .. autoattribute:: name .. autoattribute:: dataformat .. autoattribute:: content_types """ name = 'restxml' dataformat = 'xml' content_types = ['text/xml'] def decode_arg(self, value, arg): return fromxml(arg.datatype, value) def parse_arg(self, name, value): return et.fromstring(u"<%s>%s" % (name, value, name)) def parse_args(self, body): return dict((sub.tag, sub) for sub in et.fromstring(body)) def encode_result(self, funcdef, result): return et.tostring(toxml(funcdef.return_type, 'result', result)) def encode_error(self, errordetail): el = et.Element('error') et.SubElement(el, 'faultcode').text = errordetail['faultcode'] et.SubElement(el, 'faultstring').text = errordetail['faultstring'] if 'debuginfo' in errordetail: et.SubElement(el, 'debuginfo').text = errordetail['debuginfo'] return et.tostring(el)