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 Python 3.6 support #543

Merged
merged 6 commits into from
May 23, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
PYTHON_SOURCES = msgpack test setup.py

.PHONY: all
all: cython
python setup.py build_ext -i -f

.PHONY: black
black:
black -S msgpack/ test/ setup.py
black $(PYTHON_SOURCES)

.PHONY: pyupgrade
pyupgrade:
@find $(PYTHON_SOURCES) -name '*.py' -type f -exec pyupgrade --py37-plus '{}' \;

.PHONY: cython
cython:
Expand Down
1 change: 0 additions & 1 deletion msgpack/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# coding: utf-8
from .exceptions import *
from .ext import ExtType, Timestamp

Expand Down
21 changes: 6 additions & 15 deletions msgpack/ext.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# coding: utf-8
from collections import namedtuple
import datetime
import sys
Expand All @@ -15,10 +14,10 @@ def __new__(cls, code, data):
raise TypeError("data must be bytes")
if not 0 <= code <= 127:
raise ValueError("code must be 0~127")
return super(ExtType, cls).__new__(cls, code, data)
return super().__new__(cls, code, data)


class Timestamp(object):
class Timestamp:
"""Timestamp represents the Timestamp extension type in msgpack.

When built with Cython, msgpack uses C methods to pack and unpack `Timestamp`. When using pure-Python
Expand Down Expand Up @@ -47,24 +46,18 @@ def __init__(self, seconds, nanoseconds=0):
if not isinstance(nanoseconds, int):
raise TypeError("nanoseconds must be an integer")
if not (0 <= nanoseconds < 10**9):
raise ValueError(
"nanoseconds must be a non-negative integer less than 999999999."
)
raise ValueError("nanoseconds must be a non-negative integer less than 999999999.")
self.seconds = seconds
self.nanoseconds = nanoseconds

def __repr__(self):
"""String representation of Timestamp."""
return "Timestamp(seconds={0}, nanoseconds={1})".format(
self.seconds, self.nanoseconds
)
return f"Timestamp(seconds={self.seconds}, nanoseconds={self.nanoseconds})"

def __eq__(self, other):
"""Check for equality with another Timestamp object"""
if type(other) is self.__class__:
return (
self.seconds == other.seconds and self.nanoseconds == other.nanoseconds
)
return self.seconds == other.seconds and self.nanoseconds == other.nanoseconds
return False

def __ne__(self, other):
Expand Down Expand Up @@ -164,9 +157,7 @@ def to_datetime(self):
:rtype: datetime.
"""
utc = datetime.timezone.utc
return datetime.datetime.fromtimestamp(0, utc) + datetime.timedelta(
seconds=self.to_unix()
)
return datetime.datetime.fromtimestamp(0, utc) + datetime.timedelta(seconds=self.to_unix())

@staticmethod
def from_datetime(dt):
Expand Down
70 changes: 20 additions & 50 deletions msgpack/fallback.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,6 @@
import struct


if sys.version_info < (3, 5):
# Ugly hack...
RecursionError = RuntimeError

def _is_recursionerror(e):
return (
len(e.args) == 1
and isinstance(e.args[0], str)
and e.args[0].startswith("maximum recursion depth exceeded")
)

else:

def _is_recursionerror(e):
return True


if hasattr(sys, "pypy_version_info"):
# StringIO is slow on PyPy, StringIO is faster. However: PyPy's own
# StringBuilder is fastest.
Expand All @@ -32,7 +15,7 @@ def _is_recursionerror(e):
from __pypy__.builders import StringBuilder
USING_STRINGBUILDER = True

class StringIO(object):
class StringIO:
def __init__(self, s=b""):
if s:
self.builder = StringBuilder(len(s))
Expand Down Expand Up @@ -109,10 +92,8 @@ def unpackb(packed, **kwargs):
ret = unpacker._unpack()
except OutOfData:
raise ValueError("Unpack failed: incomplete input")
except RecursionError as e:
if _is_recursionerror(e):
raise StackError
raise
except RecursionError:
raise StackError
if unpacker._got_extradata():
raise ExtraData(ret, unpacker._get_extradata())
return ret
Expand Down Expand Up @@ -151,7 +132,7 @@ def unpackb(packed, **kwargs):
}


class Unpacker(object):
class Unpacker:
"""Streaming unpacker.

Arguments:
Expand Down Expand Up @@ -334,9 +315,7 @@ def __init__(
if object_pairs_hook is not None and not callable(object_pairs_hook):
raise TypeError("`object_pairs_hook` is not callable")
if object_hook is not None and object_pairs_hook is not None:
raise TypeError(
"object_pairs_hook and object_hook are mutually " "exclusive"
)
raise TypeError("object_pairs_hook and object_hook are mutually exclusive")
if not callable(ext_hook):
raise TypeError("`ext_hook` is not callable")

Expand Down Expand Up @@ -428,20 +407,18 @@ def _read_header(self):
n = b & 0b00011111
typ = TYPE_RAW
if n > self._max_str_len:
raise ValueError("%s exceeds max_str_len(%s)" % (n, self._max_str_len))
raise ValueError(f"{n} exceeds max_str_len({self._max_str_len})")
obj = self._read(n)
elif b & 0b11110000 == 0b10010000:
n = b & 0b00001111
typ = TYPE_ARRAY
if n > self._max_array_len:
raise ValueError(
"%s exceeds max_array_len(%s)" % (n, self._max_array_len)
)
raise ValueError(f"{n} exceeds max_array_len({self._max_array_len})")
elif b & 0b11110000 == 0b10000000:
n = b & 0b00001111
typ = TYPE_MAP
if n > self._max_map_len:
raise ValueError("%s exceeds max_map_len(%s)" % (n, self._max_map_len))
raise ValueError(f"{n} exceeds max_map_len({self._max_map_len})")
elif b == 0xC0:
obj = None
elif b == 0xC2:
Expand All @@ -457,15 +434,15 @@ def _read_header(self):
n = self._buffer[self._buff_i]
self._buff_i += size
if n > self._max_bin_len:
raise ValueError("%s exceeds max_bin_len(%s)" % (n, self._max_bin_len))
raise ValueError(f"{n} exceeds max_bin_len({self._max_bin_len})")
obj = self._read(n)
elif 0xC7 <= b <= 0xC9:
size, fmt, typ = _MSGPACK_HEADERS[b]
self._reserve(size)
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))
raise ValueError(f"{L} exceeds max_ext_len({self._max_ext_len})")
obj = self._read(L)
elif 0xCA <= b <= 0xD3:
size, fmt = _MSGPACK_HEADERS[b]
Expand All @@ -478,9 +455,7 @@ def _read_header(self):
elif 0xD4 <= b <= 0xD8:
size, fmt, typ = _MSGPACK_HEADERS[b]
if self._max_ext_len < size:
raise ValueError(
"%s exceeds max_ext_len(%s)" % (size, self._max_ext_len)
)
raise ValueError(f"{size} exceeds max_ext_len({self._max_ext_len})")
self._reserve(size + 1)
n, obj = struct.unpack_from(fmt, self._buffer, self._buff_i)
self._buff_i += size + 1
Expand All @@ -493,24 +468,22 @@ def _read_header(self):
n = self._buffer[self._buff_i]
self._buff_i += size
if n > self._max_str_len:
raise ValueError("%s exceeds max_str_len(%s)" % (n, self._max_str_len))
raise ValueError(f"{n} exceeds max_str_len({self._max_str_len})")
obj = self._read(n)
elif 0xDC <= b <= 0xDD:
size, fmt, typ = _MSGPACK_HEADERS[b]
self._reserve(size)
(n,) = struct.unpack_from(fmt, self._buffer, self._buff_i)
self._buff_i += size
if n > self._max_array_len:
raise ValueError(
"%s exceeds max_array_len(%s)" % (n, self._max_array_len)
)
raise ValueError(f"{n} exceeds max_array_len({self._max_array_len})")
elif 0xDE <= b <= 0xDF:
size, fmt, typ = _MSGPACK_HEADERS[b]
self._reserve(size)
(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))
raise ValueError(f"{n} exceeds max_map_len({self._max_map_len})")
else:
raise FormatError("Unknown header: 0x%x" % b)
return typ, n, obj
Expand Down Expand Up @@ -549,17 +522,14 @@ def _unpack(self, execute=EX_CONSTRUCT):
return
if self._object_pairs_hook is not None:
ret = self._object_pairs_hook(
(self._unpack(EX_CONSTRUCT), self._unpack(EX_CONSTRUCT))
for _ in range(n)
(self._unpack(EX_CONSTRUCT), self._unpack(EX_CONSTRUCT)) for _ in range(n)
)
else:
ret = {}
for _ in range(n):
key = self._unpack(EX_CONSTRUCT)
if self._strict_map_key and type(key) not in (str, bytes):
raise ValueError(
"%s is not allowed for map key" % str(type(key))
)
raise ValueError("%s is not allowed for map key" % str(type(key)))
if type(key) is str:
key = sys.intern(key)
ret[key] = self._unpack(EX_CONSTRUCT)
Expand Down Expand Up @@ -634,7 +604,7 @@ def tell(self):
return self._stream_offset


class Packer(object):
class Packer:
"""
MessagePack Packer

Expand Down Expand Up @@ -844,9 +814,9 @@ def _pack(
continue

if self._datetime and check(obj, _DateTime):
raise ValueError("Cannot serialize %r where tzinfo=None" % (obj,))
raise ValueError(f"Cannot serialize {obj!r} where tzinfo=None")

raise TypeError("Cannot serialize %r" % (obj,))
raise TypeError(f"Cannot serialize {obj!r}")

def pack(self, obj):
try:
Expand Down Expand Up @@ -933,7 +903,7 @@ def _pack_map_header(self, n):

def _pack_map_pairs(self, n, pairs, nest_limit=DEFAULT_RECURSE_LIMIT):
self._pack_map_header(n)
for (k, v) in pairs:
for k, v in pairs:
self._pack(k, nest_limit - 1)
self._pack(v, nest_limit - 1)

Expand Down
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ requires = [
"setuptools >= 35.0.2",
]
build-backend = "setuptools.build_meta"

[tool.black]
line-length = 100
target-version = ["py37"]
skip_string_normalization = true
8 changes: 5 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Also declared in pyproject.toml, if updating here please also update there
# Also declared in pyproject.toml, if updating here please also update there.
Cython~=0.29.30

# dev only tools. no need to add pyproject
black==22.3.0
# Tools required only for development. No need to add it to pyproject.toml file.
black==23.3.0
pytest==7.3.1
pyupgrade==3.3.2
1 change: 0 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ project_urls =

classifiers =
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Expand Down
9 changes: 2 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env python
# coding: utf-8
import io
import os
import sys
Expand All @@ -25,7 +24,7 @@ class NoCython(Exception):


def cythonize(src):
sys.stderr.write("cythonize: %r\n" % (src,))
sys.stderr.write(f"cythonize: {src!r}\n")
cython_compiler.compile([src], cplus=True)


Expand All @@ -36,11 +35,7 @@ def ensure_source(src):
if not have_cython:
raise NoCython
cythonize(pyx)
elif (
os.path.exists(pyx)
and os.stat(src).st_mtime < os.stat(pyx).st_mtime
and have_cython
):
elif os.path.exists(pyx) and os.stat(src).st_mtime < os.stat(pyx).st_mtime and have_cython:
cythonize(pyx)
return src

Expand Down
1 change: 0 additions & 1 deletion test/test_buffer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env python
# coding: utf-8

import sys
import pytest
Expand Down
7 changes: 3 additions & 4 deletions test/test_case.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#!/usr/bin/env python
# coding: utf-8
from msgpack import packb, unpackb


def check(length, obj, use_bin_type=True):
v = packb(obj, use_bin_type=use_bin_type)
assert len(v) == length, "%r length should be %r but get %r" % (obj, length, len(v))
assert len(v) == length, f"{obj!r} length should be {length!r} but get {len(v)!r}"
assert unpackb(v, use_list=0, raw=not use_bin_type) == obj


Expand Down Expand Up @@ -120,11 +119,11 @@ def test_match():
),
({}, b"\x80"),
(
dict([(x, x) for x in range(15)]),
{x: x for x in range(15)},
b"\x8f\x00\x00\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08\t\t\n\n\x0b\x0b\x0c\x0c\r\r\x0e\x0e",
),
(
dict([(x, x) for x in range(16)]),
{x: x for x in range(16)},
b"\xde\x00\x10\x00\x00\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08\t\t\n\n\x0b\x0b\x0c\x0c\r\r\x0e\x0e\x0f\x0f",
),
]
Expand Down
1 change: 0 additions & 1 deletion test/test_except.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env python
# coding: utf-8

from pytest import raises
from msgpack import packb, unpackb, Unpacker, FormatError, StackError, OutOfData
Expand Down
7 changes: 2 additions & 5 deletions test/test_extension.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from __future__ import print_function
import array
import msgpack
from msgpack import ExtType
Expand All @@ -17,9 +16,7 @@ def p(s):
assert p(b"A" * 16) == b"\xd8\x42" + b"A" * 16 # fixext 16
assert p(b"ABC") == b"\xc7\x03\x42ABC" # ext 8
assert p(b"A" * 0x0123) == b"\xc8\x01\x23\x42" + b"A" * 0x0123 # ext 16
assert (
p(b"A" * 0x00012345) == b"\xc9\x00\x01\x23\x45\x42" + b"A" * 0x00012345
) # ext 32
assert p(b"A" * 0x00012345) == b"\xc9\x00\x01\x23\x45\x42" + b"A" * 0x00012345 # ext 32


def test_unpack_ext_type():
Expand Down Expand Up @@ -49,7 +46,7 @@ def default(obj):
except AttributeError:
data = obj.tostring()
return ExtType(typecode, data)
raise TypeError("Unknown type object %r" % (obj,))
raise TypeError(f"Unknown type object {obj!r}")

def ext_hook(code, data):
print("ext_hook called", code, data)
Expand Down