Skip to content
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

Drop python2 support #519

Merged
merged 12 commits into from May 21, 2023
2 changes: 1 addition & 1 deletion msgpack/__init__.py
Expand Up @@ -10,7 +10,7 @@
__version__ = "1.0.4"


if os.environ.get("MSGPACK_PUREPYTHON") or sys.version_info[0] == 2:
if os.environ.get("MSGPACK_PUREPYTHON"):
from .fallback import Packer, unpackb, Unpacker
else:
try:
Expand Down
16 changes: 3 additions & 13 deletions msgpack/ext.py
Expand Up @@ -5,17 +5,7 @@
import struct


PY2 = sys.version_info[0] == 2

if PY2:
int_types = (int, long)
_utc = None
else:
int_types = int
try:
_utc = datetime.timezone.utc
except AttributeError:
_utc = datetime.timezone(datetime.timedelta(0))
_utc = datetime.timezone.utc
sblondon marked this conversation as resolved.
Show resolved Hide resolved


class ExtType(namedtuple("ExtType", "code data")):
Expand Down Expand Up @@ -55,9 +45,9 @@ def __init__(self, seconds, nanoseconds=0):

Note: Negative times (before the UNIX epoch) are represented as negative seconds + positive ns.
"""
if not isinstance(seconds, int_types):
if not isinstance(seconds, int):
raise TypeError("seconds must be an interger")
sblondon marked this conversation as resolved.
Show resolved Hide resolved
if not isinstance(nanoseconds, int_types):
if not isinstance(nanoseconds, int):
raise TypeError("nanoseconds must be an integer")
if not (0 <= nanoseconds < 10**9):
raise ValueError(
Expand Down
65 changes: 19 additions & 46 deletions msgpack/fallback.py
Expand Up @@ -4,22 +4,6 @@
import struct


PY2 = sys.version_info[0] == 2
if PY2:
int_types = (int, long)

def dict_iteritems(d):
return d.iteritems()

else:
int_types = int
unicode = str
xrange = range

def dict_iteritems(d):
return d.items()


if sys.version_info < (3, 5):
# Ugly hack...
RecursionError = RuntimeError
Expand Down Expand Up @@ -134,15 +118,6 @@ def unpackb(packed, **kwargs):
return ret


if sys.version_info < (2, 7, 6):

def _unpack_from(f, b, o=0):
"""Explicit type cast for legacy struct.unpack_from"""
return struct.unpack_from(f, bytes(b), o)

else:
_unpack_from = struct.unpack_from

_NO_FORMAT_USED = ""
_MSGPACK_HEADERS = {
0xC4: (1, _NO_FORMAT_USED, TYPE_BIN),
Expand Down Expand Up @@ -477,7 +452,7 @@ def _read_header(self):
size, fmt, typ = _MSGPACK_HEADERS[b]
self._reserve(size)
if len(fmt) > 0:
n = _unpack_from(fmt, self._buffer, self._buff_i)[0]
n = struct.unpack_from(fmt, self._buffer, self._buff_i)[0]
else:
n = self._buffer[self._buff_i]
self._buff_i += size
Expand All @@ -487,7 +462,7 @@ def _read_header(self):
elif 0xC7 <= b <= 0xC9:
size, fmt, typ = _MSGPACK_HEADERS[b]
self._reserve(size)
L, n = _unpack_from(fmt, self._buffer, self._buff_i)
L, n = struct.unpack_from(fmt, self._buffer, self._buff_i)
self._buff_i += size
if L > self._max_ext_len:
raise ValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len))
Expand All @@ -496,7 +471,7 @@ def _read_header(self):
size, fmt = _MSGPACK_HEADERS[b]
self._reserve(size)
if len(fmt) > 0:
obj = _unpack_from(fmt, self._buffer, self._buff_i)[0]
obj = struct.unpack_from(fmt, self._buffer, self._buff_i)[0]
else:
obj = self._buffer[self._buff_i]
self._buff_i += size
Expand All @@ -507,13 +482,13 @@ def _read_header(self):
"%s exceeds max_ext_len(%s)" % (size, self._max_ext_len)
)
self._reserve(size + 1)
n, obj = _unpack_from(fmt, self._buffer, self._buff_i)
n, obj = struct.unpack_from(fmt, self._buffer, self._buff_i)
self._buff_i += size + 1
elif 0xD9 <= b <= 0xDB:
size, fmt, typ = _MSGPACK_HEADERS[b]
self._reserve(size)
if len(fmt) > 0:
(n,) = _unpack_from(fmt, self._buffer, self._buff_i)
(n,) = struct.unpack_from(fmt, self._buffer, self._buff_i)
else:
n = self._buffer[self._buff_i]
self._buff_i += size
Expand All @@ -523,7 +498,7 @@ def _read_header(self):
elif 0xDC <= b <= 0xDD:
size, fmt, typ = _MSGPACK_HEADERS[b]
self._reserve(size)
(n,) = _unpack_from(fmt, self._buffer, self._buff_i)
(n,) = struct.unpack_from(fmt, self._buffer, self._buff_i)
self._buff_i += size
if n > self._max_array_len:
raise ValueError(
Expand All @@ -532,7 +507,7 @@ def _read_header(self):
elif 0xDE <= b <= 0xDF:
size, fmt, typ = _MSGPACK_HEADERS[b]
self._reserve(size)
(n,) = _unpack_from(fmt, self._buffer, self._buff_i)
(n,) = struct.unpack_from(fmt, self._buffer, self._buff_i)
self._buff_i += size
if n > self._max_map_len:
raise ValueError("%s exceeds max_map_len(%s)" % (n, self._max_map_len))
Expand All @@ -554,38 +529,38 @@ def _unpack(self, execute=EX_CONSTRUCT):
# TODO should we eliminate the recursion?
if typ == TYPE_ARRAY:
if execute == EX_SKIP:
for i in xrange(n):
for i in range(n):
# TODO check whether we need to call `list_hook`
self._unpack(EX_SKIP)
return
ret = newlist_hint(n)
for i in xrange(n):
for i in range(n):
ret.append(self._unpack(EX_CONSTRUCT))
if self._list_hook is not None:
ret = self._list_hook(ret)
# TODO is the interaction between `list_hook` and `use_list` ok?
return ret if self._use_list else tuple(ret)
if typ == TYPE_MAP:
if execute == EX_SKIP:
for i in xrange(n):
for i in range(n):
# TODO check whether we need to call hooks
self._unpack(EX_SKIP)
self._unpack(EX_SKIP)
return
if self._object_pairs_hook is not None:
ret = self._object_pairs_hook(
(self._unpack(EX_CONSTRUCT), self._unpack(EX_CONSTRUCT))
for _ in xrange(n)
for _ in range(n)
)
else:
ret = {}
for _ in xrange(n):
for _ in range(n):
key = self._unpack(EX_CONSTRUCT)
if self._strict_map_key and type(key) not in (unicode, bytes):
if self._strict_map_key and type(key) not in (str, bytes):
raise ValueError(
"%s is not allowed for map key" % str(type(key))
)
if not PY2 and type(key) is str:
if type(key) is str:
key = sys.intern(key)
ret[key] = self._unpack(EX_CONSTRUCT)
if self._object_hook is not None:
Expand Down Expand Up @@ -743,8 +718,6 @@ def __init__(
self._autoreset = autoreset
self._use_bin_type = use_bin_type
self._buffer = StringIO()
if PY2 and datetime:
raise ValueError("datetime is not supported in Python 2")
self._datetime = bool(datetime)
self._unicode_errors = unicode_errors or "strict"
if default is not None:
Expand Down Expand Up @@ -774,7 +747,7 @@ def _pack(
if obj:
return self._buffer.write(b"\xc3")
return self._buffer.write(b"\xc2")
if check(obj, int_types):
if check(obj, int):
if 0 <= obj < 0x80:
return self._buffer.write(struct.pack("B", obj))
if -0x20 <= obj < 0:
Expand Down Expand Up @@ -806,7 +779,7 @@ def _pack(
raise ValueError("%s is too large" % type(obj).__name__)
self._pack_bin_header(n)
return self._buffer.write(obj)
if check(obj, unicode):
if check(obj, str):
obj = obj.encode("utf-8", self._unicode_errors)
n = len(obj)
if n >= 2**32:
Expand Down Expand Up @@ -855,12 +828,12 @@ def _pack(
if check(obj, list_types):
n = len(obj)
self._pack_array_header(n)
for i in xrange(n):
for i in range(n):
self._pack(obj[i], nest_limit - 1)
return
if check(obj, dict):
return self._pack_map_pairs(
len(obj), dict_iteritems(obj), nest_limit - 1
len(obj), obj.items(), nest_limit - 1
)

if self._datetime and check(obj, _DateTime) and obj.tzinfo is not None:
Expand Down Expand Up @@ -1004,7 +977,7 @@ def reset(self):

def getbuffer(self):
"""Return view of internal buffer."""
if USING_STRINGBUILDER or PY2:
if USING_STRINGBUILDER:
return memoryview(self.bytes())
else:
return self._buffer.getbuffer()
3 changes: 1 addition & 2 deletions setup.py
Expand Up @@ -10,7 +10,6 @@


PYPY = hasattr(sys, "pypy_version_info")
PY2 = sys.version_info[0] == 2


class NoCython(Exception):
Expand Down Expand Up @@ -79,7 +78,7 @@ def __init__(self, *args, **kwargs):
macros = [("__LITTLE_ENDIAN__", "1")]

ext_modules = []
if not PYPY and not PY2 and not os.environ.get("MSGPACK_PUREPYTHON"):
if not PYPY and not os.environ.get("MSGPACK_PUREPYTHON"):
ext_modules.append(
Extension(
"msgpack._cmsgpack",
Expand Down
1 change: 0 additions & 1 deletion test/test_buffer.py
Expand Up @@ -6,7 +6,6 @@
from msgpack import packb, unpackb


@pytest.mark.skipif(sys.version_info[0] == 2, reason="Python 2 is not supported")
def test_unpack_buffer():
from array import array

Expand Down
15 changes: 3 additions & 12 deletions test/test_extension.py
Expand Up @@ -55,10 +55,7 @@ def ext_hook(code, data):
print("ext_hook called", code, data)
assert code == 123
obj = array.array("d")
try:
obj.frombytes(data)
except AttributeError: # PY2
obj.fromstring(data)
obj.frombytes(data)
return obj

obj = [42, b"hello", array.array("d", [1.1, 2.2, 3.3])]
Expand All @@ -67,20 +64,14 @@ def ext_hook(code, data):
assert obj == obj2


import sys

if sys.version > "3":
long = int


def test_overriding_hooks():
def default(obj):
if isinstance(obj, long):
if isinstance(obj, int):
return {"__type__": "long", "__data__": str(obj)}
else:
return obj

obj = {"testval": long(1823746192837461928374619)}
obj = {"testval": 1823746192837461928374619}
refobj = {"testval": default(obj["testval"])}
refout = msgpack.packb(refobj)
assert isinstance(refout, (str, bytes))
Expand Down
4 changes: 0 additions & 4 deletions test/test_memoryview.py
Expand Up @@ -7,10 +7,6 @@
import sys


pytestmark = pytest.mark.skipif(
sys.version_info[0] < 3, reason="Only Python 3 supports buffer protocol"
)


def make_array(f, data):
a = array(f)
Expand Down
6 changes: 0 additions & 6 deletions test/test_pack.py
Expand Up @@ -80,9 +80,6 @@ def testPackByteArrays():
check(td)


@pytest.mark.skipif(
sys.version_info < (3, 0), reason="Python 2 passes invalid surrogates"
)
def testIgnoreUnicodeErrors():
re = unpackb(
packb(b"abc\xeddef", use_bin_type=False), raw=False, unicode_errors="ignore"
Expand All @@ -96,9 +93,6 @@ def testStrictUnicodeUnpack():
unpackb(packed, raw=False, use_list=1)


@pytest.mark.skipif(
sys.version_info < (3, 0), reason="Python 2 passes invalid surrogates"
)
def testIgnoreErrorsPack():
re = unpackb(
packb("abc\uDC80\uDCFFdef", use_bin_type=True, unicode_errors="ignore"),
Expand Down
11 changes: 1 addition & 10 deletions test/test_timestamp.py
Expand Up @@ -2,10 +2,7 @@
import sys
import datetime
import msgpack
from msgpack.ext import Timestamp

if sys.version_info[0] > 2:
from msgpack.ext import _utc
from msgpack.ext import Timestamp, _utc


def test_timestamp():
Expand Down Expand Up @@ -85,29 +82,25 @@ def test_timestamp_to():
assert t.to_unix_nano() == 42000014000


@pytest.mark.skipif(sys.version_info[0] == 2, reason="datetime support is PY3+ only")
def test_timestamp_datetime():
t = Timestamp(42, 14)
assert t.to_datetime() == datetime.datetime(1970, 1, 1, 0, 0, 42, 0, tzinfo=_utc)


@pytest.mark.skipif(sys.version_info[0] == 2, reason="datetime support is PY3+ only")
def test_unpack_datetime():
t = Timestamp(42, 14)
packed = msgpack.packb(t)
unpacked = msgpack.unpackb(packed, timestamp=3)
assert unpacked == datetime.datetime(1970, 1, 1, 0, 0, 42, 0, tzinfo=_utc)


@pytest.mark.skipif(sys.version_info[0] == 2, reason="datetime support is PY3+ only")
def test_pack_unpack_before_epoch():
t_in = datetime.datetime(1960, 1, 1, tzinfo=_utc)
packed = msgpack.packb(t_in, datetime=True)
unpacked = msgpack.unpackb(packed, timestamp=3)
assert unpacked == t_in


@pytest.mark.skipif(sys.version_info[0] == 2, reason="datetime support is PY3+ only")
def test_pack_datetime():
t = Timestamp(42, 14000)
dt = t.to_datetime()
Expand All @@ -131,7 +124,6 @@ def test_pack_datetime():
assert msgpack.unpackb(packed) is None


@pytest.mark.skipif(sys.version_info[0] == 2, reason="datetime support is PY3+ only")
def test_issue451():
# https://github.com/msgpack/msgpack-python/issues/451
dt = datetime.datetime(2100, 1, 1, 1, 1, tzinfo=_utc)
Expand All @@ -142,7 +134,6 @@ def test_issue451():
assert dt == unpacked


@pytest.mark.skipif(sys.version_info[0] == 2, reason="datetime support is PY3+ only")
def test_pack_datetime_without_tzinfo():
dt = datetime.datetime(1970, 1, 1, 0, 0, 42, 14)
with pytest.raises(ValueError, match="where tzinfo=None"):
Expand Down