Improved the complex type handling by using python Descriptors for attributes. They also carry the attribute name, so that the _wsme_attributes is now a list of wsattr/wsproperty instead of a list of (name, wsattr/wsproperty)
This commit is contained in:
@@ -34,8 +34,8 @@ def tojson(datatype, value):
|
||||
"""
|
||||
if wsme.types.iscomplex(datatype):
|
||||
d = dict()
|
||||
for name, attr in wsme.types.list_attributes(datatype):
|
||||
d[name] = tojson(attr.datatype, getattr(value, name))
|
||||
for attr in wsme.types.list_attributes(datatype):
|
||||
d[attr.key] = tojson(attr.datatype, getattr(value, attr.key))
|
||||
return d
|
||||
return value
|
||||
|
||||
@@ -91,9 +91,10 @@ def fromjson(datatype, value):
|
||||
return None
|
||||
if wsme.types.iscomplex(datatype):
|
||||
obj = datatype()
|
||||
for name, attrdef in wsme.types.list_attributes(datatype):
|
||||
if name in value:
|
||||
setattr(obj, name, fromjson(attrdef.datatype, value[name]))
|
||||
for attrdef in wsme.types.list_attributes(datatype):
|
||||
if attrdef.key in value:
|
||||
setattr(obj, attrdef.key,
|
||||
fromjson(attrdef.datatype, value[attrdef.key]))
|
||||
return obj
|
||||
return value
|
||||
|
||||
|
||||
@@ -43,8 +43,9 @@ def toxml(datatype, key, value):
|
||||
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)))
|
||||
for attrdef in datatype._wsme_attributes:
|
||||
el.append(toxml(attrdef.datatype, attrdef.key,
|
||||
getattr(value, attrdef.key)))
|
||||
else:
|
||||
el.text = unicode(value)
|
||||
return el
|
||||
@@ -73,10 +74,10 @@ def fromxml(datatype, element):
|
||||
return None
|
||||
if wsme.types.iscomplex(datatype):
|
||||
obj = datatype()
|
||||
for key, attrdef in datatype._wsme_attributes:
|
||||
sub = element.find(key)
|
||||
for attrdef in datatype._wsme_attributes:
|
||||
sub = element.find(attrdef.key)
|
||||
if sub is not None:
|
||||
setattr(obj, key, fromxml(attrdef.datatype, sub))
|
||||
setattr(obj, attrdef.key, fromxml(attrdef.datatype, sub))
|
||||
return obj
|
||||
return datatype(element.text)
|
||||
|
||||
|
||||
@@ -35,10 +35,10 @@ def prepare_result(value, datatype):
|
||||
if datatype == datetime.datetime:
|
||||
return parse_isodatetime(value)
|
||||
if hasattr(datatype, '_wsme_attributes'):
|
||||
for name, attr in datatype._wsme_attributes:
|
||||
if name not in value:
|
||||
for attr in datatype._wsme_attributes:
|
||||
if attr.key not in value:
|
||||
continue
|
||||
value[name] = prepare_result(value[name], attr.datatype)
|
||||
value[attr.key] = prepare_result(value[attr.key], attr.datatype)
|
||||
return value
|
||||
if datatype == wsme.types.binary:
|
||||
return base64.decodestring(value)
|
||||
|
||||
@@ -27,7 +27,8 @@ def dumpxml(key, obj, datatype=None):
|
||||
elif type(obj) in (datetime.date, datetime.time, datetime.datetime):
|
||||
el.text = obj.isoformat()
|
||||
elif hasattr(datatype, '_wsme_attributes'):
|
||||
for name, attr in datatype._wsme_attributes:
|
||||
for attr in datatype._wsme_attributes:
|
||||
name = attr.key
|
||||
if name not in obj:
|
||||
continue
|
||||
o = obj[name]
|
||||
@@ -46,7 +47,8 @@ def loadxml(el, datatype):
|
||||
return [loadxml(item, datatype[0]) for item in el.findall('item')]
|
||||
elif len(el):
|
||||
d = {}
|
||||
for name, attr in datatype._wsme_attributes:
|
||||
for attr in datatype._wsme_attributes:
|
||||
name = attr.key
|
||||
child = el.find(name)
|
||||
print name, attr, child
|
||||
if child is not None:
|
||||
|
||||
@@ -22,13 +22,13 @@ class TestTypes(unittest.TestCase):
|
||||
attrs = Flat._wsme_attributes
|
||||
print attrs
|
||||
|
||||
assert attrs[0][0] == 'aint'
|
||||
assert isinstance(attrs[0][1], types.wsattr)
|
||||
assert attrs[0][1].datatype == int
|
||||
assert attrs[0][1].mandatory == False
|
||||
assert attrs[1][0] == 'astr'
|
||||
assert attrs[2][0] == 'auni'
|
||||
assert attrs[3][0] == 'afloat'
|
||||
assert attrs[0].key == 'aint'
|
||||
assert isinstance(attrs[0], types.wsattr)
|
||||
assert attrs[0].datatype == int
|
||||
assert attrs[0].mandatory == False
|
||||
assert attrs[1].key == 'astr'
|
||||
assert attrs[2].key == 'auni'
|
||||
assert attrs[3].key == 'afloat'
|
||||
|
||||
def test_private_attr(self):
|
||||
class WithPrivateAttrs(object):
|
||||
@@ -48,9 +48,9 @@ class TestTypes(unittest.TestCase):
|
||||
types.register_type(ForcedOrder)
|
||||
|
||||
print ForcedOrder._wsme_attributes
|
||||
assert ForcedOrder._wsme_attributes[0][0] == 'a2'
|
||||
assert ForcedOrder._wsme_attributes[1][0] == 'a1'
|
||||
assert ForcedOrder._wsme_attributes[2][0] == 'a3'
|
||||
assert ForcedOrder._wsme_attributes[0].key == 'a2'
|
||||
assert ForcedOrder._wsme_attributes[1].key == 'a1'
|
||||
assert ForcedOrder._wsme_attributes[2].key == 'a3'
|
||||
|
||||
c = gen_class()
|
||||
print c
|
||||
@@ -63,9 +63,9 @@ class TestTypes(unittest.TestCase):
|
||||
|
||||
types.register_type(c)
|
||||
|
||||
assert c._wsme_attributes[0][0] == 'a1'
|
||||
assert c._wsme_attributes[1][0] == 'a2'
|
||||
assert c._wsme_attributes[2][0] == 'a3'
|
||||
assert c._wsme_attributes[0].key == 'a1'
|
||||
assert c._wsme_attributes[1].key == 'a2'
|
||||
assert c._wsme_attributes[2].key == 'a3'
|
||||
|
||||
def test_wsproperty(self):
|
||||
class WithWSProp(object):
|
||||
@@ -84,7 +84,8 @@ class TestTypes(unittest.TestCase):
|
||||
|
||||
print WithWSProp._wsme_attributes
|
||||
assert len(WithWSProp._wsme_attributes) == 1
|
||||
a = WithWSProp._wsme_attributes[0][1]
|
||||
a = WithWSProp._wsme_attributes[0]
|
||||
assert a.key == 'aint'
|
||||
assert a.datatype == int
|
||||
assert a.mandatory
|
||||
|
||||
|
||||
@@ -42,29 +42,51 @@ class wsproperty(property):
|
||||
def __init__(self, datatype, fget, fset=None,
|
||||
mandatory=False, doc=None):
|
||||
property.__init__(self, fget, fset)
|
||||
self.key = None
|
||||
self.datatype = datatype
|
||||
self.mandatory = mandatory
|
||||
|
||||
|
||||
class wsattr(object):
|
||||
"""
|
||||
Complex type attribute definition when the datatype only is not
|
||||
enough.
|
||||
Complex type attribute definition.
|
||||
|
||||
Example::
|
||||
|
||||
class MyComplexType(object):
|
||||
optionalvalue = int
|
||||
mandatoryvalue = wsattr(int, mandatory=True)
|
||||
|
||||
After inspection, the non-wsattr attributes will be replace, and
|
||||
the above class will be equivalent to::
|
||||
|
||||
class MyComplexType(object):
|
||||
optionalvalue = wsattr(int)
|
||||
mandatoryvalue = wsattr(int, mandatory=True)
|
||||
|
||||
"""
|
||||
def __init__(self, datatype, mandatory=False):
|
||||
self.key = None # will be set by class inspection
|
||||
self.datatype = datatype
|
||||
self.mandatory = mandatory
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
if instance is None:
|
||||
return self
|
||||
return getattr(instance, '_' + self.key, Unset)
|
||||
|
||||
def __set__(self, instance, value):
|
||||
setattr(instance, '_' + self.key, value)
|
||||
|
||||
def __delete__(self, instance):
|
||||
delattr(instance, '_' + self.key)
|
||||
|
||||
|
||||
def iswsattr(attr):
|
||||
if inspect.isfunction(attr) or inspect.ismethod(attr):
|
||||
return False
|
||||
if isinstance(attr, property) and not isinstance(attr, wsproperty):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@@ -84,7 +106,7 @@ def sort_attributes(class_, attributes):
|
||||
if not len(attributes):
|
||||
return
|
||||
|
||||
attrs = dict(attributes)
|
||||
attrs = dict((a.key, a) for a in attributes)
|
||||
|
||||
if hasattr(class_, '_wsme_attr_order'):
|
||||
names_order = class_._wsme_attr_order
|
||||
@@ -111,7 +133,7 @@ def sort_attributes(class_, attributes):
|
||||
names_order = list(names)
|
||||
names_order.sort()
|
||||
|
||||
attributes[:] = [(name, attrs[name]) for name in names_order]
|
||||
attributes[:] = [attrs[name] for name in names_order]
|
||||
|
||||
|
||||
def inspect_class(class_):
|
||||
@@ -120,8 +142,6 @@ def inspect_class(class_):
|
||||
for name, attr in inspect.getmembers(class_, iswsattr):
|
||||
if name.startswith('_'):
|
||||
continue
|
||||
if isinstance(attr, property) and not isinstance(attr, wsproperty):
|
||||
continue
|
||||
|
||||
if isinstance(attr, wsattr):
|
||||
attrdef = attr
|
||||
@@ -133,8 +153,9 @@ def inspect_class(class_):
|
||||
register_type(attr)
|
||||
attrdef = wsattr(attr)
|
||||
|
||||
attributes.append((name, attrdef))
|
||||
setattr(class_, name, Unset)
|
||||
attrdef.key = name
|
||||
attributes.append(attrdef)
|
||||
setattr(class_, name, attrdef)
|
||||
|
||||
sort_attributes(class_, attributes)
|
||||
return attributes
|
||||
@@ -174,9 +195,3 @@ def list_attributes(class_):
|
||||
if not hasattr(class_, '_wsme_attributes'):
|
||||
register_type(class_)
|
||||
return class_._wsme_attributes
|
||||
|
||||
|
||||
def getattrdef(class_, name):
|
||||
for attrname, attrdef in class_._wsme_attributes:
|
||||
if attrname == name:
|
||||
return attrdef
|
||||
|
||||
Reference in New Issue
Block a user