Skip to content

Support box.error #245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 31, 2022
Merged
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
```

- Support iproto feature discovery (#206).
- Backport ConnectionPool support for Python 3.6.
- Support extra information for iproto errors (#232).
- Error extension type support (#232).

- Support pandas way to build datetime from timestamp (PR #252).

Expand Down
4 changes: 4 additions & 0 deletions docs/source/api/submodule-types.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module :py:mod:`tarantool.types`
================================

.. automodule:: tarantool.types
3 changes: 3 additions & 0 deletions docs/source/dev-guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ they are represented with in-built and custom types:
+-----------------------------+----+-------------+----+-----------------------------+
| :obj:`uuid.UUID` | -> | `UUID`_ | -> | :obj:`uuid.UUID` |
+-----------------------------+----+-------------+----+-----------------------------+
| :class:`tarantool.BoxError` | -> | `ERROR`_ | -> | :class:`tarantool.BoxError` |
+-----------------------------+----+-------------+----+-----------------------------+
| :class:`tarantool.Datetime` | -> | `DATETIME`_ | -> | :class:`tarantool.Datetime` |
+-----------------------------+----+-------------+----+-----------------------------+
| :class:`tarantool.Interval` | -> | `INTERVAL`_ | -> | :class:`tarantool.Interval` |
Expand All @@ -109,5 +111,6 @@ and iterate through it as with any other serializable object.
.. _extension types: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/
.. _DECIMAL: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-decimal-type
.. _UUID: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-uuid-type
.. _ERROR: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-error-type
.. _DATETIME: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-datetime-type
.. _INTERVAL: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-interval-type
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ API Reference
api/submodule-response.rst
api/submodule-schema.rst
api/submodule-space.rst
api/submodule-types.rst
api/submodule-utils.rst

.. Indices and tables
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
msgpack>=1.0.4
pandas
pytz
dataclasses; python_version <= '3.6'
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,5 @@ def find_version(*file_paths):
setup_requires=[
'setuptools_scm==6.4.2',
],
python_requires='>=3',
python_requires='>=3.6',
)
12 changes: 6 additions & 6 deletions tarantool/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
Interval,
)

from tarantool.connection_pool import ConnectionPool, Mode

from tarantool.types import BoxError

try:
from tarantool.version import __version__
except ImportError:
Expand Down Expand Up @@ -136,9 +140,5 @@ def connectmesh(addrs=({'host': 'localhost', 'port': 3301},), user=None,

__all__ = ['connect', 'Connection', 'connectmesh', 'MeshConnection', 'Schema',
'Error', 'DatabaseError', 'NetworkError', 'NetworkWarning',
'SchemaError', 'dbapi', 'Datetime', 'Interval', 'IntervalAdjust']

# ConnectionPool is supported only for Python 3.7 or newer.
if sys.version_info.major >= 3 and sys.version_info.minor >= 7:
from tarantool.connection_pool import ConnectionPool, Mode
__all__.extend(['ConnectionPool', 'Mode'])
'SchemaError', 'dbapi', 'Datetime', 'Interval', 'IntervalAdjust',
'ConnectionPool', 'Mode', 'BoxError',]
6 changes: 4 additions & 2 deletions tarantool/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
IPROTO_OPS = 0x28
#
IPROTO_DATA = 0x30
IPROTO_ERROR = 0x31
IPROTO_ERROR_24 = 0x31
#
IPROTO_METADATA = 0x32
IPROTO_SQL_TEXT = 0x40
Expand All @@ -36,6 +36,8 @@
IPROTO_SQL_INFO_ROW_COUNT = 0x00
IPROTO_SQL_INFO_AUTOINCREMENT_IDS = 0x01
#
IPROTO_ERROR = 0x52
#
IPROTO_VERSION = 0x54
IPROTO_FEATURES = 0x55

Expand Down Expand Up @@ -127,4 +129,4 @@
# Tarantool 2.10 protocol version is 3
CONNECTOR_IPROTO_VERSION = 3
# List of connector-supported features
CONNECTOR_FEATURES = []
CONNECTOR_FEATURES = [IPROTO_FEATURE_ERROR_EXTENSION,]
15 changes: 12 additions & 3 deletions tarantool/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,17 @@ class DatabaseError(Error):
Exception raised for errors that are related to the database.
"""

def __init__(self, *args):
def __init__(self, *args, extra_info=None):
"""
:param args: ``(code, message)`` or ``(message,)``.
:type args: :obj:`tuple`

:param extra_info: Additional `box.error`_ information
with backtrace.
:type extra_info: :class:`~tarantool.types.BoxError` or
:obj:`None`, optional

.. _box.error: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_error/error/
"""

super().__init__(*args)
Expand All @@ -59,6 +66,8 @@ def __init__(self, *args):
self.code = 0
self.message = ''

self.extra_info = extra_info


class DataError(DatabaseError):
"""
Expand Down Expand Up @@ -235,7 +244,7 @@ class NetworkError(DatabaseError):
Error related to network.
"""

def __init__(self, orig_exception=None, *args):
def __init__(self, orig_exception=None, *args, **kwargs):
"""
:param orig_exception: Exception to wrap.
:type orig_exception: optional
Expand All @@ -256,7 +265,7 @@ def __init__(self, orig_exception=None, *args):
super(NetworkError, self).__init__(
orig_exception.errno, self.message)
else:
super(NetworkError, self).__init__(orig_exception, *args)
super(NetworkError, self).__init__(orig_exception, *args, **kwargs)


class NetworkWarning(UserWarning):
Expand Down
4 changes: 2 additions & 2 deletions tarantool/msgpack_ext/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def get_int_as_bytes(data, size):

return data.to_bytes(size, byteorder=BYTEORDER, signed=True)

def encode(obj):
def encode(obj, _):
"""
Encode a datetime object.

Expand Down Expand Up @@ -134,7 +134,7 @@ def get_bytes_as_int(data, cursor, size):
part = data[cursor:cursor + size]
return int.from_bytes(part, BYTEORDER, signed=True), cursor + size

def decode(data):
def decode(data, _):
"""
Decode a datetime object.

Expand Down
4 changes: 2 additions & 2 deletions tarantool/msgpack_ext/decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ def strip_decimal_str(str_repr, scale, first_digit_ind):
# Do not strips zeroes before the decimal point
return str_repr

def encode(obj):
def encode(obj, _):
"""
Encode a decimal object.

Expand Down Expand Up @@ -335,7 +335,7 @@ def add_str_digit(digit, digits_reverted, scale):

digits_reverted.append(str(digit))

def decode(data):
def decode(data, _):
"""
Decode a decimal object.

Expand Down
52 changes: 52 additions & 0 deletions tarantool/msgpack_ext/error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
Tarantool `error`_ extension type support module.

Refer to :mod:`~tarantool.msgpack_ext.types.error`.

.. _error: https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-error-type
"""

from tarantool.types import (
encode_box_error,
decode_box_error,
)

EXT_ID = 3
"""
`error`_ type id.
"""

def encode(obj, packer):
"""
Encode an error object.

:param obj: Error to encode.
:type obj: :class:`tarantool.BoxError`

:param packer: msgpack packer to encode error dictionary.
:type packer: :class:`msgpack.Packer`

:return: Encoded error.
:rtype: :obj:`bytes`
"""

err_map = encode_box_error(obj)
return packer.pack(err_map)

def decode(data, unpacker):
"""
Decode an error object.

:param obj: Error to decode.
:type obj: :obj:`bytes`

:param unpacker: msgpack unpacker to decode error dictionary.
:type unpacker: :class:`msgpack.Unpacker`

:return: Decoded error.
:rtype: :class:`tarantool.BoxError`
"""

unpacker.feed(data)
err_map = unpacker.unpack()
return decode_box_error(err_map)
10 changes: 6 additions & 4 deletions tarantool/msgpack_ext/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
`datetime.interval`_ type id.
"""

def encode(obj):
def encode(obj, _):
"""
Encode an interval object.

Expand Down Expand Up @@ -80,13 +80,16 @@ def encode(obj):

return buf

def decode(data):
def decode(data, unpacker):
"""
Decode an interval object.

:param obj: Interval to decode.
:type obj: :obj:`bytes`

:param unpacker: msgpack unpacker to decode fields.
:type unpacker: :class:`msgpack.Unpacker`

:return: Decoded interval.
:rtype: :class:`tarantool.Interval`

Expand All @@ -108,9 +111,8 @@ def decode(data):
}

if len(data) != 0:
# To create an unpacker is the only way to parse
# Unpacker object is the only way to parse
# a sequence of values in Python msgpack module.
unpacker = msgpack.Unpacker()
unpacker.feed(data)
field_count = unpacker.unpack()
for _ in range(field_count):
Expand Down
14 changes: 11 additions & 3 deletions tarantool/msgpack_ext/packer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,36 @@
from uuid import UUID
from msgpack import ExtType

from tarantool.types import BoxError
from tarantool.msgpack_ext.types.datetime import Datetime
from tarantool.msgpack_ext.types.interval import Interval

import tarantool.msgpack_ext.decimal as ext_decimal
import tarantool.msgpack_ext.uuid as ext_uuid
import tarantool.msgpack_ext.error as ext_error
import tarantool.msgpack_ext.datetime as ext_datetime
import tarantool.msgpack_ext.interval as ext_interval

encoders = [
{'type': Decimal, 'ext': ext_decimal },
{'type': UUID, 'ext': ext_uuid },
{'type': BoxError, 'ext': ext_error },
{'type': Datetime, 'ext': ext_datetime},
{'type': Interval, 'ext': ext_interval},
]

def default(obj):
def default(obj, packer=None):
"""
:class:`msgpack.Packer` encoder.

:param obj: Object to encode.
:type obj: :class:`decimal.Decimal` or :class:`uuid.UUID` or
:class:`tarantool.Datetime` or :class:`tarantool.Interval`
or :class:`tarantool.BoxError` or :class:`tarantool.Datetime`
or :class:`tarantool.Interval`

:param packer: msgpack packer to work with common types
(like dictionary in extended error payload)
:type packer: :class:`msgpack.Packer`, optional

:return: Encoded value.
:rtype: :class:`msgpack.ExtType`
Expand All @@ -39,5 +47,5 @@ def default(obj):

for encoder in encoders:
if isinstance(obj, encoder['type']):
return ExtType(encoder['ext'].EXT_ID, encoder['ext'].encode(obj))
return ExtType(encoder['ext'].EXT_ID, encoder['ext'].encode(obj, packer))
raise TypeError("Unknown type: %r" % (obj,))
15 changes: 11 additions & 4 deletions tarantool/msgpack_ext/unpacker.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@

import tarantool.msgpack_ext.decimal as ext_decimal
import tarantool.msgpack_ext.uuid as ext_uuid
import tarantool.msgpack_ext.error as ext_error
import tarantool.msgpack_ext.datetime as ext_datetime
import tarantool.msgpack_ext.interval as ext_interval

decoders = {
ext_decimal.EXT_ID : ext_decimal.decode ,
ext_uuid.EXT_ID : ext_uuid.decode ,
ext_error.EXT_ID : ext_error.decode ,
ext_datetime.EXT_ID: ext_datetime.decode,
ext_interval.EXT_ID: ext_interval.decode,
}

def ext_hook(code, data):
def ext_hook(code, data, unpacker=None):
"""
:class:`msgpack.Unpacker` decoder.

Expand All @@ -26,13 +28,18 @@ def ext_hook(code, data):
:param data: MessagePack extension type data.
:type data: :obj:`bytes`

:param unpacker: msgpack unpacker to work with common types
(like dictionary in extended error payload)
:type unpacker: :class:`msgpack.Unpacker`, optional

:return: Decoded value.
:rtype: :class:`decimal.Decimal` or :class:`uuid.UUID` or
:class:`tarantool.Datetime` or :class:`tarantool.Interval`
or :class:`tarantool.BoxError` or :class:`tarantool.Datetime`
or :class:`tarantool.Interval`

:raise: :exc:`NotImplementedError`
"""

if code in decoders:
return decoders[code](data)
raise NotImplementedError("Unknown msgpack type: %d" % (code,))
return decoders[code](data, unpacker)
raise NotImplementedError("Unknown msgpack extension type code %d" % (code,))
4 changes: 2 additions & 2 deletions tarantool/msgpack_ext/uuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
`uuid`_ type id.
"""

def encode(obj):
def encode(obj, _):
"""
Encode an UUID object.
Expand All @@ -33,7 +33,7 @@ def encode(obj):

return obj.bytes

def decode(data):
def decode(data, _):
"""
Decode an UUID object.
Expand Down
Loading