Now supports user types (non-complex types that are base on native types), the first on being Enum
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user