11import dataclasses
2+ import functools
23import inspect
34import typing
45from collections .abc import Callable , Sequence
1516
1617@dataclasses .dataclass (frozen = True )
1718class _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+
133114def serialize_to_bytes (value : object ) -> bytes :
134115 return native_to_arc4 (value ).bytes .value
135116
0 commit comments