From b84b026c29a3f8c8dbdfcf0a39fedfd1ab00486d Mon Sep 17 00:00:00 2001 From: Christophe de Vienne Date: Wed, 25 Apr 2012 23:33:35 +0200 Subject: [PATCH] Clarified the bytes/text types handling. Now all the restjson tests pass on python 2 and 3. --- wsme/protocols/restjson.py | 2 +- wsme/tests/protocol.py | 109 ++++++++++++++++++------------------ wsme/tests/test_restjson.py | 28 +++++---- wsme/types.py | 15 ++++- 4 files changed, 87 insertions(+), 67 deletions(-) diff --git a/wsme/protocols/restjson.py b/wsme/protocols/restjson.py index 43d0b4d..504f213 100644 --- a/wsme/protocols/restjson.py +++ b/wsme/protocols/restjson.py @@ -46,7 +46,7 @@ def tojson(datatype, value): return value -@tojson.when_object(six.binary_type) +@tojson.when_object(wsme.types.bytes) def bytes_tojson(datatype, value): if value is None: return None diff --git a/wsme/tests/protocol.py b/wsme/tests/protocol.py index dff97f4..68e831a 100644 --- a/wsme/tests/protocol.py +++ b/wsme/tests/protocol.py @@ -19,7 +19,7 @@ import wsme.types warnings.filterwarnings('ignore', module='webob.dec') -binarysample = r'\x00\xff\x43' +binarysample = b('\x00\xff\x43') try: 1 / 0 @@ -39,7 +39,7 @@ class CallException(RuntimeError): self.faultcode, self.faultstring, self.debuginfo) -myenumtype = wsme.types.Enum(str, 'v1', 'v2') +myenumtype = wsme.types.Enum(wsme.types.bytes, 'v1', 'v2') class NestedInner(object): @@ -76,12 +76,12 @@ class NestedOuterApi(object): class ReturnTypes(object): - @expose(six.binary_type) - def getstr(self): - return "astring" + @expose(wsme.types.bytes) + def getbytes(self): + return b("astring") - @expose(six.text_type) - def getunicode(self): + @expose(wsme.types.text) + def gettext(self): return u('\xe3\x81\xae') @expose(int) @@ -117,21 +117,21 @@ class ReturnTypes(object): n = NestedOuter() return n - @expose([six.binary_type]) - def getstrarray(self): - return ["A", "B", "C"] + @expose([wsme.types.bytes]) + def getbytesarray(self): + return [b("A"), b("B"), b("C")] @expose([NestedOuter]) def getnestedarray(self): return [NestedOuter(), NestedOuter()] - @expose({six.binary_type: NestedOuter}) + @expose({wsme.types.bytes: NestedOuter}) def getnesteddict(self): - return {'a': NestedOuter(), 'b': NestedOuter()} + return {b('a'): NestedOuter(), b('b'): NestedOuter()} @expose(myenumtype) def getenum(self): - return 'v2' + return b('v2') @expose(NamedAttrsObject) def getnamedattrsobj(self): @@ -139,18 +139,18 @@ class ReturnTypes(object): class ArgTypes(object): - @expose(six.binary_type) - @validate(six.binary_type) - def setstr(self, value): + @expose(wsme.types.bytes) + @validate(wsme.types.bytes) + def setbytes(self, value): print(repr(value)) - assert type(value) == six.binary_type + assert type(value) == wsme.types.bytes return value - @expose(six.text_type) - @validate(six.text_type) - def setunicode(self, value): + @expose(wsme.types.text) + @validate(wsme.types.text) + def settext(self, value): print(repr(value)) - assert type(value) == six.text_type + assert type(value) == wsme.types.text return value @expose(bool) @@ -209,12 +209,12 @@ class ArgTypes(object): assert type(value) == six.binary_type return value - @expose([six.binary_type]) - @validate([six.binary_type]) - def setstrarray(self, value): + @expose([wsme.types.bytes]) + @validate([wsme.types.bytes]) + def setbytesarray(self, value): print(repr(value)) assert type(value) == list - assert type(value[0]) == six.binary_type, type(value[0]) + assert type(value[0]) == wsme.types.bytes, type(value[0]) return value @expose([datetime.datetime]) @@ -240,12 +240,12 @@ class ArgTypes(object): assert type(value[0]) == NestedOuter return value - @expose({six.binary_type: NestedOuter}) - @validate({six.binary_type: NestedOuter}) + @expose({wsme.types.bytes: NestedOuter}) + @validate({wsme.types.bytes: NestedOuter}) def setnesteddict(self, value): print(repr(value)) assert type(value) == dict - assert type(list(value.keys())[0]) == six.binary_type + assert type(list(value.keys())[0]) == wsme.types.bytes assert type(list(value.values())[0]) == NestedOuter return value @@ -253,7 +253,7 @@ class ArgTypes(object): @validate(myenumtype) def setenum(self, value): print(value) - assert type(value) == six.binary_type + assert type(value) == wsme.types.bytes return value @expose(NamedAttrsObject) @@ -344,12 +344,12 @@ class ProtocolTestCase(unittest.TestCase): r = self.call('touch') assert r is None, r - def test_return_str(self): - r = self.call('returntypes/getstr') - assert r == 'astring', r + def test_return_bytes(self): + r = self.call('returntypes/getbytes', _rt=wsme.types.bytes) + assert r == b('astring'), r - def test_return_unicode(self): - r = self.call('returntypes/getunicode') + def test_return_text(self): + r = self.call('returntypes/gettext', _rt=wsme.types.text) assert r == u('\xe3\x81\xae'), r def test_return_int(self): @@ -378,40 +378,43 @@ class ProtocolTestCase(unittest.TestCase): or r == '1994-01-26T12:00:00', r def test_return_binary(self): - r = self.call('returntypes/getbinary') - assert r == binarysample or r == base64.encodestring(binarysample), r + r = self.call('returntypes/getbinary', _rt=wsme.types.binary) + assert r == binarysample, r def test_return_nested(self): r = self.call('returntypes/getnested', _rt=NestedOuter) assert r == {'inner': {'aint': 0}}, r - def test_return_strarray(self): - r = self.call('returntypes/getstrarray', _rt=[six.binary_type]) - assert r == ['A', 'B', 'C'], r + def test_return_bytesarray(self): + r = self.call('returntypes/getbytesarray', _rt=[six.binary_type]) + assert r == [b('A'), b('B'), b('C')], r def test_return_nestedarray(self): r = self.call('returntypes/getnestedarray', _rt=[NestedOuter]) assert r == [{'inner': {'aint': 0}}, {'inner': {'aint': 0}}], r def test_return_nesteddict(self): - r = self.call('returntypes/getnesteddict', _rt={six.binary_type: NestedOuter}) - assert r == {'a': {'inner': {'aint': 0}}, 'b': {'inner': {'aint': 0}}} + r = self.call('returntypes/getnesteddict', + _rt={wsme.types.bytes: NestedOuter}) + assert r == { + b('a'): {'inner': {'aint': 0}}, + b('b'): {'inner': {'aint': 0}}}, r def test_return_enum(self): r = self.call('returntypes/getenum', _rt=myenumtype) - assert r == 'v2', r + assert r == b('v2'), r def test_return_namedattrsobj(self): r = self.call('returntypes/getnamedattrsobj', _rt=NamedAttrsObject) assert r == {'attr.1': 5, 'attr.2': 6} - def test_setstr(self): - assert self.call('argtypes/setstr', value=b('astring'), - _rt=six.binary_type) == b('astring') + def test_setbytes(self): + assert self.call('argtypes/setbytes', value=b('astring'), + _rt=wsme.types.bytes) == b('astring') - def test_setunicode(self): - assert self.call('argtypes/setunicode', value=u('\xe3\x81\xae'), - _rt=six.text_type) == u('\xe3\x81\xae') + def test_settext(self): + assert self.call('argtypes/settext', value=u('\xe3\x81\xae'), + _rt=wsme.types.text) == u('\xe3\x81\xae') def test_setint(self): r = self.call('argtypes/setint', value=3, _rt=int) @@ -457,11 +460,11 @@ class ProtocolTestCase(unittest.TestCase): _rt=NestedOuter) assert r == value - def test_setstrarray(self): + def test_setbytesarray(self): value = [b("1"), b("2"), b("three")] - r = self.call('argtypes/setstrarray', - value=(value, [six.binary_type]), - _rt=[six.binary_type]) + r = self.call('argtypes/setbytesarray', + value=(value, [wsme.types.bytes]), + _rt=[wsme.types.bytes]) assert r == value, r def test_setdatetimearray(self): @@ -496,7 +499,7 @@ class ProtocolTestCase(unittest.TestCase): assert r == value def test_setenum(self): - value = 'v1' + value = b('v1') r = self.call('argtypes/setenum', value=value, _rt=myenumtype) assert r == value diff --git a/wsme/tests/test_restjson.py b/wsme/tests/test_restjson.py index c75240e..1fdc674 100644 --- a/wsme/tests/test_restjson.py +++ b/wsme/tests/test_restjson.py @@ -3,8 +3,6 @@ import datetime import decimal import urllib -import six - import wsme.tests.protocol try: @@ -18,6 +16,14 @@ from wsme.utils import parse_isodatetime, parse_isotime, parse_isodate from wsme.types import isusertype, register_type +import six + +if six.PY3: + from urllib.parse import urlencode +else: + from urllib import urlencode + + def prepare_value(value, datatype): if isinstance(datatype, list): return [prepare_value(item, datatype[0]) for item in value] @@ -33,14 +39,16 @@ def prepare_value(value, datatype): if datatype == decimal.Decimal: return str(value) if datatype == wsme.types.binary: - return base64.encodestring(value) - if datatype == six.binary_type: + return base64.encodestring(value).decode('ascii') + if datatype == wsme.types.bytes: return value.decode('ascii') return value def prepare_result(value, datatype): print(value, datatype) + if datatype == wsme.types.binary: + return base64.decodestring(value.encode('ascii')) if isusertype(datatype): datatype = datatype.basetype if isinstance(datatype, list): @@ -63,10 +71,8 @@ def prepare_result(value, datatype): continue value[attr.key] = prepare_result(value[attr.key], attr.datatype) return value - if datatype == wsme.types.binary: - return base64.decodestring(value) - if datatype == six.binary_type: - return value.encode('utf8') + if datatype == wsme.types.bytes: + return value.encode('ascii') if type(value) != datatype: return datatype(value) return value @@ -133,7 +139,7 @@ class TestRestJson(wsme.tests.protocol.ProtocolTestCase): 'value[0].inner.aint': 54, 'value[1].inner.aint': 55 } - body = urllib.urlencode(params) + body = urlencode(params) r = self.app.post('/argtypes/setnestedarray.json', body, headers={'Content-Type': 'application/x-www-form-urlencoded'}) print(r) @@ -153,13 +159,13 @@ class TestRestJson(wsme.tests.protocol.ProtocolTestCase): "Cannot read parameters from both a body and GET/POST params" def test_inline_body(self): - params = urllib.urlencode({'body': '{"value": 4}'}) + params = urlencode({'body': '{"value": 4}'}) r = self.app.get('/argtypes/setint.json?' + params) print(r) assert json.loads(r.text) == 4 def test_empty_body(self): - params = urllib.urlencode({'body': ''}) + params = urlencode({'body': ''}) r = self.app.get('/returntypes/getint.json?' + params) print(r) assert json.loads(r.text) == 2 diff --git a/wsme/types.py b/wsme/types.py index 6d3a73a..2237556 100644 --- a/wsme/types.py +++ b/wsme/types.py @@ -7,6 +7,17 @@ import six import sys +#: The 'str' (python 2) or 'bytes' (python 3) type. +#: Its use should be restricted to +#: pure ascii strings as the protocols will generally not be +#: be able to send non-unicode strings. +#: To transmit binary strings, use the :class:`binary` type +bytes = six.binary_type + +#: Unicode string. +text = six.text_type + + class UserType(object): basetype = None @@ -28,7 +39,7 @@ class BinaryType(UserType): """ A user type that use base64 strings to carry binary data. """ - basetype = str + basetype = bytes def tobasetype(self, value): return base64.encodestring(value) @@ -71,7 +82,7 @@ class Enum(UserType): return value pod_types = six.integer_types + ( - six.binary_type, six.text_type, float, bool) + bytes, text, float, bool) dt_types = (datetime.date, datetime.time, datetime.datetime) extra_types = (binary, decimal.Decimal) native_types = pod_types + dt_types + extra_types