@@ -169,3 +169,29 @@ function pyconvert_rule_iterable(::Type{R}, x::Py, ::Type{Pair{K0,V0}}=Utils._ty
169169 V2 = Utils. _promote_type_bounded (V0, typeof (v), V1)
170170 return pyconvert_return (Pair {K2,V2} (k, v))
171171end
172+
173+ # NamedTuple
174+
175+ _nt_names_types (:: Type ) = nothing
176+ _nt_names_types (:: Type{NamedTuple} ) = (nothing , nothing )
177+ _nt_names_types (:: Type{NamedTuple{names}} ) where {names} = (names, nothing )
178+ _nt_names_types (:: Type{NamedTuple{names,types} where {names}} ) where {types} = (nothing , types)
179+ _nt_names_types (:: Type{NamedTuple{names,types}} ) where {names,types} = (names, types)
180+
181+ function pyconvert_rule_iterable (:: Type{R} , x:: Py ) where {R<: NamedTuple }
182+ # this is actually strict and only converts python named tuples (i.e. tuples with a
183+ # _fields attribute) where the field names match those from R (if specified).
184+ names_types = _nt_names_types (R)
185+ names_types === nothing && return pyconvert_unconverted ()
186+ names, types = names_types
187+ PythonCall. pyistuple (x) || return pyconvert_unconverted ()
188+ names2_ = pygetattr (x, " _fields" , pybuiltins. None)
189+ names2 = @pyconvert (names === nothing ? Tuple{Vararg{Symbol}} : typeof (names), names2_)
190+ pydel! (names2_)
191+ names === nothing || names === names2 || return pyconvert_unconverted ()
192+ types2 = types === nothing ? NTuple{length (names2),Any} : types
193+ vals = @pyconvert (types2, x)
194+ length (vals) == length (names2) || return pyconvert_unconverted ()
195+ types3 = types === nothing ? typeof (vals) : types
196+ return pyconvert_return (NamedTuple {names2,types3} (vals))
197+ end
0 commit comments