soia-client 1.0.27__tar.gz → 1.0.28__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 (33) hide show
  1. {soia_client-1.0.27 → soia_client-1.0.28}/PKG-INFO +1 -1
  2. {soia_client-1.0.27 → soia_client-1.0.28}/pyproject.toml +1 -1
  3. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/enums.py +3 -0
  4. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/service.py +111 -39
  5. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/service_client.py +1 -1
  6. {soia_client-1.0.27 → soia_client-1.0.28}/soia_client.egg-info/PKG-INFO +1 -1
  7. {soia_client-1.0.27 → soia_client-1.0.28}/tests/test_module_initializer.py +5 -0
  8. {soia_client-1.0.27 → soia_client-1.0.28}/LICENSE +0 -0
  9. {soia_client-1.0.27 → soia_client-1.0.28}/README.md +0 -0
  10. {soia_client-1.0.27 → soia_client-1.0.28}/setup.cfg +0 -0
  11. {soia_client-1.0.27 → soia_client-1.0.28}/soia/__init__.py +0 -0
  12. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/__init__.py +0 -0
  13. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/arrays.py +0 -0
  14. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/function_maker.py +0 -0
  15. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/keyed_items.py +0 -0
  16. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/method.py +0 -0
  17. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/never.py +0 -0
  18. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/optionals.py +0 -0
  19. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/primitives.py +0 -0
  20. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/repr.py +0 -0
  21. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/serializer.py +0 -0
  22. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/serializers.py +0 -0
  23. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/structs.py +0 -0
  24. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/timestamp.py +0 -0
  25. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_impl/type_adapter.py +0 -0
  26. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_module_initializer.py +0 -0
  27. {soia_client-1.0.27 → soia_client-1.0.28}/soia/_spec.py +0 -0
  28. {soia_client-1.0.27 → soia_client-1.0.28}/soia/reflection.py +0 -0
  29. {soia_client-1.0.27 → soia_client-1.0.28}/soia_client.egg-info/SOURCES.txt +0 -0
  30. {soia_client-1.0.27 → soia_client-1.0.28}/soia_client.egg-info/dependency_links.txt +0 -0
  31. {soia_client-1.0.27 → soia_client-1.0.28}/soia_client.egg-info/top_level.txt +0 -0
  32. {soia_client-1.0.27 → soia_client-1.0.28}/tests/test_serializers.py +0 -0
  33. {soia_client-1.0.27 → soia_client-1.0.28}/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.27
3
+ Version: 1.0.28
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.27"
7
+ version = "1.0.28"
8
8
  description = ""
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tyler Fibonacci", email = "gepheum@gmail.com" }]
@@ -41,6 +41,9 @@ class EnumAdapter(TypeAdapter):
41
41
  constant = constant_class()
42
42
  setattr(base_class, constant_field.attribute, constant)
43
43
 
44
+ # Add the Kind type alias.
45
+ setattr(base_class, "Kind", str)
46
+
44
47
  def finalize(
45
48
  self,
46
49
  resolve_type_fn: Callable[[_spec.Type], TypeAdapter],
@@ -5,6 +5,7 @@ from dataclasses import dataclass
5
5
  from typing import Any, Generic, Literal, TypeVar, Union, cast
6
6
 
7
7
  from soia._impl.method import Method, Request, Response
8
+ from soia._impl.never import Never
8
9
 
9
10
  RequestHeaders = TypeVar("RequestHeaders")
10
11
 
@@ -90,42 +91,84 @@ class _HandleRequestFlow(Generic[Request, Response, RequestHeaders, ResponseHead
90
91
  tuple[Any, _MethodImpl[Request, Response, RequestHeaders, ResponseHeaders]],
91
92
  RawServiceResponse,
92
93
  ]:
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
- )
94
+ if self.req_body in ["", "list"]:
95
+ return self._handle_list()
96
+
97
+ # Method invokation
98
+ method_name: str
99
+ method_number: int | None
100
+ format: str
101
+ request_data: tuple[Literal["json-code"], str] | tuple[Literal["json"], Any]
102
+
103
+ first_char = self.req_body[0]
104
+ if first_char.isspace() or first_char == "{":
105
+ # A JSON object
106
+ try:
107
+ req_body_json = json.loads(self.req_body)
108
+ except json.JSONDecodeError:
109
+ return RawServiceResponse(
110
+ "bad request: invalid JSON", "bad-request"
111
+ )
112
+ method = req_body_json.get("method", ())
113
+ if method == ():
114
+ return RawServiceResponse(
115
+ "bad request: missing 'method' field in JSON", "bad-request"
116
+ )
117
+ if isinstance(method, str):
118
+ method_name = method
119
+ method_number = None
120
+ elif isinstance(method, int):
121
+ method_name = "?"
122
+ method_number = method
123
+ else:
124
+ return RawServiceResponse(
125
+ "bad request: 'method' field must be a string or an integer",
126
+ "bad-request",
127
+ )
128
+ format = "readable"
129
+ data = req_body_json.get("request", ())
130
+ if data == ():
131
+ return RawServiceResponse(
132
+ "bad request: missing 'request' field in JSON", "bad-request"
133
+ )
134
+ request_data = ("json", data)
135
+ else:
136
+ # A colon-separated string
137
+ parts = self.req_body.split(":", 3)
138
+ if len(parts) != 4:
139
+ return RawServiceResponse(
140
+ "bad request: invalid request format", "bad-request"
141
+ )
142
+ method_name = parts[0]
143
+ method_number_str = parts[1]
144
+ format = parts[2]
145
+ request_data = ("json-code", parts[3])
146
+ if method_number_str:
147
+ try:
148
+ method_number = int(method_number_str)
149
+ except Exception:
150
+ return RawServiceResponse(
151
+ "bad request: can't parse method number", "bad-request"
152
+ )
153
+ else:
154
+ method_number = None
155
+ self.format = format
156
+ if method_number is None:
157
+ # Try to get the method number by name
158
+ all_methods = self.number_to_method_impl.values()
159
+ name_matches = [m for m in all_methods if m.method.name == method_name]
160
+ if not name_matches:
161
+ return RawServiceResponse(
162
+ f"bad request: method not found: {method_name}",
163
+ "bad-request",
164
+ )
165
+ elif len(name_matches) != 1:
166
+ return RawServiceResponse(
167
+ f"bad request: method name '{method_name}' is ambiguous; "
168
+ "use method number instead",
169
+ "bad-request",
170
+ )
171
+ method_number = name_matches[0].method.number
129
172
  method_impl = self.number_to_method_impl.get(method_number)
130
173
  if not method_impl:
131
174
  return RawServiceResponse(
@@ -133,15 +176,44 @@ class _HandleRequestFlow(Generic[Request, Response, RequestHeaders, ResponseHead
133
176
  "bad-request",
134
177
  )
135
178
  try:
136
- req: Any = method_impl.method.request_serializer.from_json_code(
137
- request_data
138
- )
179
+ req: Any
180
+ if request_data[0] == "json-code":
181
+ req = method_impl.method.request_serializer.from_json_code(
182
+ request_data
183
+ )
184
+ elif request_data[0] == "json":
185
+ req = method_impl.method.request_serializer.from_json(
186
+ request_data
187
+ )
188
+ else:
189
+ _: Never = request_data[0]
190
+ del _
139
191
  except Exception as e:
140
192
  return RawServiceResponse(
141
193
  f"bad request: can't parse JSON: {e}", "bad-request"
142
194
  )
143
195
  return (req, method_impl)
144
196
 
197
+ def _handle_list(self) -> RawServiceResponse:
198
+ def method_to_json(method: Method) -> Any:
199
+ return {
200
+ "method": method.name,
201
+ "number": method.number,
202
+ "request": method.request_serializer.type_descriptor.as_json(),
203
+ "response": method.response_serializer.type_descriptor.as_json(),
204
+ }
205
+
206
+ json_code = json.dumps(
207
+ {
208
+ "methods": [
209
+ method_to_json(method_impl.method)
210
+ for method_impl in self.number_to_method_impl.values()
211
+ ]
212
+ },
213
+ indent=2,
214
+ )
215
+ return RawServiceResponse(json_code, "ok-json")
216
+
145
217
  def _response_to_json(
146
218
  self,
147
219
  res: Response,
@@ -121,7 +121,7 @@ class ServiceClient:
121
121
  request_json,
122
122
  ]
123
123
  )
124
-
124
+
125
125
  request_headers = {
126
126
  **headers,
127
127
  "Content-Type": "text/plain; charset=utf-8",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soia-client
3
- Version: 1.0.27
3
+ Version: 1.0.28
4
4
  Author-email: Tyler Fibonacci <gepheum@gmail.com>
5
5
  License: MIT License
6
6
 
@@ -702,6 +702,11 @@ class ModuleInitializerTestCase(unittest.TestCase):
702
702
  {"kind": "error", "value": "An error occurred"},
703
703
  )
704
704
 
705
+ def test_enum_kind(self):
706
+ module = self.init_test_module()
707
+ primary_color_cls = module["Status"]
708
+ self.assertEqual(primary_color_cls.Kind, str)
709
+
705
710
  def test_enum_wrap_around_mutable_struct(self):
706
711
  module = self.init_test_module()
707
712
  json_value_cls = module["JsonValue"]
File without changes
File without changes
File without changes
File without changes