Skip to content
This repository was archived by the owner on Dec 10, 2018. It is now read-only.

add TDecodeException to pure python binary.py's write_struct #289

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion tests/test_protocol_binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

from io import BytesIO

import pytest
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should not import a third-party package IMHO


from thriftpy._compat import u
from thriftpy.thrift import TType, TPayload
from thriftpy.thrift import TType, TPayload, TDecodeException
from thriftpy.utils import hexlify
from thriftpy.protocol import binary as proto

Expand Down Expand Up @@ -111,6 +113,33 @@ def test_write_message_begin_not_strict():
hexlify(b.getvalue())


def test_write_decode_error():
b = BytesIO()
p = proto.TBinaryProtocol(b)

class T(TPayload):
thrift_spec = {
1: (TType.I32, "id", False),
2: (TType.LIST, "phones", TType.STRING, False),
3: (TType.STRUCT, "item", TItem, False),
4: (TType.MAP, "mm", (TType.STRING, (TType.STRUCT, TItem)), False)
}
default_spec = [("id", None), ("phones", None), ("item", None),
("mm", None)]

cases = [
(T(id="hello"), "Field 'id(1)' of 'T' needs type 'I32', but the value is `'hello'`"), # noqa
(T(phones=[90, 12]), "Field 'phones(2)' of 'T' needs type 'LIST<STRING>', but the value is `[90, 12]`"), # noqa
(T(item=12), "Field 'item(3)' of 'T' needs type 'TItem', but the value is `12`"), # noqa
(T(mm=[45, 56]), "Field 'mm(4)' of 'T' needs type 'MAP<STRING, TItem>', but the value is `[45, 56]`") # noqa
]

for obj, res in cases:
with pytest.raises(TDecodeException) as exc:
p.write_struct(obj)
assert str(exc.value) == res


def test_read_message_begin():
b = BytesIO(b"\x80\x01\x00\x0b\x00\x00\x00\x04test\x00\x00\x00\x01")
res = proto.TBinaryProtocol(b).read_message_begin()
Expand Down
99 changes: 58 additions & 41 deletions thriftpy/protocol/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import struct

from ..thrift import TType
from ..thrift import TType, TDecodeException

from .exc import TProtocolException

Expand Down Expand Up @@ -86,6 +86,60 @@ def write_map_begin(outbuf, ktype, vtype, size):
outbuf.write(pack_i8(ktype) + pack_i8(vtype) + pack_i32(size))


def write_struct(outbuf, val):
for fid in iter(val.thrift_spec):
f_spec = val.thrift_spec[fid]
if len(f_spec) == 3:
f_type, f_name, f_req = f_spec
f_container_spec = None
else:
f_type, f_name, f_container_spec, f_req = f_spec

v = getattr(val, f_name)
if v is None:
continue

write_field_begin(outbuf, f_type, fid)
try:
write_val(outbuf, f_type, v, f_container_spec)
except (TypeError, AttributeError, AssertionError, OverflowError, struct.error):
raise TDecodeException(val.__class__.__name__, fid, f_name, v,
f_type, f_container_spec)

write_field_stop(outbuf)


def write_dict(outbuf, val, spec):
if isinstance(spec[0], int):
k_type = spec[0]
k_spec = None
else:
k_type, k_spec = spec[0]

if isinstance(spec[1], int):
v_type = spec[1]
v_spec = None
else:
v_type, v_spec = spec[1]

write_map_begin(outbuf, k_type, v_type, len(val))
for k in iter(val):
write_val(outbuf, k_type, k, k_spec)
write_val(outbuf, v_type, val[k], v_spec)


def write_list(outbuf, val, spec):
if isinstance(spec, tuple):
e_type, t_spec = spec[0], spec[1]
else:
e_type, t_spec = spec, None

val_len = len(val)
write_list_begin(outbuf, e_type, val_len)
for e_val in val:
write_val(outbuf, e_type, e_val, t_spec)


def write_val(outbuf, ttype, val, spec=None):
if ttype == TType.BOOL:
if val:
Expand Down Expand Up @@ -114,50 +168,13 @@ def write_val(outbuf, ttype, val, spec=None):
outbuf.write(pack_string(val))

elif ttype == TType.SET or ttype == TType.LIST:
if isinstance(spec, tuple):
e_type, t_spec = spec[0], spec[1]
else:
e_type, t_spec = spec, None

val_len = len(val)
write_list_begin(outbuf, e_type, val_len)
for e_val in val:
write_val(outbuf, e_type, e_val, t_spec)
write_list(outbuf, val, spec)

elif ttype == TType.MAP:
if isinstance(spec[0], int):
k_type = spec[0]
k_spec = None
else:
k_type, k_spec = spec[0]

if isinstance(spec[1], int):
v_type = spec[1]
v_spec = None
else:
v_type, v_spec = spec[1]

write_map_begin(outbuf, k_type, v_type, len(val))
for k in iter(val):
write_val(outbuf, k_type, k, k_spec)
write_val(outbuf, v_type, val[k], v_spec)
write_dict(outbuf, val, spec)

elif ttype == TType.STRUCT:
for fid in iter(val.thrift_spec):
f_spec = val.thrift_spec[fid]
if len(f_spec) == 3:
f_type, f_name, f_req = f_spec
f_container_spec = None
else:
f_type, f_name, f_container_spec, f_req = f_spec

v = getattr(val, f_name)
if v is None:
continue

write_field_begin(outbuf, f_type, fid)
write_val(outbuf, f_type, v, f_container_spec)
write_field_stop(outbuf)
write_struct(outbuf, val)


def read_message_begin(inbuf, strict=True):
Expand Down