soia-client 1.0.7__py3-none-any.whl → 1.0.9__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_client-1.0.7.dist-info → soia_client-1.0.9.dist-info}/METADATA +3 -3
- soia_client-1.0.9.dist-info/RECORD +23 -0
- {soia_client-1.0.7.dist-info → soia_client-1.0.9.dist-info}/WHEEL +1 -1
- soialib/impl/arrays.py +5 -8
- soialib/impl/enums.py +80 -37
- soialib/impl/function_maker.py +7 -11
- soialib/impl/optionals.py +0 -1
- soialib/impl/primitives.py +52 -11
- soialib/impl/repr.py +0 -1
- soialib/impl/structs.py +74 -36
- soialib/impl/type_adapter.py +0 -1
- soialib/module_initializer.py +0 -1
- soialib/serializer.py +7 -4
- soialib/serializers.py +3 -1
- soialib/timestamp.py +3 -2
- soia_client-1.0.7.dist-info/RECORD +0 -24
- soialib/impl/encoding.py +0 -41
- {soia_client-1.0.7.dist-info → soia_client-1.0.9.dist-info/licenses}/LICENSE +0 -0
- {soia_client-1.0.7.dist-info → soia_client-1.0.9.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: soia-client
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Author-email: Tyler Fibonacci <gepheum@gmail.com>
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -31,4 +31,4 @@ Classifier: Programming Language :: Python :: 3
|
|
|
31
31
|
Requires-Python: >=3.10
|
|
32
32
|
Description-Content-Type: text/markdown
|
|
33
33
|
License-File: LICENSE
|
|
34
|
-
|
|
34
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
soia_client-1.0.9.dist-info/licenses/LICENSE,sha256=SaAftKkX6hfSOiPdENQPS70tifH3PDHgazq8eK2Pwfw,1064
|
|
2
|
+
soialib/__init__.py,sha256=h_ENkLJEdrh1mGhCoKgIadDasSulW3zK9_qhEsTviXY,429
|
|
3
|
+
soialib/keyed_items.py,sha256=q7MCn82obf-0jh7FcAhuw4eh9-wtuHIpkEFcSfc8EaY,338
|
|
4
|
+
soialib/method.py,sha256=2qWG4jMqYhS3hA8y8YDu3iqzhXA_AKebpB38RWNmsYQ,452
|
|
5
|
+
soialib/module_initializer.py,sha256=Riq6B6cS9HUG1W8tyaa0GkiG3F4fGX70bKU0UOCnrRw,4205
|
|
6
|
+
soialib/never.py,sha256=bYU63XyNX4e2wOUXQHhHWGO-an4IFr9_ur1ut6GmGN0,47
|
|
7
|
+
soialib/serializer.py,sha256=jSDanuJ4TEPWEO0ssYSKy2CDLUPV0zwCUzNGHk6uFRQ,3122
|
|
8
|
+
soialib/serializers.py,sha256=vNLNsfuu6dF0ivJF6v7wQj5Yr6uo2kZHXG5tMrYiVTA,2564
|
|
9
|
+
soialib/spec.py,sha256=w-eMMUqOchOlCJaXppyAa2JpSBrMo9lrMvqz8VaEX4I,3699
|
|
10
|
+
soialib/timestamp.py,sha256=lXBNH8mPmzflkNjSKZSBl2XS-ot9N8N92B_zGO2SMtU,4078
|
|
11
|
+
soialib/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
soialib/impl/arrays.py,sha256=4p-4FoM_hrQfwSIF1KmYGjJyP6SGyV7rD_L7D46d9Z4,4863
|
|
13
|
+
soialib/impl/enums.py,sha256=kSH0Puub0pZJ4hAVw52y0ktDX1IkHpPan3IDp6OHIIs,15990
|
|
14
|
+
soialib/impl/function_maker.py,sha256=MvCDv1WwzKGJzZbNCnJ_8-MP3m1xTIabumXA-9Ydd9M,5639
|
|
15
|
+
soialib/impl/optionals.py,sha256=pWuhfTIYM7669Rko-oVlBhHLqO3vgASW7fL0Yos3AWM,2076
|
|
16
|
+
soialib/impl/primitives.py,sha256=AHMVvs3BWvPfggsdFnY5dwwqAwiNVFYJa_rJ-WjMJfk,7424
|
|
17
|
+
soialib/impl/repr.py,sha256=7WX0bEAVENTjlyZIcbT8TcJylS7IRIyafGCmqaIMxFM,1413
|
|
18
|
+
soialib/impl/structs.py,sha256=GKTw6p_HasS9E520l74QFldIjUhjxM8sm4poPnR7fFg,25539
|
|
19
|
+
soialib/impl/type_adapter.py,sha256=e72nBDqOP0uWNY10EtG7qOvRTzujA-LUVA30Ff7eeac,1935
|
|
20
|
+
soia_client-1.0.9.dist-info/METADATA,sha256=ACwdLcQ3vRHTS1sWzLRk1A14mQxlhEP7WX0KtQU_8FY,1666
|
|
21
|
+
soia_client-1.0.9.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
22
|
+
soia_client-1.0.9.dist-info/top_level.txt,sha256=2vPmAo5G0SrCxYrNdJKJJVdpalYppgjO2mmz2PtsFUI,8
|
|
23
|
+
soia_client-1.0.9.dist-info/RECORD,,
|
soialib/impl/arrays.py
CHANGED
|
@@ -73,16 +73,13 @@ class _ArrayAdapter(TypeAdapter):
|
|
|
73
73
|
return attr_expr
|
|
74
74
|
|
|
75
75
|
def to_json_expr(self, in_expr: ExprLike, readable: bool) -> ExprLike:
|
|
76
|
-
|
|
77
|
-
item_to_json
|
|
78
|
-
if item_to_json == e:
|
|
76
|
+
item_to_json = self.item_adapter.to_json_expr("_e", readable)
|
|
77
|
+
if Expr.join(item_to_json) == Expr.join("_e"):
|
|
79
78
|
return in_expr
|
|
80
79
|
return Expr.join(
|
|
81
80
|
"[",
|
|
82
81
|
item_to_json,
|
|
83
|
-
" for ",
|
|
84
|
-
e,
|
|
85
|
-
" in ",
|
|
82
|
+
" for _e in ",
|
|
86
83
|
in_expr,
|
|
87
84
|
"]",
|
|
88
85
|
)
|
|
@@ -93,8 +90,8 @@ class _ArrayAdapter(TypeAdapter):
|
|
|
93
90
|
return Expr.join(
|
|
94
91
|
listuple_class_local,
|
|
95
92
|
"([",
|
|
96
|
-
self.item_adapter.from_json_expr(
|
|
97
|
-
" for
|
|
93
|
+
self.item_adapter.from_json_expr("_e"),
|
|
94
|
+
" for _e in ",
|
|
98
95
|
json_expr,
|
|
99
96
|
"] or ",
|
|
100
97
|
empty_listuple_local,
|
soialib/impl/enums.py
CHANGED
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
# TODO: unrecognized fields (and handles removed fields)
|
|
3
|
-
|
|
1
|
+
import copy
|
|
4
2
|
from collections.abc import Callable, Sequence
|
|
5
3
|
from dataclasses import FrozenInstanceError, dataclass
|
|
6
4
|
from typing import Any, Final, Union
|
|
7
5
|
|
|
8
6
|
from soialib import spec as _spec
|
|
9
|
-
from soialib.impl import
|
|
10
|
-
from soialib.impl.function_maker import (
|
|
11
|
-
BodyBuilder,
|
|
12
|
-
BodySpan,
|
|
13
|
-
Expr,
|
|
14
|
-
ExprLike,
|
|
15
|
-
Line,
|
|
16
|
-
make_function,
|
|
17
|
-
)
|
|
7
|
+
from soialib.impl.function_maker import BodyBuilder, Expr, ExprLike, Line, make_function
|
|
18
8
|
from soialib.impl.repr import repr_impl
|
|
19
9
|
from soialib.impl.type_adapter import TypeAdapter
|
|
20
10
|
|
|
@@ -40,7 +30,6 @@ class EnumAdapter(TypeAdapter):
|
|
|
40
30
|
|
|
41
31
|
private_is_enum_attr = _name_private_is_enum_attr(spec.id)
|
|
42
32
|
self.private_is_enum_attr = private_is_enum_attr
|
|
43
|
-
# TODO: comment
|
|
44
33
|
setattr(base_class, private_is_enum_attr, True)
|
|
45
34
|
|
|
46
35
|
# Add the constants.
|
|
@@ -81,7 +70,10 @@ class EnumAdapter(TypeAdapter):
|
|
|
81
70
|
setattr(base_class, f"wrap_{value_field.spec.name}", wrap_fn)
|
|
82
71
|
|
|
83
72
|
base_class._fj = _make_from_json_fn(
|
|
84
|
-
self.all_constant_fields,
|
|
73
|
+
self.all_constant_fields,
|
|
74
|
+
value_fields,
|
|
75
|
+
set(self.spec.removed_numbers),
|
|
76
|
+
base_class,
|
|
85
77
|
)
|
|
86
78
|
|
|
87
79
|
# Mark finalization as done.
|
|
@@ -116,7 +108,6 @@ class EnumAdapter(TypeAdapter):
|
|
|
116
108
|
|
|
117
109
|
def from_json_expr(self, json_expr: ExprLike) -> Expr:
|
|
118
110
|
fn_name = "_fj"
|
|
119
|
-
# TODO: comment
|
|
120
111
|
from_json_fn = getattr(self.gen_class, fn_name, None)
|
|
121
112
|
if from_json_fn:
|
|
122
113
|
return Expr.join(Expr.local("_fj?", from_json_fn), "(", json_expr, ")")
|
|
@@ -149,7 +140,6 @@ def _make_base_class(spec: _spec.Enum) -> type:
|
|
|
149
140
|
raise FrozenInstanceError(self.__class__.__qualname__)
|
|
150
141
|
|
|
151
142
|
def __eq__(self, other: Any) -> bool:
|
|
152
|
-
# TODO: make it work with unrecognized versus UNKNOWN
|
|
153
143
|
if isinstance(other, BaseClass):
|
|
154
144
|
return other.kind == self.kind and other.value == self.value
|
|
155
145
|
return NotImplemented
|
|
@@ -164,7 +154,7 @@ def _make_base_class(spec: _spec.Enum) -> type:
|
|
|
164
154
|
|
|
165
155
|
|
|
166
156
|
def _make_constant_class(base_class: type, spec: _spec.ConstantField) -> type:
|
|
167
|
-
class
|
|
157
|
+
class Constant(base_class):
|
|
168
158
|
__slots__ = ()
|
|
169
159
|
|
|
170
160
|
kind: Final[str] = spec.name
|
|
@@ -183,7 +173,37 @@ def _make_constant_class(base_class: type, spec: _spec.ConstantField) -> type:
|
|
|
183
173
|
def __repr__(self) -> str:
|
|
184
174
|
return f"{base_class.__qualname__}.{spec.attribute}"
|
|
185
175
|
|
|
186
|
-
return
|
|
176
|
+
return Constant
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _make_unrecognized_class(base_class: type) -> type:
|
|
180
|
+
"""Wraps around an unrecognized dense JSON.
|
|
181
|
+
|
|
182
|
+
Looks and acts just like the UNKNOWN constant, except that its JSON representation
|
|
183
|
+
is the original unrecognized dense JSON.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
class Unrecognized(base_class):
|
|
187
|
+
__slots__ = ("_dj",)
|
|
188
|
+
|
|
189
|
+
kind: Final[str] = "?"
|
|
190
|
+
_number: Final[int] = 0
|
|
191
|
+
# dense JSON
|
|
192
|
+
_dj: Any
|
|
193
|
+
# readable JSON
|
|
194
|
+
_rj: Final[str] = "?"
|
|
195
|
+
# has value
|
|
196
|
+
_hv: Final[bool] = False
|
|
197
|
+
|
|
198
|
+
def __init__(self, dj: Any):
|
|
199
|
+
# Do not call super().__init__().
|
|
200
|
+
object.__setattr__(self, "_dj", copy.deepcopy(dj))
|
|
201
|
+
object.__setattr__(self, "value", None)
|
|
202
|
+
|
|
203
|
+
def __repr__(self) -> str:
|
|
204
|
+
return f"{base_class.__qualname__}.UNKNOWN"
|
|
205
|
+
|
|
206
|
+
return Unrecognized
|
|
187
207
|
|
|
188
208
|
|
|
189
209
|
def _make_value_class(
|
|
@@ -193,7 +213,7 @@ def _make_value_class(
|
|
|
193
213
|
) -> type:
|
|
194
214
|
number = field_spec.number
|
|
195
215
|
|
|
196
|
-
class
|
|
216
|
+
class Value(base_class):
|
|
197
217
|
__slots__ = ()
|
|
198
218
|
|
|
199
219
|
kind: Final[str] = field_spec.name
|
|
@@ -213,7 +233,7 @@ def _make_value_class(
|
|
|
213
233
|
body = value_repr.repr
|
|
214
234
|
return f"{base_class.__qualname__}.wrap_{field_spec.name}({body})"
|
|
215
235
|
|
|
216
|
-
ret =
|
|
236
|
+
ret = Value
|
|
217
237
|
|
|
218
238
|
ret._dj = property(
|
|
219
239
|
make_function(
|
|
@@ -272,7 +292,7 @@ def _make_wrap_fn(field: _ValueField) -> Callable[[Any], Any]:
|
|
|
272
292
|
builder.append_ln(
|
|
273
293
|
Expr.local("setattr", object.__setattr__),
|
|
274
294
|
"(ret, 'value', ",
|
|
275
|
-
field.field_type.to_frozen_expr(
|
|
295
|
+
field.field_type.to_frozen_expr("value"),
|
|
276
296
|
")",
|
|
277
297
|
)
|
|
278
298
|
builder.append_ln("return ret")
|
|
@@ -286,9 +306,13 @@ def _make_wrap_fn(field: _ValueField) -> Callable[[Any], Any]:
|
|
|
286
306
|
def _make_from_json_fn(
|
|
287
307
|
constant_fields: Sequence[_spec.ConstantField],
|
|
288
308
|
value_fields: Sequence[_ValueField],
|
|
309
|
+
removed_numbers: set[int],
|
|
289
310
|
base_class: type,
|
|
290
311
|
) -> Callable[[Any], Any]:
|
|
312
|
+
unrecognized_class = _make_unrecognized_class(base_class)
|
|
313
|
+
unrecognized_class_local = Expr.local("Unrecognized", unrecognized_class)
|
|
291
314
|
obj_setattr_local = Expr.local("obj_settatr", object.__setattr__)
|
|
315
|
+
removed_numbers_local = Expr.local("removed_numbers", removed_numbers)
|
|
292
316
|
|
|
293
317
|
key_to_constant: dict[Union[int, str], Any] = {}
|
|
294
318
|
for field in constant_fields:
|
|
@@ -318,21 +342,26 @@ def _make_from_json_fn(
|
|
|
318
342
|
builder.append_ln(" if json == 0:")
|
|
319
343
|
builder.append_ln(" return ", unknown_constant_local)
|
|
320
344
|
else:
|
|
345
|
+
# `json.__class__ is int` is significantly faster than `isinstance(json, int)`
|
|
321
346
|
builder.append_ln(" if json.__class__ is int:")
|
|
322
347
|
builder.append_ln(" try:")
|
|
323
348
|
builder.append_ln(" return ", key_to_constant_local, "[json]")
|
|
324
349
|
builder.append_ln(" except:")
|
|
325
|
-
|
|
326
|
-
|
|
350
|
+
if removed_numbers:
|
|
351
|
+
builder.append_ln(" if json in ", removed_numbers_local, ":")
|
|
352
|
+
builder.append_ln(" return ", unknown_constant_local)
|
|
353
|
+
builder.append_ln(" return ", unrecognized_class_local, "(json)")
|
|
327
354
|
|
|
328
355
|
def append_number_branches(numbers: list[int], indent: str) -> None:
|
|
329
356
|
if len(numbers) == 1:
|
|
330
357
|
number = numbers[0]
|
|
331
358
|
field = key_to_field[number]
|
|
332
359
|
value_class_local = Expr.local("cls?", field.value_class)
|
|
333
|
-
value_expr = field.field_type.from_json_expr(
|
|
360
|
+
value_expr = field.field_type.from_json_expr("json[1]")
|
|
334
361
|
builder.append_ln(f"{indent}ret = ", value_class_local, "()")
|
|
335
|
-
builder.append_ln(
|
|
362
|
+
builder.append_ln(
|
|
363
|
+
indent, obj_setattr_local, '(ret, "value", ', value_expr, ")"
|
|
364
|
+
)
|
|
336
365
|
builder.append_ln(f"{indent}return ret")
|
|
337
366
|
else:
|
|
338
367
|
indented = f" {indent}"
|
|
@@ -344,18 +373,24 @@ def _make_from_json_fn(
|
|
|
344
373
|
builder.append_ln(f"{indent}else:")
|
|
345
374
|
append_number_branches(numbers[mid_index:], indented)
|
|
346
375
|
|
|
376
|
+
# `json.__class__ is list` is significantly faster than `isinstance(json, list)`
|
|
347
377
|
builder.append_ln(" elif json.__class__ is list:")
|
|
378
|
+
builder.append_ln(" number = json[0]")
|
|
348
379
|
if not value_fields:
|
|
349
|
-
#
|
|
350
|
-
|
|
380
|
+
# The field was either removed or is an unrecognized field.
|
|
381
|
+
if removed_numbers:
|
|
382
|
+
builder.append_ln(" if number in ", removed_numbers_local, ":")
|
|
383
|
+
builder.append_ln(" return ", unknown_constant_local)
|
|
384
|
+
builder.append_ln(" return ", unrecognized_class_local, "(json)")
|
|
351
385
|
else:
|
|
352
386
|
if len(value_fields) == 1:
|
|
353
387
|
builder.append_ln(f" if number != {value_fields[0].spec.number}:")
|
|
354
388
|
else:
|
|
355
389
|
builder.append_ln(" if number not in ", value_keys_local, ":")
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
390
|
+
if removed_numbers:
|
|
391
|
+
builder.append_ln(" if number in ", removed_numbers_local, ":")
|
|
392
|
+
builder.append_ln(" return ", unknown_constant_local)
|
|
393
|
+
builder.append_ln(" return ", unrecognized_class_local, "(json)")
|
|
359
394
|
append_number_branches(sorted(numbers), " ")
|
|
360
395
|
|
|
361
396
|
# READABLE FORMAT
|
|
@@ -367,7 +402,7 @@ def _make_from_json_fn(
|
|
|
367
402
|
builder.append_ln(" try:")
|
|
368
403
|
builder.append_ln(" return ", key_to_constant_local, "[json]")
|
|
369
404
|
builder.append_ln(" except:")
|
|
370
|
-
#
|
|
405
|
+
# In readable mode, drop unrecognized values and use UNKNOWN instead.
|
|
371
406
|
builder.append_ln(" return ", unknown_constant_local)
|
|
372
407
|
|
|
373
408
|
def append_name_branches(names: list[str], indent: str) -> None:
|
|
@@ -377,30 +412,38 @@ def _make_from_json_fn(
|
|
|
377
412
|
value_class_local = Expr.local("cls?", field.value_class)
|
|
378
413
|
value_expr = field.field_type.from_json_expr("json['value']")
|
|
379
414
|
builder.append_ln(f"{indent}ret = ", value_class_local, "()")
|
|
380
|
-
builder.append_ln(
|
|
415
|
+
builder.append_ln(
|
|
416
|
+
indent, obj_setattr_local, '(ret, "value", ', value_expr, ")"
|
|
417
|
+
)
|
|
381
418
|
builder.append_ln(f"{indent}return ret")
|
|
382
419
|
else:
|
|
383
420
|
indented = f" {indent}"
|
|
384
421
|
mid_index = int(len(names) / 2)
|
|
385
422
|
mid_name = names[mid_index - 1]
|
|
386
423
|
operator = "==" if mid_index == 1 else "<="
|
|
387
|
-
builder.append_ln(f"{indent}if
|
|
424
|
+
builder.append_ln(f"{indent}if kind {operator} '{mid_name}':")
|
|
388
425
|
append_name_branches(names[0:mid_index], indented)
|
|
389
426
|
builder.append_ln(f"{indent}else:")
|
|
390
427
|
append_name_branches(names[mid_index:], indented)
|
|
391
428
|
|
|
392
429
|
builder.append_ln(" elif isinstance(json, dict):")
|
|
393
430
|
if not value_fields:
|
|
394
|
-
# TODO: comment
|
|
395
431
|
builder.append_ln(" return ", unknown_constant_local)
|
|
396
432
|
else:
|
|
397
|
-
builder.append_ln("
|
|
398
|
-
builder.append_ln(" if
|
|
399
|
-
# TODO: comment
|
|
433
|
+
builder.append_ln(" kind = json['kind']")
|
|
434
|
+
builder.append_ln(" if kind not in ", value_keys_local, ":")
|
|
400
435
|
builder.append_ln(" return ", unknown_constant_local)
|
|
401
436
|
builder.append_ln(" else:")
|
|
402
437
|
append_name_branches(sorted(names), " ")
|
|
403
438
|
|
|
439
|
+
# In the unlikely event that json.loads() returns an instance of a subclass of int.
|
|
440
|
+
builder.append_ln(" elif isinstance(json, int):")
|
|
441
|
+
builder.append_ln(" json = int(json)")
|
|
442
|
+
builder.append_ln(" elif isinstance(json, list):")
|
|
443
|
+
builder.append_ln(" json = list(json)")
|
|
444
|
+
builder.append_ln(" else:")
|
|
445
|
+
builder.append_ln(" return TypeError()")
|
|
446
|
+
|
|
404
447
|
return make_function(
|
|
405
448
|
name="from_json",
|
|
406
449
|
params=["json"],
|
soialib/impl/function_maker.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import itertools
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Any, Callable,
|
|
3
|
+
from typing import Any, Callable, Iterable, Optional, Sequence, Union
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def make_function(
|
|
@@ -49,12 +49,12 @@ def make_function(
|
|
|
49
49
|
|
|
50
50
|
locals = make_locals()
|
|
51
51
|
|
|
52
|
-
def line_to_code(
|
|
53
|
-
if isinstance(
|
|
54
|
-
return
|
|
55
|
-
return
|
|
52
|
+
def line_to_code(ln: Union[str, Line]) -> str:
|
|
53
|
+
if isinstance(ln, str):
|
|
54
|
+
return ln
|
|
55
|
+
return ln._to_code(locals)
|
|
56
56
|
|
|
57
|
-
body_str = "\n ".join(line_to_code(
|
|
57
|
+
body_str = "\n ".join(line_to_code(ln) for ln in body) if body else "pass"
|
|
58
58
|
text = f"""
|
|
59
59
|
def __create_function__({', '.join(locals.locals.keys())}):
|
|
60
60
|
def {name}({', '.join(p._to_code(locals) for p in params)}):
|
|
@@ -120,15 +120,11 @@ class LineSpan:
|
|
|
120
120
|
_EMPTY_LINE_SPAN = LineSpan(())
|
|
121
121
|
|
|
122
122
|
|
|
123
|
-
# TODO: comment
|
|
124
123
|
LineSpanLike = Union[str, LineSpan]
|
|
125
124
|
|
|
126
125
|
|
|
127
126
|
# A type alias to use when the line span is a Python expression.
|
|
128
127
|
Expr = LineSpan
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
# TODO: comment
|
|
132
128
|
ExprLike = LineSpanLike
|
|
133
129
|
|
|
134
130
|
|
|
@@ -156,7 +152,7 @@ class BodyBuilder:
|
|
|
156
152
|
self._lines.append(Line.join(*spans))
|
|
157
153
|
|
|
158
154
|
def extend(self, other: Iterable[Union[str, Line]], indent: str = ""):
|
|
159
|
-
self._lines.extend(Line.join(indent,
|
|
155
|
+
self._lines.extend(Line.join(indent, ln) for ln in other)
|
|
160
156
|
return self
|
|
161
157
|
|
|
162
158
|
def build(self) -> Body:
|
soialib/impl/optionals.py
CHANGED
soialib/impl/primitives.py
CHANGED
|
@@ -33,9 +33,9 @@ class _BoolAdapter(AbstractPrimitiveAdapter):
|
|
|
33
33
|
readable: bool,
|
|
34
34
|
) -> ExprLike:
|
|
35
35
|
if readable:
|
|
36
|
-
return Expr.join("(
|
|
36
|
+
return Expr.join("(True if ", in_expr, " else False)")
|
|
37
37
|
else:
|
|
38
|
-
return in_expr
|
|
38
|
+
return Expr.join("(1 if ", in_expr, " else 0)")
|
|
39
39
|
|
|
40
40
|
def from_json_expr(self, json_expr: ExprLike) -> Expr:
|
|
41
41
|
return Expr.join("(True if ", json_expr, " else False)")
|
|
@@ -52,31 +52,72 @@ class _AbstractIntAdapter(AbstractPrimitiveAdapter):
|
|
|
52
52
|
return "0"
|
|
53
53
|
|
|
54
54
|
def to_frozen_expr(self, arg_expr: ExprLike) -> Expr:
|
|
55
|
-
|
|
55
|
+
# Must accept float inputs and turn them into ints.
|
|
56
|
+
return Expr.join("(0).__class__(", arg_expr, ")")
|
|
56
57
|
|
|
57
58
|
def is_not_default_expr(self, arg_expr: ExprLike, attr_expr: ExprLike) -> ExprLike:
|
|
58
59
|
return arg_expr
|
|
59
60
|
|
|
60
|
-
def to_json_expr(self, in_expr: ExprLike, readable: bool) -> ExprLike:
|
|
61
|
-
return in_expr
|
|
62
|
-
|
|
63
61
|
def from_json_expr(self, json_expr: ExprLike) -> Expr:
|
|
64
|
-
|
|
62
|
+
# Must accept float inputs and string inputs and turn them into ints.
|
|
63
|
+
return Expr.join(
|
|
64
|
+
"(0).__class__(",
|
|
65
|
+
json_expr,
|
|
66
|
+
")",
|
|
67
|
+
)
|
|
65
68
|
|
|
66
69
|
|
|
67
70
|
@dataclass(frozen=True)
|
|
68
71
|
class _Int32Adapter(_AbstractIntAdapter):
|
|
69
|
-
|
|
72
|
+
def to_json_expr(self, in_expr: ExprLike, readable: bool) -> Expr:
|
|
73
|
+
return Expr.join(
|
|
74
|
+
"(-2147483648 if ",
|
|
75
|
+
in_expr,
|
|
76
|
+
" <= -2147483648 else ",
|
|
77
|
+
in_expr,
|
|
78
|
+
" if ",
|
|
79
|
+
in_expr,
|
|
80
|
+
" < 2147483647 else 2147483647)",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _int64_to_json(i: int) -> int | str:
|
|
85
|
+
if i < -9007199254740991: # min safe integer in JavaScript
|
|
86
|
+
if i <= -9223372036854775808:
|
|
87
|
+
return "-9223372036854775808"
|
|
88
|
+
else:
|
|
89
|
+
return str(i)
|
|
90
|
+
elif i <= 9007199254740991: # max safe integer in JavaScript
|
|
91
|
+
return i
|
|
92
|
+
elif i < 9223372036854775807:
|
|
93
|
+
return str(i)
|
|
94
|
+
else:
|
|
95
|
+
return "9223372036854775807"
|
|
70
96
|
|
|
71
97
|
|
|
72
98
|
@dataclass(frozen=True)
|
|
73
99
|
class _Int64Adapter(_AbstractIntAdapter):
|
|
74
|
-
|
|
100
|
+
def to_json_expr(self, in_expr: ExprLike, readable: bool) -> Expr:
|
|
101
|
+
return Expr.join(Expr.local("int64_to_json", _int64_to_json), "(", in_expr, ")")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _uint64_to_json(i: int) -> int | str:
|
|
105
|
+
if i <= 0:
|
|
106
|
+
return 0
|
|
107
|
+
elif i <= 9007199254740991: # max safe integer in JavaScript
|
|
108
|
+
return i
|
|
109
|
+
elif i < 18446744073709551615:
|
|
110
|
+
return f"{i}"
|
|
111
|
+
else:
|
|
112
|
+
return "18446744073709551615"
|
|
75
113
|
|
|
76
114
|
|
|
77
115
|
@dataclass(frozen=True)
|
|
78
116
|
class _Uint64Adapter(_AbstractIntAdapter):
|
|
79
|
-
|
|
117
|
+
def to_json_expr(self, in_expr: ExprLike, readable: bool) -> Expr:
|
|
118
|
+
return Expr.join(
|
|
119
|
+
Expr.local("uint64_to_json", _uint64_to_json), "(", in_expr, ")"
|
|
120
|
+
)
|
|
80
121
|
|
|
81
122
|
|
|
82
123
|
INT32_ADAPTER: Final[TypeAdapter] = _Int32Adapter()
|
|
@@ -203,7 +244,7 @@ class _BytesAdapter(AbstractPrimitiveAdapter):
|
|
|
203
244
|
|
|
204
245
|
def from_json_expr(self, json_expr: ExprLike) -> Expr:
|
|
205
246
|
return Expr.join(
|
|
206
|
-
Expr.local("fromhex", _BytesAdapter._fromhex_fn), "(", json_expr, ")
|
|
247
|
+
Expr.local("fromhex", _BytesAdapter._fromhex_fn), "(", json_expr, ' or "")'
|
|
207
248
|
)
|
|
208
249
|
|
|
209
250
|
|
soialib/impl/repr.py
CHANGED
|
@@ -31,7 +31,6 @@ def repr_impl(input: Any) -> ReprResult:
|
|
|
31
31
|
return ReprResult(f"[\n{body}]", complex=True)
|
|
32
32
|
elif isinstance(input, str):
|
|
33
33
|
if "\n" in input:
|
|
34
|
-
# TODO: comment
|
|
35
34
|
lines = input.split("\n")
|
|
36
35
|
body = "".join(f" {repr(line)},\n" for line in lines)
|
|
37
36
|
return ReprResult(f"'\\n'.join([\n{body}])", complex=True)
|
soialib/impl/structs.py
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
import copy
|
|
1
2
|
from collections.abc import Callable, Sequence
|
|
2
3
|
from dataclasses import FrozenInstanceError, dataclass
|
|
3
|
-
from typing import Any, Final, Union
|
|
4
|
+
from typing import Any, Final, Union, cast
|
|
4
5
|
|
|
5
6
|
from soialib import spec as _spec
|
|
6
|
-
from soialib.impl.encoding import ARRAY_WIRE, LEN_BYTES, SMALL_ARRAY_WIRES
|
|
7
7
|
from soialib.impl.function_maker import (
|
|
8
8
|
BodyBuilder,
|
|
9
|
-
BodySpan,
|
|
10
9
|
Expr,
|
|
11
10
|
ExprLike,
|
|
12
11
|
Line,
|
|
@@ -50,8 +49,8 @@ class StructAdapter(TypeAdapter):
|
|
|
50
49
|
self.private_is_frozen_attr = _name_private_is_frozen_attr(spec.id)
|
|
51
50
|
|
|
52
51
|
slots = tuple(f.attribute for f in self.spec.fields) + (
|
|
53
|
-
#
|
|
54
|
-
"
|
|
52
|
+
# Unrecognized fields encountered during deserialization.
|
|
53
|
+
"_unrecognized",
|
|
55
54
|
# Lowest number greater than the number of every field with a non-default
|
|
56
55
|
# value.
|
|
57
56
|
"_array_len",
|
|
@@ -59,8 +58,8 @@ class StructAdapter(TypeAdapter):
|
|
|
59
58
|
frozen_class = self.gen_class = _make_dataclass(slots)
|
|
60
59
|
frozen_class.__name__ = spec.class_name
|
|
61
60
|
frozen_class.__qualname__ = spec.class_qualname
|
|
62
|
-
frozen_class.__setattr__ = _Frozen.__setattr__
|
|
63
|
-
frozen_class.__delattr__ = _Frozen.__delattr__
|
|
61
|
+
frozen_class.__setattr__ = cast(Any, _Frozen.__setattr__)
|
|
62
|
+
frozen_class.__delattr__ = cast(Any, _Frozen.__delattr__)
|
|
64
63
|
# We haven't added an __init__ method to the frozen class yet, so frozen_class()
|
|
65
64
|
# returns an object with no attribute set. We'll set the attributes of DEFAULT
|
|
66
65
|
# at the finalization step.
|
|
@@ -98,7 +97,6 @@ class StructAdapter(TypeAdapter):
|
|
|
98
97
|
)
|
|
99
98
|
)
|
|
100
99
|
|
|
101
|
-
# TODO: do I even need this?
|
|
102
100
|
# Aim to have dependencies finalized *before* the dependent. It's not always
|
|
103
101
|
# possible, because there can be cyclic dependencies.
|
|
104
102
|
# The function returned by the do_x_fn() method of a dependency is marginally
|
|
@@ -125,16 +123,21 @@ class StructAdapter(TypeAdapter):
|
|
|
125
123
|
setattr(frozen_class, self.private_is_frozen_attr, True)
|
|
126
124
|
setattr(mutable_class, self.private_is_frozen_attr, False)
|
|
127
125
|
|
|
128
|
-
frozen_class.__init__ =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
126
|
+
frozen_class.__init__ = cast(
|
|
127
|
+
Any,
|
|
128
|
+
_make_frozen_class_init_fn(
|
|
129
|
+
fields,
|
|
130
|
+
frozen_class=frozen_class,
|
|
131
|
+
simple_class=simple_class,
|
|
132
|
+
),
|
|
132
133
|
)
|
|
133
|
-
mutable_class.__init__ = _make_mutable_class_init_fn(fields)
|
|
134
|
+
mutable_class.__init__ = cast(Any, _make_mutable_class_init_fn(fields))
|
|
134
135
|
|
|
135
136
|
frozen_class.__eq__ = _make_eq_fn(fields)
|
|
136
|
-
frozen_class.__hash__ = _make_hash_fn(fields, self.record_hash)
|
|
137
|
-
frozen_class.__repr__ = mutable_class.__repr__ =
|
|
137
|
+
frozen_class.__hash__ = cast(Any, _make_hash_fn(fields, self.record_hash))
|
|
138
|
+
frozen_class.__repr__ = mutable_class.__repr__ = cast(
|
|
139
|
+
Any, _make_repr_fn(fields)
|
|
140
|
+
)
|
|
138
141
|
|
|
139
142
|
frozen_class._tdj = _make_to_dense_json_fn(fields=fields)
|
|
140
143
|
frozen_class._trj = _make_to_readable_json_fn(fields=fields)
|
|
@@ -162,7 +165,8 @@ class StructAdapter(TypeAdapter):
|
|
|
162
165
|
return Expr.local("_d?", self.gen_class.DEFAULT)
|
|
163
166
|
|
|
164
167
|
def to_frozen_expr(self, arg_expr: ExprLike) -> Expr:
|
|
165
|
-
#
|
|
168
|
+
# The goal of referring to private_is_frozen_attr is to raise an error if the
|
|
169
|
+
# struct has the wrong type.
|
|
166
170
|
return Expr.join(
|
|
167
171
|
"(",
|
|
168
172
|
arg_expr,
|
|
@@ -174,7 +178,7 @@ class StructAdapter(TypeAdapter):
|
|
|
174
178
|
)
|
|
175
179
|
|
|
176
180
|
def is_not_default_expr(self, arg_expr: ExprLike, attr_expr: ExprLike) -> Expr:
|
|
177
|
-
return Expr.join(attr_expr,
|
|
181
|
+
return Expr.join(attr_expr, "._array_len")
|
|
178
182
|
|
|
179
183
|
def to_json_expr(
|
|
180
184
|
self,
|
|
@@ -185,7 +189,7 @@ class StructAdapter(TypeAdapter):
|
|
|
185
189
|
|
|
186
190
|
def from_json_expr(self, json_expr: ExprLike) -> Expr:
|
|
187
191
|
fn_name = "_fj"
|
|
188
|
-
#
|
|
192
|
+
# The _fj method may not have been added to the class yet.
|
|
189
193
|
from_json_fn = getattr(self.gen_class, fn_name, None)
|
|
190
194
|
if from_json_fn:
|
|
191
195
|
return Expr.join(Expr.local("_fj?", from_json_fn), "(", json_expr, ")")
|
|
@@ -231,6 +235,8 @@ def _make_frozen_class_init_fn(
|
|
|
231
235
|
|
|
232
236
|
# Set the params.
|
|
233
237
|
params: Params = ["_self"]
|
|
238
|
+
if fields:
|
|
239
|
+
params.append("*")
|
|
234
240
|
for field in fields:
|
|
235
241
|
params.append(
|
|
236
242
|
Param(
|
|
@@ -262,8 +268,8 @@ def _make_frozen_class_init_fn(
|
|
|
262
268
|
spans.append("0")
|
|
263
269
|
return Expr.join(*spans)
|
|
264
270
|
|
|
265
|
-
if len(fields) <
|
|
266
|
-
# If the class has less than
|
|
271
|
+
if len(fields) < 3:
|
|
272
|
+
# If the class has less than 3 fields, it is faster to assign every field with
|
|
267
273
|
# object.__setattr__().
|
|
268
274
|
for field in fields:
|
|
269
275
|
attribute = field.field.attribute
|
|
@@ -275,10 +281,12 @@ def _make_frozen_class_init_fn(
|
|
|
275
281
|
field.type.to_frozen_expr(attribute),
|
|
276
282
|
")",
|
|
277
283
|
)
|
|
284
|
+
# Set the _unrecognized field.
|
|
285
|
+
builder.append_ln(obj_setattr, '(_self, "_unrecognized", ())')
|
|
278
286
|
# Set array length.
|
|
279
287
|
builder.append_ln(
|
|
280
288
|
obj_setattr,
|
|
281
|
-
|
|
289
|
+
'(_self, "_array_len", ',
|
|
282
290
|
array_len_expr(),
|
|
283
291
|
")",
|
|
284
292
|
)
|
|
@@ -300,8 +308,10 @@ def _make_frozen_class_init_fn(
|
|
|
300
308
|
" = ",
|
|
301
309
|
field.type.to_frozen_expr(attribute),
|
|
302
310
|
)
|
|
311
|
+
# Set the _unrecognized field.
|
|
312
|
+
builder.append_ln("_self._unrecognized = ()")
|
|
303
313
|
# Set array length.
|
|
304
|
-
builder.append_ln(
|
|
314
|
+
builder.append_ln("_self._array_len = ", array_len_expr())
|
|
305
315
|
# Change back the __class__.
|
|
306
316
|
builder.append_ln("_self.__class__ = ", Expr.local("Frozen", frozen_class))
|
|
307
317
|
|
|
@@ -333,6 +343,7 @@ def _make_mutable_class_init_fn(fields: Sequence[_Field]) -> Callable[[Any], Non
|
|
|
333
343
|
" = ",
|
|
334
344
|
attribute,
|
|
335
345
|
)
|
|
346
|
+
builder.append_ln("_self._unrecognized = ()")
|
|
336
347
|
return make_function(
|
|
337
348
|
name="__init__",
|
|
338
349
|
params=params,
|
|
@@ -349,7 +360,7 @@ def _make_to_mutable_fn(
|
|
|
349
360
|
Returns the implementation of the to_mutable() method of the frozen class.
|
|
350
361
|
"""
|
|
351
362
|
builder = BodyBuilder()
|
|
352
|
-
# Create an instance of the simple class. We'll later change its __class__
|
|
363
|
+
# Create an instance of the simple class. We'll later change its __class__ attr.
|
|
353
364
|
builder.append_ln(
|
|
354
365
|
"ret = ",
|
|
355
366
|
Expr.local("Simple", simple_class),
|
|
@@ -358,7 +369,7 @@ def _make_to_mutable_fn(
|
|
|
358
369
|
for field in fields:
|
|
359
370
|
attribute = field.field.attribute
|
|
360
371
|
builder.append_ln("ret.", attribute, " = self.", attribute)
|
|
361
|
-
|
|
372
|
+
builder.append_ln("ret._unrecognized = self._unrecognized")
|
|
362
373
|
builder.append_ln(
|
|
363
374
|
"ret.__class__ = ",
|
|
364
375
|
Expr.local("mutable_class", mutable_class),
|
|
@@ -381,7 +392,7 @@ def _make_to_frozen_fn(
|
|
|
381
392
|
"""
|
|
382
393
|
|
|
383
394
|
builder = BodyBuilder()
|
|
384
|
-
# Create an instance of the simple class. We'll later change its __class__
|
|
395
|
+
# Create an instance of the simple class. We'll later change its __class__ attr.
|
|
385
396
|
builder.append_ln(
|
|
386
397
|
"ret = ",
|
|
387
398
|
Expr.local("Simple", simple_class),
|
|
@@ -396,10 +407,18 @@ def _make_to_frozen_fn(
|
|
|
396
407
|
field.type.to_frozen_expr(f"self.{attribute}"),
|
|
397
408
|
)
|
|
398
409
|
|
|
399
|
-
#
|
|
410
|
+
# Set the _unrecognized field.
|
|
411
|
+
builder.append_ln("ret._unrecognized = self._unrecognized")
|
|
400
412
|
|
|
401
413
|
def array_len_expr() -> Expr:
|
|
402
414
|
spans: list[LineSpanLike] = []
|
|
415
|
+
spans.append(
|
|
416
|
+
LineSpan.join(
|
|
417
|
+
f"{_get_num_slots(fields)} + ",
|
|
418
|
+
Expr.local("_len", len),
|
|
419
|
+
"(ret._unrecognized) if ret._unrecognized else ",
|
|
420
|
+
)
|
|
421
|
+
)
|
|
403
422
|
# Fields are sorted by number.
|
|
404
423
|
for field in reversed(fields):
|
|
405
424
|
attr_expr = f"ret.{field.field.attribute}"
|
|
@@ -413,8 +432,10 @@ def _make_to_frozen_fn(
|
|
|
413
432
|
spans.append("0")
|
|
414
433
|
return Expr.join(*spans)
|
|
415
434
|
|
|
435
|
+
# Set the _unrecognized field.
|
|
436
|
+
builder.append_ln("ret._unrecognized = self._unrecognized")
|
|
416
437
|
# Set array length.
|
|
417
|
-
builder.append_ln(
|
|
438
|
+
builder.append_ln("ret._array_len = ", array_len_expr())
|
|
418
439
|
|
|
419
440
|
builder.append_ln(
|
|
420
441
|
"ret.__class__ = ",
|
|
@@ -461,7 +482,6 @@ def _make_hash_fn(
|
|
|
461
482
|
"""
|
|
462
483
|
|
|
463
484
|
components: list[ExprLike] = []
|
|
464
|
-
# TODO: comment
|
|
465
485
|
components.append(str(record_hash))
|
|
466
486
|
for field in fields:
|
|
467
487
|
components.append(f"self.{field.field.attribute}")
|
|
@@ -487,8 +507,7 @@ def _make_repr_fn(fields: Sequence[_Field]) -> Callable[[Any], Any]:
|
|
|
487
507
|
repr_local = Expr.local("repr", repr_impl)
|
|
488
508
|
for field in fields:
|
|
489
509
|
attribute = field.field.attribute
|
|
490
|
-
#
|
|
491
|
-
# TODO: comment; we use try because maybe in mutable we have the wrong type and we still want to be able to repr it
|
|
510
|
+
# is_not_default_expr only works on a frozen expression.
|
|
492
511
|
to_frozen_expr = field.type.to_frozen_expr(f"(self.{attribute})")
|
|
493
512
|
is_not_default_expr = field.type.is_not_default_expr(
|
|
494
513
|
to_frozen_expr, to_frozen_expr
|
|
@@ -512,7 +531,7 @@ def _make_repr_fn(fields: Sequence[_Field]) -> Callable[[Any], Any]:
|
|
|
512
531
|
|
|
513
532
|
def _make_to_dense_json_fn(fields: Sequence[_Field]) -> Callable[[Any], Any]:
|
|
514
533
|
builder = BodyBuilder()
|
|
515
|
-
builder.append_ln(
|
|
534
|
+
builder.append_ln("l = self._array_len")
|
|
516
535
|
builder.append_ln("ret = [0] * l")
|
|
517
536
|
for field in fields:
|
|
518
537
|
builder.append_ln(f"if l <= {field.field.number}:")
|
|
@@ -524,7 +543,10 @@ def _make_to_dense_json_fn(fields: Sequence[_Field]) -> Callable[[Any], Any]:
|
|
|
524
543
|
readable=False,
|
|
525
544
|
),
|
|
526
545
|
)
|
|
527
|
-
|
|
546
|
+
num_slots = _get_num_slots(fields)
|
|
547
|
+
builder.append_ln(f"if l <= {num_slots}:")
|
|
548
|
+
builder.append_ln(" return ret")
|
|
549
|
+
builder.append_ln(f"ret[{num_slots}:] = self._unrecognized")
|
|
528
550
|
builder.append_ln("return ret")
|
|
529
551
|
return make_function(
|
|
530
552
|
name="to_dense_json",
|
|
@@ -550,7 +572,6 @@ def _make_to_readable_json_fn(fields: Sequence[_Field]) -> Callable[[Any], Any]:
|
|
|
550
572
|
readable=True,
|
|
551
573
|
),
|
|
552
574
|
)
|
|
553
|
-
# TODO: unknown fields
|
|
554
575
|
builder.append_ln("return ret")
|
|
555
576
|
return make_function(
|
|
556
577
|
name="to_readable_json",
|
|
@@ -592,10 +613,20 @@ def _make_from_json_fn(
|
|
|
592
613
|
f" if array_len <= {number} else ",
|
|
593
614
|
field.type.from_json_expr(item_expr),
|
|
594
615
|
)
|
|
616
|
+
num_slots = _get_num_slots(fields)
|
|
617
|
+
builder.append_ln(f" if array_len <= {num_slots}:")
|
|
618
|
+
builder.append_ln(" ret._unrecognized = ()")
|
|
595
619
|
builder.append_ln(
|
|
596
|
-
|
|
620
|
+
" ret._array_len = ",
|
|
597
621
|
_adjust_array_len_expr("array_len", removed_numbers),
|
|
598
622
|
)
|
|
623
|
+
builder.append_ln(" else:")
|
|
624
|
+
builder.append_ln(
|
|
625
|
+
" ret._unrecognized = ",
|
|
626
|
+
Expr.local("deepcopy", copy.deepcopy),
|
|
627
|
+
f"(json[{num_slots}:])",
|
|
628
|
+
)
|
|
629
|
+
builder.append_ln(" ret._array_len = array_len")
|
|
599
630
|
|
|
600
631
|
builder.append_ln("else:")
|
|
601
632
|
builder.append_ln(" array_len = 0")
|
|
@@ -607,11 +638,13 @@ def _make_from_json_fn(
|
|
|
607
638
|
builder.append_ln(f" array_len = {field.field.number}")
|
|
608
639
|
builder.append_ln(
|
|
609
640
|
f" {lvalue} = ",
|
|
610
|
-
field.type.from_json_expr(
|
|
641
|
+
field.type.from_json_expr(f'json["{name}"]'),
|
|
611
642
|
)
|
|
612
643
|
builder.append_ln(" else:")
|
|
613
644
|
builder.append_ln(f" {lvalue} = ", field.type.default_expr())
|
|
614
|
-
|
|
645
|
+
# Drop unrecognized fields in readable mode.
|
|
646
|
+
builder.append_ln(" ret._unrecognized = ()")
|
|
647
|
+
builder.append_ln(" ret._array_len = array_len")
|
|
615
648
|
|
|
616
649
|
builder.append_ln("ret.__class__ = ", Expr.local("Frozen", frozen_class))
|
|
617
650
|
builder.append_ln("return ret")
|
|
@@ -683,6 +716,7 @@ def _init_default(default: Any, fields: Sequence[_Field]) -> None:
|
|
|
683
716
|
body=(Line.join("return ", field.type.default_expr()),),
|
|
684
717
|
)
|
|
685
718
|
object.__setattr__(default, attribute, get_field_default())
|
|
719
|
+
object.__setattr__(default, "_unrecognized", ())
|
|
686
720
|
object.__setattr__(default, "_array_len", 0)
|
|
687
721
|
|
|
688
722
|
|
|
@@ -728,3 +762,7 @@ def _name_private_is_frozen_attr(record_id: str) -> str:
|
|
|
728
762
|
record_name = _spec.RecordId.parse(record_id).name
|
|
729
763
|
hex_hash = hex(abs(hash(record_id)))[:6]
|
|
730
764
|
return f"_is_{record_name}_{hex_hash}"
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
def _get_num_slots(fields: Sequence[_Field]) -> int:
|
|
768
|
+
return (fields[-1].field.number + 1) if fields else 0
|
soialib/impl/type_adapter.py
CHANGED
soialib/module_initializer.py
CHANGED
soialib/serializer.py
CHANGED
|
@@ -39,14 +39,17 @@ class Serializer(Generic[T]):
|
|
|
39
39
|
)
|
|
40
40
|
object.__setattr__(self, "_from_json_fn", _make_from_json_fn(adapter))
|
|
41
41
|
|
|
42
|
-
def to_json(self, input: T,
|
|
43
|
-
if
|
|
42
|
+
def to_json(self, input: T, *, readable=False) -> Any:
|
|
43
|
+
if readable:
|
|
44
44
|
return self._to_readable_json_fn(input)
|
|
45
45
|
else:
|
|
46
46
|
return self._to_dense_json_fn(input)
|
|
47
47
|
|
|
48
|
-
def to_json_code(self, input: T,
|
|
49
|
-
|
|
48
|
+
def to_json_code(self, input: T, readable=False) -> str:
|
|
49
|
+
if readable:
|
|
50
|
+
return jsonlib.dumps(self._to_readable_json_fn(input), indent=2)
|
|
51
|
+
else:
|
|
52
|
+
return jsonlib.dumps(self._to_dense_json_fn(input), separators=(",", ":"))
|
|
50
53
|
|
|
51
54
|
def from_json(self, json: Any) -> T:
|
|
52
55
|
return self._from_json_fn(json)
|
soialib/serializers.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Final, Literal,
|
|
1
|
+
from typing import Final, Literal, TypeVar, overload
|
|
2
2
|
|
|
3
3
|
from soialib.impl import primitives
|
|
4
4
|
from soialib.impl.arrays import get_array_adapter
|
|
@@ -38,6 +38,8 @@ def primitive_serializer(primitive: Literal["timestamp"]) -> Serializer[Timestam
|
|
|
38
38
|
def primitive_serializer(primitive: Literal["string"]) -> Serializer[str]: ...
|
|
39
39
|
@overload
|
|
40
40
|
def primitive_serializer(primitive: Literal["bytes"]) -> Serializer[bytes]: ...
|
|
41
|
+
|
|
42
|
+
|
|
41
43
|
def primitive_serializer(
|
|
42
44
|
primitive: (
|
|
43
45
|
Literal["bool"]
|
soialib/timestamp.py
CHANGED
|
@@ -54,6 +54,7 @@ class Timestamp:
|
|
|
54
54
|
def __sub__(self, other: datetime.timedelta) -> "Timestamp": ...
|
|
55
55
|
@overload
|
|
56
56
|
def __sub__(self, other: "Timestamp") -> datetime.timedelta: ...
|
|
57
|
+
|
|
57
58
|
def __sub__(
|
|
58
59
|
self, other: Union["Timestamp", datetime.timedelta]
|
|
59
60
|
) -> Union["Timestamp", datetime.timedelta]:
|
|
@@ -97,7 +98,7 @@ class Timestamp:
|
|
|
97
98
|
|
|
98
99
|
def _trj(self) -> Any:
|
|
99
100
|
"""To readable JSON."""
|
|
100
|
-
iso = self._iso_format
|
|
101
|
+
iso = self._iso_format()
|
|
101
102
|
if iso:
|
|
102
103
|
return {
|
|
103
104
|
"unix_millis": self.unix_millis,
|
|
@@ -111,7 +112,7 @@ class Timestamp:
|
|
|
111
112
|
def _iso_format(self) -> str:
|
|
112
113
|
try:
|
|
113
114
|
dt = self.to_datetime_or_raise()
|
|
114
|
-
except:
|
|
115
|
+
except Exception:
|
|
115
116
|
return ""
|
|
116
117
|
if dt:
|
|
117
118
|
ret = dt.isoformat()
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
soialib/__init__.py,sha256=h_ENkLJEdrh1mGhCoKgIadDasSulW3zK9_qhEsTviXY,429
|
|
2
|
-
soialib/keyed_items.py,sha256=q7MCn82obf-0jh7FcAhuw4eh9-wtuHIpkEFcSfc8EaY,338
|
|
3
|
-
soialib/method.py,sha256=2qWG4jMqYhS3hA8y8YDu3iqzhXA_AKebpB38RWNmsYQ,452
|
|
4
|
-
soialib/module_initializer.py,sha256=dDdYtlWlN7kEWlxuI0lguPsI-7zXAShswjw9j-_-AWY,4229
|
|
5
|
-
soialib/never.py,sha256=bYU63XyNX4e2wOUXQHhHWGO-an4IFr9_ur1ut6GmGN0,47
|
|
6
|
-
soialib/serializer.py,sha256=2R1BYEvpG3eDYMFXSKljPTMweOiuu8OMq7Xf1Bv2Wsk,3008
|
|
7
|
-
soialib/serializers.py,sha256=7zmCdtaUkuy1vIGP1OBn06x8FryYep15bujrFoeGw9A,2572
|
|
8
|
-
soialib/spec.py,sha256=w-eMMUqOchOlCJaXppyAa2JpSBrMo9lrMvqz8VaEX4I,3699
|
|
9
|
-
soialib/timestamp.py,sha256=6VNKAJs7EwxBm3tEEk1Cy1FhAD6lSNdlB3K2Sg6QHIA,4065
|
|
10
|
-
soialib/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
soialib/impl/arrays.py,sha256=NOKIfD3tvYnWycVk00aaf4m5Nx4_G5sVxdh2cwu4-l0,4902
|
|
12
|
-
soialib/impl/encoding.py,sha256=8k2j0nhGloFIBUEZLiiHANwhopusaQcDRePPS4sUhPc,1413
|
|
13
|
-
soialib/impl/enums.py,sha256=o3KylHoh66jcb1TUHeOorjaxHPoqHkbk7PXR5hKldrI,14121
|
|
14
|
-
soialib/impl/function_maker.py,sha256=PYqHqnZf8nELfEnRcoyyUbPAHyr99ROHhmHGj9ldB6U,5684
|
|
15
|
-
soialib/impl/optionals.py,sha256=QUcxx0i7oAQl7IcnxKyNW11xi7fbs2hmuSP1pfrTSTY,2120
|
|
16
|
-
soialib/impl/primitives.py,sha256=X_ywRmVZI_Q5tatb1Qil6Xvdyh1J4ElfI-LOCn671Zk,5975
|
|
17
|
-
soialib/impl/repr.py,sha256=z-_jGNKuY__DfwQKW40xzZFf_eG6wGMJvuY_2hJrETA,1441
|
|
18
|
-
soialib/impl/structs.py,sha256=vkOklmPzvWyFLFkMYbuDF8Rv_lkhzA66J-Uo9FJcVQc,24028
|
|
19
|
-
soialib/impl/type_adapter.py,sha256=IP3jF3wPgwKhWd7LKMmbbv1qUTv1HBAyMdpVrIg0_S0,2063
|
|
20
|
-
soia_client-1.0.7.dist-info/LICENSE,sha256=SaAftKkX6hfSOiPdENQPS70tifH3PDHgazq8eK2Pwfw,1064
|
|
21
|
-
soia_client-1.0.7.dist-info/METADATA,sha256=s5Ll-b2a11xX7z7c1yWbXt1-zu4BZEh9ZxOP5y8jXsA,1645
|
|
22
|
-
soia_client-1.0.7.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
23
|
-
soia_client-1.0.7.dist-info/top_level.txt,sha256=2vPmAo5G0SrCxYrNdJKJJVdpalYppgjO2mmz2PtsFUI,8
|
|
24
|
-
soia_client-1.0.7.dist-info/RECORD,,
|
soialib/impl/encoding.py
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
from struct import pack
|
|
2
|
-
from typing import Final
|
|
3
|
-
|
|
4
|
-
from soialib.impl.function_maker import Expr
|
|
5
|
-
|
|
6
|
-
EMPTY_STRING_WIRE: Final[Expr] = Expr.local("EMPTY_STRING_WIRE", bytes([242]))
|
|
7
|
-
STRING_WIRE: Final[Expr] = Expr.local("STRING_WIRE", bytes([243]))
|
|
8
|
-
EMPTY_BYTE_STRING_WIRE: Final[Expr] = Expr.local("EMPTY_BYTE_STRING_WIRE", bytes([244]))
|
|
9
|
-
BYTE_STRING_WIRE: Final[Expr] = Expr.local("BYTE_STRING_WIRE", bytes([245]))
|
|
10
|
-
SMALL_ARRAY_WIRES: Final[Expr] = Expr.local(
|
|
11
|
-
"SMALL_ARRAY_WIRES", tuple(bytes(b) for b in range(246, 250))
|
|
12
|
-
)
|
|
13
|
-
ARRAY_WIRE: Final[Expr] = Expr.local("ARRAY_WIRE", bytes(250))
|
|
14
|
-
NULL_WIRE: Final[Expr] = Expr.local("NULL_WIRE", bytes([255]))
|
|
15
|
-
|
|
16
|
-
_LOW_INT_BYTES: Final[tuple[bytes, ...]] = tuple([bytes([i]) for i in range(232)])
|
|
17
|
-
_ZERO_BYTES: Final[bytes] = _LOW_INT_BYTES[0]
|
|
18
|
-
_ONE_BYTES: Final[bytes] = _LOW_INT_BYTES[1]
|
|
19
|
-
|
|
20
|
-
LOW_INT_BYTES: Final[Expr] = Expr.local("LOW_INT_BYTES", _LOW_INT_BYTES)
|
|
21
|
-
ZERO_BYTES: Final[Expr] = Expr.local("ZERO_BYTES_LOCAL", _ZERO_BYTES)
|
|
22
|
-
ONE_BYTES: Final[Expr] = Expr.local("ONE_BYTES_LOCAL", _ONE_BYTES)
|
|
23
|
-
|
|
24
|
-
PACK: Final[Expr] = Expr.local("pack", pack)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def _len_bytes_high(l: int) -> bytes:
|
|
28
|
-
if l < 65536:
|
|
29
|
-
return pack("H", l)
|
|
30
|
-
elif l < 2147483648:
|
|
31
|
-
return pack("I", l)
|
|
32
|
-
raise OverflowError(f"len={l}")
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
LEN_BYTES: Final[Expr] = Expr.join(
|
|
36
|
-
"(",
|
|
37
|
-
LOW_INT_BYTES,
|
|
38
|
-
"[l] if l < 232 else ",
|
|
39
|
-
Expr.local("len_bytes_high", _len_bytes_high),
|
|
40
|
-
"(l))",
|
|
41
|
-
)
|
|
File without changes
|
|
File without changes
|