From d845b6500a3debc92399afc6c2e3b90ff56db745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Egelund-M=C3=BCller?= Date: Fri, 26 Feb 2021 15:50:00 +0100 Subject: [PATCH 1/3] Nicer error when packing a datetime without tzinfo --- msgpack/_packer.pyx | 4 +++- msgpack/fallback.py | 4 +++- test/test_timestamp.py | 11 +++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/msgpack/_packer.pyx b/msgpack/_packer.pyx index e6cd2c7d..0030cce6 100644 --- a/msgpack/_packer.pyx +++ b/msgpack/_packer.pyx @@ -274,7 +274,9 @@ cdef class Packer(object): if ret == 0: ret = msgpack_pack_raw_body(&self.pk, view.buf, L) PyBuffer_Release(&view); - elif self.datetime and PyDateTime_CheckExact(o) and datetime_tzinfo(o) is not None: + elif self.datetime and PyDateTime_CheckExact(o): + if datetime_tzinfo(o) is None: + PyErr_Format(ValueError, b"can not serialize '%.200s' object where tzinfo=None", Py_TYPE(o).tp_name) delta = o - epoch if not PyDelta_CheckExact(delta): raise ValueError("failed to calculate delta") diff --git a/msgpack/fallback.py b/msgpack/fallback.py index 898fe146..3cb6d40f 100644 --- a/msgpack/fallback.py +++ b/msgpack/fallback.py @@ -865,7 +865,9 @@ def _pack( len(obj), dict_iteritems(obj), nest_limit - 1 ) - if self._datetime and check(obj, _DateTime) and obj.tzinfo is not None: + if self._datetime and check(obj, _DateTime): + if obj.tzinfo is None: + raise ValueError("Cannot serialize %r where tzinfo=None" % (obj,)) obj = Timestamp.from_datetime(obj) default_used = 1 continue diff --git a/test/test_timestamp.py b/test/test_timestamp.py index 6a29be77..b5c2fe26 100644 --- a/test/test_timestamp.py +++ b/test/test_timestamp.py @@ -140,3 +140,14 @@ def test_issue451(): unpacked = msgpack.unpackb(packed, timestamp=3) 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"): + packed = msgpack.packb(dt, datetime=True) + + dt = datetime.datetime(1970, 1, 1, 0, 0, 42, 14, tzinfo=_utc) + packed = msgpack.packb(dt, datetime=True) + unpacked = msgpack.unpackb(packed, timestamp=3) + assert unpacked == dt From c67be05e6708587553d2673c291524c73dd8a6f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Egelund-M=C3=BCller?= Date: Fri, 26 Feb 2021 16:24:47 +0100 Subject: [PATCH 2/3] Fix formatting error --- test/test_timestamp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_timestamp.py b/test/test_timestamp.py index b5c2fe26..886f1178 100644 --- a/test/test_timestamp.py +++ b/test/test_timestamp.py @@ -141,6 +141,7 @@ def test_issue451(): unpacked = msgpack.unpackb(packed, timestamp=3) 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) From 9dc102ab9ac308082bbad25f67a7c2fd3592560d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Egelund-M=C3=BCller?= Date: Wed, 10 Mar 2021 16:27:04 +0100 Subject: [PATCH 3/3] Respect default function before throwing an error when packing a datetime without tzinfo --- msgpack/_packer.pyx | 6 +++--- msgpack/fallback.py | 8 +++++--- test/test_timestamp.py | 4 ++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/msgpack/_packer.pyx b/msgpack/_packer.pyx index 0030cce6..396da0c2 100644 --- a/msgpack/_packer.pyx +++ b/msgpack/_packer.pyx @@ -274,9 +274,7 @@ cdef class Packer(object): if ret == 0: ret = msgpack_pack_raw_body(&self.pk, view.buf, L) PyBuffer_Release(&view); - elif self.datetime and PyDateTime_CheckExact(o): - if datetime_tzinfo(o) is None: - PyErr_Format(ValueError, b"can not serialize '%.200s' object where tzinfo=None", Py_TYPE(o).tp_name) + elif self.datetime and PyDateTime_CheckExact(o) and datetime_tzinfo(o) is not None: delta = o - epoch if not PyDelta_CheckExact(delta): raise ValueError("failed to calculate delta") @@ -287,6 +285,8 @@ cdef class Packer(object): o = self._default(o) default_used = 1 continue + elif self.datetime and PyDateTime_CheckExact(o): + PyErr_Format(ValueError, b"can not serialize '%.200s' object where tzinfo=None", Py_TYPE(o).tp_name) else: PyErr_Format(TypeError, b"can not serialize '%.200s' object", Py_TYPE(o).tp_name) return ret diff --git a/msgpack/fallback.py b/msgpack/fallback.py index 3cb6d40f..d510c479 100644 --- a/msgpack/fallback.py +++ b/msgpack/fallback.py @@ -865,9 +865,7 @@ def _pack( len(obj), dict_iteritems(obj), nest_limit - 1 ) - if self._datetime and check(obj, _DateTime): - if obj.tzinfo is None: - raise ValueError("Cannot serialize %r where tzinfo=None" % (obj,)) + if self._datetime and check(obj, _DateTime) and obj.tzinfo is not None: obj = Timestamp.from_datetime(obj) default_used = 1 continue @@ -876,6 +874,10 @@ def _pack( obj = self._default(obj) default_used = 1 continue + + if self._datetime and check(obj, _DateTime): + raise ValueError("Cannot serialize %r where tzinfo=None" % (obj,)) + raise TypeError("Cannot serialize %r" % (obj,)) def pack(self, obj): diff --git a/test/test_timestamp.py b/test/test_timestamp.py index 886f1178..4e26489b 100644 --- a/test/test_timestamp.py +++ b/test/test_timestamp.py @@ -148,6 +148,10 @@ def test_pack_datetime_without_tzinfo(): with pytest.raises(ValueError, match="where tzinfo=None"): packed = msgpack.packb(dt, datetime=True) + dt = datetime.datetime(1970, 1, 1, 0, 0, 42, 14) + packed = msgpack.packb(dt, datetime=True, default=lambda x: None) + assert packed == msgpack.packb(None) + dt = datetime.datetime(1970, 1, 1, 0, 0, 42, 14, tzinfo=_utc) packed = msgpack.packb(dt, datetime=True) unpacked = msgpack.unpackb(packed, timestamp=3)