Skip to content

Commit

Permalink
fix: Support nested generic dataclasses
Browse files Browse the repository at this point in the history
  • Loading branch information
yukinarit committed Nov 6, 2022
1 parent 50ce93e commit 3562139
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 7 deletions.
55 changes: 55 additions & 0 deletions examples/generics_nested.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from dataclasses import dataclass
from typing import Generic, TypeVar

from serde import from_dict, serde, to_dict


@serde
@dataclass
class EventData:
pass


@serde
@dataclass
class A(EventData):
a: str


# Additional subclasses of EventData exist

Data = TypeVar("Data", bound=EventData)


@serde
@dataclass
class Payload(Generic[Data]):
id: int
data: Data


@serde
@dataclass
class Event(Generic[Data]):
name: str
payload: Payload[Data]


def main():
event_a = Event("a", Payload(1, A("a_str")))

a_dict = to_dict(event_a)
new_event_a = from_dict(Event[A], a_dict)

print(event_a)
print(new_event_a)

payload = Payload(1, A("a_str"))
payload_dict = to_dict(payload)
new_payload = from_dict(Payload[A], payload_dict)
print(payload)
print(new_payload)


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions examples/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import flatten
import forward_reference
import generics
import generics_nested
import jsonfile
import lazy_type_evaluation
import literal
Expand Down Expand Up @@ -52,6 +53,7 @@ def run_all():
run(type_datetime)
run(union_tagging)
run(generics)
run(generics_nested)
run(lazy_type_evaluation)
run(literal)
run(type_check_strict)
Expand Down
15 changes: 8 additions & 7 deletions serde/de.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ def dataclass(self, arg: DeField) -> str:
else:
var = arg.datavar

opts = "reuse_instances=reuse_instances"
opts = "maybe_generic=maybe_generic, reuse_instances=reuse_instances"
return f"{typename(arg.type)}.{SERDE_SCOPE}.funcs['{self.func}'](data={var}, {opts})"

def opt(self, arg: DeField) -> str:
Expand All @@ -645,8 +645,8 @@ def opt(self, arg: DeField) -> str:
... class Foo:
... o: Optional[List[int]]
>>> Renderer('foo').render(DeField(Optional[Foo], 'f', datavar='data'))
'(Foo.__serde__.funcs[\\'foo\\'](data=data["f"], reuse_instances=reuse_instances)) \
if data.get("f") is not None else None'
'(Foo.__serde__.funcs[\\'foo\\'](data=data["f"], maybe_generic=maybe_generic, \
reuse_instances=reuse_instances)) if data.get("f") is not None else None'
"""
value = arg[0]
if arg.iterbased:
Expand Down Expand Up @@ -697,13 +697,13 @@ def tuple(self, arg: DeField) -> str:
>>> Renderer('foo').render(DeField(Tuple[str, int, List[int], Foo], 'd', datavar='data'))
'(coerce(str, data["d"][0]), coerce(int, data["d"][1]), \
[coerce(int, v) for v in data["d"][2]], \
Foo.__serde__.funcs[\\'foo\\'](data=data["d"][3], reuse_instances=reuse_instances),)'
Foo.__serde__.funcs[\\'foo\\'](data=data["d"][3], maybe_generic=maybe_generic, reuse_instances=reuse_instances),)'
>>> field = DeField(Tuple[str, int, List[int], Foo], 'd', datavar='data', index=0, iterbased=True)
>>> Renderer('foo').render(field)
"(coerce(str, data[0][0]), coerce(int, data[0][1]), \
[coerce(int, v) for v in data[0][2]], Foo.__serde__.funcs['foo'](data=data[0][3], \
reuse_instances=reuse_instances),)"
maybe_generic=maybe_generic, reuse_instances=reuse_instances),)"
"""
if is_bare_tuple(arg.type):
return f'tuple({arg.data})'
Expand All @@ -725,8 +725,9 @@ def dict(self, arg: DeField) -> str:
>>> @deserialize
... class Foo: pass
>>> Renderer('foo').render(DeField(Dict[Foo, List[Foo]], 'f', datavar='data'))
'{Foo.__serde__.funcs[\\'foo\\'](data=k, reuse_instances=reuse_instances): \
[Foo.__serde__.funcs[\\'foo\\'](data=v, reuse_instances=reuse_instances) for v in v] for k, v in data["f"].items()}'
'{Foo.__serde__.funcs[\\'foo\\'](data=k, maybe_generic=maybe_generic, reuse_instances=reuse_instances): \
[Foo.__serde__.funcs[\\'foo\\'](data=v, maybe_generic=maybe_generic, reuse_instances=reuse_instances) for v in v] \
for k, v in data["f"].items()}'
"""
if is_bare_dict(arg.type):
return arg.data
Expand Down

0 comments on commit 3562139

Please sign in to comment.