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
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -220,9 +220,9 @@ and `raw=True` options.

```pycon
>>> import msgpack
>>> msgpack.unpackb(msgpack.packb([b'spam', u'eggs'], use_bin_type=False), raw=True)
>>> msgpack.unpackb(msgpack.packb([b'spam', 'eggs'], use_bin_type=False), raw=True)
[b'spam', b'eggs']
>>> msgpack.unpackb(msgpack.packb([b'spam', u'eggs'], use_bin_type=True), raw=False)
>>> msgpack.unpackb(msgpack.packb([b'spam', 'eggs'], use_bin_type=True), raw=False)
[b'spam', 'eggs']
```

Expand Down
20 changes: 10 additions & 10 deletions docs/conf.py
Expand Up @@ -40,8 +40,8 @@
master_doc = "index"

# General information about the project.
project = u"msgpack"
copyright = u"Inada Naoki"
project = "msgpack"
copyright = "Inada Naoki"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
Expand Down Expand Up @@ -181,7 +181,7 @@
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
("index", "msgpack.tex", u"msgpack Documentation", u"Author", "manual"),
("index", "msgpack.tex", "msgpack Documentation", "Author", "manual"),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down Expand Up @@ -209,7 +209,7 @@

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [("index", "msgpack", u"msgpack Documentation", [u"Author"], 1)]
man_pages = [("index", "msgpack", "msgpack Documentation", ["Author"], 1)]

# If true, show URL addresses after external links.
# man_show_urls = False
Expand All @@ -224,8 +224,8 @@
(
"index",
"msgpack",
u"msgpack Documentation",
u"Author",
"msgpack Documentation",
"Author",
"msgpack",
"One line description of project.",
"Miscellaneous",
Expand All @@ -245,10 +245,10 @@
# -- Options for Epub output ---------------------------------------------------

# Bibliographic Dublin Core info.
epub_title = u"msgpack"
epub_author = u"Author"
epub_publisher = u"Author"
epub_copyright = u"2013, Author"
epub_title = "msgpack"
epub_author = "Author"
epub_publisher = "Author"
epub_copyright = "2013, Author"

# The language of the text. It defaults to the language option
# or en if the language is not set.
Expand Down
2 changes: 1 addition & 1 deletion msgpack/__init__.py
Expand Up @@ -10,7 +10,7 @@
__version__ = "1.0.5"


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
1 change: 0 additions & 1 deletion msgpack/_packer.pyx
Expand Up @@ -98,7 +98,6 @@ cdef class Packer(object):
If set to true, datetime with tzinfo is packed into Timestamp type.
Note that the tzinfo is stripped in the timestamp.
You can get UTC datetime with `timestamp=3` option of the Unpacker.
(Python 2 is not supported).

:param str unicode_errors:
The error handler for encoding unicode. (default: 'strict')
Expand Down
2 changes: 1 addition & 1 deletion msgpack/_unpacker.pyx
Expand Up @@ -236,7 +236,7 @@ cdef class Unpacker(object):
0 - Timestamp
1 - float (Seconds from the EPOCH)
2 - int (Nanoseconds from the EPOCH)
3 - datetime.datetime (UTC). Python 2 is not supported.
3 - datetime.datetime (UTC).

:param bool strict_map_key:
If true (default), only str or bytes are accepted for map (dict) keys.
Expand Down
24 changes: 4 additions & 20 deletions msgpack/ext.py
Expand Up @@ -5,19 +5,6 @@
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))


class ExtType(namedtuple("ExtType", "code data")):
"""ExtType represents ext type in msgpack."""

Expand Down Expand Up @@ -55,9 +42,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 integer")
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 Expand Up @@ -174,20 +161,17 @@ def to_unix_nano(self):
def to_datetime(self):
"""Get the timestamp as a UTC datetime.

Python 2 is not supported.

:rtype: datetime.
"""
return datetime.datetime.fromtimestamp(0, _utc) + datetime.timedelta(
utc = datetime.timezone.utc
return datetime.datetime.fromtimestamp(0, utc) + datetime.timedelta(
seconds=self.to_unix()
)

@staticmethod
def from_datetime(dt):
"""Create a Timestamp from datetime with tzinfo.

Python 2 is not supported.

:rtype: Timestamp
"""
return Timestamp.from_unix(dt.timestamp())
70 changes: 20 additions & 50 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 @@ -202,7 +177,7 @@ class Unpacker(object):
0 - Timestamp
1 - float (Seconds from the EPOCH)
2 - int (Nanoseconds from the EPOCH)
3 - datetime.datetime (UTC). Python 2 is not supported.
3 - datetime.datetime (UTC).

:param bool strict_map_key:
If true (default), only str or bytes are accepted for map (dict) keys.
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 @@ -698,7 +673,6 @@ class Packer(object):
If set to true, datetime with tzinfo is packed into Timestamp type.
Note that the tzinfo is stripped in the timestamp.
You can get UTC datetime with `timestamp=3` option of the Unpacker.
(Python 2 is not supported).

:param str unicode_errors:
The error handler for encoding unicode. (default: 'strict')
Expand Down Expand Up @@ -743,8 +717,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 +746,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 +778,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,13 +827,11 @@ 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
)
return self._pack_map_pairs(len(obj), obj.items(), nest_limit - 1)

if self._datetime and check(obj, _DateTime) and obj.tzinfo is not None:
obj = Timestamp.from_datetime(obj)
Expand Down Expand Up @@ -1004,7 +974,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
2 changes: 1 addition & 1 deletion test/test_case.py
Expand Up @@ -134,4 +134,4 @@ def test_match():


def test_unicode():
assert unpackb(packb(u"foobar"), use_list=1) == u"foobar"
assert unpackb(packb("foobar"), use_list=1) == "foobar"