Structured types basically work with rest+json
This commit is contained in:
parent
8fe674922f
commit
ee40938d64
@ -3,6 +3,8 @@ syntax: glob
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.egg-info
|
||||
*.swp
|
||||
~*
|
||||
.coverage
|
||||
|
||||
syntax: regexp
|
||||
|
@ -1,2 +1,4 @@
|
||||
from controller import *
|
||||
from controller import expose, validate, WSRoot
|
||||
from types import wsattr, wsproperty
|
||||
|
||||
import restjson
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user