Skip to content

Commit 1da4430

Browse files
committed
Refactor sync/aio models
Refactor Object model for both sync and aio to be able to initialize keys found in parent classes if those parent classes are also jsonversation.Object
1 parent 1e867d4 commit 1da4430

6 files changed

Lines changed: 58 additions & 12 deletions

File tree

jsonversation/aio/models.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,17 @@ def __init__(self) -> None:
3131
self._parsed_keys = []
3232
self._value = {}
3333

34-
for key, type_hint in type(self).__annotations__.items():
34+
# initialize keys for potential parent classes
35+
for cls in self.__class__.mro()[1:-1]:
36+
if cls.__name__ == "Object" or cls.__name__ == "StreamingObject":
37+
break
38+
39+
self._initialize_attributes(cls.__annotations__)
40+
41+
self._initialize_attributes(type(self).__annotations__)
42+
43+
def _initialize_attributes(self, attributes: dict[str, Any]) -> None:
44+
for key, type_hint in attributes.items():
3545
self._keys.append(key)
3646

3747
# Handle List[T]

jsonversation/sync/models.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,42 @@
55

66

77
class StreamingObject[T]:
8+
_on_complete_funcs: list[Callable[[T], None]]
9+
10+
def __init__(self) -> None:
11+
self._on_complete_funcs = []
12+
813
def update(self, value: T) -> None:
914
return None
1015

1116
def _complete(self) -> None: ...
1217

18+
def on_complete(self, func: Callable[[T], None]) -> None:
19+
self._on_complete_funcs.append(func)
20+
1321

1422
class Object(StreamingObject[dict[str, Any]]):
1523
_keys: list[str]
1624
_parsed_keys: list[str]
17-
_on_complete_funcs: list[Callable[[dict[str, Any]], None]]
1825
_value: dict[str, Any]
1926

2027
def __init__(self) -> None:
2128
super().__init__()
2229
self._keys = []
2330
self._parsed_keys = []
2431
self._value = {}
25-
self._on_complete_funcs = []
2632

27-
for key, type_hint in type(self).__annotations__.items():
33+
# initialize keys for potential parent classes
34+
for cls in self.__class__.mro()[1:-1]:
35+
if cls.__name__ == "Object" or cls.__name__ == "StreamingObject":
36+
break
37+
38+
self._initialize_attributes(cls.__annotations__)
39+
40+
self._initialize_attributes(type(self).__annotations__)
41+
42+
def _initialize_attributes(self, attributes: dict[str, Any]) -> None:
43+
for key, type_hint in attributes.items():
2844
self._keys.append(key)
2945

3046
# Handle List[T]
@@ -167,23 +183,19 @@ def get_value(self) -> list[T]:
167183
return self._values
168184

169185

170-
class Atomic[T](StreamingObject[T]):
186+
class Atomic[T](StreamingObject[T | None]):
171187
_is_empty: bool
172188
_value: T | None
173-
_on_complete_funcs: list[Callable[[T | None], None]]
174189

175190
def __init__(self, item_cls: type[T]) -> None:
176191
self._is_empty = True
177192
self._value = None
178193
self._on_complete_funcs = []
179194

180-
def update(self, value: T) -> None:
195+
def update(self, value: T | None) -> None:
181196
self._value = value
182197
self._is_empty = False
183198

184-
def on_complete(self, func: Callable[[T | None], None]) -> None:
185-
self._on_complete_funcs.append(func)
186-
187199
def _complete(self) -> None:
188200
if not self._is_empty:
189201
for func in self._on_complete_funcs:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "jsonversation"
3-
version = "0.1.2"
3+
version = "0.1.3"
44
description = "Fast and type-safe JSON stream parser"
55
authors = [{name = "Vetted.ai Engineering", email = "engineering@vetted.ai"}]
66
readme = "README.md"

tests/aio/models/test_object.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,3 +763,15 @@ async def footer_callback(value: str) -> None:
763763

764764
assert footer_completed == ["Stream Footer"] # Completed on final _complete()
765765
assert len(completed_values) == 1
766+
767+
768+
def test_object_creation_with_parent_classes() -> None:
769+
class ParentObject(jv.Object):
770+
name: jv.String
771+
772+
class ChildObject(ParentObject):
773+
test_field: jv.String
774+
775+
o = ChildObject()
776+
777+
assert o.__getattribute__("name") is not None

tests/sync/models/test_object.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,3 +755,15 @@ def footer_callback(value: str) -> None:
755755

756756
assert footer_completed == ["Stream Footer"] # Completed on final _complete()
757757
assert len(completed_values) == 1
758+
759+
760+
def test_object_creation_with_parent_classes() -> None:
761+
class ParentObject(jv.Object):
762+
name: jv.String
763+
764+
class ChildObject(ParentObject):
765+
test_field: jv.String
766+
767+
o = ChildObject()
768+
769+
assert o.__getattribute__("name") is not None

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)