soia-client 1.0.3__tar.gz → 1.0.5__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.3 → soia_client-1.0.5}/PKG-INFO +1 -1
  2. {soia_client-1.0.3 → soia_client-1.0.5}/pyproject.toml +1 -1
  3. {soia_client-1.0.3 → soia_client-1.0.5}/soia_client.egg-info/PKG-INFO +1 -1
  4. {soia_client-1.0.3 → soia_client-1.0.5}/soia_client.egg-info/SOURCES.txt +3 -0
  5. soia_client-1.0.5/soialib/__init__.py +19 -0
  6. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/impl/arrays.py +7 -6
  7. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/impl/optionals.py +3 -3
  8. soia_client-1.0.5/soialib/method.py +17 -0
  9. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/module_initializer.py +24 -3
  10. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/serializer.py +14 -1
  11. soia_client-1.0.5/soialib/serializers.py +74 -0
  12. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/spec.py +17 -1
  13. {soia_client-1.0.3 → soia_client-1.0.5}/tests/test_module_initializer.py +86 -34
  14. soia_client-1.0.5/tests/test_serializers.py +47 -0
  15. {soia_client-1.0.3 → soia_client-1.0.5}/tests/test_timestamp.py +4 -0
  16. soia_client-1.0.3/soialib/__init__.py +0 -5
  17. {soia_client-1.0.3 → soia_client-1.0.5}/LICENSE +0 -0
  18. {soia_client-1.0.3 → soia_client-1.0.5}/README +0 -0
  19. {soia_client-1.0.3 → soia_client-1.0.5}/setup.cfg +0 -0
  20. {soia_client-1.0.3 → soia_client-1.0.5}/soia_client.egg-info/dependency_links.txt +0 -0
  21. {soia_client-1.0.3 → soia_client-1.0.5}/soia_client.egg-info/top_level.txt +0 -0
  22. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/impl/__init__.py +0 -0
  23. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/impl/encoding.py +0 -0
  24. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/impl/enums.py +0 -0
  25. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/impl/function_maker.py +0 -0
  26. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/impl/primitives.py +0 -0
  27. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/impl/repr.py +0 -0
  28. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/impl/structs.py +0 -0
  29. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/impl/type_adapter.py +0 -0
  30. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/keyed_items.py +0 -0
  31. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/never.py +0 -0
  32. {soia_client-1.0.3 → soia_client-1.0.5}/soialib/timestamp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: soia-client
3
- Version: 1.0.3
3
+ Version: 1.0.5
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.3"
7
+ version = "1.0.5"
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.3
3
+ Version: 1.0.5
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
  )
@@ -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,
@@ -43,7 +45,7 @@ def init_module_classes(
43
45
  type.key_attributes,
44
46
  )
45
47
  elif isinstance(type, spec.OptionalType):
46
- return optionals.get_optional_adapter(resolve_type(type.value))
48
+ return optionals.get_optional_adapter(resolve_type(type.other))
47
49
  elif isinstance(type, str):
48
50
  # A record id.
49
51
  return record_id_to_adapter[type]
@@ -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
+ }
@@ -24,7 +24,7 @@ class ArrayType:
24
24
 
25
25
  @dataclass(frozen=True)
26
26
  class OptionalType:
27
- value: "Type"
27
+ other: "Type"
28
28
 
29
29
 
30
30
  # TODO: comment
@@ -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=(
@@ -259,19 +260,41 @@ class ModuleInitializerTestCase(unittest.TestCase):
259
260
  ),
260
261
  ),
261
262
  ),
263
+ methods=(
264
+ spec.Method(
265
+ name="FirstMethod",
266
+ number=-300,
267
+ request_type="my/module.soia:Point",
268
+ response_type="my/module.soia:Shape",
269
+ ),
270
+ spec.Method(
271
+ name="SecondMethod",
272
+ number=-301,
273
+ request_type="my/module.soia:Point",
274
+ response_type="my/module.soia:Shape",
275
+ _var_name="MethodVar",
276
+ ),
277
+ ),
278
+ constants=(
279
+ spec.Constant(
280
+ name="C",
281
+ type="my/module.soia:Point",
282
+ json_code="[1.5, 2.5]",
283
+ ),
284
+ ),
262
285
  globals=globals,
263
286
  record_id_to_adapter={},
264
287
  )
265
288
  return globals
266
289
 
267
290
  def test_struct_getters(self):
268
- point_cls = self.init_test_module_classes()["Point"]
291
+ point_cls = self.init_test_module()["Point"]
269
292
  point = point_cls(x=1.5, y=2.5)
270
293
  self.assertEqual(point.x, 1.5)
271
294
  self.assertEqual(point.y, 2.5)
272
295
 
273
296
  def test_to_mutable(self):
274
- point_cls = self.init_test_module_classes()["Point"]
297
+ point_cls = self.init_test_module()["Point"]
275
298
  point = point_cls(x=1.5, y=2.5)
276
299
  mutable = point.to_mutable()
277
300
  mutable.x = 4.0
@@ -281,7 +304,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
281
304
  self.assertIs(point.to_frozen(), point)
282
305
 
283
306
  def test_struct_eq(self):
284
- point_cls = self.init_test_module_classes()["Point"]
307
+ point_cls = self.init_test_module()["Point"]
285
308
  a = point_cls(x=1.5, y=2.5)
286
309
  b = point_cls(x=1.5, y=2.5)
287
310
  c = point_cls(x=1.5, y=3.0)
@@ -292,11 +315,11 @@ class ModuleInitializerTestCase(unittest.TestCase):
292
315
  self.assertEqual(point_cls(), point_cls(x=0.0, y=0.0))
293
316
 
294
317
  def test_or_mutable(self):
295
- point_cls = self.init_test_module_classes()["Point"]
318
+ point_cls = self.init_test_module()["Point"]
296
319
  point_cls.OrMutable
297
320
 
298
321
  def test_default_values(self):
299
- primitives_cls = self.init_test_module_classes()["Primitives"]
322
+ primitives_cls = self.init_test_module()["Primitives"]
300
323
  a = primitives_cls(
301
324
  bool=False,
302
325
  bytes=b"",
@@ -311,29 +334,29 @@ class ModuleInitializerTestCase(unittest.TestCase):
311
334
  self.assertEqual(hash(a), hash(b))
312
335
 
313
336
  def test_to_dense_json(self):
314
- point_cls = self.init_test_module_classes()["Point"]
337
+ point_cls = self.init_test_module()["Point"]
315
338
  point = point_cls(x=1.5, y=2.5)
316
339
  json = point_cls.SERIALIZER.to_json(point)
317
340
  self.assertEqual(json, [1.5, 2.5])
318
341
 
319
342
  def test_to_readable_json(self):
320
- point_cls = self.init_test_module_classes()["Point"]
343
+ point_cls = self.init_test_module()["Point"]
321
344
  point = point_cls(x=1.5, y=2.5)
322
345
  json = point_cls.SERIALIZER.to_json(point, readable_flavor=True)
323
346
  self.assertEqual(json, {"x": 1.5, "y": 2.5})
324
347
 
325
348
  def test_from_dense_json(self):
326
- point_cls = self.init_test_module_classes()["Point"]
349
+ point_cls = self.init_test_module()["Point"]
327
350
  point = point_cls.SERIALIZER.from_json([1.5, 2.5])
328
351
  self.assertEqual(point, point_cls(x=1.5, y=2.5))
329
352
 
330
353
  def test_from_readable_json(self):
331
- point_cls = self.init_test_module_classes()["Point"]
354
+ point_cls = self.init_test_module()["Point"]
332
355
  point = point_cls.SERIALIZER.from_json({"x": 1.5, "y": 2.5})
333
356
  self.assertEqual(point, point_cls(x=1.5, y=2.5))
334
357
 
335
358
  def test_struct_ctor_accepts_mutable_struct(self):
336
- module = self.init_test_module_classes()
359
+ module = self.init_test_module()
337
360
  segment_cls = module["Segment"]
338
361
  point_cls = module["Point"]
339
362
  segment = segment_cls(
@@ -350,7 +373,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
350
373
  )
351
374
 
352
375
  def test_struct_ctor_checks_type_of_struct_param(self):
353
- module = self.init_test_module_classes()
376
+ module = self.init_test_module()
354
377
  segment_cls = module["Segment"]
355
378
  try:
356
379
  segment_cls(
@@ -362,11 +385,11 @@ class ModuleInitializerTestCase(unittest.TestCase):
362
385
  self.assertIn("Point", str(e))
363
386
 
364
387
  def test_struct_ctor_raises_error_if_unknown_arg(self):
365
- module = self.init_test_module_classes()
388
+ module = self.init_test_module()
366
389
  segment_cls = module["Segment"]
367
390
 
368
391
  def test_to_frozen_checks_type_of_struct_field(self):
369
- module = self.init_test_module_classes()
392
+ module = self.init_test_module()
370
393
  segment_cls = module["Segment"]
371
394
  mutable = segment_cls.Mutable()
372
395
  mutable.a = segment_cls.DEFAULT # Should be a Point
@@ -377,7 +400,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
377
400
  self.assertIn("Point", str(e))
378
401
 
379
402
  def test_struct_ctor_accepts_mutable_list(self):
380
- module = self.init_test_module_classes()
403
+ module = self.init_test_module()
381
404
  shape_cls = module["Shape"]
382
405
  point_cls = module["Point"]
383
406
  shape = shape_cls(
@@ -397,7 +420,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
397
420
  )
398
421
 
399
422
  def test_listuple_not_copied(self):
400
- module = self.init_test_module_classes()
423
+ module = self.init_test_module()
401
424
  shape_cls = module["Shape"]
402
425
  point_cls = module["Point"]
403
426
  shape = shape_cls(
@@ -412,7 +435,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
412
435
  self.assertIsNot(other_shape.points.__class__, tuple)
413
436
 
414
437
  def test_single_empty_listuple_instance(self):
415
- module = self.init_test_module_classes()
438
+ module = self.init_test_module()
416
439
  shape_cls = module["Shape"]
417
440
  shape = shape_cls(
418
441
  points=[],
@@ -422,7 +445,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
422
445
  self.assertIsNot(shape.points, ())
423
446
 
424
447
  def test_optional(self):
425
- module = self.init_test_module_classes()
448
+ module = self.init_test_module()
426
449
  segment_cls = module["Segment"]
427
450
  point_cls = module["Point"]
428
451
  segment = segment_cls(
@@ -437,7 +460,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
437
460
  )
438
461
 
439
462
  def test_enum_unknown_constant(self):
440
- module = self.init_test_module_classes()
463
+ module = self.init_test_module()
441
464
  primary_color_cls = module["PrimaryColor"]
442
465
  unknown = primary_color_cls.UNKNOWN
443
466
  self.assertEqual(unknown.kind, "?")
@@ -448,7 +471,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
448
471
  self.assertEqual(serializer.to_json(unknown, readable_flavor=True), "?")
449
472
 
450
473
  def test_enum_user_defined_constant(self):
451
- module = self.init_test_module_classes()
474
+ module = self.init_test_module()
452
475
  primary_color_cls = module["PrimaryColor"]
453
476
  red = primary_color_cls.RED
454
477
  self.assertEqual(red.kind, "RED")
@@ -459,7 +482,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
459
482
  self.assertEqual(serializer.to_json(red, readable_flavor=True), "RED")
460
483
 
461
484
  def test_enum_wrap(self):
462
- module = self.init_test_module_classes()
485
+ module = self.init_test_module()
463
486
  status_cls = module["Status"]
464
487
  error = status_cls.wrap_error("An error occurred")
465
488
  self.assertEqual(error.kind, "error")
@@ -473,7 +496,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
473
496
  )
474
497
 
475
498
  def test_enum_wrap_around_mutable_struct(self):
476
- module = self.init_test_module_classes()
499
+ module = self.init_test_module()
477
500
  json_value_cls = module["JsonValue"]
478
501
  json_object_cls = json_value_cls.Object
479
502
  json_object = json_value_cls.wrap_object(json_object_cls().to_mutable())
@@ -484,7 +507,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
484
507
  )
485
508
 
486
509
  def test_class_name(self):
487
- module = self.init_test_module_classes()
510
+ module = self.init_test_module()
488
511
  shape_cls = module["Shape"]
489
512
  json_value_cls = module["JsonValue"]
490
513
  json_object_cls = json_value_cls.Object
@@ -496,7 +519,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
496
519
  self.assertEqual(json_object_cls.__qualname__, "JsonValue.Object")
497
520
 
498
521
  def test_struct_repr(self):
499
- module = self.init_test_module_classes()
522
+ module = self.init_test_module()
500
523
  point_cls = module["Point"]
501
524
  self.assertEqual(
502
525
  repr(point_cls(x=1.5)),
@@ -609,7 +632,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
609
632
  )
610
633
 
611
634
  def test_enum_constant_repr(self):
612
- module = self.init_test_module_classes()
635
+ module = self.init_test_module()
613
636
  primary_color_cls = module["PrimaryColor"]
614
637
  parent_cls = module["Parent"]
615
638
  nested_enum_cls = parent_cls.NestedEnum
@@ -618,7 +641,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
618
641
  self.assertEqual(repr(nested_enum_cls.UNKNOWN), "Parent.NestedEnum.UNKNOWN")
619
642
 
620
643
  def test_enum_value_repr(self):
621
- module = self.init_test_module_classes()
644
+ module = self.init_test_module()
622
645
  status_cls = module["Status"]
623
646
  json_value_cls = module["JsonValue"]
624
647
  json_object_cls = json_value_cls.Object
@@ -656,7 +679,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
656
679
  )
657
680
 
658
681
  def test_find_in_keyed_items(self):
659
- json_value_cls = self.init_test_module_classes()["JsonValue"]
682
+ json_value_cls = self.init_test_module()["JsonValue"]
660
683
  object_cls = json_value_cls.Object
661
684
  entry_cls = json_value_cls.ObjectEntry
662
685
  json_object = object_cls(
@@ -688,7 +711,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
688
711
  self.assertIs(entries.find_or_default("zoo"), entry_cls.DEFAULT)
689
712
 
690
713
  def test_find_in_keyed_items_with_complex_path(self):
691
- module = self.init_test_module_classes()
714
+ module = self.init_test_module()
692
715
  stuff_cls = module["Stuff"]
693
716
  enum_wrapper_cls = module["EnumWrapper"]
694
717
  status_cls = module["Status"]
@@ -714,14 +737,14 @@ class ModuleInitializerTestCase(unittest.TestCase):
714
737
  self.assertIs(enum_wrappers.find("error"), enum_wrappers[1])
715
738
 
716
739
  def test_name_overrides(self):
717
- name_overrides_cls = self.init_test_module_classes()["Stuff"].NameOverrides
740
+ name_overrides_cls = self.init_test_module()["Stuff"].NameOverrides
718
741
  name_overrides = name_overrides_cls(y=3)
719
742
  self.assertEqual(name_overrides.y, 3)
720
743
  self.assertEqual(name_overrides_cls.__name__, "NameOverrides")
721
744
  self.assertEqual(name_overrides_cls.__qualname__, "Stuff.NameOverrides")
722
745
 
723
746
  def test_mutable_getter_of_struct(self):
724
- module = self.init_test_module_classes()
747
+ module = self.init_test_module()
725
748
  segment_cls = module["Segment"]
726
749
  point_cls = module["Point"]
727
750
  segment = segment_cls(
@@ -739,7 +762,7 @@ class ModuleInitializerTestCase(unittest.TestCase):
739
762
  self.assertEqual(str(e), "expected: Point or Point.Mutable; found: str")
740
763
 
741
764
  def test_mutable_getter_of_array(self):
742
- module = self.init_test_module_classes()
765
+ module = self.init_test_module()
743
766
  shape_cls = module["Shape"]
744
767
  point_cls = module["Point"]
745
768
  shape = shape_cls(
@@ -751,3 +774,32 @@ class ModuleInitializerTestCase(unittest.TestCase):
751
774
  points = shape.mutable_points
752
775
  self.assertIsInstance(points, list)
753
776
  self.assertIs(shape.mutable_points, points)
777
+
778
+ def test_methods(self):
779
+ module = self.init_test_module()
780
+ first_method = module["FirstMethod"]
781
+ self.assertEqual(
782
+ first_method,
783
+ Method(
784
+ name="FirstMethod",
785
+ number=-300,
786
+ request_serializer=module["Point"].SERIALIZER,
787
+ response_serializer=module["Shape"].SERIALIZER,
788
+ ),
789
+ )
790
+ second_method = module["MethodVar"]
791
+ self.assertEqual(
792
+ second_method,
793
+ Method(
794
+ name="SecondMethod",
795
+ number=-301,
796
+ request_serializer=module["Point"].SERIALIZER,
797
+ response_serializer=module["Shape"].SERIALIZER,
798
+ ),
799
+ )
800
+
801
+ def test_constants(self):
802
+ module = self.init_test_module()
803
+ c = module["C"]
804
+ Point = module["Point"]
805
+ 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