Skip to content

Commit 9b6b5d9

Browse files
committed
Marshalling: Restore CrateDB standard encoder
1 parent 0684b99 commit 9b6b5d9

File tree

2 files changed

+35
-10
lines changed

2 files changed

+35
-10
lines changed

src/crate/client/http.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
# software solely pursuant to the terms of the relevant commercial agreement.
2121

2222

23+
import calendar
24+
import datetime as dt
2325
import heapq
2426
import io
2527
import logging
@@ -84,19 +86,38 @@ def super_len(o):
8486
return None
8587

8688

87-
def cratedb_json_encoder(obj: t.Any) -> str:
89+
epoch_aware = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
90+
epoch_naive = dt.datetime(1970, 1, 1)
91+
92+
93+
def json_encoder(obj: t.Any) -> t.Union[int, str]:
8894
"""
8995
Encoder function for orjson, with additional type support.
9096
91-
- Python's `Decimal` type.
97+
- Python's `Decimal` type will be serialized to `str`.
98+
- Python's `dt.datetime` and `dt.date` types will be
99+
serialized to `int` after converting to milliseconds
100+
since epoch.
92101
- freezegun's `FakeDatetime` type.
93102
94103
https://github.com/ijl/orjson#default
95104
"""
96105
if isinstance(obj, Decimal):
97106
return str(obj)
98-
elif hasattr(obj, "isoformat"):
99-
return obj.isoformat()
107+
if isinstance(obj, dt.datetime):
108+
if obj.tzinfo is not None:
109+
delta = obj - epoch_aware
110+
else:
111+
delta = obj - epoch_naive
112+
return int(
113+
delta.microseconds / 1000.0
114+
+ (delta.seconds + delta.days * 24 * 3600) * 1000.0
115+
)
116+
if isinstance(obj, dt.date):
117+
return calendar.timegm(obj.timetuple()) * 1000
118+
# Freezegun support. Needed?
119+
# if hasattr(obj, "isoformat"):
120+
# return obj.isoformat() # noqa: ERA001
100121
raise TypeError
101122

102123

@@ -108,8 +129,12 @@ def json_dumps(obj: t.Any) -> bytes:
108129
"""
109130
return orjson.dumps(
110131
obj,
111-
default=cratedb_json_encoder,
112-
option=(orjson.OPT_NON_STR_KEYS | orjson.OPT_SERIALIZE_NUMPY),
132+
default=json_encoder,
133+
option=(
134+
orjson.OPT_PASSTHROUGH_DATETIME
135+
| orjson.OPT_NON_STR_KEYS
136+
| orjson.OPT_SERIALIZE_NUMPY
137+
),
113138
)
114139

115140

tests/client/test_http.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ def test_datetime_is_converted_to_ts(self, request):
318318
# convert string to dict
319319
# because the order of the keys isn't deterministic
320320
data = json.loads(request.call_args[1]["data"])
321-
self.assertEqual(data["args"], ["2015-02-28T07:31:40"])
321+
self.assertEqual(data["args"], [1425108700000])
322322
client.close()
323323

324324
@patch(REQUEST, autospec=True)
@@ -329,7 +329,7 @@ def test_date_is_converted_to_ts(self, request):
329329
day = dt.date(2016, 4, 21)
330330
client.sql("insert into users (dt) values (?)", (day,))
331331
data = json.loads(request.call_args[1]["data"])
332-
self.assertEqual(data["args"], ["2016-04-21"])
332+
self.assertEqual(data["args"], [1461196800000])
333333
client.close()
334334

335335
def test_socket_options_contain_keepalive(self):
@@ -725,9 +725,9 @@ class TestCrateJsonEncoder(TestCase):
725725
def test_naive_datetime(self):
726726
data = dt.datetime.fromisoformat("2023-06-26T09:24:00.123")
727727
result = json_dumps(data)
728-
self.assertEqual(result, b'"2023-06-26T09:24:00.123000"')
728+
self.assertEqual(result, b"1687771440123")
729729

730730
def test_aware_datetime(self):
731731
data = dt.datetime.fromisoformat("2023-06-26T09:24:00.123+02:00")
732732
result = json_dumps(data)
733-
self.assertEqual(result, b'"2023-06-26T09:24:00.123000+02:00"')
733+
self.assertEqual(result, b"1687764240123")

0 commit comments

Comments
 (0)