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.
- {soia_client-1.0.4 → soia_client-1.0.6}/PKG-INFO +1 -1
- {soia_client-1.0.4 → soia_client-1.0.6}/pyproject.toml +1 -1
- {soia_client-1.0.4 → soia_client-1.0.6}/soia_client.egg-info/PKG-INFO +1 -1
- {soia_client-1.0.4 → soia_client-1.0.6}/soia_client.egg-info/SOURCES.txt +3 -0
- soia_client-1.0.6/soialib/__init__.py +19 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/arrays.py +7 -6
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/optionals.py +3 -3
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/structs.py +13 -1
- soia_client-1.0.6/soialib/method.py +17 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/module_initializer.py +23 -2
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/serializer.py +14 -1
- soia_client-1.0.6/soialib/serializers.py +74 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/spec.py +16 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/tests/test_module_initializer.py +106 -34
- soia_client-1.0.6/tests/test_serializers.py +47 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/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.6}/LICENSE +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/README +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/setup.cfg +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soia_client.egg-info/dependency_links.txt +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soia_client.egg-info/top_level.txt +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/__init__.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/encoding.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/enums.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/function_maker.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/primitives.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/repr.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/impl/type_adapter.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/keyed_items.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/soialib/never.py +0 -0
- {soia_client-1.0.4 → soia_client-1.0.6}/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
|
)
|
|
@@ -146,7 +146,7 @@ class StructAdapter(TypeAdapter):
|
|
|
146
146
|
)
|
|
147
147
|
|
|
148
148
|
# Initialize DEFAULT.
|
|
149
|
-
self.gen_class.DEFAULT
|
|
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
|
|
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=(
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|
|
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
|