299 lines
8.1 KiB
Python
299 lines
8.1 KiB
Python
from __future__ import absolute_import
|
|
|
|
import datetime
|
|
|
|
import six
|
|
|
|
import xml.etree.ElementTree as et
|
|
|
|
from simplegeneric import generic
|
|
|
|
import wsme.types
|
|
from wsme.exc import UnknownArgument, InvalidInput
|
|
|
|
import re
|
|
|
|
content_type = 'text/xml'
|
|
accept_content_types = [
|
|
content_type,
|
|
]
|
|
|
|
time_re = re.compile(r'(?P<h>[0-2][0-9]):(?P<m>[0-5][0-9]):(?P<s>[0-6][0-9])')
|
|
|
|
|
|
def xml_indent(elem, level=0):
|
|
i = "\n" + level * " "
|
|
if len(elem):
|
|
if not elem.text or not elem.text.strip():
|
|
elem.text = i + " "
|
|
for e in elem:
|
|
xml_indent(e, level + 1)
|
|
if not e.tail or not e.tail.strip():
|
|
e.tail = i
|
|
if level and (not elem.tail or not elem.tail.strip()):
|
|
elem.tail = i
|
|
|
|
|
|
@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.isusertype(datatype):
|
|
return toxml(datatype.basetype,
|
|
key, datatype.tobasetype(value))
|
|
elif wsme.types.iscomplex(datatype):
|
|
for attrdef in datatype._wsme_attributes:
|
|
attrvalue = getattr(value, attrdef.key)
|
|
if attrvalue is not wsme.types.Unset:
|
|
el.append(toxml(attrdef.datatype, attrdef.name,
|
|
attrvalue))
|
|
else:
|
|
el.text = six.text_type(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.isusertype(datatype):
|
|
return datatype.frombasetype(fromxml(datatype.basetype, element))
|
|
if wsme.types.iscomplex(datatype):
|
|
obj = datatype()
|
|
for attrdef in wsme.types.list_attributes(datatype):
|
|
sub = element.find(attrdef.name)
|
|
if sub is not None:
|
|
val_fromxml = fromxml(attrdef.datatype, sub)
|
|
if getattr(attrdef, 'readonly', False):
|
|
raise InvalidInput(attrdef.name, val_fromxml,
|
|
"Cannot set read only field.")
|
|
setattr(obj, attrdef.key, val_fromxml)
|
|
elif attrdef.mandatory:
|
|
raise InvalidInput(attrdef.name, None,
|
|
"Mandatory field missing.")
|
|
return wsme.types.validate_value(datatype, obj)
|
|
if datatype is wsme.types.bytes:
|
|
return element.text.encode('ascii')
|
|
return datatype(element.text)
|
|
|
|
|
|
@toxml.when_type(wsme.types.ArrayType)
|
|
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.item_type, 'item', item))
|
|
return el
|
|
|
|
|
|
@toxml.when_type(wsme.types.DictType)
|
|
def dict_toxml(datatype, key, value):
|
|
el = et.Element(key)
|
|
if value is None:
|
|
el.set('nil', 'true')
|
|
else:
|
|
for item in value.items():
|
|
key = toxml(datatype.key_type, 'key', item[0])
|
|
value = toxml(datatype.value_type, 'value', item[1])
|
|
node = et.Element('item')
|
|
node.append(key)
|
|
node.append(value)
|
|
el.append(node)
|
|
return el
|
|
|
|
|
|
@toxml.when_object(wsme.types.bytes)
|
|
def bytes_toxml(datatype, key, value):
|
|
el = et.Element(key)
|
|
if value is None:
|
|
el.set('nil', 'true')
|
|
else:
|
|
el.text = value.decode('ascii')
|
|
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
|
|
|
|
|
|
@fromxml.when_type(wsme.types.ArrayType)
|
|
def array_fromxml(datatype, element):
|
|
if element.get('nil') == 'true':
|
|
return None
|
|
return [
|
|
fromxml(datatype.item_type, item)
|
|
for item in element.findall('item')
|
|
]
|
|
|
|
|
|
@fromxml.when_object(bool)
|
|
def bool_fromxml(datatype, element):
|
|
if element.get('nil') == 'true':
|
|
return None
|
|
return element.text.lower() != 'false'
|
|
|
|
|
|
@fromxml.when_type(wsme.types.DictType)
|
|
def dict_fromxml(datatype, element):
|
|
if element.get('nil') == 'true':
|
|
return None
|
|
return dict((
|
|
(fromxml(datatype.key_type, item.find('key')),
|
|
fromxml(datatype.value_type, item.find('value')))
|
|
for item in element.findall('item')))
|
|
|
|
|
|
@fromxml.when_object(wsme.types.text)
|
|
def unicode_fromxml(datatype, element):
|
|
if element.get('nil') == 'true':
|
|
return None
|
|
return wsme.types.text(element.text) if element.text else six.u('')
|
|
|
|
|
|
@fromxml.when_object(datetime.date)
|
|
def date_fromxml(datatype, element):
|
|
if element.get('nil') == 'true':
|
|
return None
|
|
return wsme.utils.parse_isodate(element.text)
|
|
|
|
|
|
@fromxml.when_object(datetime.time)
|
|
def time_fromxml(datatype, element):
|
|
if element.get('nil') == 'true':
|
|
return None
|
|
return wsme.utils.parse_isotime(element.text)
|
|
|
|
|
|
@fromxml.when_object(datetime.datetime)
|
|
def datetime_fromxml(datatype, element):
|
|
if element.get('nil') == 'true':
|
|
return None
|
|
return wsme.utils.parse_isodatetime(element.text)
|
|
|
|
|
|
def parse(s, datatypes, bodyarg):
|
|
if hasattr(s, 'read'):
|
|
tree = et.parse(s)
|
|
else:
|
|
tree = et.fromstring(s)
|
|
if bodyarg:
|
|
name = list(datatypes.keys())[0]
|
|
return {name: fromxml(datatypes[name], tree)}
|
|
else:
|
|
kw = {}
|
|
extra_args = []
|
|
for sub in tree:
|
|
if sub.tag not in datatypes:
|
|
extra_args.append(sub.tag)
|
|
kw[sub.tag] = fromxml(datatypes[sub.tag], sub)
|
|
if extra_args:
|
|
raise UnknownArgument(', '.join(extra_args))
|
|
return kw
|
|
|
|
|
|
def encode_result(value, datatype, **options):
|
|
return et.tostring(toxml(
|
|
datatype, options.get('nested_result_attrname', 'result'), value
|
|
))
|
|
|
|
|
|
def encode_error(context, 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)
|
|
|
|
|
|
def encode_sample_value(datatype, value, format=False):
|
|
r = toxml(datatype, 'value', value)
|
|
if format:
|
|
xml_indent(r)
|
|
content = et.tostring(r)
|
|
return ('xml', content)
|
|
|
|
|
|
def encode_sample_params(params, format=False):
|
|
node = et.Element('parameters')
|
|
for name, datatype, value in params:
|
|
node.append(toxml(datatype, name, value))
|
|
if format:
|
|
xml_indent(node)
|
|
content = et.tostring(node)
|
|
return ('xml', content)
|
|
|
|
|
|
def encode_sample_result(datatype, value, format=False):
|
|
r = toxml(datatype, 'result', value)
|
|
if format:
|
|
xml_indent(r)
|
|
content = et.tostring(r)
|
|
return ('xml', content)
|