From 436dab1b52da67762bd19b0afe8a32e62a44bd4d Mon Sep 17 00:00:00 2001 From: Christophe de Vienne Date: Fri, 28 Oct 2011 11:34:34 +0200 Subject: [PATCH] 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) --- wsme/protocols/restjson.py | 11 +++++----- wsme/protocols/restxml.py | 11 +++++----- wsme/tests/test_restjson.py | 6 +++--- wsme/tests/test_restxml.py | 6 ++++-- wsme/tests/test_types.py | 29 +++++++++++++------------ wsme/types.py | 43 +++++++++++++++++++++++++------------ 6 files changed, 63 insertions(+), 43 deletions(-) diff --git a/wsme/protocols/restjson.py b/wsme/protocols/restjson.py index 33f1d5e..447f58a 100644 --- a/wsme/protocols/restjson.py +++ b/wsme/protocols/restjson.py @@ -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 diff --git a/wsme/protocols/restxml.py b/wsme/protocols/restxml.py index 28c4d91..a05e889 100644 --- a/wsme/protocols/restxml.py +++ b/wsme/protocols/restxml.py @@ -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) diff --git a/wsme/tests/test_restjson.py b/wsme/tests/test_restjson.py index 214f620..beba674 100644 --- a/wsme/tests/test_restjson.py +++ b/wsme/tests/test_restjson.py @@ -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) diff --git a/wsme/tests/test_restxml.py b/wsme/tests/test_restxml.py index eca5d8b..b7e7b6c 100644 --- a/wsme/tests/test_restxml.py +++ b/wsme/tests/test_restxml.py @@ -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: diff --git a/wsme/tests/test_types.py b/wsme/tests/test_types.py index a9a4493..baadfd6 100644 --- a/wsme/tests/test_types.py +++ b/wsme/tests/test_types.py @@ -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 diff --git a/wsme/types.py b/wsme/types.py index c0e2295..d19064a 100644 --- a/wsme/types.py +++ b/wsme/types.py @@ -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