Skip to content

Commit b02ea20

Browse files
msgpack: support error extension type
Tarantool supports error extension type since version 2.4.1 [1], encoding was introduced in Tarantool 2.10.0 [2]. This patch introduced the support of Tarantool error extension type in msgpack decoders and encoders. Tarantool error extension type objects are decoded to `tarantool.BoxError` type. `tarantool.BoxError` may be encoded to Tarantool error extension type objects. Error extension type internals are the same as errors extra information: the only difference is that extra information is encoded as a separate error dictionary field and error extension type objects is encoded as MessagePack extension type objects. The only way to receive an error extension type object from Tarantool is to receive an explicitly built `box.error` object: either from `return box.error.new(...)` or a tuple with it. All errors raised within Tarantool (including those raised with `box.error(...)`) are encoded based on the same rules as simple errors due to backward compatibility. It is possible to create error extension type objects with Python code, but it not likely to be really useful since most of their fields is computed on error initialization on server side (even for custom error types): ``` tarantool.BoxError([ tarantool.BoxErrorStackUnit( type='ClientError', file='[string " local err = box.error.ne..."]', line=1, message='Unknown error', errno=0, errcode=0, ) ]) ``` 1. tarantool/tarantool#4398 2. tarantool/tarantool#6433 Part of #232
1 parent 216cc37 commit b02ea20

File tree

17 files changed

+577
-98
lines changed

17 files changed

+577
-98
lines changed

doc/dev-guide.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ they are represented with in-built and custom types:
8383
+-----------------------------+----+-------------+----+-----------------------------+
8484
| :obj:`uuid.UUID` | -> | `UUID`_ | -> | :obj:`uuid.UUID` |
8585
+-----------------------------+----+-------------+----+-----------------------------+
86+
| :class:`tarantool.BoxError` | -> | `ERROR`_ | -> | :class:`tarantool.BoxError` |
87+
+-----------------------------+----+-------------+----+-----------------------------+
8688
| :class:`tarantool.Datetime` | -> | `DATETIME`_ | -> | :class:`tarantool.Datetime` |
8789
+-----------------------------+----+-------------+----+-----------------------------+
8890
| :class:`tarantool.Interval` | -> | `INTERVAL`_ | -> | :class:`tarantool.Interval` |
@@ -109,5 +111,6 @@ and iterate through it as with any other serializable object.
109111
.. _extension types: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/
110112
.. _DECIMAL: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-decimal-type
111113
.. _UUID: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-uuid-type
114+
.. _ERROR: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-error-type
112115
.. _DATETIME: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-datetime-type
113116
.. _INTERVAL: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-interval-type

tarantool/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242

4343
from tarantool.connection_pool import ConnectionPool, Mode
4444

45+
from tarantool.types import BoxError
46+
4547
__version__ = "0.9.0"
4648

4749

@@ -136,4 +138,4 @@ def connectmesh(addrs=({'host': 'localhost', 'port': 3301},), user=None,
136138
__all__ = ['connect', 'Connection', 'connectmesh', 'MeshConnection', 'Schema',
137139
'Error', 'DatabaseError', 'NetworkError', 'NetworkWarning',
138140
'SchemaError', 'dbapi', 'Datetime', 'Interval', 'IntervalAdjust',
139-
'ConnectionPool', 'Mode']
141+
'ConnectionPool', 'Mode', 'BoxError',]

tarantool/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,4 @@
129129
# Tarantool 2.10 protocol version is 3
130130
CONNECTOR_IPROTO_VERSION = 3
131131
# List of connector-supported features
132-
CONNECTOR_FEATURES = []
132+
CONNECTOR_FEATURES = [IPROTO_FEATURE_ERROR_EXTENSION,]

tarantool/msgpack_ext/datetime.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
`datetime`_ type id.
1414
"""
1515

16-
def encode(obj):
16+
def encode(obj, _):
1717
"""
1818
Encode a datetime object.
1919
@@ -28,7 +28,7 @@ def encode(obj):
2828

2929
return obj.msgpack_encode()
3030

31-
def decode(data):
31+
def decode(data, _):
3232
"""
3333
Decode a datetime object.
3434

tarantool/msgpack_ext/decimal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ def strip_decimal_str(str_repr, scale, first_digit_ind):
225225
# Do not strips zeroes before the decimal point
226226
return str_repr
227227

228-
def encode(obj):
228+
def encode(obj, _):
229229
"""
230230
Encode a decimal object.
231231
@@ -335,7 +335,7 @@ def add_str_digit(digit, digits_reverted, scale):
335335

336336
digits_reverted.append(str(digit))
337337

338-
def decode(data):
338+
def decode(data, _):
339339
"""
340340
Decode a decimal object.
341341

tarantool/msgpack_ext/error.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
Tarantool `error`_ extension type support module.
3+
4+
Refer to :mod:`~tarantool.msgpack_ext.types.error`.
5+
6+
.. _error: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-error-type
7+
"""
8+
9+
from tarantool.types import (
10+
encode_box_error,
11+
decode_box_error,
12+
)
13+
14+
EXT_ID = 3
15+
"""
16+
`error`_ type id.
17+
"""
18+
19+
def encode(obj, packer):
20+
"""
21+
Encode an error object.
22+
23+
:param obj: Error to encode.
24+
:type obj: :class:`tarantool.BoxError`
25+
26+
:param packer: msgpack packer to encode error dictionary.
27+
:type packer: :class:`msgpack.Packer`
28+
29+
:return: Encoded error.
30+
:rtype: :obj:`bytes`
31+
"""
32+
33+
err_map = encode_box_error(obj)
34+
return packer.pack(err_map)
35+
36+
def decode(data, unpacker):
37+
"""
38+
Decode an error object.
39+
40+
:param obj: Error to decode.
41+
:type obj: :obj:`bytes`
42+
43+
:param unpacker: msgpack unpacker to decode error dictionary.
44+
:type unpacker: :class:`msgpack.Unpacker`
45+
46+
:return: Decoded error.
47+
:rtype: :class:`tarantool.BoxError`
48+
"""
49+
50+
unpacker.feed(data)
51+
err_map = unpacker.unpack()
52+
return decode_box_error(err_map)

tarantool/msgpack_ext/interval.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
`datetime.interval`_ type id.
1414
"""
1515

16-
def encode(obj):
16+
def encode(obj, _):
1717
"""
1818
Encode an interval object.
1919
@@ -28,7 +28,7 @@ def encode(obj):
2828

2929
return obj.msgpack_encode()
3030

31-
def decode(data):
31+
def decode(data, _):
3232
"""
3333
Decode an interval object.
3434

tarantool/msgpack_ext/packer.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,36 @@
88
from uuid import UUID
99
from msgpack import ExtType
1010

11+
from tarantool.types import BoxError
1112
from tarantool.msgpack_ext.types.datetime import Datetime
1213
from tarantool.msgpack_ext.types.interval import Interval
1314

1415
import tarantool.msgpack_ext.decimal as ext_decimal
1516
import tarantool.msgpack_ext.uuid as ext_uuid
17+
import tarantool.msgpack_ext.error as ext_error
1618
import tarantool.msgpack_ext.datetime as ext_datetime
1719
import tarantool.msgpack_ext.interval as ext_interval
1820

1921
encoders = [
2022
{'type': Decimal, 'ext': ext_decimal },
2123
{'type': UUID, 'ext': ext_uuid },
24+
{'type': BoxError, 'ext': ext_error },
2225
{'type': Datetime, 'ext': ext_datetime},
2326
{'type': Interval, 'ext': ext_interval},
2427
]
2528

26-
def default(obj):
29+
def default(obj, packer=None):
2730
"""
2831
:class:`msgpack.Packer` encoder.
2932
3033
:param obj: Object to encode.
3134
:type obj: :class:`decimal.Decimal` or :class:`uuid.UUID` or
32-
:class:`tarantool.Datetime` or :class:`tarantool.Interval`
35+
or :class:`tarantool.BoxError` or :class:`tarantool.Datetime`
36+
or :class:`tarantool.Interval`
37+
38+
:param packer: msgpack packer to work with common types
39+
(like dictionary in extended error payload)
40+
:type packer: :class:`msgpack.Packer`, optional
3341
3442
:return: Encoded value.
3543
:rtype: :class:`msgpack.ExtType`
@@ -39,5 +47,5 @@ def default(obj):
3947

4048
for encoder in encoders:
4149
if isinstance(obj, encoder['type']):
42-
return ExtType(encoder['ext'].EXT_ID, encoder['ext'].encode(obj))
50+
return ExtType(encoder['ext'].EXT_ID, encoder['ext'].encode(obj, packer))
4351
raise TypeError("Unknown type: %r" % (obj,))

tarantool/msgpack_ext/unpacker.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@
66

77
import tarantool.msgpack_ext.decimal as ext_decimal
88
import tarantool.msgpack_ext.uuid as ext_uuid
9+
import tarantool.msgpack_ext.error as ext_error
910
import tarantool.msgpack_ext.datetime as ext_datetime
1011
import tarantool.msgpack_ext.interval as ext_interval
1112

1213
decoders = {
1314
ext_decimal.EXT_ID : ext_decimal.decode ,
1415
ext_uuid.EXT_ID : ext_uuid.decode ,
16+
ext_error.EXT_ID : ext_error.decode ,
1517
ext_datetime.EXT_ID: ext_datetime.decode,
1618
ext_interval.EXT_ID: ext_interval.decode,
1719
}
1820

19-
def ext_hook(code, data):
21+
def ext_hook(code, data, unpacker=None):
2022
"""
2123
:class:`msgpack.Unpacker` decoder.
2224
@@ -26,13 +28,18 @@ def ext_hook(code, data):
2628
:param data: MessagePack extension type data.
2729
:type data: :obj:`bytes`
2830
31+
:param unpacker: msgpack unpacker to work with common types
32+
(like dictionary in extended error payload)
33+
:type unpacker: :class:`msgpack.Unpacker`, optional
34+
2935
:return: Decoded value.
3036
:rtype: :class:`decimal.Decimal` or :class:`uuid.UUID` or
31-
:class:`tarantool.Datetime` or :class:`tarantool.Interval`
37+
or :class:`tarantool.BoxError` or :class:`tarantool.Datetime`
38+
or :class:`tarantool.Interval`
3239
3340
:raise: :exc:`NotImplementedError`
3441
"""
3542

3643
if code in decoders:
37-
return decoders[code](data)
44+
return decoders[code](data, unpacker)
3845
raise NotImplementedError("Unknown msgpack extension type code %d" % (code,))

tarantool/msgpack_ext/uuid.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
`uuid`_ type id.
2121
"""
2222

23-
def encode(obj):
23+
def encode(obj, _):
2424
"""
2525
Encode an UUID object.
2626
@@ -33,7 +33,7 @@ def encode(obj):
3333

3434
return obj.bytes
3535

36-
def decode(data):
36+
def decode(data, _):
3737
"""
3838
Decode an UUID object.
3939

0 commit comments

Comments
 (0)