soia-client 1.1.1__tar.gz → 1.1.3__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.1.1 → soia_client-1.1.3}/PKG-INFO +1 -1
- {soia_client-1.1.1 → soia_client-1.1.3}/pyproject.toml +1 -1
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/enums.py +8 -2
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/service.py +29 -2
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/reflection.py +29 -4
- {soia_client-1.1.1 → soia_client-1.1.3}/soia_client.egg-info/PKG-INFO +1 -1
- {soia_client-1.1.1 → soia_client-1.1.3}/tests/test_module_initializer.py +5 -2
- {soia_client-1.1.1 → soia_client-1.1.3}/LICENSE +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/README.md +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/setup.cfg +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/__init__.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/__init__.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/arrays.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/function_maker.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/keep.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/keyed_items.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/method.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/never.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/optionals.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/primitives.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/repr.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/serializer.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/serializers.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/service_client.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/structs.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/timestamp.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_impl/type_adapter.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_module_initializer.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia/_spec.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia_client.egg-info/SOURCES.txt +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia_client.egg-info/dependency_links.txt +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/soia_client.egg-info/top_level.txt +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/tests/test_serializers.py +0 -0
- {soia_client-1.1.1 → soia_client-1.1.3}/tests/test_timestamp.py +0 -0
|
@@ -3,12 +3,11 @@ from collections.abc import Callable, Sequence
|
|
|
3
3
|
from dataclasses import FrozenInstanceError, dataclass
|
|
4
4
|
from typing import Any, Final, Union
|
|
5
5
|
|
|
6
|
+
from soia import _spec, reflection
|
|
6
7
|
from soia._impl.function_maker import BodyBuilder, Expr, ExprLike, Line, make_function
|
|
7
8
|
from soia._impl.repr import repr_impl
|
|
8
9
|
from soia._impl.type_adapter import TypeAdapter
|
|
9
10
|
|
|
10
|
-
from soia import _spec, reflection
|
|
11
|
-
|
|
12
11
|
|
|
13
12
|
class EnumAdapter(TypeAdapter):
|
|
14
13
|
__slots__ = (
|
|
@@ -139,6 +138,13 @@ class EnumAdapter(TypeAdapter):
|
|
|
139
138
|
kind="enum",
|
|
140
139
|
id=record_id,
|
|
141
140
|
fields=tuple(
|
|
141
|
+
reflection.Field(
|
|
142
|
+
name=field.name,
|
|
143
|
+
number=field.number,
|
|
144
|
+
type=None,
|
|
145
|
+
)
|
|
146
|
+
for field in self.all_constant_fields if field.number != 0
|
|
147
|
+
) + tuple(
|
|
142
148
|
reflection.Field(
|
|
143
149
|
name=field.spec.name,
|
|
144
150
|
number=field.spec.number,
|
|
@@ -26,29 +26,50 @@ class _MethodImpl(Generic[Request, Response, RequestHeaders, ResponseHeaders]):
|
|
|
26
26
|
@dataclass(frozen=True)
|
|
27
27
|
class RawServiceResponse:
|
|
28
28
|
data: str
|
|
29
|
-
type: Literal["ok-json", "bad-request", "server-error"]
|
|
29
|
+
type: Literal["ok-json", "ok-html", "bad-request", "server-error"]
|
|
30
30
|
|
|
31
31
|
@property
|
|
32
32
|
def status_code(self):
|
|
33
|
-
if self.type == "ok-json":
|
|
33
|
+
if self.type == "ok-json" or self.type == "ok-html":
|
|
34
34
|
return 200
|
|
35
35
|
elif self.type == "bad-request":
|
|
36
36
|
return 400
|
|
37
37
|
elif self.type == "server-error":
|
|
38
38
|
return 500
|
|
39
39
|
else:
|
|
40
|
+
_: Never = self.type
|
|
40
41
|
raise TypeError(f"Unknown response type: {self.type}")
|
|
41
42
|
|
|
42
43
|
@property
|
|
43
44
|
def content_type(self):
|
|
44
45
|
if self.type == "ok-json":
|
|
45
46
|
return "application/json"
|
|
47
|
+
if self.type == "ok-html":
|
|
48
|
+
return "text/html; charset=utf-8"
|
|
46
49
|
elif self.type == "bad-request" or self.type == "server-error":
|
|
47
50
|
return "text/plain; charset=utf-8"
|
|
48
51
|
else:
|
|
52
|
+
_: Never = self.type
|
|
49
53
|
raise TypeError(f"Unknown response type: {self.type}")
|
|
50
54
|
|
|
51
55
|
|
|
56
|
+
# Copied from
|
|
57
|
+
# https://github.com/gepheum/restudio/blob/main/index.jsdeliver.html
|
|
58
|
+
_RESTUDIO_HTML = """<!DOCTYPE html>
|
|
59
|
+
|
|
60
|
+
<html>
|
|
61
|
+
<head>
|
|
62
|
+
<meta charset="utf-8" />
|
|
63
|
+
<title>RESTudio</title>
|
|
64
|
+
<script src="dist/restudio-standalone.js"></script>
|
|
65
|
+
</head>
|
|
66
|
+
<body style="margin: 0; padding: 0;">
|
|
67
|
+
<restudio-app></restudio-app>
|
|
68
|
+
</body>
|
|
69
|
+
</html>
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
|
|
52
73
|
@dataclass()
|
|
53
74
|
class _HandleRequestFlow(Generic[Request, Response, RequestHeaders, ResponseHeaders]):
|
|
54
75
|
req_body: str
|
|
@@ -94,6 +115,9 @@ class _HandleRequestFlow(Generic[Request, Response, RequestHeaders, ResponseHead
|
|
|
94
115
|
if self.req_body in ["", "list"]:
|
|
95
116
|
return self._handle_list()
|
|
96
117
|
|
|
118
|
+
if self.req_body == "restudio":
|
|
119
|
+
return self._handle_restudio()
|
|
120
|
+
|
|
97
121
|
# Method invokation
|
|
98
122
|
method_name: str
|
|
99
123
|
method_number: int | None
|
|
@@ -211,6 +235,9 @@ class _HandleRequestFlow(Generic[Request, Response, RequestHeaders, ResponseHead
|
|
|
211
235
|
)
|
|
212
236
|
return RawServiceResponse(json_code, "ok-json")
|
|
213
237
|
|
|
238
|
+
def _handle_restudio(self) -> RawServiceResponse:
|
|
239
|
+
return RawServiceResponse(_RESTUDIO_HTML, "ok-html")
|
|
240
|
+
|
|
214
241
|
def _response_to_json(
|
|
215
242
|
self,
|
|
216
243
|
res: Response,
|
|
@@ -109,11 +109,19 @@ class _Serializer(Generic[_T]):
|
|
|
109
109
|
from_json: Callable[[Any], _T]
|
|
110
110
|
|
|
111
111
|
|
|
112
|
+
@dataclass(frozen=True, eq=True)
|
|
113
|
+
class _NoDefault:
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
_NO_DEFAULT: Final = _NoDefault()
|
|
118
|
+
|
|
119
|
+
|
|
112
120
|
@dataclass(frozen=True)
|
|
113
121
|
class _FieldSerializer(Generic[_T]):
|
|
114
122
|
name: str
|
|
115
123
|
serializer: _Serializer[_T]
|
|
116
|
-
default:
|
|
124
|
+
default: _T | _NoDefault = _NO_DEFAULT
|
|
117
125
|
|
|
118
126
|
|
|
119
127
|
def _primitive_serializer(check_type_fn: Callable[[Any], _T]) -> _Serializer[_T]:
|
|
@@ -144,7 +152,7 @@ def _dataclass_serializer(
|
|
|
144
152
|
def field_from_json(field: _FieldSerializer) -> Any:
|
|
145
153
|
value_json = json.get(field.name)
|
|
146
154
|
if value_json is None:
|
|
147
|
-
if field.default
|
|
155
|
+
if field.default != _NO_DEFAULT:
|
|
148
156
|
return field.default
|
|
149
157
|
else:
|
|
150
158
|
# Will raise an exception.
|
|
@@ -196,6 +204,22 @@ def _forwarding_serializer(
|
|
|
196
204
|
return _Serializer(to_json, from_json)
|
|
197
205
|
|
|
198
206
|
|
|
207
|
+
def _optional_serializer(
|
|
208
|
+
other_serializer: _Serializer[_T],
|
|
209
|
+
) -> _Serializer[Optional[_T]]:
|
|
210
|
+
def to_json(input: Optional[_T]) -> Any:
|
|
211
|
+
if input is None:
|
|
212
|
+
return None
|
|
213
|
+
return other_serializer.to_json(input)
|
|
214
|
+
|
|
215
|
+
def from_json(json: Any) -> Optional[_T]:
|
|
216
|
+
if json is None:
|
|
217
|
+
return None
|
|
218
|
+
return other_serializer.from_json(json)
|
|
219
|
+
|
|
220
|
+
return _Serializer(to_json, from_json)
|
|
221
|
+
|
|
222
|
+
|
|
199
223
|
# ==============================================================================
|
|
200
224
|
# INTERNAL: JSON serialization of TypeDescriptor
|
|
201
225
|
# ==============================================================================
|
|
@@ -292,9 +316,10 @@ _FIELD_SERIALIZER: Final = _dataclass_serializer(
|
|
|
292
316
|
"name",
|
|
293
317
|
_primitive_serializer(str),
|
|
294
318
|
),
|
|
295
|
-
_FieldSerializer(
|
|
319
|
+
_FieldSerializer[Optional[Type]](
|
|
296
320
|
"type",
|
|
297
|
-
_forwarding_serializer(_type_serializer),
|
|
321
|
+
_optional_serializer(_forwarding_serializer(_type_serializer)),
|
|
322
|
+
default=None,
|
|
298
323
|
),
|
|
299
324
|
_FieldSerializer(
|
|
300
325
|
"number",
|
|
@@ -2,11 +2,10 @@ import dataclasses
|
|
|
2
2
|
import unittest
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
+
from soia import KeyedItems, Method, Timestamp, _spec
|
|
5
6
|
from soia._module_initializer import init_module
|
|
6
7
|
from soia.reflection import TypeDescriptor
|
|
7
8
|
|
|
8
|
-
from soia import KeyedItems, Method, Timestamp, _spec
|
|
9
|
-
|
|
10
9
|
|
|
11
10
|
class ModuleInitializerTestCase(unittest.TestCase):
|
|
12
11
|
def init_test_module(self) -> dict[str, Any]:
|
|
@@ -1201,6 +1200,10 @@ class ModuleInitializerTestCase(unittest.TestCase):
|
|
|
1201
1200
|
"kind": "enum",
|
|
1202
1201
|
"id": "my/module.soia:JsonValue",
|
|
1203
1202
|
"fields": [
|
|
1203
|
+
{
|
|
1204
|
+
"name": "NULL",
|
|
1205
|
+
"number": 1,
|
|
1206
|
+
},
|
|
1204
1207
|
{
|
|
1205
1208
|
"name": "bool",
|
|
1206
1209
|
"type": {"kind": "primitive", "value": "bool"},
|
|
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
|
|
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
|