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:
Christophe de Vienne
2011-10-28 11:34:34 +02:00
parent a4290c7ae9
commit 436dab1b52
6 changed files with 63 additions and 43 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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:

View File

@@ -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

View File

@@ -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