soia-client 1.1.5__py3-none-any.whl → 1.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of soia-client might be problematic. Click here for more details.
- soia/_impl/arrays.py +59 -8
- soia/_impl/binary.py +270 -0
- soia/_impl/enums.py +223 -40
- soia/_impl/optionals.py +47 -8
- soia/_impl/primitives.py +176 -23
- soia/_impl/serializer.py +40 -16
- soia/_impl/service.py +1 -3
- soia/_impl/structs.py +312 -88
- soia/_impl/timestamp.py +4 -5
- soia/_impl/type_adapter.py +38 -3
- {soia_client-1.1.5.dist-info → soia_client-1.1.6.dist-info}/METADATA +1 -1
- soia_client-1.1.6.dist-info/RECORD +28 -0
- soia_client-1.1.5.dist-info/RECORD +0 -27
- {soia_client-1.1.5.dist-info → soia_client-1.1.6.dist-info}/WHEEL +0 -0
- {soia_client-1.1.5.dist-info → soia_client-1.1.6.dist-info}/licenses/LICENSE +0 -0
- {soia_client-1.1.5.dist-info → soia_client-1.1.6.dist-info}/top_level.txt +0 -0
soia/_impl/primitives.py
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import base64
|
|
2
|
+
import struct
|
|
2
3
|
from collections.abc import Callable
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from typing import Any, Final, final
|
|
5
6
|
|
|
6
7
|
from soia import _spec, reflection
|
|
8
|
+
from soia._impl.binary import (
|
|
9
|
+
decode_bool,
|
|
10
|
+
decode_float,
|
|
11
|
+
decode_int32,
|
|
12
|
+
decode_int64,
|
|
13
|
+
decode_uint64,
|
|
14
|
+
encode_float32,
|
|
15
|
+
encode_float64,
|
|
16
|
+
encode_int32,
|
|
17
|
+
encode_int64,
|
|
18
|
+
encode_length_prefix,
|
|
19
|
+
encode_uint64,
|
|
20
|
+
)
|
|
7
21
|
from soia._impl.function_maker import Expr, ExprLike
|
|
8
22
|
from soia._impl.timestamp import Timestamp
|
|
9
|
-
from soia._impl.type_adapter import TypeAdapter
|
|
23
|
+
from soia._impl.type_adapter import ByteStream, TypeAdapter, T
|
|
10
24
|
|
|
11
25
|
|
|
12
|
-
class AbstractPrimitiveAdapter(TypeAdapter):
|
|
26
|
+
class AbstractPrimitiveAdapter(TypeAdapter[T]):
|
|
13
27
|
@final
|
|
14
28
|
def finalize(
|
|
15
29
|
self,
|
|
@@ -29,7 +43,7 @@ class AbstractPrimitiveAdapter(TypeAdapter):
|
|
|
29
43
|
return None
|
|
30
44
|
|
|
31
45
|
|
|
32
|
-
class _BoolAdapter(AbstractPrimitiveAdapter):
|
|
46
|
+
class _BoolAdapter(AbstractPrimitiveAdapter[bool]):
|
|
33
47
|
def default_expr(self) -> ExprLike:
|
|
34
48
|
return "False"
|
|
35
49
|
|
|
@@ -49,9 +63,24 @@ class _BoolAdapter(AbstractPrimitiveAdapter):
|
|
|
49
63
|
else:
|
|
50
64
|
return Expr.join("(1 if ", in_expr, " else 0)")
|
|
51
65
|
|
|
52
|
-
def from_json_expr(
|
|
66
|
+
def from_json_expr(
|
|
67
|
+
self, json_expr: ExprLike, keep_unrecognized_expr: ExprLike
|
|
68
|
+
) -> Expr:
|
|
53
69
|
return Expr.join("(True if ", json_expr, " else False)")
|
|
54
70
|
|
|
71
|
+
@staticmethod
|
|
72
|
+
def encode(
|
|
73
|
+
value: bool,
|
|
74
|
+
buffer: bytearray,
|
|
75
|
+
) -> None:
|
|
76
|
+
buffer.append(1 if value else 0)
|
|
77
|
+
|
|
78
|
+
def encode_fn(self) -> Callable[[bool, bytearray], None]:
|
|
79
|
+
return _BoolAdapter.encode
|
|
80
|
+
|
|
81
|
+
def decode_fn(self) -> Callable[[ByteStream], bool]:
|
|
82
|
+
return decode_bool
|
|
83
|
+
|
|
55
84
|
def get_type(self) -> reflection.Type:
|
|
56
85
|
return reflection.PrimitiveType(
|
|
57
86
|
kind="primitive",
|
|
@@ -59,11 +88,11 @@ class _BoolAdapter(AbstractPrimitiveAdapter):
|
|
|
59
88
|
)
|
|
60
89
|
|
|
61
90
|
|
|
62
|
-
BOOL_ADAPTER: Final[TypeAdapter] = _BoolAdapter()
|
|
91
|
+
BOOL_ADAPTER: Final[TypeAdapter[bool]] = _BoolAdapter()
|
|
63
92
|
|
|
64
93
|
|
|
65
94
|
@dataclass(frozen=True)
|
|
66
|
-
class _AbstractIntAdapter(AbstractPrimitiveAdapter):
|
|
95
|
+
class _AbstractIntAdapter(AbstractPrimitiveAdapter[int]):
|
|
67
96
|
"""Type adapter implementation for int32, int64 and uint64."""
|
|
68
97
|
|
|
69
98
|
def default_expr(self) -> ExprLike:
|
|
@@ -76,7 +105,9 @@ class _AbstractIntAdapter(AbstractPrimitiveAdapter):
|
|
|
76
105
|
def is_not_default_expr(self, arg_expr: ExprLike, attr_expr: ExprLike) -> ExprLike:
|
|
77
106
|
return arg_expr
|
|
78
107
|
|
|
79
|
-
def from_json_expr(
|
|
108
|
+
def from_json_expr(
|
|
109
|
+
self, json_expr: ExprLike, keep_unrecognized_expr: ExprLike
|
|
110
|
+
) -> Expr:
|
|
80
111
|
# Must accept float inputs and string inputs and turn them into ints.
|
|
81
112
|
return Expr.join(
|
|
82
113
|
"(0).__class__(",
|
|
@@ -98,6 +129,12 @@ class _Int32Adapter(_AbstractIntAdapter):
|
|
|
98
129
|
" < 2147483647 else 2147483647)",
|
|
99
130
|
)
|
|
100
131
|
|
|
132
|
+
def encode_fn(self) -> Callable[[int, bytearray], None]:
|
|
133
|
+
return encode_int32
|
|
134
|
+
|
|
135
|
+
def decode_fn(self) -> Callable[[ByteStream], int]:
|
|
136
|
+
return decode_int32
|
|
137
|
+
|
|
101
138
|
def get_type(self) -> reflection.Type:
|
|
102
139
|
return reflection.PrimitiveType(
|
|
103
140
|
kind="primitive",
|
|
@@ -124,6 +161,12 @@ class _Int64Adapter(_AbstractIntAdapter):
|
|
|
124
161
|
def to_json_expr(self, in_expr: ExprLike, readable: bool) -> Expr:
|
|
125
162
|
return Expr.join(Expr.local("int64_to_json", _int64_to_json), "(", in_expr, ")")
|
|
126
163
|
|
|
164
|
+
def encode_fn(self) -> Callable[[int, bytearray], None]:
|
|
165
|
+
return encode_int64
|
|
166
|
+
|
|
167
|
+
def decode_fn(self) -> Callable[[ByteStream], int]:
|
|
168
|
+
return decode_int64
|
|
169
|
+
|
|
127
170
|
def get_type(self) -> reflection.Type:
|
|
128
171
|
return reflection.PrimitiveType(
|
|
129
172
|
kind="primitive",
|
|
@@ -149,6 +192,12 @@ class _Uint64Adapter(_AbstractIntAdapter):
|
|
|
149
192
|
Expr.local("uint64_to_json", _uint64_to_json), "(", in_expr, ")"
|
|
150
193
|
)
|
|
151
194
|
|
|
195
|
+
def encode_fn(self) -> Callable[[int, bytearray], None]:
|
|
196
|
+
return encode_uint64
|
|
197
|
+
|
|
198
|
+
def decode_fn(self) -> Callable[[ByteStream], int]:
|
|
199
|
+
return decode_uint64
|
|
200
|
+
|
|
152
201
|
def get_type(self) -> reflection.Type:
|
|
153
202
|
return reflection.PrimitiveType(
|
|
154
203
|
kind="primitive",
|
|
@@ -156,13 +205,13 @@ class _Uint64Adapter(_AbstractIntAdapter):
|
|
|
156
205
|
)
|
|
157
206
|
|
|
158
207
|
|
|
159
|
-
INT32_ADAPTER: Final[TypeAdapter] = _Int32Adapter()
|
|
160
|
-
INT64_ADAPTER: Final[TypeAdapter] = _Int64Adapter()
|
|
161
|
-
UINT64_ADAPTER: Final[TypeAdapter] = _Uint64Adapter()
|
|
208
|
+
INT32_ADAPTER: Final[TypeAdapter[int]] = _Int32Adapter()
|
|
209
|
+
INT64_ADAPTER: Final[TypeAdapter[int]] = _Int64Adapter()
|
|
210
|
+
UINT64_ADAPTER: Final[TypeAdapter[int]] = _Uint64Adapter()
|
|
162
211
|
|
|
163
212
|
|
|
164
213
|
@dataclass(frozen=True)
|
|
165
|
-
class _AbstractFloatAdapter(AbstractPrimitiveAdapter):
|
|
214
|
+
class _AbstractFloatAdapter(AbstractPrimitiveAdapter[float]):
|
|
166
215
|
"""Type adapter implementation for float32 and float64."""
|
|
167
216
|
|
|
168
217
|
def default_expr(self) -> ExprLike:
|
|
@@ -177,14 +226,22 @@ class _AbstractFloatAdapter(AbstractPrimitiveAdapter):
|
|
|
177
226
|
def to_json_expr(self, in_expr: ExprLike, readable: bool) -> ExprLike:
|
|
178
227
|
return in_expr
|
|
179
228
|
|
|
180
|
-
def from_json_expr(
|
|
229
|
+
def from_json_expr(
|
|
230
|
+
self, json_expr: ExprLike, keep_unrecognized_expr: ExprLike
|
|
231
|
+
) -> Expr:
|
|
181
232
|
return Expr.join("(", json_expr, " + 0.0)")
|
|
182
233
|
|
|
234
|
+
def decode_fn(self) -> Callable[[ByteStream], float]:
|
|
235
|
+
return decode_float
|
|
236
|
+
|
|
183
237
|
|
|
184
238
|
@dataclass(frozen=True)
|
|
185
239
|
class _Float32Adapter(_AbstractFloatAdapter):
|
|
186
240
|
"""Type adapter implementation for float32."""
|
|
187
241
|
|
|
242
|
+
def encode_fn(self) -> Callable[[float, bytearray], None]:
|
|
243
|
+
return encode_float32
|
|
244
|
+
|
|
188
245
|
def get_type(self) -> reflection.Type:
|
|
189
246
|
return reflection.PrimitiveType(
|
|
190
247
|
kind="primitive",
|
|
@@ -194,7 +251,10 @@ class _Float32Adapter(_AbstractFloatAdapter):
|
|
|
194
251
|
|
|
195
252
|
@dataclass(frozen=True)
|
|
196
253
|
class _Float64Adapter(_AbstractFloatAdapter):
|
|
197
|
-
"""Type adapter implementation for
|
|
254
|
+
"""Type adapter implementation for float64."""
|
|
255
|
+
|
|
256
|
+
def encode_fn(self) -> Callable[[float, bytearray], None]:
|
|
257
|
+
return encode_float64
|
|
198
258
|
|
|
199
259
|
def get_type(self) -> reflection.Type:
|
|
200
260
|
return reflection.PrimitiveType(
|
|
@@ -203,11 +263,16 @@ class _Float64Adapter(_AbstractFloatAdapter):
|
|
|
203
263
|
)
|
|
204
264
|
|
|
205
265
|
|
|
206
|
-
FLOAT32_ADAPTER: Final[TypeAdapter] = _Float32Adapter()
|
|
207
|
-
FLOAT64_ADAPTER: Final[TypeAdapter] = _Float64Adapter()
|
|
266
|
+
FLOAT32_ADAPTER: Final[TypeAdapter[float]] = _Float32Adapter()
|
|
267
|
+
FLOAT64_ADAPTER: Final[TypeAdapter[float]] = _Float64Adapter()
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _clamp_unix_millis(unix_millis: int) -> int:
|
|
271
|
+
"""Clamp unix milliseconds to valid range for JavaScript dates."""
|
|
272
|
+
return max(-8640000000000000, min(unix_millis, 8640000000000000))
|
|
208
273
|
|
|
209
274
|
|
|
210
|
-
class _TimestampAdapter(AbstractPrimitiveAdapter):
|
|
275
|
+
class _TimestampAdapter(AbstractPrimitiveAdapter[Timestamp]):
|
|
211
276
|
def default_expr(self) -> Expr:
|
|
212
277
|
return Expr.local("_EPOCH", Timestamp.EPOCH)
|
|
213
278
|
|
|
@@ -236,10 +301,34 @@ class _TimestampAdapter(AbstractPrimitiveAdapter):
|
|
|
236
301
|
else:
|
|
237
302
|
return Expr.join(in_expr, ".unix_millis")
|
|
238
303
|
|
|
239
|
-
def from_json_expr(
|
|
304
|
+
def from_json_expr(
|
|
305
|
+
self, json_expr: ExprLike, keep_unrecognized_expr: ExprLike
|
|
306
|
+
) -> Expr:
|
|
240
307
|
fn = Expr.local("_timestamp_from_json", _timestamp_from_json)
|
|
241
308
|
return Expr.join(fn, "(", json_expr, ")")
|
|
242
309
|
|
|
310
|
+
@staticmethod
|
|
311
|
+
def encode(
|
|
312
|
+
value: Timestamp,
|
|
313
|
+
buffer: bytearray,
|
|
314
|
+
) -> None:
|
|
315
|
+
unix_millis = _clamp_unix_millis(value.unix_millis)
|
|
316
|
+
if unix_millis == 0:
|
|
317
|
+
buffer.append(0)
|
|
318
|
+
else:
|
|
319
|
+
buffer.append(239)
|
|
320
|
+
buffer.extend(struct.pack("<q", unix_millis))
|
|
321
|
+
|
|
322
|
+
def encode_fn(self) -> Callable[[Timestamp, bytearray], None]:
|
|
323
|
+
return _TimestampAdapter.encode
|
|
324
|
+
|
|
325
|
+
@staticmethod
|
|
326
|
+
def decode(stream: ByteStream) -> Timestamp:
|
|
327
|
+
return Timestamp(unix_millis=_clamp_unix_millis(decode_int64(stream)))
|
|
328
|
+
|
|
329
|
+
def decode_fn(self) -> Callable[[ByteStream], Timestamp]:
|
|
330
|
+
return _TimestampAdapter.decode
|
|
331
|
+
|
|
243
332
|
def get_type(self) -> reflection.Type:
|
|
244
333
|
return reflection.PrimitiveType(
|
|
245
334
|
kind="primitive",
|
|
@@ -254,10 +343,10 @@ def _timestamp_from_json(json: Any) -> Timestamp:
|
|
|
254
343
|
return Timestamp(unix_millis=json["unix_millis"])
|
|
255
344
|
|
|
256
345
|
|
|
257
|
-
TIMESTAMP_ADAPTER: Final[TypeAdapter] = _TimestampAdapter()
|
|
346
|
+
TIMESTAMP_ADAPTER: Final[TypeAdapter[Timestamp]] = _TimestampAdapter()
|
|
258
347
|
|
|
259
348
|
|
|
260
|
-
class _StringAdapter(AbstractPrimitiveAdapter):
|
|
349
|
+
class _StringAdapter(AbstractPrimitiveAdapter[str]):
|
|
261
350
|
def default_expr(self) -> ExprLike:
|
|
262
351
|
return '""'
|
|
263
352
|
|
|
@@ -270,9 +359,42 @@ class _StringAdapter(AbstractPrimitiveAdapter):
|
|
|
270
359
|
def to_json_expr(self, in_expr: ExprLike, readable: bool) -> ExprLike:
|
|
271
360
|
return in_expr
|
|
272
361
|
|
|
273
|
-
def from_json_expr(
|
|
362
|
+
def from_json_expr(
|
|
363
|
+
self, json_expr: ExprLike, keep_unrecognized_expr: ExprLike
|
|
364
|
+
) -> Expr:
|
|
274
365
|
return Expr.join("('' + (", json_expr, " or ''))")
|
|
275
366
|
|
|
367
|
+
@staticmethod
|
|
368
|
+
def encode(
|
|
369
|
+
value: str,
|
|
370
|
+
buffer: bytearray,
|
|
371
|
+
) -> None:
|
|
372
|
+
if not value:
|
|
373
|
+
buffer.append(242)
|
|
374
|
+
else:
|
|
375
|
+
buffer.append(243)
|
|
376
|
+
bytes_data = value.encode("utf-8")
|
|
377
|
+
length = len(bytes_data)
|
|
378
|
+
encode_length_prefix(length, buffer)
|
|
379
|
+
buffer.extend(bytes_data)
|
|
380
|
+
|
|
381
|
+
def encode_fn(self) -> Callable[[str, bytearray], None]:
|
|
382
|
+
return _StringAdapter.encode
|
|
383
|
+
|
|
384
|
+
@staticmethod
|
|
385
|
+
def decode(stream: ByteStream) -> str:
|
|
386
|
+
wire = stream.read(1)[0]
|
|
387
|
+
if wire == 242:
|
|
388
|
+
return ""
|
|
389
|
+
else:
|
|
390
|
+
# Should be wire 243
|
|
391
|
+
length = decode_int64(stream)
|
|
392
|
+
bytes_data = stream.read(length)
|
|
393
|
+
return bytes_data.decode("utf-8")
|
|
394
|
+
|
|
395
|
+
def decode_fn(self) -> Callable[[ByteStream], str]:
|
|
396
|
+
return _StringAdapter.decode
|
|
397
|
+
|
|
276
398
|
def get_type(self) -> reflection.Type:
|
|
277
399
|
return reflection.PrimitiveType(
|
|
278
400
|
kind="primitive",
|
|
@@ -283,7 +405,7 @@ class _StringAdapter(AbstractPrimitiveAdapter):
|
|
|
283
405
|
STRING_ADAPTER: Final[TypeAdapter] = _StringAdapter()
|
|
284
406
|
|
|
285
407
|
|
|
286
|
-
class _BytesAdapter(AbstractPrimitiveAdapter):
|
|
408
|
+
class _BytesAdapter(AbstractPrimitiveAdapter[bytes]):
|
|
287
409
|
def default_expr(self) -> ExprLike:
|
|
288
410
|
return 'b""'
|
|
289
411
|
|
|
@@ -305,11 +427,42 @@ class _BytesAdapter(AbstractPrimitiveAdapter):
|
|
|
305
427
|
").decode('utf-8')",
|
|
306
428
|
)
|
|
307
429
|
|
|
308
|
-
def from_json_expr(
|
|
430
|
+
def from_json_expr(
|
|
431
|
+
self, json_expr: ExprLike, keep_unrecognized_expr: ExprLike
|
|
432
|
+
) -> Expr:
|
|
309
433
|
return Expr.join(
|
|
310
434
|
Expr.local("b64decode", base64.b64decode), "(", json_expr, ' or "")'
|
|
311
435
|
)
|
|
312
436
|
|
|
437
|
+
@staticmethod
|
|
438
|
+
def encode(
|
|
439
|
+
value: bytes,
|
|
440
|
+
buffer: bytearray,
|
|
441
|
+
) -> None:
|
|
442
|
+
if len(value) == 0:
|
|
443
|
+
buffer.append(244)
|
|
444
|
+
else:
|
|
445
|
+
buffer.append(245)
|
|
446
|
+
length = len(value)
|
|
447
|
+
encode_length_prefix(length, buffer)
|
|
448
|
+
buffer.extend(value)
|
|
449
|
+
|
|
450
|
+
def encode_fn(self) -> Callable[[bytes, bytearray], None]:
|
|
451
|
+
return _BytesAdapter.encode
|
|
452
|
+
|
|
453
|
+
@staticmethod
|
|
454
|
+
def decode(stream: ByteStream) -> bytes:
|
|
455
|
+
wire = stream.read_wire()
|
|
456
|
+
if wire in (0, 244):
|
|
457
|
+
return b""
|
|
458
|
+
else:
|
|
459
|
+
# Should be wire 245
|
|
460
|
+
length = decode_int64(stream)
|
|
461
|
+
return stream.read(length)
|
|
462
|
+
|
|
463
|
+
def decode_fn(self) -> Callable[[ByteStream], bytes]:
|
|
464
|
+
return _BytesAdapter.decode
|
|
465
|
+
|
|
313
466
|
def get_type(self) -> reflection.Type:
|
|
314
467
|
return reflection.PrimitiveType(
|
|
315
468
|
kind="primitive",
|
|
@@ -317,4 +470,4 @@ class _BytesAdapter(AbstractPrimitiveAdapter):
|
|
|
317
470
|
)
|
|
318
471
|
|
|
319
472
|
|
|
320
|
-
BYTES_ADAPTER: Final[TypeAdapter] = _BytesAdapter()
|
|
473
|
+
BYTES_ADAPTER: Final[TypeAdapter[bytes]] = _BytesAdapter()
|
soia/_impl/serializer.py
CHANGED
|
@@ -8,7 +8,7 @@ from weakref import WeakValueDictionary
|
|
|
8
8
|
from soia import reflection
|
|
9
9
|
from soia._impl.function_maker import Expr, LineSpan, make_function
|
|
10
10
|
from soia._impl.never import Never
|
|
11
|
-
from soia._impl.type_adapter import TypeAdapter
|
|
11
|
+
from soia._impl.type_adapter import ByteStream, TypeAdapter
|
|
12
12
|
|
|
13
13
|
T = TypeVar("T")
|
|
14
14
|
|
|
@@ -21,26 +21,33 @@ class Serializer(Generic[T]):
|
|
|
21
21
|
"_to_dense_json_fn",
|
|
22
22
|
"_to_readable_json_fn",
|
|
23
23
|
"_from_json_fn",
|
|
24
|
+
"_encode_fn",
|
|
25
|
+
"_decode_fn",
|
|
24
26
|
"__dict__",
|
|
25
27
|
)
|
|
26
28
|
|
|
27
|
-
_adapter: TypeAdapter
|
|
29
|
+
_adapter: TypeAdapter[T]
|
|
28
30
|
_to_dense_json_fn: Callable[[T], Any]
|
|
29
31
|
_to_readable_json_fn: Callable[[T], Any]
|
|
30
|
-
_from_json_fn: Callable[[Any], T]
|
|
32
|
+
_from_json_fn: Callable[[Any, bool], T]
|
|
33
|
+
_encode_fn: Callable[[T, bytearray], None]
|
|
34
|
+
_decode_fn: Callable[[ByteStream], T]
|
|
31
35
|
|
|
32
36
|
def __init__(self, adapter: Never):
|
|
33
37
|
# Use Never (^) as a trick to make the constructor internal.
|
|
34
|
-
|
|
38
|
+
as_adapter = cast(TypeAdapter[T], adapter)
|
|
39
|
+
object.__setattr__(self, "_adapter", as_adapter)
|
|
35
40
|
object.__setattr__(
|
|
36
|
-
self, "_to_dense_json_fn", _make_to_json_fn(
|
|
41
|
+
self, "_to_dense_json_fn", _make_to_json_fn(as_adapter, readable=False)
|
|
37
42
|
)
|
|
38
43
|
object.__setattr__(
|
|
39
44
|
self,
|
|
40
45
|
"_to_readable_json_fn",
|
|
41
|
-
_make_to_json_fn(
|
|
46
|
+
_make_to_json_fn(as_adapter, readable=True),
|
|
42
47
|
)
|
|
43
|
-
object.__setattr__(self, "_from_json_fn", _make_from_json_fn(
|
|
48
|
+
object.__setattr__(self, "_from_json_fn", _make_from_json_fn(as_adapter))
|
|
49
|
+
object.__setattr__(self, "_encode_fn", as_adapter.encode_fn())
|
|
50
|
+
object.__setattr__(self, "_decode_fn", as_adapter.decode_fn())
|
|
44
51
|
|
|
45
52
|
def to_json(self, input: T, *, readable=False) -> Any:
|
|
46
53
|
if readable:
|
|
@@ -54,11 +61,26 @@ class Serializer(Generic[T]):
|
|
|
54
61
|
else:
|
|
55
62
|
return jsonlib.dumps(self._to_dense_json_fn(input), separators=(",", ":"))
|
|
56
63
|
|
|
57
|
-
def from_json(self, json: Any) -> T:
|
|
58
|
-
return self._from_json_fn(json)
|
|
64
|
+
def from_json(self, json: Any, keep_unrecognized_fields: bool = False) -> T:
|
|
65
|
+
return self._from_json_fn(json, keep_unrecognized_fields)
|
|
59
66
|
|
|
60
|
-
def from_json_code(
|
|
61
|
-
|
|
67
|
+
def from_json_code(
|
|
68
|
+
self, json_code: str, keep_unrecognized_fields: bool = False
|
|
69
|
+
) -> T:
|
|
70
|
+
return self._from_json_fn(jsonlib.loads(json_code), keep_unrecognized_fields)
|
|
71
|
+
|
|
72
|
+
def to_bytes(self, input: T) -> bytes:
|
|
73
|
+
buffer = bytearray(b"soia")
|
|
74
|
+
self._encode_fn(input, buffer)
|
|
75
|
+
return bytes(buffer)
|
|
76
|
+
|
|
77
|
+
def from_bytes(self, bytes: bytes, keep_unrecognized_fields: bool = False) -> T:
|
|
78
|
+
if bytes.startswith(b"soia"):
|
|
79
|
+
stream = ByteStream(
|
|
80
|
+
bytes, position=4, keep_unrecognized_fields=keep_unrecognized_fields
|
|
81
|
+
)
|
|
82
|
+
return self._decode_fn(stream)
|
|
83
|
+
return self.from_json_code(bytes.decode("utf-8"))
|
|
62
84
|
|
|
63
85
|
@cached_property
|
|
64
86
|
def type_descriptor(self) -> reflection.TypeDescriptor:
|
|
@@ -82,13 +104,13 @@ _type_adapter_to_serializer: WeakValueDictionary[TypeAdapter, Serializer] = (
|
|
|
82
104
|
)
|
|
83
105
|
|
|
84
106
|
|
|
85
|
-
def make_serializer(adapter: TypeAdapter) -> Serializer:
|
|
107
|
+
def make_serializer(adapter: TypeAdapter[T]) -> Serializer[T]:
|
|
86
108
|
return _type_adapter_to_serializer.setdefault(
|
|
87
109
|
adapter, Serializer(cast(Never, adapter))
|
|
88
110
|
)
|
|
89
111
|
|
|
90
112
|
|
|
91
|
-
def _make_to_json_fn(adapter: TypeAdapter, readable: bool) -> Callable[[
|
|
113
|
+
def _make_to_json_fn(adapter: TypeAdapter[T], readable: bool) -> Callable[[T], Any]:
|
|
92
114
|
return make_function(
|
|
93
115
|
name="to_json",
|
|
94
116
|
params=["input"],
|
|
@@ -104,11 +126,13 @@ def _make_to_json_fn(adapter: TypeAdapter, readable: bool) -> Callable[[Any], An
|
|
|
104
126
|
)
|
|
105
127
|
|
|
106
128
|
|
|
107
|
-
def _make_from_json_fn(adapter: TypeAdapter) -> Callable[[Any],
|
|
129
|
+
def _make_from_json_fn(adapter: TypeAdapter[T]) -> Callable[[Any, bool], T]:
|
|
108
130
|
return make_function(
|
|
109
131
|
name="from_json",
|
|
110
|
-
params=["json"],
|
|
132
|
+
params=["json", "keep_unrecognized_fields"],
|
|
111
133
|
body=[
|
|
112
|
-
LineSpan.join(
|
|
134
|
+
LineSpan.join(
|
|
135
|
+
"return ", adapter.from_json_expr("json", "keep_unrecognized_fields")
|
|
136
|
+
),
|
|
113
137
|
],
|
|
114
138
|
)
|
soia/_impl/service.py
CHANGED
|
@@ -130,9 +130,7 @@ class _HandleRequestFlow(Generic[Request, Response, RequestHeaders, ResponseHead
|
|
|
130
130
|
try:
|
|
131
131
|
req_body_json = json.loads(self.req_body)
|
|
132
132
|
except json.JSONDecodeError:
|
|
133
|
-
return RawServiceResponse(
|
|
134
|
-
"bad request: invalid JSON", "bad-request"
|
|
135
|
-
)
|
|
133
|
+
return RawServiceResponse("bad request: invalid JSON", "bad-request")
|
|
136
134
|
method = req_body_json.get("method", ())
|
|
137
135
|
if method == ():
|
|
138
136
|
return RawServiceResponse(
|