From acc7ce8a7aead57915c41becb3c467ee70c7da59 Mon Sep 17 00:00:00 2001 From: Charly C <changaco@changaco.oy.lc> Date: Wed, 20 Feb 2019 16:56:32 +0100 Subject: [PATCH 1/3] don't encode dates as datetimes by default --- cbor2/encoder.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cbor2/encoder.py b/cbor2/encoder.py index b6e24e4d..e150a07f 100644 --- a/cbor2/encoder.py +++ b/cbor2/encoder.py @@ -66,6 +66,9 @@ class CBOREncoder(object): when True, use "canonical" CBOR representation; this typically involves sorting maps, sets, etc. into a pre-determined order ensuring that serializations are comparable without decoding + :param bool date_as_datetime: set to ``True`` to serialize date objects as + datetimes (CBOR tag 0), which was the default behavior in previous + releases (cbor2 <= 4.1.2). .. _CBOR: https://cbor.io/ """ @@ -76,7 +79,8 @@ class CBOREncoder(object): '_canonical') def __init__(self, fp, datetime_as_timestamp=False, timezone=None, - value_sharing=False, default=None, canonical=False): + value_sharing=False, default=None, canonical=False, + date_as_datetime=False): self.fp = fp self.datetime_as_timestamp = datetime_as_timestamp self.timezone = timezone @@ -87,6 +91,8 @@ def __init__(self, fp, datetime_as_timestamp=False, timezone=None, self._encoders = default_encoders.copy() if canonical: self._encoders.update(canonical_encoders) + if date_as_datetime: + self._encoders[date] = CBOREncoder.encode_date def _find_encoder(self, obj_type): for type_, enc in list(iteritems(self._encoders)): @@ -471,7 +477,6 @@ def encode_undefined(self, value): (FrozenDict, CBOREncoder.encode_map), (type(undefined), CBOREncoder.encode_undefined), (datetime, CBOREncoder.encode_datetime), - (date, CBOREncoder.encode_date), (type(re.compile('')), CBOREncoder.encode_regexp), (('fractions', 'Fraction'), CBOREncoder.encode_rational), (('email.message', 'Message'), CBOREncoder.encode_mime), From ec61f88bd8598ccd189871c6ec372e8f555a75fd Mon Sep 17 00:00:00 2001 From: Charly C <changaco@changaco.oy.lc> Date: Wed, 20 Feb 2019 17:37:20 +0100 Subject: [PATCH 2/3] update the tests --- tests/test_encoder.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_encoder.py b/tests/test_encoder.py index bae4da08..0eb29d67 100644 --- a/tests/test_encoder.py +++ b/tests/test_encoder.py @@ -236,8 +236,13 @@ def test_datetime(impl, value, as_timestamp, expected): def test_date(impl): + with pytest.raises(impl.CBOREncodeError): + impl.dumps(date(2013, 3, 21)) + + +def test_date_as_datetime(impl): expected = unhexlify('c074323031332d30332d32315430303a30303a30305a') - assert impl.dumps(date(2013, 3, 21), timezone=timezone.utc) == expected + assert impl.dumps(date(2013, 3, 21), timezone=timezone.utc, date_as_datetime=True) == expected def test_naive_datetime(impl): From 1eb4bd5820a7a3bfec040de951ef9554db553253 Mon Sep 17 00:00:00 2001 From: Changaco <changaco@changaco.oy.lc> Date: Tue, 4 Jun 2019 08:57:50 +0200 Subject: [PATCH 3/3] modify the C implementation accordingly --- source/encoder.c | 21 ++++++++++++++++----- source/module.c | 2 ++ source/module.h | 1 + 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/source/encoder.c b/source/encoder.c index 701fedc5..a1058d41 100644 --- a/source/encoder.c +++ b/source/encoder.c @@ -111,19 +111,21 @@ CBOREncoder_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) // CBOREncoder.__init__(self, fp=None, datetime_as_timestamp=0, timezone=None, -// value_sharing=False, default=None, canonical=False) +// value_sharing=False, default=None, canonical=False, +// date_as_datetime=False) int CBOREncoder_init(CBOREncoderObject *self, PyObject *args, PyObject *kwargs) { static char *keywords[] = { "fp", "datetime_as_timestamp", "timezone", "value_sharing", "default", - "canonical", NULL + "canonical", "date_as_datetime", NULL }; - PyObject *tmp, *fp = NULL, *default_handler = NULL, *tz = NULL; + PyObject *tmp, *fp = NULL, *default_handler = NULL, *tz = NULL, + *date_as_datetime = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|pOpOB", keywords, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|pOpOBO", keywords, &fp, &self->timestamp_format, &tz, &self->value_sharing, - &default_handler, &self->enc_style)) + &default_handler, &self->enc_style, &date_as_datetime)) return -1; if (_CBOREncoder_set_fp(self, fp, NULL) == -1) @@ -153,6 +155,15 @@ CBOREncoder_init(CBOREncoderObject *self, PyObject *args, PyObject *kwargs) _CBOR2_str_update, _CBOR2_canonical_encoders, NULL)) return -1; } + if (date_as_datetime) { + PyObject *encode_date = PyObject_GetAttr((PyObject *) &CBOREncoderType, _CBOR2_str_encode_date); + if (!encode_date) + return -1; + PyObject *datetime_class = PyDateTimeAPI->DateType; + if (PyObject_SetItem(self->encoders, datetime_class, encode_date) == -1) + return -1; + Py_DECREF(encode_date); + } return 0; } diff --git a/source/module.c b/source/module.c index 0baf9b6b..d4ac8de4 100644 --- a/source/module.c +++ b/source/module.c @@ -601,6 +601,7 @@ PyObject *_CBOR2_str_datestr_re = NULL; PyObject *_CBOR2_str_Decimal = NULL; PyObject *_CBOR2_str_default_encoders = NULL; PyObject *_CBOR2_str_denominator = NULL; +PyObject *_CBOR2_str_encode_date = NULL; PyObject *_CBOR2_str_Fraction = NULL; PyObject *_CBOR2_str_fromtimestamp = NULL; PyObject *_CBOR2_str_FrozenDict = NULL; @@ -861,6 +862,7 @@ PyInit__cbor2(void) INTERN_STRING(Decimal); INTERN_STRING(default_encoders); INTERN_STRING(denominator); + INTERN_STRING(encode_date); INTERN_STRING(Fraction); INTERN_STRING(fromtimestamp); INTERN_STRING(FrozenDict); diff --git a/source/module.h b/source/module.h index 56443af6..62e9038f 100644 --- a/source/module.h +++ b/source/module.h @@ -43,6 +43,7 @@ extern PyObject *_CBOR2_str_datestr_re; extern PyObject *_CBOR2_str_Decimal; extern PyObject *_CBOR2_str_default_encoders; extern PyObject *_CBOR2_str_denominator; +extern PyObject *_CBOR2_str_encode_date; extern PyObject *_CBOR2_str_Fraction; extern PyObject *_CBOR2_str_fromtimestamp; extern PyObject *_CBOR2_str_FrozenDict;