soia-client 1.0.7__tar.gz → 1.0.9__tar.gz
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 → soia_client-1.0.9}/PKG-INFO +3 -2
- {soia_client-1.0.7 → soia_client-1.0.9}/README +4 -0
- {soia_client-1.0.7 → soia_client-1.0.9}/pyproject.toml +1 -1
- {soia_client-1.0.7 → soia_client-1.0.9}/soia_client.egg-info/PKG-INFO +3 -2
- {soia_client-1.0.7 → soia_client-1.0.9}/soia_client.egg-info/SOURCES.txt +0 -1
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/impl/arrays.py +5 -8
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/impl/enums.py +80 -37
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/impl/function_maker.py +7 -11
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/impl/optionals.py +0 -1
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/impl/primitives.py +52 -11
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/impl/repr.py +0 -1
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/impl/structs.py +74 -36
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/impl/type_adapter.py +0 -1
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/module_initializer.py +0 -1
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/serializer.py +7 -4
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/serializers.py +3 -1
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/timestamp.py +3 -2
- {soia_client-1.0.7 → soia_client-1.0.9}/tests/test_module_initializer.py +295 -25
- soia_client-1.0.9/tests/test_serializers.py +155 -0
- {soia_client-1.0.7 → soia_client-1.0.9}/tests/test_timestamp.py +1 -1
- soia_client-1.0.7/soialib/impl/encoding.py +0 -41
- soia_client-1.0.7/tests/test_serializers.py +0 -47
- {soia_client-1.0.7 → soia_client-1.0.9}/LICENSE +0 -0
- {soia_client-1.0.7 → soia_client-1.0.9}/setup.cfg +0 -0
- {soia_client-1.0.7 → soia_client-1.0.9}/soia_client.egg-info/dependency_links.txt +0 -0
- {soia_client-1.0.7 → soia_client-1.0.9}/soia_client.egg-info/top_level.txt +0 -0
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/__init__.py +0 -0
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/impl/__init__.py +0 -0
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/keyed_items.py +0 -0
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/method.py +0 -0
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/never.py +0 -0
- {soia_client-1.0.7 → soia_client-1.0.9}/soialib/spec.py +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,3 +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
|
+
Dynamic: license-file
|
|
@@ -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,3 +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
|
+
Dynamic: license-file
|
|
@@ -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,
|
|
@@ -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"],
|
|
@@ -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:
|
|
@@ -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
|
|
|
@@ -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)
|