Skip to content

Commit

Permalink
Drop Python 3.6 support (#543)
Browse files Browse the repository at this point in the history
The following steps have been taken:

1. Black was updated to latest version. The code has been formatted with
the new version.
2. The pyupgrade utility is installed. This helped to remove all the
code that was needed to support Python < 3.7.

Fix #541.

Co-authored-by: Inada Naoki <songofacandy@gmail.com>
  • Loading branch information
evgenymarkov and methane committed May 23, 2023
1 parent feec062 commit c8d0751
Show file tree
Hide file tree
Showing 23 changed files with 60 additions and 119 deletions.
8 changes: 7 additions & 1 deletion Makefile
@@ -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
@@ -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
@@ -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
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
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
@@ -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
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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

0 comments on commit c8d0751

Please sign in to comment.