soia-client 1.0.4__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.
- {soia_client-1.0.4 → soia_client-1.0.5}/PKG-INFO +1 -1
- {soia_client-1.0.4 → soia_client-1.0.5}/pyproject.toml +1 -1
- {soia_client-1.0.4 → soia_client-1.0.5}/soia_client.egg-info/PKG-INFO +1 -1
- {soia_client-1.0.4 → soia_client-1.0.5}/soia_client.egg-info/SOURCES.txt +3 -0
- soia_client-1.0.5/soialib/__init__.py +19 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/impl/arrays.py +7 -6
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/impl/optionals.py +3 -3
- soia_client-1.0.5/soialib/method.py +17 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/module_initializer.py +23 -2
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/serializer.py +14 -1
- soia_client-1.0.5/soialib/serializers.py +74 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/spec.py +16 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/tests/test_module_initializer.py +86 -34
- soia_client-1.0.5/tests/test_serializers.py +47 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/tests/test_timestamp.py +4 -0
- soia_client-1.0.4/soialib/__init__.py +0 -5
- {soia_client-1.0.4 → soia_client-1.0.5}/LICENSE +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/README +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/setup.cfg +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soia_client.egg-info/dependency_links.txt +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soia_client.egg-info/top_level.txt +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/impl/__init__.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/impl/encoding.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/impl/enums.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/impl/function_maker.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/impl/primitives.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/impl/repr.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/impl/structs.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/impl/type_adapter.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/keyed_items.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/never.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.5}/soialib/timestamp.py +0 -0
|
@@ -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
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
111
|
-
|
|
111
|
+
_ItemAndKeyAttributes = tuple[TypeAdapter, tuple[str, ...]]
|
|
112
|
+
_ItemToArrayAdapter = WeakValueDictionary[_ItemAndKeyAttributes, _ArrayAdapter]
|
|
112
113
|
|
|
113
|
-
|
|
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
|
|
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:
|
|
73
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
14
|
+
def init_test_module(self) -> dict[str, Any]:
|
|
14
15
|
globals: dict[str, Any] = {}
|
|
15
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|