soia-client 1.0.4__tar.gz → 1.0.6__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.

Files changed (32) hide show
  1. {soia_client-1.0.4 → soia_client-1.0.6}/PKG-INFO +1 -1
  2. {soia_client-1.0.4 → soia_client-1.0.6}/pyproject.toml +1 -1
  3. {soia_client-1.0.4 → soia_client-1.0.6}/soia_client.egg-info/PKG-INFO +1 -1
  4. {soia_client-1.0.4 → soia_client-1.0.6}/soia_client.egg-info/SOURCES.txt +3 -0
  5. soia_client-1.0.6/soialib/__init__.py +19 -0
  6. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/arrays.py +7 -6
  7. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/optionals.py +3 -3
  8. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/structs.py +13 -1
  9. soia_client-1.0.6/soialib/method.py +17 -0
  10. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/module_initializer.py +23 -2
  11. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/serializer.py +14 -1
  12. soia_client-1.0.6/soialib/serializers.py +74 -0
  13. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/spec.py +16 -0
  14. {soia_client-1.0.4 → soia_client-1.0.6}/tests/test_module_initializer.py +106 -34
  15. soia_client-1.0.6/tests/test_serializers.py +47 -0
  16. {soia_client-1.0.4 → soia_client-1.0.6}/tests/test_timestamp.py +4 -0
  17. soia_client-1.0.4/soialib/__init__.py +0 -5
  18. {soia_client-1.0.4 → soia_client-1.0.6}/LICENSE +0 -0
  19. {soia_client-1.0.4 → soia_client-1.0.6}/README +0 -0
  20. {soia_client-1.0.4 → soia_client-1.0.6}/setup.cfg +0 -0
  21. {soia_client-1.0.4 → soia_client-1.0.6}/soia_client.egg-info/dependency_links.txt +0 -0
  22. {soia_client-1.0.4 → soia_client-1.0.6}/soia_client.egg-info/top_level.txt +0 -0
  23. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/__init__.py +0 -0
  24. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/encoding.py +0 -0
  25. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/enums.py +0 -0
  26. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/function_maker.py +0 -0
  27. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/primitives.py +0 -0
  28. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/repr.py +0 -0
  29. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/type_adapter.py +0 -0
  30. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/keyed_items.py +0 -0
  31. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/never.py +0 -0
  32. {soia_client-1.0.4 → soia_client-1.0.6}/soialib/timestamp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: soia-client
3
- Version: 1.0.4
3
+ Version: 1.0.6
4
4
  Author-email: Tyler Fibonacci <gepheum@gmail.com>
5
5
  License: MIT License
6
6
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "soia-client"
7
- version = "1.0.4"
7
+ version = "1.0.6"
8
8
  description = ""
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tyler Fibonacci", email = "gepheum@gmail.com" }]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: soia-client
3
- Version: 1.0.4
3
+ Version: 1.0.6
4
4
  Author-email: Tyler Fibonacci <gepheum@gmail.com>
5
5
  License: MIT License
6
6
 
@@ -7,9 +7,11 @@ soia_client.egg-info/dependency_links.txt
7
7
  soia_client.egg-info/top_level.txt
8
8
  soialib/__init__.py
9
9
  soialib/keyed_items.py
10
+ soialib/method.py
10
11
  soialib/module_initializer.py
11
12
  soialib/never.py
12
13
  soialib/serializer.py
14
+ soialib/serializers.py
13
15
  soialib/spec.py
14
16
  soialib/timestamp.py
15
17
  soialib/impl/__init__.py
@@ -23,4 +25,5 @@ soialib/impl/repr.py
23
25
  soialib/impl/structs.py
24
26
  soialib/impl/type_adapter.py
25
27
  tests/test_module_initializer.py
28
+ tests/test_serializers.py
26
29
  tests/test_timestamp.py
@@ -0,0 +1,19 @@
1
+ from soialib.keyed_items import KeyedItems
2
+ from soialib.method import Method
3
+ from soialib.serializer import Serializer
4
+ from soialib.serializers import (
5
+ array_serializer,
6
+ optional_serializer,
7
+ primitive_serializer,
8
+ )
9
+ from soialib.timestamp import Timestamp
10
+
11
+ __all__ = [
12
+ "KeyedItems",
13
+ "Method",
14
+ "Serializer",
15
+ "Timestamp",
16
+ "array_serializer",
17
+ "optional_serializer",
18
+ "primitive_serializer",
19
+ ]
@@ -1,7 +1,7 @@
1
1
  from collections.abc import Callable
2
2
  from dataclasses import FrozenInstanceError
3
3
  from typing import Generic, Optional
4
- from weakref import WeakKeyDictionary
4
+ from weakref import WeakValueDictionary
5
5
 
6
6
  from soialib import spec
7
7
  from soialib.impl.function_maker import Any, Expr, ExprLike, Line, make_function
@@ -19,8 +19,9 @@ def get_array_adapter(
19
19
  else:
20
20
  listuple_class = _new_listuple_class()
21
21
  array_adapter = _ArrayAdapter(item_adapter, listuple_class)
22
- key_spec_to_array_adapter = _item_to_array_adapters.setdefault(item_adapter, {})
23
- return key_spec_to_array_adapter.setdefault(key_attributes, array_adapter)
22
+ return _item_to_array_adapter.setdefault(
23
+ (item_adapter, key_attributes), array_adapter
24
+ )
24
25
 
25
26
 
26
27
  class _ArrayAdapter(TypeAdapter):
@@ -107,10 +108,10 @@ class _ArrayAdapter(TypeAdapter):
107
108
  self.item_adapter.finalize(resolve_type_fn)
108
109
 
109
110
 
110
- _KeyAttributesToArrayAdapter = dict[tuple[str, ...], _ArrayAdapter]
111
- _ItemToArrayAdapters = WeakKeyDictionary[TypeAdapter, _KeyAttributesToArrayAdapter]
111
+ _ItemAndKeyAttributes = tuple[TypeAdapter, tuple[str, ...]]
112
+ _ItemToArrayAdapter = WeakValueDictionary[_ItemAndKeyAttributes, _ArrayAdapter]
112
113
 
113
- _item_to_array_adapters: _ItemToArrayAdapters = WeakKeyDictionary()
114
+ _item_to_array_adapter: _ItemToArrayAdapter = WeakValueDictionary()
114
115
 
115
116
 
116
117
  def _new_listuple_class() -> type:
@@ -1,7 +1,7 @@
1
1
  from collections.abc import Callable
2
2
  from dataclasses import dataclass
3
3
  from typing import TypeVar
4
- from weakref import WeakKeyDictionary
4
+ from weakref import WeakValueDictionary
5
5
 
6
6
  from soialib import spec
7
7
  from soialib.impl.encoding import NULL_WIRE
@@ -69,6 +69,6 @@ class _OptionalAdapter(TypeAdapter):
69
69
  self.other_adapter.finalize(resolve_type_fn)
70
70
 
71
71
 
72
- _other_adapter_to_optional_adapter: WeakKeyDictionary[TypeAdapter, TypeAdapter] = (
73
- WeakKeyDictionary()
72
+ _other_adapter_to_optional_adapter: WeakValueDictionary[TypeAdapter, TypeAdapter] = (
73
+ WeakValueDictionary()
74
74
  )
@@ -146,7 +146,7 @@ class StructAdapter(TypeAdapter):
146
146
  )
147
147
 
148
148
  # Initialize DEFAULT.
149
- self.gen_class.DEFAULT.__init__()
149
+ _init_default(self.gen_class.DEFAULT, fields)
150
150
 
151
151
  # Define mutable getters
152
152
  for field in fields:
@@ -674,6 +674,18 @@ def _adjust_array_len_expr(var: str, removed_numbers: tuple[int, ...]) -> str:
674
674
  return ret
675
675
 
676
676
 
677
+ def _init_default(default: Any, fields: Sequence[_Field]) -> None:
678
+ for field in fields:
679
+ attribute = field.field.attribute
680
+ get_field_default = make_function(
681
+ name="get_default",
682
+ params=(),
683
+ body=(Line.join("return ", field.type.default_expr()),),
684
+ )
685
+ object.__setattr__(default, attribute, get_field_default())
686
+ object.__setattr__(default, "_array_len", 0)
687
+
688
+
677
689
  def _make_mutable_getter(field: _Field) -> Callable[[Any], Any]:
678
690
  # Two cases: the field either has struct type or array type.
679
691
  attribute = field.field.attribute
@@ -0,0 +1,17 @@
1
+ from dataclasses import dataclass
2
+ from typing import Generic, TypeVar
3
+
4
+ from soialib.serializer import Serializer
5
+
6
+ Request = TypeVar("Request")
7
+ Response = TypeVar("Response")
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class Method(Generic[Request, Response]):
12
+ """Identifies a procedure (the "P" in "RPC") on both client side and server side."""
13
+
14
+ name: str
15
+ number: int
16
+ request_serializer: Serializer[Request]
17
+ response_serializer: Serializer[Response]
@@ -1,6 +1,6 @@
1
1
  from typing import Any, Union
2
2
 
3
- from soialib import spec
3
+ from soialib import method, spec
4
4
  from soialib.impl import arrays, enums, optionals, primitives, structs
5
5
  from soialib.impl.type_adapter import TypeAdapter
6
6
  from soialib.serializer import make_serializer
@@ -11,8 +11,10 @@ RecordAdapter = Union[structs.StructAdapter, enums.EnumAdapter]
11
11
  _record_id_to_adapter: dict[str, RecordAdapter] = {}
12
12
 
13
13
 
14
- def init_module_classes(
14
+ def init_module(
15
15
  records: tuple[spec.Record, ...],
16
+ methods: tuple[spec.Method, ...],
17
+ constants: tuple[spec.Constant, ...],
16
18
  globals: dict[str, Any],
17
19
  # For testing
18
20
  record_id_to_adapter: dict[str, RecordAdapter] = _record_id_to_adapter,
@@ -77,3 +79,22 @@ def init_module_classes(
77
79
  gen_class._parent_class = None
78
80
  # TODO: comment
79
81
  gen_class.SERIALIZER = make_serializer(adapter)
82
+
83
+ # Now that al the classes have been initialized, create the methods.
84
+ for method_spec in methods:
85
+ var_name = method_spec._var_name or method_spec.name
86
+ request_serializer = make_serializer(resolve_type(method_spec.request_type))
87
+ response_serializer = make_serializer(resolve_type(method_spec.response_type))
88
+ globals[var_name] = method.Method(
89
+ name=method_spec.name,
90
+ number=method_spec.number,
91
+ request_serializer=request_serializer,
92
+ response_serializer=response_serializer,
93
+ )
94
+ del var_name, request_serializer, response_serializer
95
+
96
+ # Create the constants.
97
+ for constant in constants:
98
+ serializer = make_serializer(resolve_type(constant.type))
99
+ globals[constant.name] = serializer.from_json_code(constant.json_code)
100
+ del serializer
@@ -2,6 +2,7 @@ import json as jsonlib
2
2
  from collections.abc import Callable
3
3
  from dataclasses import FrozenInstanceError
4
4
  from typing import Any, Generic, TypeVar, cast, final
5
+ from weakref import WeakValueDictionary
5
6
 
6
7
  from soialib.impl.function_maker import Expr, LineSpan, make_function
7
8
  from soialib.impl.type_adapter import TypeAdapter
@@ -13,17 +14,21 @@ T = TypeVar("T")
13
14
  @final
14
15
  class Serializer(Generic[T]):
15
16
  __slots__ = (
17
+ "__weakref__",
18
+ "_adapter",
16
19
  "_to_dense_json_fn",
17
20
  "_to_readable_json_fn",
18
21
  "_from_json_fn",
19
22
  )
20
23
 
24
+ _adapter: TypeAdapter
21
25
  _to_dense_json_fn: Callable[[T], Any]
22
26
  _to_readable_json_fn: Callable[[T], Any]
23
27
  _from_json_fn: Callable[[Any], T]
24
28
 
25
29
  def __init__(self, adapter: Never):
26
30
  # Use Never (^) as a trick to make the constructor internal.
31
+ object.__setattr__(self, "_adapter", adapter)
27
32
  object.__setattr__(
28
33
  self, "_to_dense_json_fn", _make_to_json_fn(adapter, readable=False)
29
34
  )
@@ -56,8 +61,16 @@ class Serializer(Generic[T]):
56
61
  raise FrozenInstanceError(self.__class__.__qualname__)
57
62
 
58
63
 
64
+ # A cache to make sure we only create one Serializer for each TypeAdapter.
65
+ _type_adapter_to_serializer: WeakValueDictionary[TypeAdapter, Serializer] = (
66
+ WeakValueDictionary()
67
+ )
68
+
69
+
59
70
  def make_serializer(adapter: TypeAdapter) -> Serializer:
60
- return Serializer(cast(Never, adapter))
71
+ return _type_adapter_to_serializer.setdefault(
72
+ adapter, Serializer(cast(Never, adapter))
73
+ )
61
74
 
62
75
 
63
76
  def _make_to_json_fn(adapter: TypeAdapter, readable: bool) -> Callable[[Any], Any]:
@@ -0,0 +1,74 @@
1
+ from typing import Final, Literal, Optional, TypeVar, overload
2
+
3
+ from soialib.impl import primitives
4
+ from soialib.impl.arrays import get_array_adapter
5
+ from soialib.impl.optionals import get_optional_adapter
6
+ from soialib.serializer import Serializer, make_serializer
7
+ from soialib.timestamp import Timestamp
8
+
9
+ Item = TypeVar("Item")
10
+ Other = TypeVar("Other")
11
+
12
+
13
+ def array_serializer(item_serializer: Serializer[Item]) -> Serializer[tuple[Item, ...]]:
14
+ return make_serializer(get_array_adapter(item_serializer._adapter, ()))
15
+
16
+
17
+ def optional_serializer(
18
+ other_serializer: Serializer[Other],
19
+ ) -> Serializer[Other | None]:
20
+ return make_serializer(get_optional_adapter(other_serializer._adapter))
21
+
22
+
23
+ @overload
24
+ def primitive_serializer(primitive: Literal["bool"]) -> Serializer[bool]: ...
25
+ @overload
26
+ def primitive_serializer(primitive: Literal["int32"]) -> Serializer[int]: ...
27
+ @overload
28
+ def primitive_serializer(primitive: Literal["int64"]) -> Serializer[int]: ...
29
+ @overload
30
+ def primitive_serializer(primitive: Literal["uint64"]) -> Serializer[int]: ...
31
+ @overload
32
+ def primitive_serializer(primitive: Literal["float32"]) -> Serializer[float]: ...
33
+ @overload
34
+ def primitive_serializer(primitive: Literal["float64"]) -> Serializer[float]: ...
35
+ @overload
36
+ def primitive_serializer(primitive: Literal["timestamp"]) -> Serializer[Timestamp]: ...
37
+ @overload
38
+ def primitive_serializer(primitive: Literal["string"]) -> Serializer[str]: ...
39
+ @overload
40
+ def primitive_serializer(primitive: Literal["bytes"]) -> Serializer[bytes]: ...
41
+ def primitive_serializer(
42
+ primitive: (
43
+ Literal["bool"]
44
+ | Literal["int32"]
45
+ | Literal["int64"]
46
+ | Literal["uint64"]
47
+ | Literal["float32"]
48
+ | Literal["float64"]
49
+ | Literal["timestamp"]
50
+ | Literal["string"]
51
+ | Literal["bytes"]
52
+ ),
53
+ ) -> (
54
+ Serializer[bool]
55
+ | Serializer[int]
56
+ | Serializer[float]
57
+ | Serializer[Timestamp]
58
+ | Serializer[str]
59
+ | Serializer[bytes]
60
+ ):
61
+ return _PRIMITIVE_TO_SERIALIZER[primitive]
62
+
63
+
64
+ _PRIMITIVE_TO_SERIALIZER: Final = {
65
+ "bool": make_serializer(primitives.BOOL_ADAPTER),
66
+ "int32": make_serializer(primitives.INT32_ADAPTER),
67
+ "int64": make_serializer(primitives.INT64_ADAPTER),
68
+ "uint64": make_serializer(primitives.UINT64_ADAPTER),
69
+ "float32": make_serializer(primitives.FLOAT32_ADAPTER),
70
+ "float64": make_serializer(primitives.FLOAT64_ADAPTER),
71
+ "timestamp": make_serializer(primitives.TIMESTAMP_ADAPTER),
72
+ "string": make_serializer(primitives.STRING_ADAPTER),
73
+ "bytes": make_serializer(primitives.BYTES_ADAPTER),
74
+ }
@@ -141,3 +141,19 @@ class RecordId:
141
141
  qualname=parent_qualname,
142
142
  name_parts=parent_name_parts,
143
143
  )
144
+
145
+
146
+ @dataclass(frozen=True)
147
+ class Method:
148
+ name: str
149
+ number: int
150
+ request_type: Type
151
+ response_type: Type
152
+ _var_name: str = "" # If different from 'name'
153
+
154
+
155
+ @dataclass(frozen=True)
156
+ class Constant:
157
+ name: str
158
+ type: Type
159
+ json_code: str
@@ -5,15 +5,16 @@ from typing import Any
5
5
 
6
6
  from soialib import spec
7
7
  from soialib.keyed_items import KeyedItems
8
- from soialib.module_initializer import init_module_classes
8
+ from soialib.method import Method
9
+ from soialib.module_initializer import init_module
9
10
  from soialib.timestamp import Timestamp
10
11
 
11
12
 
12
13
  class ModuleInitializerTestCase(unittest.TestCase):
13
- def init_test_module_classes(self) -> dict[str, Any]:
14
+ def init_test_module(self) -> dict[str, Any]:
14
15
  globals: dict[str, Any] = {}
15
- init_module_classes(
16
- (
16
+ init_module(
17
+ records=(
17
18
  spec.Struct(
18
19
  id="my/module.soia:Point",
19
20
  fields=(
@@ -258,6 +259,48 @@ class ModuleInitializerTestCase(unittest.TestCase):
258
259
  ),
259
260
  ),
260
261
  ),
262
+ spec.Struct(
263
+ id="my/module.soia:RecOuter",
264
+ fields=(
265
+ spec.Field(
266
+ name="r",
267
+ number=0,
268
+ type="my/module.soia:RecOuter.RecInner",
269
+ ),
270
+ ),
271
+ ),
272
+ spec.Struct(
273
+ id="my/module.soia:RecOuter.RecInner",
274
+ fields=(
275
+ spec.Field(
276
+ name="r",
277
+ number=0,
278
+ type="my/module.soia:RecOuter",
279
+ ),
280
+ ),
281
+ ),
282
+ ),
283
+ methods=(
284
+ spec.Method(
285
+ name="FirstMethod",
286
+ number=-300,
287
+ request_type="my/module.soia:Point",
288
+ response_type="my/module.soia:Shape",
289
+ ),
290
+ spec.Method(
291
+ name="SecondMethod",
292
+ number=-301,
293
+ request_type="my/module.soia:Point",
294
+ response_type="my/module.soia:Shape",
295
+ _var_name="MethodVar",
296
+ ),
297
+ ),
298
+ constants=(
299
+ spec.Constant(
300
+ name="C",
301
+ type="my/module.soia:Point",
302
+ json_code="[1.5, 2.5]",
303
+ ),
261
304
  ),
262
305
  globals=globals,
263
306
  record_id_to_adapter={},
@@ -265,13 +308,13 @@ class ModuleInitializerTestCase(unittest.TestCase):
265
308
  return globals
266
309
 
267
310
  def test_struct_getters(self):
268
- point_cls = self.init_test_module_classes()["Point"]
311
+ point_cls = self.init_test_module()["Point"]
269
312
  point = point_cls(x=1.5, y=2.5)
270
313
  self.assertEqual(point.x, 1.5)
271
314
  self.assertEqual(point.y, 2.5)
272
315
 
273
316
  def test_to_mutable(self):
274
- point_cls = self.init_test_module_classes()["Point"]
317
+ point_cls = self.init_test_module()["Point"]
275
318
  point = point_cls(x=1.5, y=2.5)
276
319
  mutable = point.to_mutable()
277
320
  mutable.x = 4.0
@@ -281,7 +324,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
281
324
  self.assertIs(point.to_frozen(), point)
282
325
 
283
326
  def test_struct_eq(self):
284
- point_cls = self.init_test_module_classes()["Point"]
327
+ point_cls = self.init_test_module()["Point"]
285
328
  a = point_cls(x=1.5, y=2.5)
286
329
  b = point_cls(x=1.5, y=2.5)
287
330
  c = point_cls(x=1.5, y=3.0)
@@ -292,11 +335,11 @@ class ModuleInitializerTestCase(unittest.TestCase):
292
335
  self.assertEqual(point_cls(), point_cls(x=0.0, y=0.0))
293
336
 
294
337
  def test_or_mutable(self):
295
- point_cls = self.init_test_module_classes()["Point"]
338
+ point_cls = self.init_test_module()["Point"]
296
339
  point_cls.OrMutable
297
340
 
298
341
  def test_default_values(self):
299
- primitives_cls = self.init_test_module_classes()["Primitives"]
342
+ primitives_cls = self.init_test_module()["Primitives"]
300
343
  a = primitives_cls(
301
344
  bool=False,
302
345
  bytes=b"",
@@ -311,29 +354,29 @@ class ModuleInitializerTestCase(unittest.TestCase):
311
354
  self.assertEqual(hash(a), hash(b))
312
355
 
313
356
  def test_to_dense_json(self):
314
- point_cls = self.init_test_module_classes()["Point"]
357
+ point_cls = self.init_test_module()["Point"]
315
358
  point = point_cls(x=1.5, y=2.5)
316
359
  json = point_cls.SERIALIZER.to_json(point)
317
360
  self.assertEqual(json, [1.5, 2.5])
318
361
 
319
362
  def test_to_readable_json(self):
320
- point_cls = self.init_test_module_classes()["Point"]
363
+ point_cls = self.init_test_module()["Point"]
321
364
  point = point_cls(x=1.5, y=2.5)
322
365
  json = point_cls.SERIALIZER.to_json(point, readable_flavor=True)
323
366
  self.assertEqual(json, {"x": 1.5, "y": 2.5})
324
367
 
325
368
  def test_from_dense_json(self):
326
- point_cls = self.init_test_module_classes()["Point"]
369
+ point_cls = self.init_test_module()["Point"]
327
370
  point = point_cls.SERIALIZER.from_json([1.5, 2.5])
328
371
  self.assertEqual(point, point_cls(x=1.5, y=2.5))
329
372
 
330
373
  def test_from_readable_json(self):
331
- point_cls = self.init_test_module_classes()["Point"]
374
+ point_cls = self.init_test_module()["Point"]
332
375
  point = point_cls.SERIALIZER.from_json({"x": 1.5, "y": 2.5})
333
376
  self.assertEqual(point, point_cls(x=1.5, y=2.5))
334
377
 
335
378
  def test_struct_ctor_accepts_mutable_struct(self):
336
- module = self.init_test_module_classes()
379
+ module = self.init_test_module()
337
380
  segment_cls = module["Segment"]
338
381
  point_cls = module["Point"]
339
382
  segment = segment_cls(
@@ -350,7 +393,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
350
393
  )
351
394
 
352
395
  def test_struct_ctor_checks_type_of_struct_param(self):
353
- module = self.init_test_module_classes()
396
+ module = self.init_test_module()
354
397
  segment_cls = module["Segment"]
355
398
  try:
356
399
  segment_cls(
@@ -362,11 +405,11 @@ class ModuleInitializerTestCase(unittest.TestCase):
362
405
  self.assertIn("Point", str(e))
363
406
 
364
407
  def test_struct_ctor_raises_error_if_unknown_arg(self):
365
- module = self.init_test_module_classes()
408
+ module = self.init_test_module()
366
409
  segment_cls = module["Segment"]
367
410
 
368
411
  def test_to_frozen_checks_type_of_struct_field(self):
369
- module = self.init_test_module_classes()
412
+ module = self.init_test_module()
370
413
  segment_cls = module["Segment"]
371
414
  mutable = segment_cls.Mutable()
372
415
  mutable.a = segment_cls.DEFAULT # Should be a Point
@@ -377,7 +420,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
377
420
  self.assertIn("Point", str(e))
378
421
 
379
422
  def test_struct_ctor_accepts_mutable_list(self):
380
- module = self.init_test_module_classes()
423
+ module = self.init_test_module()
381
424
  shape_cls = module["Shape"]
382
425
  point_cls = module["Point"]
383
426
  shape = shape_cls(
@@ -397,7 +440,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
397
440
  )
398
441
 
399
442
  def test_listuple_not_copied(self):
400
- module = self.init_test_module_classes()
443
+ module = self.init_test_module()
401
444
  shape_cls = module["Shape"]
402
445
  point_cls = module["Point"]
403
446
  shape = shape_cls(
@@ -412,7 +455,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
412
455
  self.assertIsNot(other_shape.points.__class__, tuple)
413
456
 
414
457
  def test_single_empty_listuple_instance(self):
415
- module = self.init_test_module_classes()
458
+ module = self.init_test_module()
416
459
  shape_cls = module["Shape"]
417
460
  shape = shape_cls(
418
461
  points=[],
@@ -422,7 +465,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
422
465
  self.assertIsNot(shape.points, ())
423
466
 
424
467
  def test_optional(self):
425
- module = self.init_test_module_classes()
468
+ module = self.init_test_module()
426
469
  segment_cls = module["Segment"]
427
470
  point_cls = module["Point"]
428
471
  segment = segment_cls(
@@ -437,7 +480,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
437
480
  )
438
481
 
439
482
  def test_enum_unknown_constant(self):
440
- module = self.init_test_module_classes()
483
+ module = self.init_test_module()
441
484
  primary_color_cls = module["PrimaryColor"]
442
485
  unknown = primary_color_cls.UNKNOWN
443
486
  self.assertEqual(unknown.kind, "?")
@@ -448,7 +491,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
448
491
  self.assertEqual(serializer.to_json(unknown, readable_flavor=True), "?")
449
492
 
450
493
  def test_enum_user_defined_constant(self):
451
- module = self.init_test_module_classes()
494
+ module = self.init_test_module()
452
495
  primary_color_cls = module["PrimaryColor"]
453
496
  red = primary_color_cls.RED
454
497
  self.assertEqual(red.kind, "RED")
@@ -459,7 +502,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
459
502
  self.assertEqual(serializer.to_json(red, readable_flavor=True), "RED")
460
503
 
461
504
  def test_enum_wrap(self):
462
- module = self.init_test_module_classes()
505
+ module = self.init_test_module()
463
506
  status_cls = module["Status"]
464
507
  error = status_cls.wrap_error("An error occurred")
465
508
  self.assertEqual(error.kind, "error")
@@ -473,7 +516,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
473
516
  )
474
517
 
475
518
  def test_enum_wrap_around_mutable_struct(self):
476
- module = self.init_test_module_classes()
519
+ module = self.init_test_module()
477
520
  json_value_cls = module["JsonValue"]
478
521
  json_object_cls = json_value_cls.Object
479
522
  json_object = json_value_cls.wrap_object(json_object_cls().to_mutable())
@@ -484,7 +527,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
484
527
  )
485
528
 
486
529
  def test_class_name(self):
487
- module = self.init_test_module_classes()
530
+ module = self.init_test_module()
488
531
  shape_cls = module["Shape"]
489
532
  json_value_cls = module["JsonValue"]
490
533
  json_object_cls = json_value_cls.Object
@@ -496,7 +539,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
496
539
  self.assertEqual(json_object_cls.__qualname__, "JsonValue.Object")
497
540
 
498
541
  def test_struct_repr(self):
499
- module = self.init_test_module_classes()
542
+ module = self.init_test_module()
500
543
  point_cls = module["Point"]
501
544
  self.assertEqual(
502
545
  repr(point_cls(x=1.5)),
@@ -609,7 +652,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
609
652
  )
610
653
 
611
654
  def test_enum_constant_repr(self):
612
- module = self.init_test_module_classes()
655
+ module = self.init_test_module()
613
656
  primary_color_cls = module["PrimaryColor"]
614
657
  parent_cls = module["Parent"]
615
658
  nested_enum_cls = parent_cls.NestedEnum
@@ -618,7 +661,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
618
661
  self.assertEqual(repr(nested_enum_cls.UNKNOWN), "Parent.NestedEnum.UNKNOWN")
619
662
 
620
663
  def test_enum_value_repr(self):
621
- module = self.init_test_module_classes()
664
+ module = self.init_test_module()
622
665
  status_cls = module["Status"]
623
666
  json_value_cls = module["JsonValue"]
624
667
  json_object_cls = json_value_cls.Object
@@ -656,7 +699,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
656
699
  )
657
700
 
658
701
  def test_find_in_keyed_items(self):
659
- json_value_cls = self.init_test_module_classes()["JsonValue"]
702
+ json_value_cls = self.init_test_module()["JsonValue"]
660
703
  object_cls = json_value_cls.Object
661
704
  entry_cls = json_value_cls.ObjectEntry
662
705
  json_object = object_cls(
@@ -688,7 +731,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
688
731
  self.assertIs(entries.find_or_default("zoo"), entry_cls.DEFAULT)
689
732
 
690
733
  def test_find_in_keyed_items_with_complex_path(self):
691
- module = self.init_test_module_classes()
734
+ module = self.init_test_module()
692
735
  stuff_cls = module["Stuff"]
693
736
  enum_wrapper_cls = module["EnumWrapper"]
694
737
  status_cls = module["Status"]
@@ -714,14 +757,14 @@ class ModuleInitializerTestCase(unittest.TestCase):
714
757
  self.assertIs(enum_wrappers.find("error"), enum_wrappers[1])
715
758
 
716
759
  def test_name_overrides(self):
717
- name_overrides_cls = self.init_test_module_classes()["Stuff"].NameOverrides
760
+ name_overrides_cls = self.init_test_module()["Stuff"].NameOverrides
718
761
  name_overrides = name_overrides_cls(y=3)
719
762
  self.assertEqual(name_overrides.y, 3)
720
763
  self.assertEqual(name_overrides_cls.__name__, "NameOverrides")
721
764
  self.assertEqual(name_overrides_cls.__qualname__, "Stuff.NameOverrides")
722
765
 
723
766
  def test_mutable_getter_of_struct(self):
724
- module = self.init_test_module_classes()
767
+ module = self.init_test_module()
725
768
  segment_cls = module["Segment"]
726
769
  point_cls = module["Point"]
727
770
  segment = segment_cls(
@@ -739,7 +782,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
739
782
  self.assertEqual(str(e), "expected: Point or Point.Mutable; found: str")
740
783
 
741
784
  def test_mutable_getter_of_array(self):
742
- module = self.init_test_module_classes()
785
+ module = self.init_test_module()
743
786
  shape_cls = module["Shape"]
744
787
  point_cls = module["Point"]
745
788
  shape = shape_cls(
@@ -751,3 +794,32 @@ class ModuleInitializerTestCase(unittest.TestCase):
751
794
  points = shape.mutable_points
752
795
  self.assertIsInstance(points, list)
753
796
  self.assertIs(shape.mutable_points, points)
797
+
798
+ def test_methods(self):
799
+ module = self.init_test_module()
800
+ first_method = module["FirstMethod"]
801
+ self.assertEqual(
802
+ first_method,
803
+ Method(
804
+ name="FirstMethod",
805
+ number=-300,
806
+ request_serializer=module["Point"].SERIALIZER,
807
+ response_serializer=module["Shape"].SERIALIZER,
808
+ ),
809
+ )
810
+ second_method = module["MethodVar"]
811
+ self.assertEqual(
812
+ second_method,
813
+ Method(
814
+ name="SecondMethod",
815
+ number=-301,
816
+ request_serializer=module["Point"].SERIALIZER,
817
+ response_serializer=module["Shape"].SERIALIZER,
818
+ ),
819
+ )
820
+
821
+ def test_constants(self):
822
+ module = self.init_test_module()
823
+ c = module["C"]
824
+ Point = module["Point"]
825
+ self.assertEqual(c, Point(1.5, 2.5))
@@ -0,0 +1,47 @@
1
+ import unittest
2
+
3
+ from soialib import (
4
+ Timestamp,
5
+ array_serializer,
6
+ optional_serializer,
7
+ primitive_serializer,
8
+ )
9
+
10
+
11
+ class TimestampTestCase(unittest.TestCase):
12
+ def test_primitive_serializers(self):
13
+ self.assertEqual(primitive_serializer("bool").to_json_code(True), "true")
14
+ self.assertEqual(primitive_serializer("int32").to_json_code(7), "7")
15
+ self.assertEqual(
16
+ primitive_serializer("int64").to_json_code(2147483648), "2147483648"
17
+ )
18
+ self.assertEqual(
19
+ primitive_serializer("uint64").to_json_code(2147483648),
20
+ "2147483648",
21
+ )
22
+ self.assertEqual(primitive_serializer("float32").to_json_code(3.14), "3.14")
23
+ self.assertEqual(primitive_serializer("float64").to_json_code(3.14), "3.14")
24
+ self.assertEqual(
25
+ primitive_serializer("timestamp").to_json_code(
26
+ Timestamp.from_unix_millis(3)
27
+ ),
28
+ "3",
29
+ )
30
+ self.assertEqual(primitive_serializer("string").to_json_code("foo"), '"foo"')
31
+ self.assertEqual(primitive_serializer("bytes").to_json_code(b"foo"), '"666f6f"')
32
+
33
+ def test_array_serializer(self):
34
+ self.assertEqual(
35
+ array_serializer(primitive_serializer("bool")).to_json_code([True, False]),
36
+ "[true, false]",
37
+ )
38
+
39
+ def test_optional_serializer(self):
40
+ self.assertEqual(
41
+ optional_serializer(primitive_serializer("bool")).to_json_code(True),
42
+ "true",
43
+ )
44
+ self.assertEqual(
45
+ optional_serializer(primitive_serializer("bool")).to_json_code(None),
46
+ "null",
47
+ )
@@ -3,6 +3,7 @@ import unittest
3
3
  from datetime import datetime, timedelta, timezone
4
4
  from typing import Any
5
5
 
6
+ import soialib
6
7
  from soialib.timestamp import Timestamp
7
8
 
8
9
 
@@ -121,3 +122,6 @@ class TimestampTestCase(unittest.TestCase):
121
122
  repr(Timestamp.MIN),
122
123
  "Timestamp(unix_millis=-8640000000000000)",
123
124
  )
125
+
126
+ def test_exported_from_soialib(self):
127
+ self.assertIs(soialib.Timestamp, Timestamp)
@@ -1,5 +0,0 @@
1
- from soialib.keyed_items import KeyedItems
2
- from soialib.serializer import Serializer
3
- from soialib.timestamp import Timestamp
4
-
5
- __all__ = ["KeyedItems", "Serializer", "Timestamp"]
File without changes
File without changes
File without changes