Now supports user types (non-complex types that are base on native types), the first on being Enum

This commit is contained in:
Christophe de Vienne
2011-10-28 17:16:41 +02:00
parent 190faf8e82
commit 87f442c872
6 changed files with 111 additions and 7 deletions

View File

@@ -42,7 +42,10 @@ def toxml(datatype, key, value):
if value is None:
el.set('nil', 'true')
else:
if wsme.types.iscomplex(datatype):
if wsme.types.isusertype(datatype):
return toxml(datatype.basetype,
key, datatype.tobasetype(value))
elif wsme.types.iscomplex(datatype):
for attrdef in datatype._wsme_attributes:
el.append(toxml(attrdef.datatype, attrdef.key,
getattr(value, attrdef.key)))
@@ -72,6 +75,9 @@ def fromxml(datatype, element):
"""
if element.get('nil', False):
return None
if wsme.types.isusertype(datatype):
return datatype.frombasetype(
fromxml(datatype.basetype, element))
if wsme.types.iscomplex(datatype):
obj = datatype()
for attrdef in datatype._wsme_attributes:

View File

@@ -29,6 +29,9 @@ class CallException(RuntimeError):
self.faultcode, self.faultstring, self.debuginfo)
myenumtype = wsme.types.Enum(str, set(['v1', 'v2']))
class NestedInner(object):
aint = int
@@ -103,6 +106,10 @@ class ReturnTypes(object):
def getnestedarray(self):
return [NestedOuter(), NestedOuter()]
@expose(myenumtype)
def getenum(self):
return 'v2'
class ArgTypes(object):
@expose(str)
@@ -206,6 +213,12 @@ class ArgTypes(object):
assert type(value[0]) == NestedOuter
return value
@expose(myenumtype)
@validate(myenumtype)
def setenum(self, value):
print value
assert type(value) == str
return value
class WithErrors(object):
@expose()
@@ -328,6 +341,10 @@ class ProtocolTestCase(unittest.TestCase):
r = self.call('returntypes/getnestedarray', _rt=[NestedOuter])
assert r == [{'inner': {'aint': 0}}, {'inner': {'aint': 0}}], r
def test_return_enum(self):
r = self.call('returntypes/getenum', _rt=myenumtype)
assert r == 'v2', r
def test_setstr(self):
assert self.call('argtypes/setstr', value='astring') == 'astring'
@@ -405,6 +422,12 @@ class ProtocolTestCase(unittest.TestCase):
_rt=[NestedOuter])
assert r == value
def test_setenum(self):
value = 'v1'
r = self.call('argtypes/setenum', value=value,
_rt=myenumtype)
assert r == value
def test_nested_api(self):
r = self.call('nested/inner/deepfunction', _rt=bool)
assert r is True

View File

@@ -11,6 +11,7 @@ except:
import wsme.protocols.restjson
from wsme.protocols.restjson import fromjson
from wsme.utils import *
from wsme.types import isusertype
def prepare_value(value, datatype):
@@ -26,6 +27,8 @@ def prepare_value(value, datatype):
def prepare_result(value, datatype):
if isusertype(datatype):
datatype = datatype.basetype
if isinstance(datatype, list):
return [prepare_result(item, datatype[0]) for item in value]
if datatype == datetime.date:

View File

@@ -4,6 +4,7 @@ import base64
import wsme.tests.protocol
from wsme.utils import *
from wsme.types import isusertype
try:
import xml.etree.ElementTree as et
@@ -56,6 +57,8 @@ def loadxml(el, datatype):
print d
return d
else:
if isusertype(datatype):
datatype = datatype.basetype
if datatype == datetime.date:
return parse_isodate(el.text)
if datatype == datetime.time:

View File

@@ -133,3 +133,25 @@ class TestTypes(unittest.TestCase):
assert len(AType._wsme_attributes) == 0
assert AType().test == 'test'
def test_enum(self):
aenum = types.Enum(str, ['v1', 'v2'])
assert aenum.basetype is str
class AType(object):
a = aenum
types.register_type(AType)
assert AType.a.datatype is aenum
obj = AType()
obj.a = 'v1'
assert obj.a == 'v1'
try:
obj.a = 'v3'
assert False, 'ValueError was not raised'
except ValueError, e:
assert str(e) == "Value 'v3' is invalid (should be one of: v1, v2)"

View File

@@ -5,6 +5,46 @@ import inspect
binary = object()
class UserType(object):
basetype = None
def validate(self, value):
return
def tobasetype(self, value):
return value
def frombasetype(self, value):
return value
class Enum(UserType):
"""
A simple enumeration type. Can be based on any non-complex type.
:param basetype: The actual data type
:param values: A set of possible values
If nullable, 'None' should be added the values set.
"""
def __init__(self, basetype, values):
self.basetype = basetype
self.values = values
def validate(self, value):
if value not in self.values:
raise ValueError("Value '%s' is invalid (should be one of: %s)" % (
value, ', '.join(self.values)))
def tobasetype(self, value):
return value
def frombasetype(self, value):
return value
def isusertype(class_):
return isinstance(class_, UserType)
pod_types = [str, unicode, int, float, bool]
dt_types = [datetime.date, datetime.time, datetime.datetime]
extra_types = [binary, decimal.Decimal]
@@ -24,6 +64,17 @@ def iscomplex(datatype):
return hasattr(datatype, '_wsme_attributes')
def validate_value(datatype, value):
print datatype
if hasattr(datatype, 'validate'):
return datatype.validate(value)
else:
if value is not None and not isinstance(value, datatype):
raise ValueError(
"Wrong type. Expected '%s', got '%s'" % (
datatype, type(value)
))
class wsproperty(property):
"""
A specialised :class:`property` to define typed-property on complex types.
@@ -76,11 +127,7 @@ class wsattr(object):
return getattr(instance, '_' + self.key, Unset)
def __set__(self, instance, value):
if value is not None and not isinstance(value, self.datatype):
raise ValueError(
"Wrong type for attribute %s. Expected '%s', got '%s'" % (
self.key, self.datatype, type(value)
))
validate_value(self.datatype, value)
setattr(instance, '_' + self.key, value)
def __delete__(self, instance):
@@ -177,7 +224,7 @@ def register_type(class_):
"""
if class_ is None or \
class_ in native_types or \
hasattr(class_, '_wsme_attributes'):
isusertype(class_) or iscomplex(class_):
return
if isinstance(class_, list):