soia-client 1.0.9__tar.gz → 1.0.11__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 (32) hide show
  1. {soia_client-1.0.9 → soia_client-1.0.11}/PKG-INFO +1 -1
  2. {soia_client-1.0.9 → soia_client-1.0.11}/pyproject.toml +1 -1
  3. {soia_client-1.0.9 → soia_client-1.0.11}/soia_client.egg-info/PKG-INFO +1 -1
  4. {soia_client-1.0.9 → soia_client-1.0.11}/soia_client.egg-info/SOURCES.txt +2 -0
  5. soia_client-1.0.11/soialib/_.py +3 -0
  6. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/__init__.py +2 -0
  7. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/impl/arrays.py +2 -2
  8. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/impl/structs.py +2 -0
  9. soia_client-1.0.11/soialib/service_client.py +70 -0
  10. {soia_client-1.0.9 → soia_client-1.0.11}/tests/test_module_initializer.py +28 -2
  11. {soia_client-1.0.9 → soia_client-1.0.11}/tests/test_serializers.py +12 -0
  12. {soia_client-1.0.9 → soia_client-1.0.11}/LICENSE +0 -0
  13. {soia_client-1.0.9 → soia_client-1.0.11}/README +0 -0
  14. {soia_client-1.0.9 → soia_client-1.0.11}/setup.cfg +0 -0
  15. {soia_client-1.0.9 → soia_client-1.0.11}/soia_client.egg-info/dependency_links.txt +0 -0
  16. {soia_client-1.0.9 → soia_client-1.0.11}/soia_client.egg-info/top_level.txt +0 -0
  17. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/impl/__init__.py +0 -0
  18. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/impl/enums.py +0 -0
  19. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/impl/function_maker.py +0 -0
  20. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/impl/optionals.py +0 -0
  21. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/impl/primitives.py +0 -0
  22. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/impl/repr.py +0 -0
  23. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/impl/type_adapter.py +0 -0
  24. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/keyed_items.py +0 -0
  25. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/method.py +0 -0
  26. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/module_initializer.py +0 -0
  27. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/never.py +0 -0
  28. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/serializer.py +0 -0
  29. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/serializers.py +0 -0
  30. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/spec.py +0 -0
  31. {soia_client-1.0.9 → soia_client-1.0.11}/soialib/timestamp.py +0 -0
  32. {soia_client-1.0.9 → soia_client-1.0.11}/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.9
3
+ Version: 1.0.11
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.9"
7
+ version = "1.0.11"
8
8
  description = ""
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tyler Fibonacci", email = "gepheum@gmail.com" }]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soia-client
3
- Version: 1.0.9
3
+ Version: 1.0.11
4
4
  Author-email: Tyler Fibonacci <gepheum@gmail.com>
5
5
  License: MIT License
6
6
 
@@ -5,6 +5,7 @@ soia_client.egg-info/PKG-INFO
5
5
  soia_client.egg-info/SOURCES.txt
6
6
  soia_client.egg-info/dependency_links.txt
7
7
  soia_client.egg-info/top_level.txt
8
+ soialib/_.py
8
9
  soialib/__init__.py
9
10
  soialib/keyed_items.py
10
11
  soialib/method.py
@@ -12,6 +13,7 @@ soialib/module_initializer.py
12
13
  soialib/never.py
13
14
  soialib/serializer.py
14
15
  soialib/serializers.py
16
+ soialib/service_client.py
15
17
  soialib/spec.py
16
18
  soialib/timestamp.py
17
19
  soialib/impl/__init__.py
@@ -0,0 +1,3 @@
1
+ from typing import Any, Final
2
+
3
+ _: Final[Any] = None
@@ -6,12 +6,14 @@ from soialib.serializers import (
6
6
  optional_serializer,
7
7
  primitive_serializer,
8
8
  )
9
+ from soialib.service_client import ServiceClient
9
10
  from soialib.timestamp import Timestamp
10
11
 
11
12
  __all__ = [
12
13
  "KeyedItems",
13
14
  "Method",
14
15
  "Serializer",
16
+ "ServiceClient",
15
17
  "Timestamp",
16
18
  "array_serializer",
17
19
  "optional_serializer",
@@ -118,14 +118,14 @@ def _new_listuple_class() -> type:
118
118
  return Listuple
119
119
 
120
120
 
121
- def _new_keyed_items_class(attributes: tuple[str, ...], default_expr: ExprLike):
121
+ def _new_keyed_items_class(key_attributes: tuple[str, ...], default_expr: ExprLike):
122
122
  key_items = make_function(
123
123
  name="key_items",
124
124
  params=["items"],
125
125
  body=[
126
126
  "ret = {}",
127
127
  "for item in items:",
128
- f" ret[item.{'.'.join(attributes)}] = item",
128
+ f" ret[item.{'.'.join(key_attributes)}] = item",
129
129
  "return ret",
130
130
  ],
131
131
  )
@@ -457,6 +457,8 @@ def _make_eq_fn(
457
457
  """
458
458
 
459
459
  builder = BodyBuilder()
460
+ builder.append_ln("if other is self:")
461
+ builder.append_ln(" return True")
460
462
  builder.append_ln("if other.__class__ is self.__class__:")
461
463
  operands: list[ExprLike]
462
464
  if fields:
@@ -0,0 +1,70 @@
1
+ import http.client
2
+ from typing import Final, Mapping, TypeVar
3
+ from urllib.parse import urlparse
4
+
5
+ from soialib import Method
6
+
7
+ Request = TypeVar("Request")
8
+ Response = TypeVar("Response")
9
+
10
+
11
+ class ServiceClient:
12
+ _scheme: Final[str]
13
+ _host: Final[str] # May include the port
14
+ _path: Final[str]
15
+
16
+ def __init__(self, service_url: str, use_https: bool = False):
17
+ parsed_url = urlparse(service_url)
18
+ if parsed_url.query:
19
+ raise ValueError("Service URL must not contain a query string")
20
+ scheme = parsed_url.scheme
21
+ if scheme not in ["http", "https"]:
22
+ raise ValueError("Service URL must start with http:// or https://")
23
+ self._scheme = scheme
24
+ self._host = parsed_url.netloc
25
+ self._path = parsed_url.path
26
+
27
+ def invoke_remote(
28
+ self,
29
+ method: Method[Request, Response],
30
+ request: Request,
31
+ headers: Mapping[str, str] = {},
32
+ ) -> Response:
33
+ request_json = method.request_serializer.to_json_code(request)
34
+ body = ":".join(
35
+ [
36
+ method.name,
37
+ str(method.number),
38
+ "",
39
+ request_json,
40
+ ]
41
+ )
42
+ headers = {
43
+ **headers,
44
+ "Content-Type": "text/plain; charset=utf-8",
45
+ "Content-Length": str(len(body)),
46
+ }
47
+ if self._scheme == "https":
48
+ conn = http.client.HTTPSConnection(self._host)
49
+ else:
50
+ conn = http.client.HTTPConnection(self._host)
51
+ try:
52
+ conn.request(
53
+ "POST",
54
+ self._path,
55
+ body=body,
56
+ headers=headers,
57
+ )
58
+ response = conn.getresponse()
59
+ status_code = response.status
60
+ content_type = response.getheader("Content-Type")
61
+ response_data = response.read().decode("utf-8", errors="ignore")
62
+ finally:
63
+ conn.close()
64
+ if status_code in range(200, 300):
65
+ return method.response_serializer.from_json_code(response_data)
66
+ else:
67
+ message = f"HTTP response status {status_code}"
68
+ if content_type == "text/plain":
69
+ message = f"{message}: {response_data}"
70
+ raise RuntimeError(message)
@@ -294,6 +294,21 @@ class ModuleInitializerTestCase(unittest.TestCase):
294
294
  ),
295
295
  ),
296
296
  ),
297
+ spec.Struct(
298
+ id="my/module.soia:Rec",
299
+ fields=(
300
+ spec.Field(
301
+ name="r",
302
+ number=0,
303
+ type="my/module.soia:Rec",
304
+ ),
305
+ spec.Field(
306
+ name="x",
307
+ number=1,
308
+ type=spec.PrimitiveType.INT32,
309
+ ),
310
+ ),
311
+ ),
297
312
  spec.Struct(
298
313
  id="my/module.soia:Foobar",
299
314
  fields=(
@@ -426,7 +441,6 @@ class ModuleInitializerTestCase(unittest.TestCase):
426
441
 
427
442
  def test_primitives_repr(self):
428
443
  primitives_cls = self.init_test_module()["Primitives"]
429
- serializer = primitives_cls.SERIALIZER
430
444
  p = primitives_cls(
431
445
  bool=True,
432
446
  bytes=b"a",
@@ -454,7 +468,6 @@ class ModuleInitializerTestCase(unittest.TestCase):
454
468
 
455
469
  def test_cannot_mutate_frozen_class(self):
456
470
  point_cls = self.init_test_module()["Point"]
457
- serializer = point_cls.SERIALIZER
458
471
  point = point_cls(x=1.5, y=2.5)
459
472
  try:
460
473
  point.x = 3.5
@@ -530,6 +543,14 @@ class ModuleInitializerTestCase(unittest.TestCase):
530
543
  self.assertEqual(serializer.to_json_code(foobar), "[0,0,0,0,[2.0]]")
531
544
  self.assertEqual(serializer.from_json_code("[0,0,0,0,[2.0]]"), foobar)
532
545
 
546
+ def test_recursive_struct(self):
547
+ rec_cls = self.init_test_module()["Rec"]
548
+ r = rec_cls(r=rec_cls(r=rec_cls.DEFAULT, x=1))
549
+ serializer = rec_cls.SERIALIZER
550
+ self.assertEqual(serializer.to_json_code(r), "[[[],1]]")
551
+ self.assertEqual(serializer.from_json_code("[[[],1]]"), r)
552
+ self.assertEqual(str(r), "Rec(\n r=Rec(x=1),\n)")
553
+
533
554
  def test_struct_ctor_accepts_mutable_struct(self):
534
555
  module = self.init_test_module()
535
556
  segment_cls = module["Segment"]
@@ -562,6 +583,11 @@ class ModuleInitializerTestCase(unittest.TestCase):
562
583
  def test_struct_ctor_raises_error_if_unknown_arg(self):
563
584
  module = self.init_test_module()
564
585
  segment_cls = module["Segment"]
586
+ try:
587
+ segment_cls(foo=4)
588
+ self.fail("Expected to fail")
589
+ except Exception:
590
+ pass
565
591
 
566
592
  def test_to_frozen_checks_type_of_struct_field(self):
567
593
  module = self.init_test_module()
@@ -97,6 +97,18 @@ class TimestampTestCase(unittest.TestCase):
97
97
  self.assertEqual(
98
98
  primitive_serializer("float32").to_json_code(float("nan")), "NaN"
99
99
  )
100
+ self.assertEqual(primitive_serializer("float32").from_json_code("3.14"), 3.14)
101
+ self.assertEqual(
102
+ primitive_serializer("float32").from_json_code("Infinity"), float("inf")
103
+ )
104
+ self.assertEqual(
105
+ primitive_serializer("float32").from_json_code("-Infinity"), -float("inf")
106
+ )
107
+ self.assertNotEqual(
108
+ primitive_serializer("float32").from_json_code("NaN"),
109
+ primitive_serializer("float32").from_json_code("NaN"),
110
+ )
111
+
100
112
  self.assertEqual(primitive_serializer("float64").to_json_code(3.14), "3.14")
101
113
  self.assertEqual(
102
114
  primitive_serializer("float64").to_json_code(3.14, readable=True),
File without changes
File without changes
File without changes