Skip to content

Commit c8d9ee8

Browse files
committed
refactor: simplify get_native_to_arc4_serializer implementation
1 parent 6f0eb8c commit c8d9ee8

File tree

1 file changed

+73
-92
lines changed

1 file changed

+73
-92
lines changed

src/_algopy_testing/serialize.py

Lines changed: 73 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import dataclasses
2+
import functools
23
import inspect
34
import typing
45
from collections.abc import Callable, Sequence
@@ -15,7 +16,6 @@
1516

1617
@dataclasses.dataclass(frozen=True)
1718
class _Serializer(typing.Generic[_T, _U]):
18-
native_type: type[_T]
1919
arc4_type: type[_U]
2020
native_to_arc4: Callable[[_T], _U]
2121
arc4_to_native: Callable[[_U], _T]
@@ -25,111 +25,92 @@ def identity(i: _T) -> _T:
2525
return i
2626

2727

28-
def get_native_to_arc4_serializer(typ: type[_T]) -> _Serializer: # type: ignore[type-arg] # noqa: PLR0911, PLR0912
28+
def get_native_to_arc4_serializer(typ: type) -> _Serializer[typing.Any, typing.Any]:
2929
from _algopy_testing import arc4
30-
from _algopy_testing.models import Account
31-
from _algopy_testing.primitives import BigUInt, Bytes, ImmutableArray, String
30+
from _algopy_testing.primitives import ImmutableArray
3231
from _algopy_testing.protocols import UInt64Backed
3332

34-
origin_typ = typing.get_origin(typ)
35-
36-
if inspect.isclass(typ) and issubclass(typ, arc4._ABIEncoded):
37-
return _Serializer(
38-
native_type=typ, arc4_type=typ, native_to_arc4=identity, arc4_to_native=identity
39-
)
40-
# For types that are expected to be simple classes for these specific checks
41-
if inspect.isclass(typ):
42-
if issubclass(typ, bool):
43-
return _Serializer(
44-
native_type=typ,
45-
arc4_type=arc4.Bool,
46-
native_to_arc4=arc4.Bool,
47-
arc4_to_native=lambda n: n.native,
48-
)
33+
origin_type = typing.get_origin(typ)
34+
if origin_type is tuple:
35+
return _get_tuple_serializer(typing.get_args(typ))
36+
elif isinstance(typ, type):
37+
if issubclass(typ, arc4._ABIEncoded):
38+
return _Serializer(arc4_type=typ, native_to_arc4=identity, arc4_to_native=identity)
39+
for native_type, simple_arc4_type in _simple_native_to_arc4_type_map().items():
40+
if issubclass(typ, native_type):
41+
return _Serializer(
42+
arc4_type=simple_arc4_type,
43+
native_to_arc4=simple_arc4_type,
44+
arc4_to_native=lambda n: n.native,
45+
)
4946
if issubclass(typ, UInt64Backed):
5047
return _Serializer(
51-
native_type=typ,
5248
arc4_type=arc4.UInt64,
5349
native_to_arc4=lambda n: arc4.UInt64(n.int_),
5450
arc4_to_native=lambda a: typ.from_int(a.native),
5551
)
56-
if issubclass(typ, BigUInt):
57-
return _Serializer(
58-
native_type=typ,
59-
arc4_type=arc4.UInt512,
60-
native_to_arc4=arc4.UInt512,
61-
arc4_to_native=lambda a: a.native,
62-
)
63-
if issubclass(typ, Account):
64-
return _Serializer(
65-
native_type=typ,
66-
arc4_type=arc4.Address,
67-
native_to_arc4=arc4.Address,
68-
arc4_to_native=lambda a: a.native,
69-
)
70-
if issubclass(typ, UInt64):
71-
return _Serializer(
72-
native_type=typ,
73-
arc4_type=arc4.UInt64,
74-
native_to_arc4=arc4.UInt64,
75-
arc4_to_native=lambda a: a.native,
76-
)
77-
if issubclass(typ, Bytes):
78-
return _Serializer(
79-
native_type=typ,
80-
arc4_type=arc4.DynamicBytes,
81-
native_to_arc4=arc4.DynamicBytes,
82-
arc4_to_native=lambda a: a.native,
83-
)
84-
if issubclass(typ, String):
52+
if typing.NamedTuple in getattr(typ, "__orig_bases__", []):
53+
tuple_fields = tuple(inspect.get_annotations(typ).values())
54+
if any(isinstance(f, str) for f in tuple_fields):
55+
raise TypeError("string annotations in typing.NamedTuple fields are not supported")
56+
return _get_tuple_serializer(tuple_fields)
57+
if issubclass(typ, ImmutableArray):
58+
native_element_type = typ._element_type
59+
element_serializer = get_native_to_arc4_serializer(native_element_type)
60+
arc4_element_type = element_serializer.arc4_type
61+
arc4_type = arc4.DynamicArray[arc4_element_type] # type: ignore[valid-type]
8562
return _Serializer(
86-
native_type=typ,
87-
arc4_type=arc4.String,
88-
native_to_arc4=arc4.String,
89-
arc4_to_native=lambda a: a.native,
63+
arc4_type=arc4_type,
64+
native_to_arc4=lambda arr: arc4_type(
65+
*(element_serializer.native_to_arc4(e) for e in arr)
66+
),
67+
arc4_to_native=lambda arr: typ(
68+
*(element_serializer.arc4_to_native(e) for e in arr)
69+
),
9070
)
91-
92-
if origin_typ is tuple or (inspect.isclass(typ) and issubclass(typ, tuple)):
93-
if typing.NamedTuple in getattr(typ, "__orig_bases__", []):
94-
tuple_fields: Sequence[type] = list(inspect.get_annotations(typ).values())
95-
else:
96-
tuple_fields = typing.get_args(typ)
97-
serializers = [get_native_to_arc4_serializer(i) for i in tuple_fields]
98-
99-
def _items_to_arc4(items: Sequence[object]) -> tuple[object, ...]:
100-
result = []
101-
for item, serializer in zip(items, serializers, strict=True):
102-
result.append(serializer.native_to_arc4(item))
103-
return tuple(result)
104-
105-
def _items_to_native(items: Sequence[object]) -> tuple[object, ...]:
106-
result = []
107-
for item, serializer in zip(items, serializers, strict=True):
108-
result.append(serializer.arc4_to_native(item))
109-
return tuple(result)
110-
111-
return _Serializer(
112-
native_type=typ,
113-
arc4_type=arc4.Tuple[*(s.arc4_type for s in serializers)], # type: ignore[misc]
114-
native_to_arc4=lambda t: arc4.Tuple(_items_to_arc4(t)),
115-
arc4_to_native=lambda t: _items_to_native(t),
116-
)
117-
if issubclass(typ, ImmutableArray):
118-
native_element_type = typ._element_type
119-
element_serializer = get_native_to_arc4_serializer(native_element_type)
120-
arc4_element_type = element_serializer.arc4_type
121-
arc4_type = arc4.DynamicArray[arc4_element_type] # type: ignore[valid-type]
122-
return _Serializer(
123-
native_type=typ,
124-
arc4_type=arc4_type,
125-
native_to_arc4=lambda arr: arc4_type(
126-
*(element_serializer.native_to_arc4(e) for e in arr)
127-
),
128-
arc4_to_native=lambda arr: typ(*(element_serializer.arc4_to_native(e) for e in arr)),
129-
)
13071
raise TypeError(f"unserializable type: {typ}")
13172

13273

74+
@functools.cache
75+
def _simple_native_to_arc4_type_map() -> dict[type, type]:
76+
from _algopy_testing import arc4
77+
from _algopy_testing.models import Account
78+
from _algopy_testing.primitives import BigUInt, Bytes, String
79+
80+
return {
81+
bool: arc4.Bool,
82+
UInt64: arc4.UInt64,
83+
BigUInt: arc4.UInt512,
84+
Account: arc4.Address,
85+
Bytes: arc4.DynamicBytes,
86+
String: arc4.String,
87+
}
88+
89+
90+
def _get_tuple_serializer(item_types: tuple[type, ...]) -> _Serializer[typing.Any, typing.Any]:
91+
from _algopy_testing import arc4
92+
93+
serializers = [get_native_to_arc4_serializer(i) for i in item_types]
94+
95+
def _items_to_arc4(items: Sequence[object]) -> tuple[object, ...]:
96+
result = []
97+
for item, serializer in zip(items, serializers, strict=True):
98+
result.append(serializer.native_to_arc4(item))
99+
return tuple(result)
100+
101+
def _items_to_native(items: Sequence[object]) -> tuple[object, ...]:
102+
result = []
103+
for item, serializer in zip(items, serializers, strict=True):
104+
result.append(serializer.arc4_to_native(item))
105+
return tuple(result)
106+
107+
return _Serializer(
108+
arc4_type=arc4.Tuple[*(s.arc4_type for s in serializers)], # type: ignore[misc]
109+
native_to_arc4=lambda t: arc4.Tuple(_items_to_arc4(t)),
110+
arc4_to_native=lambda t: _items_to_native(t),
111+
)
112+
113+
133114
def serialize_to_bytes(value: object) -> bytes:
134115
return native_to_arc4(value).bytes.value
135116

0 commit comments

Comments
 (0)