Skip to content

Support msgpack 1.0.0 #173

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
Aug 29, 2020
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
9 changes: 9 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ cache: false
env:
matrix:
- TARGET=test
PYTHON_MSGPACK=msgpack-python==0.4.0
- TARGET=test
PYTHON_MSGPACK=msgpack==0.5.0
- TARGET=test
PYTHON_MSGPACK=msgpack==0.6.0
- TARGET=test
PYTHON_MSGPACK=msgpack==0.6.2
- TARGET=test
PYTHON_MSGPACK=msgpack==1.0.0
- OS=el DIST=6
- OS=el DIST=7
- OS=fedora DIST=28
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
msgpack-python>=0.4.0
msgpack>=0.4.0
7 changes: 7 additions & 0 deletions tarantool/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
NetworkError,
DatabaseError,
InterfaceError,
ConfigurationError,
SchemaError,
NetworkWarning,
SchemaReloadException,
Expand Down Expand Up @@ -79,6 +80,7 @@ class Connection(object):
Error = tarantool.error
DatabaseError = DatabaseError
InterfaceError = InterfaceError
ConfigurationError = ConfigurationError
SchemaError = SchemaError
NetworkError = NetworkError

Expand All @@ -101,6 +103,11 @@ def __init__(self, host, port,
creates network connection.
if False than you have to call connect() manualy.
'''

if msgpack.version >= (1, 0, 0) and encoding not in (None, 'utf-8'):
raise ConfigurationError("Only None and 'utf-8' encoding option " +
"values are supported with msgpack>=1.0.0")

if os.name == 'nt':
libc = ctypes.WinDLL(
ctypes.util.find_library('Ws2_32'), use_last_error=True
Expand Down
106 changes: 67 additions & 39 deletions tarantool/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,34 @@ def __init__(self, conn):
self._sync = None
self._body = ''

packer_kwargs = dict()

# use_bin_type=True is default since msgpack-1.0.0.
#
# The option controls whether to pack binary (non-unicode)
# string values as mp_bin or as mp_str.
#
# The default behaviour of the connector is to pack both
# bytes and Unicode strings as mp_str.
#
# msgpack-0.5.0 (and only this version) warns when the
# option is unset:
#
# | FutureWarning: use_bin_type option is not specified.
# | Default value of the option will be changed in future
# | version.
#
# The option is supported since msgpack-0.4.0, so we can
# just always set it for all msgpack versions to get rid
# of the warning on msgpack-0.5.0 and to keep our
# behaviour on msgpack-1.0.0.
packer_kwargs['use_bin_type'] = False

self.packer = msgpack.Packer(**packer_kwargs)

def _dumps(self, src):
return self.packer.pack(src)

def __bytes__(self):
return self.header(len(self._body)) + self._body

Expand All @@ -82,11 +110,11 @@ def sync(self):

def header(self, length):
self._sync = self.conn.generate_sync()
header = msgpack.dumps({IPROTO_CODE: self.request_type,
IPROTO_SYNC: self._sync,
IPROTO_SCHEMA_ID: self.conn.schema_version})
header = self._dumps({IPROTO_CODE: self.request_type,
IPROTO_SYNC: self._sync,
IPROTO_SCHEMA_ID: self.conn.schema_version})

return msgpack.dumps(length + len(header)) + header
return self._dumps(length + len(header)) + header


class RequestInsert(Request):
Expand All @@ -102,8 +130,8 @@ def __init__(self, conn, space_no, values):
super(RequestInsert, self).__init__(conn)
assert isinstance(values, (tuple, list))

request_body = msgpack.dumps({IPROTO_SPACE_ID: space_no,
IPROTO_TUPLE: values})
request_body = self._dumps({IPROTO_SPACE_ID: space_no,
IPROTO_TUPLE: values})

self._body = request_body

Expand Down Expand Up @@ -131,19 +159,19 @@ def sha1(values):
hash2 = sha1((hash1,))
scramble = sha1((salt, hash2))
scramble = strxor(hash1, scramble)
request_body = msgpack.dumps({IPROTO_USER_NAME: user,
IPROTO_TUPLE: ("chap-sha1", scramble)})
request_body = self._dumps({IPROTO_USER_NAME: user,
IPROTO_TUPLE: ("chap-sha1", scramble)})
self._body = request_body

def header(self, length):
self._sync = self.conn.generate_sync()
# Set IPROTO_SCHEMA_ID: 0 to avoid SchemaReloadException
# It is ok to use 0 in auth every time.
header = msgpack.dumps({IPROTO_CODE: self.request_type,
IPROTO_SYNC: self._sync,
IPROTO_SCHEMA_ID: 0})
header = self._dumps({IPROTO_CODE: self.request_type,
IPROTO_SYNC: self._sync,
IPROTO_SCHEMA_ID: 0})

return msgpack.dumps(length + len(header)) + header
return self._dumps(length + len(header)) + header


class RequestReplace(Request):
Expand All @@ -159,8 +187,8 @@ def __init__(self, conn, space_no, values):
super(RequestReplace, self).__init__(conn)
assert isinstance(values, (tuple, list))

request_body = msgpack.dumps({IPROTO_SPACE_ID: space_no,
IPROTO_TUPLE: values})
request_body = self._dumps({IPROTO_SPACE_ID: space_no,
IPROTO_TUPLE: values})

self._body = request_body

Expand All @@ -177,9 +205,9 @@ def __init__(self, conn, space_no, index_no, key):
'''
super(RequestDelete, self).__init__(conn)

request_body = msgpack.dumps({IPROTO_SPACE_ID: space_no,
IPROTO_INDEX_ID: index_no,
IPROTO_KEY: key})
request_body = self._dumps({IPROTO_SPACE_ID: space_no,
IPROTO_INDEX_ID: index_no,
IPROTO_KEY: key})

self._body = request_body

Expand All @@ -193,12 +221,12 @@ class RequestSelect(Request):
# pylint: disable=W0231
def __init__(self, conn, space_no, index_no, key, offset, limit, iterator):
super(RequestSelect, self).__init__(conn)
request_body = msgpack.dumps({IPROTO_SPACE_ID: space_no,
IPROTO_INDEX_ID: index_no,
IPROTO_OFFSET: offset,
IPROTO_LIMIT: limit,
IPROTO_ITERATOR: iterator,
IPROTO_KEY: key})
request_body = self._dumps({IPROTO_SPACE_ID: space_no,
IPROTO_INDEX_ID: index_no,
IPROTO_OFFSET: offset,
IPROTO_LIMIT: limit,
IPROTO_ITERATOR: iterator,
IPROTO_KEY: key})

self._body = request_body

Expand All @@ -214,10 +242,10 @@ class RequestUpdate(Request):
def __init__(self, conn, space_no, index_no, key, op_list):
super(RequestUpdate, self).__init__(conn)

request_body = msgpack.dumps({IPROTO_SPACE_ID: space_no,
IPROTO_INDEX_ID: index_no,
IPROTO_KEY: key,
IPROTO_TUPLE: op_list})
request_body = self._dumps({IPROTO_SPACE_ID: space_no,
IPROTO_INDEX_ID: index_no,
IPROTO_KEY: key,
IPROTO_TUPLE: op_list})

self._body = request_body

Expand All @@ -235,8 +263,8 @@ def __init__(self, conn, name, args, call_16):
super(RequestCall, self).__init__(conn)
assert isinstance(args, (list, tuple))

request_body = msgpack.dumps({IPROTO_FUNCTION_NAME: name,
IPROTO_TUPLE: args})
request_body = self._dumps({IPROTO_FUNCTION_NAME: name,
IPROTO_TUPLE: args})

self._body = request_body

Expand All @@ -252,8 +280,8 @@ def __init__(self, conn, name, args):
super(RequestEval, self).__init__(conn)
assert isinstance(args, (list, tuple))

request_body = msgpack.dumps({IPROTO_EXPR: name,
IPROTO_TUPLE: args})
request_body = self._dumps({IPROTO_EXPR: name,
IPROTO_TUPLE: args})

self._body = request_body

Expand All @@ -280,10 +308,10 @@ class RequestUpsert(Request):
def __init__(self, conn, space_no, index_no, tuple_value, op_list):
super(RequestUpsert, self).__init__(conn)

request_body = msgpack.dumps({IPROTO_SPACE_ID: space_no,
IPROTO_INDEX_ID: index_no,
IPROTO_TUPLE: tuple_value,
IPROTO_OPS: op_list})
request_body = self._dumps({IPROTO_SPACE_ID: space_no,
IPROTO_INDEX_ID: index_no,
IPROTO_TUPLE: tuple_value,
IPROTO_OPS: op_list})

self._body = request_body

Expand All @@ -297,7 +325,7 @@ class RequestJoin(Request):
# pylint: disable=W0231
def __init__(self, conn, server_uuid):
super(RequestJoin, self).__init__(conn)
request_body = msgpack.dumps({IPROTO_SERVER_UUID: server_uuid})
request_body = self._dumps({IPROTO_SERVER_UUID: server_uuid})
self._body = request_body


Expand All @@ -312,7 +340,7 @@ def __init__(self, conn, cluster_uuid, server_uuid, vclock):
super(RequestSubscribe, self).__init__(conn)
assert isinstance(vclock, dict)

request_body = msgpack.dumps({
request_body = self._dumps({
IPROTO_CLUSTER_UUID: cluster_uuid,
IPROTO_SERVER_UUID: server_uuid,
IPROTO_VCLOCK: vclock
Expand All @@ -329,6 +357,6 @@ class RequestOK(Request):
# pylint: disable=W0231
def __init__(self, conn, sync):
super(RequestOK, self).__init__(conn)
request_body = msgpack.dumps({IPROTO_CODE: self.request_type,
IPROTO_SYNC: sync})
request_body = self._dumps({IPROTO_CODE: self.request_type,
IPROTO_SYNC: sync})
self._body = request_body
31 changes: 27 additions & 4 deletions tarantool/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,38 @@ def __init__(self, conn, response):
# created in the __new__().
# super(Response, self).__init__()

unpacker_kwargs = dict()

# Decode msgpack arrays into Python lists (not tuples).
unpacker_kwargs['use_list'] = True

# Use raw=False instead of encoding='utf-8'.
if msgpack.version >= (0, 5, 2) and conn.encoding == 'utf-8':
# Get rid of the following warning.
# > PendingDeprecationWarning: encoding is deprecated,
# > Use raw=False instead.
unpacker = msgpack.Unpacker(use_list=True, raw=False)
unpacker_kwargs['raw'] = False
elif conn.encoding is not None:
unpacker = msgpack.Unpacker(use_list=True, encoding=conn.encoding)
else:
unpacker = msgpack.Unpacker(use_list=True)
unpacker_kwargs['encoding'] = conn.encoding

# raw=False is default since msgpack-1.0.0.
#
# The option decodes mp_str to bytes, not a Unicode
# string (when True).
if msgpack.version >= (1, 0, 0) and conn.encoding is None:
unpacker_kwargs['raw'] = True

# encoding option is not supported since msgpack-1.0.0,
# but it is handled in the Connection constructor.
assert(msgpack.version < (1, 0, 0) or conn.encoding in (None, 'utf-8'))

# strict_map_key=True is default since msgpack-1.0.0.
#
# The option forbids non-string keys in a map (when True).
if msgpack.version >= (1, 0, 0):
unpacker_kwargs['strict_map_key'] = False

unpacker = msgpack.Unpacker(**unpacker_kwargs)

unpacker.feed(response)
header = unpacker.unpack()
Expand Down
7 changes: 6 additions & 1 deletion test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ echo "deb http://download.tarantool.org/tarantool/2x/ubuntu/ ${release} main" |
sudo apt-get update > /dev/null
sudo apt-get -q -y install tarantool

# Install module requirements.
#
# Keep it in sync with requirements.txt.
pip install "${PYTHON_MSGPACK:-msgpack==1.0.0}"
python -c 'import msgpack; print(msgpack.version)'

# Install testing dependencies.
pip install -r requirements.txt
pip install pyyaml

# Run tests.
Expand Down