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.

@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: soia-client
3
- Version: 1.0.7
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.3.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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
- e = Expr.join("_e")
77
- item_to_json = self.item_adapter.to_json_expr(e, readable)
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(Expr.join("_e")),
97
- " for e in ",
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
- # TODO: test
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 primitives
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, value_fields, base_class
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 ConstantClass(base_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 ConstantClass
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 ValueClass(base_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 = ValueClass
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(Expr.join("value")),
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
- # TODO: handle unrecognized fields!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
326
- builder.append_ln(" return ...")
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(Expr.join("json[1]"))
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(indent, obj_setattr_local, "(ret, ", value_expr, ")")
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
- # TODO: handle unrecognized fields!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
350
- builder.append_ln(" return ...")
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
- # TODO: handle unrecognized fields!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
357
- builder.append_ln(" return ...")
358
- builder.append_ln(" number = json[0]")
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
- # TODO: comment
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(indent, obj_setattr_local, "(ret, ", value_expr, ")")
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 name {operator} '{mid_name}':")
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(" name = json['name']")
398
- builder.append_ln(" if name not in ", value_keys_local, ":")
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, Dict, Iterable, List, Optional, Sequence, Tuple, Union
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(l: Union[str, Line]) -> str:
53
- if isinstance(l, str):
54
- return l
55
- return l._to_code(locals)
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(l) for l in body) if body else "pass"
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, l) for l in other)
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
@@ -4,7 +4,6 @@ from typing import TypeVar
4
4
  from weakref import WeakValueDictionary
5
5
 
6
6
  from soialib import spec
7
- from soialib.impl.encoding import NULL_WIRE
8
7
  from soialib.impl.function_maker import Expr, ExprLike
9
8
  from soialib.impl.type_adapter import TypeAdapter
10
9
 
@@ -33,9 +33,9 @@ class _BoolAdapter(AbstractPrimitiveAdapter):
33
33
  readable: bool,
34
34
  ) -> ExprLike:
35
35
  if readable:
36
- return Expr.join("(1 if ", in_expr, " else 0)")
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
- return Expr.join("(", arg_expr, " | 0)")
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
- return Expr.join("(", json_expr, " | 0)")
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
- pass
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
- pass
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
- pass
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
- # Unknown fields encountered during deserialization.
54
- "_unknown",
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__ = _make_frozen_class_init_fn(
129
- fields,
130
- frozen_class=frozen_class,
131
- simple_class=simple_class,
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__ = _make_repr_fn(fields)
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
- # TODO: comment
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, f"._array_len")
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
- # TODO: comment
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) < 4:
266
- # If the class has less than 4 fields, it is faster to assign every field with
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
- f'(_self, "_array_len", ',
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(f"_self._array_len = ", array_len_expr())
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__ attribute.
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
- # TODO: unknown field
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__ attribute.
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
- # TODO: unknown field
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(f"ret._array_len = ", array_len_expr())
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
- # TODO: comment, is_not_default_expr only works on a frozen expression...
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(f"l = self._array_len")
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
- # TODO: unknown fields
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
- f" ret._array_len = ",
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(Expr.join(f'json["{name}"]')),
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
- builder.append_ln(f" ret._array_len = array_len")
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
@@ -49,7 +49,6 @@ class TypeAdapter(Protocol):
49
49
  """
50
50
  ...
51
51
 
52
- # TODO: comment!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
53
52
  def finalize(
54
53
  self,
55
54
  resolve_type_fn: Callable[[spec.Type], "TypeAdapter"],
@@ -77,7 +77,6 @@ def init_module(
77
77
  else:
78
78
  globals[class_name] = gen_class
79
79
  gen_class._parent_class = None
80
- # TODO: comment
81
80
  gen_class.SERIALIZER = make_serializer(adapter)
82
81
 
83
82
  # Now that al the classes have been initialized, create the methods.
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, readable_flavor=False) -> Any:
43
- if readable_flavor:
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, readable_flavor=False) -> str:
49
- return jsonlib.dumps(self.to_json(input, readable_flavor))
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, Optional, TypeVar, overload
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
- )