Structured types basically work with rest+json

This commit is contained in:
Christophe de Vienne 2011-09-20 12:28:15 +02:00
parent 8fe674922f
commit ee40938d64
8 changed files with 155 additions and 24 deletions

View File

@ -3,6 +3,8 @@ syntax: glob
*.pyc
*.pyo
*.egg-info
*.swp
~*
.coverage
syntax: regexp

View File

@ -1,2 +1,4 @@
from controller import *
from controller import expose, validate, WSRoot
from types import wsattr, wsproperty
import restjson

View File

@ -3,6 +3,7 @@ import traceback
import weakref
from wsme import exc
from wsme.types import register_type
__all__ = ['expose', 'validate', 'WSRoot']
@ -54,6 +55,7 @@ def register_protocol(protocol):
class expose(object):
def __init__(self, return_type=None):
self.return_type = return_type
register_type(return_type)
def __call__(self, func):
fd = FunctionDefinition.get(func)

View File

@ -13,10 +13,10 @@ except ImportError:
def prepare_encode(value, datatype):
if datatype in wsme.types.pod_types:
return value
if datatype in wsme.types.structured_types:
if wsme.types.isstructured(datatype):
d = dict()
for name, datatype, mandatory in wsme.types.list_attributes(datatype):
d[name] = prepare_encode(getattr(value, name), datatype)
for name, attr in wsme.types.list_attributes(datatype):
d[name] = prepare_encode(getattr(value, name), attr.datatype)
return d
if datatype in wsme.types.dt_types:
return value.isoformat()

View File

@ -12,6 +12,7 @@ from wsme import *
warnings.filterwarnings('ignore', module='webob.dec')
class CallException(RuntimeError):
def __init__(self, faultcode, faultstring, debuginfo):
self.faultcode = faultcode
@ -22,6 +23,21 @@ class CallException(RuntimeError):
return 'faultcode=%s, faultstring=%s, debuginfo=%s' % (
self.faultcode, self.faultstring, self.debuginfo)
class NestedInner(object):
aint = int
def __init__(self, aint=None):
self.aint = aint
class NestedOuter(object):
inner = NestedInner
def __init__(self):
self.inner = NestedInner(0)
class ReturnTypes(object):
@expose(str)
def getstr(self):
@ -55,12 +71,18 @@ class ReturnTypes(object):
def getdate(self):
return datetime.datetime(1994, 1, 26, 12, 0, 0)
@expose(NestedOuter)
def getnested(self):
n = NestedOuter()
return n
class WithErrors(object):
@expose()
def divide_by_zero(self):
1 / 0
class WSTestRoot(WSRoot):
returntypes = ReturnTypes()
witherrors = WithErrors()
@ -116,4 +138,6 @@ class ProtocolTestCase(unittest.TestCase):
r = self.call('returntypes/getfloat')
assert r == 3.14159265, r
def test_return_nested(self):
r = self.call('returntypes/getnested')
assert r == {'inner': {'aint': 0}}, r

View File

@ -17,6 +17,7 @@ class TestRestJson(wsme.tests.protocol.ProtocolTestCase):
'Content-Type': 'application/json',
},
expect_errors=True)
print "Received:", res.body
r = json.loads(res.body)
if 'result' in r:
return r['result']
@ -25,6 +26,5 @@ class TestRestJson(wsme.tests.protocol.ProtocolTestCase):
r['faultcode'],
r['faultstring'],
r.get('debuginfo'))
return json.loads(res.body)
return json.loads(res.body)

View File

@ -1,6 +1,7 @@
import unittest
from wsme import types
class TestTypes(unittest.TestCase):
def test_flat_type(self):
class Flat(object):
@ -12,12 +13,58 @@ class TestTypes(unittest.TestCase):
types.register_type(Flat)
assert len(Flat._wsme_attributes) == 4
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'
def test_private_attr(self):
class WithPrivateAttrs(object):
_private = 12
types.register_type(WithPrivateAttrs)
assert len(WithPrivateAttrs._wsme_attributes) == 0
def test_wsproperty(self):
class WithWSProp(object):
def __init__(self):
self._aint = 0
def get_aint(self):
return self._aint
def set_aint(self, value):
self._aint = value
aint = types.wsproperty(int, get_aint, set_aint, mandatory=True)
types.register_type(WithWSProp)
assert len(WithWSProp._wsme_attributes) == 1
a = WithWSProp._wsme_attributes[0][1]
assert a.datatype == int
assert a.mandatory
o = WithWSProp()
o.aint = 12
assert o.aint == 12
def test_nested(self):
class Inner(object):
aint = int
class Outer(object):
inner = Inner
types.register_type(Outer)
assert hasattr(Inner, '_wsme_attributes')
assert len(Inner._wsme_attributes) == 1

View File

@ -13,10 +13,15 @@ native_types = pod_types + dt_types + extra_types
structured_types = []
def isstructured(datatype):
return hasattr(datatype, '_wsme_attributes')
class wsproperty(property):
def __init__(self, datatype, fget, fset=None,
mandatory=False, doc=None):
property.__init__(self, fget, fset, doc)
property.__init__(self, fget, fset)
self.datatype = datatype
self.mandatory = mandatory
@ -26,32 +31,81 @@ class wsattr(object):
self.mandatory = mandatory
def iswsattr(attr):
if inspect.isfunction(attr) or inspect.ismethod(attr):
return False
return True
def sort_attributes(class_, attributes):
"""Sort a class attributes list.
3 mechanisms are attempted :
#. Look for a _wsme_attr_order attribute on the class_. This allow
to define an arbitrary order of the attributes (usefull for
generated types).
#. Access the object source code to find the declaration order.
#. Sort by alphabetically"""
attrs = dict(attributes)
if hasattr(class_, '_wsme_attr_order'):
names_order = class_._wsme_attr_order
else:
names = attrs.keys()
names_order = []
try:
lines = []
for line in inspect.getsourcelines(class_)[0]:
line = line.strip().replace(" ", "")
if '=' in line:
aname = line[:line.index('=')]
if aname in names and aname not in names_order:
names_order.append(aname)
assert len(names_order) == len(names)
except IOError, e:
names_order = list(names)
names_order.sort()
attributes[:] = [(name, attrs[name]) for name in names_order]
def inspect_class(class_):
"""Extract a list of (name, wsattr|wsproperty) for the given class_"""
attributes = []
for name in dir(class_):
for name, attr in inspect.getmembers(class_, iswsattr):
if name.startswith('_'):
continue
attr = getattr(class_, name)
if inspect.isfunction(attr):
continue
if inspect.ismethod(attr):
continue
if not isinstance(attr, wsattr):
attrdef = wsattr(attr)
else:
attrdef = attr
attributes.append((name, wsattr))
if isinstance(attr, wsattr):
attrdef = attr
elif isinstance(attr, wsproperty):
attrdef = attr
else:
if attr not in native_types and inspect.isclass(attr):
print name, attr
register_type(attr)
attrdef = wsattr(attr)
attributes.append((name, attrdef))
sort_attributes(class_, attributes)
return attributes
def register_type(class_):
if hasattr(class_, '_wsme_attributes'):
if class_ is None or \
class_ in native_types or \
hasattr(class_, '_wsme_attributes'):
return
class_._wsme_attributes = inspect_class(class_)
structured_types.append(weakref.ref(class_))
def list_attributes(class_):
return class_._wsme_attributes
def list_attributes(class_):
if not hasattr(class_, '_wsme_attributes'):
register_type(class_)
return class_._wsme_attributes