soia-client 1.0.20__tar.gz → 1.0.21__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.
Files changed (34) hide show
  1. {soia_client-1.0.20 → soia_client-1.0.21}/PKG-INFO +1 -1
  2. {soia_client-1.0.20 → soia_client-1.0.21}/pyproject.toml +1 -1
  3. {soia_client-1.0.20 → soia_client-1.0.21}/soia/__init__.py +3 -3
  4. soia_client-1.0.21/soia/_impl/service.py +339 -0
  5. {soia_client-1.0.20 → soia_client-1.0.21}/soia_client.egg-info/PKG-INFO +1 -1
  6. soia_client-1.0.20/soia/_impl/service.py +0 -205
  7. {soia_client-1.0.20 → soia_client-1.0.21}/LICENSE +0 -0
  8. {soia_client-1.0.20 → soia_client-1.0.21}/README.md +0 -0
  9. {soia_client-1.0.20 → soia_client-1.0.21}/setup.cfg +0 -0
  10. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/__init__.py +0 -0
  11. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/arrays.py +0 -0
  12. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/enums.py +0 -0
  13. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/function_maker.py +0 -0
  14. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/keyed_items.py +0 -0
  15. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/method.py +0 -0
  16. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/never.py +0 -0
  17. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/optionals.py +0 -0
  18. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/primitives.py +0 -0
  19. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/repr.py +0 -0
  20. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/serializer.py +0 -0
  21. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/serializers.py +0 -0
  22. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/service_client.py +0 -0
  23. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/structs.py +0 -0
  24. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/timestamp.py +0 -0
  25. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_impl/type_adapter.py +0 -0
  26. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_module_initializer.py +0 -0
  27. {soia_client-1.0.20 → soia_client-1.0.21}/soia/_spec.py +0 -0
  28. {soia_client-1.0.20 → soia_client-1.0.21}/soia/reflection.py +0 -0
  29. {soia_client-1.0.20 → soia_client-1.0.21}/soia_client.egg-info/SOURCES.txt +0 -0
  30. {soia_client-1.0.20 → soia_client-1.0.21}/soia_client.egg-info/dependency_links.txt +0 -0
  31. {soia_client-1.0.20 → soia_client-1.0.21}/soia_client.egg-info/top_level.txt +0 -0
  32. {soia_client-1.0.20 → soia_client-1.0.21}/tests/test_module_initializer.py +0 -0
  33. {soia_client-1.0.20 → soia_client-1.0.21}/tests/test_serializers.py +0 -0
  34. {soia_client-1.0.20 → soia_client-1.0.21}/tests/test_timestamp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soia-client
3
- Version: 1.0.20
3
+ Version: 1.0.21
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.20"
7
+ version = "1.0.21"
8
8
  description = ""
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tyler Fibonacci", email = "gepheum@gmail.com" }]
@@ -8,7 +8,7 @@ from soia._impl.serializers import (
8
8
  optional_serializer,
9
9
  primitive_serializer,
10
10
  )
11
- from soia._impl.service import RequestHeaders, ResponseHeaders, Service
11
+ from soia._impl.service import RawServiceResponse, Service, ServiceAsync
12
12
  from soia._impl.service_client import ServiceClient
13
13
  from soia._impl.timestamp import Timestamp
14
14
 
@@ -18,10 +18,10 @@ __all__ = [
18
18
  "_",
19
19
  "KeyedItems",
20
20
  "Method",
21
- "RequestHeaders",
22
- "ResponseHeaders",
21
+ "RawServiceResponse",
23
22
  "Serializer",
24
23
  "Service",
24
+ "ServiceAsync",
25
25
  "ServiceClient",
26
26
  "Timestamp",
27
27
  "array_serializer",
@@ -0,0 +1,339 @@
1
+ import inspect
2
+ import json
3
+ from collections.abc import Awaitable, Callable
4
+ from dataclasses import dataclass
5
+ from typing import Any, Generic, Literal, TypeVar, Union, cast
6
+
7
+ from soia._impl.method import Method, Request, Response
8
+
9
+ RequestHeaders = TypeVar("RequestHeaders")
10
+
11
+ ResponseHeaders = TypeVar("ResponseHeaders")
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class _MethodImpl(Generic[Request, Response, RequestHeaders, ResponseHeaders]):
16
+ method: Method[Request, Response]
17
+ impl: Callable[
18
+ # Parameters
19
+ [Request, RequestHeaders, ResponseHeaders],
20
+ # Return type
21
+ Union[Response, Awaitable[Response]],
22
+ ]
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class RawServiceResponse:
27
+ data: str
28
+ type: Literal["ok-json", "bad-request", "server-error"]
29
+
30
+ @property
31
+ def status_code(self):
32
+ if self.type == "ok-json":
33
+ return 200
34
+ elif self.type == "bad-request":
35
+ return 400
36
+ elif self.type == "server-error":
37
+ return 500
38
+ else:
39
+ raise TypeError(f"Unknown response type: {self.type}")
40
+
41
+ @property
42
+ def content_type(self):
43
+ if self.type == "ok-json":
44
+ return "application/json"
45
+ elif self.type == "bad-request" or self.type == "server-error":
46
+ return "text/plain; charset=utf-8"
47
+ else:
48
+ raise TypeError(f"Unknown response type: {self.type}")
49
+
50
+
51
+ @dataclass()
52
+ class _HandleRequestFlow(Generic[Request, Response, RequestHeaders, ResponseHeaders]):
53
+ req_body: str
54
+ req_headers: RequestHeaders
55
+ res_headers: ResponseHeaders
56
+ number_to_method_impl: dict[
57
+ int, _MethodImpl[Any, Any, RequestHeaders, ResponseHeaders]
58
+ ]
59
+ _format: str = ""
60
+
61
+ def run(self) -> RawServiceResponse:
62
+ req_impl_pair_or_raw_response = self._parse_request()
63
+ if isinstance(req_impl_pair_or_raw_response, RawServiceResponse):
64
+ return req_impl_pair_or_raw_response
65
+ req, method_impl = req_impl_pair_or_raw_response
66
+ try:
67
+ res = method_impl.impl(req, self.req_headers, self.res_headers)
68
+ except Exception as e:
69
+ return RawServiceResponse(f"server error: {e}", "server-error")
70
+ if inspect.isawaitable(res):
71
+ raise TypeError("Method implementation must be synchronous")
72
+ return self._response_to_json(res, method_impl)
73
+
74
+ async def run_async(self) -> RawServiceResponse:
75
+ req_impl_pair_or_raw_response = self._parse_request()
76
+ if isinstance(req_impl_pair_or_raw_response, RawServiceResponse):
77
+ return req_impl_pair_or_raw_response
78
+ req, method_impl = req_impl_pair_or_raw_response
79
+ try:
80
+ res: Any = method_impl.impl(req, self.req_headers, self.res_headers)
81
+ if inspect.isawaitable(res):
82
+ res = await res
83
+ except Exception as e:
84
+ return RawServiceResponse(f"server error: {e}", "server-error")
85
+ return self._response_to_json(res, method_impl)
86
+
87
+ def _parse_request(
88
+ self,
89
+ ) -> Union[
90
+ tuple[Any, _MethodImpl[Request, Response, RequestHeaders, ResponseHeaders]],
91
+ RawServiceResponse,
92
+ ]:
93
+ if self.req_body == "list":
94
+
95
+ def method_to_json(method: Method) -> Any:
96
+ return {
97
+ "method": method.name,
98
+ "number": method.number,
99
+ "request": method.request_serializer.type_descriptor.as_json(),
100
+ "response": method.response_serializer.type_descriptor.as_json(),
101
+ }
102
+
103
+ json_code = json.dumps(
104
+ {
105
+ "methods": [
106
+ method_to_json(method_impl.method)
107
+ for method_impl in self.number_to_method_impl.values()
108
+ ]
109
+ },
110
+ indent=2,
111
+ )
112
+ return RawServiceResponse(json_code, "ok-json")
113
+
114
+ parts = self.req_body.split(":", 3)
115
+ if len(parts) != 4:
116
+ return RawServiceResponse(
117
+ "bad request: invalid request format", "bad-request"
118
+ )
119
+ method_name = parts[0]
120
+ method_number_str = parts[1]
121
+ self.format = parts[2]
122
+ request_data = parts[3]
123
+ try:
124
+ method_number = int(method_number_str)
125
+ except Exception:
126
+ return RawServiceResponse(
127
+ "bad request: can't parse method number", "bad-request"
128
+ )
129
+ method_impl = self.number_to_method_impl.get(method_number)
130
+ if not method_impl:
131
+ return RawServiceResponse(
132
+ f"bad request: method not found: {method_name}; number: {method_number}",
133
+ "bad-request",
134
+ )
135
+ try:
136
+ req: Any = method_impl.method.request_serializer.from_json_code(
137
+ request_data
138
+ )
139
+ except Exception as e:
140
+ return RawServiceResponse(
141
+ f"bad request: can't parse JSON: {e}", "bad-request"
142
+ )
143
+ return (req, method_impl)
144
+
145
+ def _response_to_json(
146
+ self,
147
+ res: Response,
148
+ method_impl: _MethodImpl[Request, Response, RequestHeaders, ResponseHeaders],
149
+ ) -> RawServiceResponse:
150
+ try:
151
+ res_json = method_impl.method.response_serializer.to_json_code(
152
+ res, readable=(self.format == "readable")
153
+ )
154
+ except Exception as e:
155
+ return RawServiceResponse(
156
+ f"server error: can't serialize response to JSON: {e}", "server-error"
157
+ )
158
+ return RawServiceResponse(res_json, "ok-json")
159
+
160
+
161
+ class _ServiceImpl(Generic[RequestHeaders, ResponseHeaders]):
162
+ _number_to_method_impl: dict[
163
+ int, _MethodImpl[Any, Any, RequestHeaders, ResponseHeaders]
164
+ ]
165
+
166
+ def __init__(self):
167
+ self._number_to_method_impl = {}
168
+
169
+ def add_method(
170
+ self,
171
+ method: Method[Request, Response],
172
+ impl: Union[
173
+ # Sync
174
+ Callable[[Request], Response],
175
+ Callable[[Request, RequestHeaders], Response],
176
+ Callable[[Request, RequestHeaders, ResponseHeaders], Response],
177
+ # Async
178
+ Callable[[Request], Awaitable[Response]],
179
+ Callable[[Request, RequestHeaders], Awaitable[Response]],
180
+ Callable[[Request, RequestHeaders, ResponseHeaders], Awaitable[Response]],
181
+ ],
182
+ ) -> None:
183
+ signature = inspect.Signature.from_callable(impl)
184
+ num_positional_params = 0
185
+ for param in signature.parameters.values():
186
+ if param.kind in (
187
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
188
+ inspect.Parameter.POSITIONAL_ONLY,
189
+ ):
190
+ num_positional_params += 1
191
+ if param.kind == inspect.Parameter.VAR_POSITIONAL:
192
+ raise ValueError("Method implementation cannot accept *args")
193
+ if num_positional_params not in range(1, 4):
194
+ raise ValueError(
195
+ "Method implementation must accept 1 to 3 positional parameters"
196
+ )
197
+
198
+ def resolved_impl(
199
+ req: Request, req_headers: RequestHeaders, res_headers: ResponseHeaders
200
+ ) -> Response:
201
+ if num_positional_params == 1:
202
+ return cast(Callable[[Request], Response], impl)(req)
203
+ elif num_positional_params == 2:
204
+ return cast(Callable[[Request, RequestHeaders], Response], impl)(
205
+ req, req_headers
206
+ )
207
+ else:
208
+ return cast(
209
+ Callable[[Request, RequestHeaders, ResponseHeaders], Response], impl
210
+ )(req, req_headers, res_headers)
211
+
212
+ number = method.number
213
+ if number in self._number_to_method_impl:
214
+ raise ValueError(
215
+ f"Method with the same number already registered ({number})"
216
+ )
217
+ self._number_to_method_impl[number] = _MethodImpl(
218
+ method=method,
219
+ impl=resolved_impl,
220
+ )
221
+
222
+ def handle_request(
223
+ self,
224
+ req_body: str,
225
+ req_headers: RequestHeaders,
226
+ res_headers: ResponseHeaders,
227
+ ) -> RawServiceResponse:
228
+ flow = _HandleRequestFlow(
229
+ req_body=req_body,
230
+ req_headers=req_headers,
231
+ res_headers=res_headers,
232
+ number_to_method_impl=self._number_to_method_impl,
233
+ )
234
+ return flow.run()
235
+
236
+ async def handle_request_async(
237
+ self,
238
+ req_body: str,
239
+ req_headers: RequestHeaders,
240
+ res_headers: ResponseHeaders,
241
+ ) -> RawServiceResponse:
242
+ flow = _HandleRequestFlow(
243
+ req_body=req_body,
244
+ req_headers=req_headers,
245
+ res_headers=res_headers,
246
+ number_to_method_impl=self._number_to_method_impl,
247
+ )
248
+ return await flow.run_async()
249
+
250
+
251
+ class Service(Generic[RequestHeaders, ResponseHeaders]):
252
+ """Wraps around the implementation of a soia service on the server side.
253
+
254
+ Usage: call '.add_method()' to register method implementations, then call
255
+ '.handle_request()' from the function called by your web framework when an
256
+ HTTP request is received at your service's endpoint.
257
+
258
+ Example with Flask:
259
+
260
+ from flask import Response, request
261
+ from werkzeug.datastructures import Headers
262
+
263
+
264
+ s = soia.Service[Headers, Headers]()
265
+ s.add_method(...)
266
+ s.add_method(...)
267
+
268
+ @app.route("/myapi", methods=["GET", "POST"])
269
+ def myapi():
270
+ if request.method == "POST":
271
+ req_body = request.get_data(as_text=True)
272
+ else:
273
+ query_string = request.query_string.decode("utf-8")
274
+ req_body = urllib.parse.unquote(query_string)
275
+ req_headers = request.headers
276
+ res_headers = Headers()
277
+ raw_response = s.handle_request(req_body, req_headers, res_headers)
278
+ return Response(
279
+ raw_response.data,
280
+ status=raw_response.status_code,
281
+ content_type=raw_response.content_type,
282
+ headers=res_headers,
283
+ )
284
+ """
285
+
286
+ _impl: _ServiceImpl[RequestHeaders, ResponseHeaders]
287
+
288
+ def __init__(self):
289
+ self._impl = _ServiceImpl[RequestHeaders, ResponseHeaders]()
290
+
291
+ def add_method(
292
+ self,
293
+ method: Method[Request, Response],
294
+ impl: Union[
295
+ Callable[[Request], Response],
296
+ Callable[[Request, RequestHeaders], Response],
297
+ Callable[[Request, RequestHeaders, ResponseHeaders], Response],
298
+ ],
299
+ ) -> None:
300
+ self._impl.add_method(method, impl)
301
+
302
+ def handle_request(
303
+ self,
304
+ req_body: str,
305
+ req_headers: RequestHeaders,
306
+ res_headers: ResponseHeaders,
307
+ ) -> RawServiceResponse:
308
+ return self._impl.handle_request(req_body, req_headers, res_headers)
309
+
310
+
311
+ class ServiceAsync(Generic[RequestHeaders, ResponseHeaders]):
312
+ _impl: _ServiceImpl[RequestHeaders, ResponseHeaders]
313
+
314
+ def __init__(self):
315
+ self._impl = _ServiceImpl[RequestHeaders, ResponseHeaders]()
316
+
317
+ def add_method(
318
+ self,
319
+ method: Method[Request, Response],
320
+ impl: Union[
321
+ # Sync
322
+ Callable[[Request], Response],
323
+ Callable[[Request, RequestHeaders], Response],
324
+ Callable[[Request, RequestHeaders, ResponseHeaders], Response],
325
+ # Async
326
+ Callable[[Request], Awaitable[Response]],
327
+ Callable[[Request, RequestHeaders], Awaitable[Response]],
328
+ Callable[[Request, RequestHeaders, ResponseHeaders], Awaitable[Response]],
329
+ ],
330
+ ) -> None:
331
+ self._impl.add_method(method, impl)
332
+
333
+ async def handle_request(
334
+ self,
335
+ req_body: str,
336
+ req_headers: RequestHeaders,
337
+ res_headers: ResponseHeaders,
338
+ ) -> RawServiceResponse:
339
+ return await self._impl.handle_request_async(req_body, req_headers, res_headers)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soia-client
3
- Version: 1.0.20
3
+ Version: 1.0.21
4
4
  Author-email: Tyler Fibonacci <gepheum@gmail.com>
5
5
  License: MIT License
6
6
 
@@ -1,205 +0,0 @@
1
- import inspect
2
- import json
3
- from dataclasses import dataclass
4
- from typing import Any, Callable, Generic, Literal, TypeVar, Union, cast
5
-
6
- from soia._impl.method import Method, Request, Response
7
-
8
- RequestHeaders = TypeVar("RequestHeaders")
9
-
10
- ResponseHeaders = TypeVar("ResponseHeaders")
11
-
12
-
13
- class Service(Generic[RequestHeaders, ResponseHeaders]):
14
- """Wraps around the implementation of a soia service on the server side.
15
-
16
- Usage: call '.add_method()' to register method implementations, then call
17
- '.handle_request()' from the function called by your web framework when an
18
- HTTP request is received at your service's endpoint.
19
-
20
- Example with Flask:
21
-
22
- from flask import Response, request
23
- from werkzeug.datastructures import Headers
24
-
25
-
26
- s = soia.Service[Headers, Headers]()
27
- s.add_method(...)
28
- s.add_method(...)
29
-
30
- @app.route("/myapi", methods=["GET", "POST"])
31
- def myapi():
32
- if request.method == "POST":
33
- req_body = request.get_data(as_text=True)
34
- else:
35
- query_string = request.query_string.decode("utf-8")
36
- req_body = urllib.parse.unquote(query_string)
37
- req_headers = request.headers
38
- res_headers = Headers()
39
- raw_response = s.handle_request(req_body, req_headers, res_headers)
40
- return Response(
41
- raw_response.data,
42
- status=raw_response.status_code,
43
- content_type=raw_response.content_type,
44
- headers=res_headers,
45
- )
46
- """
47
-
48
- _number_to_method_impl: dict[int, "_MethodImpl"]
49
-
50
- def __init__(self):
51
- self._number_to_method_impl = {}
52
-
53
- def add_method(
54
- self,
55
- method: Method[Request, Response],
56
- impl: Union[
57
- Callable[[Request], Response],
58
- Callable[[Request, RequestHeaders], Response],
59
- Callable[[Request, RequestHeaders, ResponseHeaders], Response],
60
- ],
61
- ) -> "Service":
62
- signature = inspect.Signature.from_callable(impl)
63
- num_positional_params = 0
64
- for param in signature.parameters.values():
65
- if param.kind in (
66
- inspect.Parameter.POSITIONAL_OR_KEYWORD,
67
- inspect.Parameter.POSITIONAL_ONLY,
68
- ):
69
- num_positional_params += 1
70
- if param.kind == inspect.Parameter.VAR_POSITIONAL:
71
- raise ValueError("Method implementation cannot accept *args")
72
- if num_positional_params not in range(1, 4):
73
- raise ValueError(
74
- "Method implementation must accept 1 to 3 positional parameters"
75
- )
76
-
77
- def resolved_impl(
78
- req: Request, req_headers: RequestHeaders, res_headers: ResponseHeaders
79
- ) -> Response:
80
- if num_positional_params == 1:
81
- return cast(Callable[[Request], Response], impl)(req)
82
- elif num_positional_params == 2:
83
- return cast(Callable[[Request, RequestHeaders], Response], impl)(
84
- req, req_headers
85
- )
86
- else:
87
- return cast(
88
- Callable[[Request, RequestHeaders, ResponseHeaders], Response], impl
89
- )(req, req_headers, res_headers)
90
-
91
- number = method.number
92
- if number in self._number_to_method_impl:
93
- raise ValueError(
94
- f"Method with the same number already registered ({number})"
95
- )
96
- self._number_to_method_impl[number] = _MethodImpl(
97
- method=method,
98
- impl=resolved_impl,
99
- )
100
- return self
101
-
102
- @dataclass(frozen=True)
103
- class RawResponse:
104
- data: str
105
- type: Literal["ok-json", "bad-request", "server-error"]
106
-
107
- @property
108
- def status_code(self):
109
- if self.type == "ok-json":
110
- return 200
111
- elif self.type == "bad-request":
112
- return 400
113
- elif self.type == "server-error":
114
- return 500
115
- else:
116
- raise TypeError(f"Unknown response type: {self.type}")
117
-
118
- @property
119
- def content_type(self):
120
- if self.type == "ok-json":
121
- return "application/json"
122
- elif self.type == "bad-request" or self.type == "server-error":
123
- return "text/plain; charset=utf-8"
124
- else:
125
- raise TypeError(f"Unknown response type: {self.type}")
126
-
127
- def handle_request(
128
- self,
129
- req_body: str,
130
- req_headers: RequestHeaders,
131
- res_headers: ResponseHeaders,
132
- ) -> RawResponse:
133
- if req_body == "list":
134
-
135
- def method_to_json(method: Method) -> Any:
136
- return {
137
- "method": method.name,
138
- "number": method.number,
139
- "request": method.request_serializer.type_descriptor.as_json(),
140
- "response": method.response_serializer.type_descriptor.as_json(),
141
- }
142
-
143
- json_code = json.dumps(
144
- {
145
- "methods": [
146
- method_to_json(method_impl.method)
147
- for method_impl in self._number_to_method_impl.values()
148
- ]
149
- },
150
- indent=2,
151
- )
152
- return self.RawResponse(json_code, "ok-json")
153
-
154
- parts = req_body.split(":", 3)
155
- if len(parts) != 4:
156
- return self.RawResponse(
157
- "bad request: invalid request format", "bad-request"
158
- )
159
- method_name = parts[0]
160
- method_number_str = parts[1]
161
- format = parts[2]
162
- request_data = parts[3]
163
- try:
164
- method_number = int(method_number_str)
165
- except Exception:
166
- return self.RawResponse(
167
- "bad request: can't parse method number", "bad-request"
168
- )
169
- method_impl = self._number_to_method_impl.get(method_number)
170
- if not method_impl:
171
- return self.RawResponse(
172
- f"bad request: method not found: {method_name}; number: {method_number}",
173
- "bad-request",
174
- )
175
-
176
- try:
177
- req: Any = method_impl.method.request_serializer.from_json_code(
178
- request_data
179
- )
180
- except Exception as e:
181
- return self.RawResponse(
182
- f"bad request: can't parse JSON: {e}", "bad-request"
183
- )
184
-
185
- try:
186
- res: Any = method_impl.impl(req, req_headers, res_headers)
187
- except Exception as e:
188
- return self.RawResponse(f"server error: {e}", "server-error")
189
-
190
- try:
191
- res_json = method_impl.method.response_serializer.to_json_code(
192
- res, readable=(format == "readable")
193
- )
194
- except Exception as e:
195
- return self.RawResponse(
196
- f"server error: can't serialize response to JSON: {e}", "server-error"
197
- )
198
-
199
- return self.RawResponse(res_json, "ok-json")
200
-
201
-
202
- @dataclass(frozen=True)
203
- class _MethodImpl(Generic[Request, Response, RequestHeaders, ResponseHeaders]):
204
- method: Method[Request, Response]
205
- impl: Callable[[Request, RequestHeaders, ResponseHeaders], Response]
File without changes
File without changes
File without changes
File without changes