66
77import dataclasses
88import datetime
9+ import functools
910import inspect
1011import warnings
1112from enum import Enum
@@ -63,41 +64,44 @@ def _is_type_kind_convertible_to(src_type_kind: str, dst_type_kind: str) -> bool
6364 )
6465
6566
66- def _get_cached_type_info (
67- type_to_analyze : Any ,
68- cache : dict [Any , AnalyzedTypeInfo ],
69- ) -> AnalyzedTypeInfo :
70- """Retrieve or compute and cache the type information for a given type."""
71- if type_to_analyze not in cache :
72- cache [type_to_analyze ] = analyze_type_info (type_to_analyze )
73- return cache [type_to_analyze ]
67+ def _get_type_info_safe (type_to_analyze : Any ) -> AnalyzedTypeInfo :
68+ """Safely get type info, bypassing cache if type is not hashable."""
69+
70+ @functools .cache
71+ def _get_cached_type_info () -> AnalyzedTypeInfo :
72+ """cache the computed type information for a given type."""
73+ return analyze_type_info (type_to_analyze )
74+
75+ try :
76+ return _get_cached_type_info (type_to_analyze )
77+ except TypeError :
78+ return analyze_type_info (type_to_analyze )
7479
7580
7681def _encode_engine_value_core (
7782 value : Any ,
78- type_hint : Type [Any ] | str | None = None ,
79- type_variant : AnalyzedTypeInfo | None = None ,
80- _elem_type_cache : dict [Any , AnalyzedTypeInfo ] | None = None ,
83+ type_info : AnalyzedTypeInfo | None = None ,
8184) -> Any :
8285 """Core encoding logic for converting Python values to engine values."""
83- _elem_type_cache = _elem_type_cache or {}
8486
8587 if dataclasses .is_dataclass (value ):
8688 fields = dataclasses .fields (value )
8789 return [
88- encode_engine_value (
90+ _encode_engine_value_core (
8991 getattr (value , f .name ),
90- type_hint = f .type ,
92+ type_info = _get_type_info_safe ( f .type ) ,
9193 )
9294 for f in fields
9395 ]
9496
9597 if is_namedtuple_type (type (value )):
9698 annotations = type (value ).__annotations__
9799 return [
98- encode_engine_value (
100+ _encode_engine_value_core (
99101 getattr (value , name ),
100- type_hint = annotations .get (name ),
102+ type_info = _get_type_info_safe (annotations .get (name ))
103+ if annotations .get (name )
104+ else None ,
101105 )
102106 for name in value ._fields
103107 ]
@@ -110,66 +114,51 @@ def _encode_engine_value_core(
110114
111115 if isinstance (value , (list , tuple )):
112116 if (
113- type_variant
114- and isinstance (type_variant .variant , AnalyzedListType )
115- and type_variant .variant .elem_type
117+ type_info
118+ and isinstance (type_info .variant , AnalyzedListType )
119+ and type_info .variant .elem_type
116120 ):
117- elem_type_info = _get_cached_type_info (
118- type_variant .variant .elem_type , _elem_type_cache
119- )
121+ elem_type_info = _get_type_info_safe (type_info .variant .elem_type )
120122 return [
121123 _encode_engine_value_core (
122124 v ,
123- type_hint = None ,
124- type_variant = elem_type_info ,
125- _elem_type_cache = _elem_type_cache ,
125+ type_info = elem_type_info ,
126126 )
127127 for v in value
128128 ]
129129 else :
130- return [encode_engine_value (v , type_hint ) for v in value ]
130+ return [_encode_engine_value_core (v , type_info = None ) for v in value ]
131131
132132 if isinstance (value , dict ):
133133 # Determine if this is a JSON type
134134 is_json_type = False
135- if type_variant and isinstance (type_variant .variant , AnalyzedBasicType ):
136- is_json_type = type_variant .variant .kind == "Json"
137- elif type_hint :
138- hint_type_info = type_variant or _get_cached_type_info (
139- type_hint , _elem_type_cache
140- )
141- is_json_type = (
142- isinstance (hint_type_info .variant , AnalyzedBasicType )
143- and hint_type_info .variant .kind == "Json"
144- )
135+ if type_info and isinstance (type_info .variant , AnalyzedBasicType ):
136+ is_json_type = type_info .variant .kind == "Json"
145137
146138 # Handle empty dict
147139 if not value :
148- return value if (not type_hint or is_json_type ) else []
140+ return value if (not type_info or is_json_type ) else []
149141
150142 # Handle KTable
151143 first_val = next (iter (value .values ()))
152144 if is_struct_type (type (first_val )):
153145 return [
154- [encode_engine_value (k , type_hint )] + encode_engine_value (v , type_hint )
146+ [_encode_engine_value_core (k , type_info = None )]
147+ + _encode_engine_value_core (v , type_info = None )
155148 for k , v in value .items ()
156149 ]
157150
158151 # Handle regular dict
159152 if (
160- type_variant
161- and isinstance (type_variant .variant , AnalyzedDictType )
162- and type_variant .variant .value_type
153+ type_info
154+ and isinstance (type_info .variant , AnalyzedDictType )
155+ and type_info .variant .value_type
163156 ):
164- value_type_info = _get_cached_type_info (
165- type_variant .variant .value_type , _elem_type_cache
166- )
157+ value_type_info = _get_type_info_safe (type_info .variant .value_type )
167158 return {
168159 k : _encode_engine_value_core (
169160 v ,
170- type_hint = None ,
171- type_variant = value_type_info ,
172- _elem_type_cache = _elem_type_cache ,
161+ type_info = value_type_info ,
173162 )
174163 for k , v in value .items ()
175164 }
@@ -189,11 +178,11 @@ def encode_engine_value(value: Any, type_hint: Type[Any] | str) -> Any:
189178 The encoded engine value
190179 """
191180 # Analyze type once and reuse the result
192- type_info = _get_cached_type_info (type_hint , {} )
181+ type_info = _get_type_info_safe (type_hint )
193182 if isinstance (type_info .variant , AnalyzedUnknownType ):
194183 raise ValueError (f"Type annotation `{ type_info .core_type } ` is unsupported" )
195184
196- return _encode_engine_value_core (value , type_hint = type_hint , type_variant = type_info )
185+ return _encode_engine_value_core (value , type_info )
197186
198187
199188def make_engine_value_decoder (
0 commit comments