|
| 1 | +PyTuple(x = pytuple()) = PyTuple{Tuple}(x) |
| 2 | + |
| 3 | +ispy(::PyTuple) = true |
| 4 | +Py(x::PyTuple) = x.py |
| 5 | + |
| 6 | +@generated function static_length(::PyTuple{T}) where {T} |
| 7 | + try |
| 8 | + fieldcount(T) |
| 9 | + catch |
| 10 | + nothing |
| 11 | + end |
| 12 | +end |
| 13 | + |
| 14 | +@generated function min_length(::PyTuple{T}) where {T} |
| 15 | + count(!Base.isvarargtype, T.parameters) |
| 16 | +end |
| 17 | + |
| 18 | +function check_length(x::PyTuple) |
| 19 | + len = pylen(x.py) |
| 20 | + explen = PythonCall.Wrap.static_length(x) |
| 21 | + if explen === nothing |
| 22 | + minlen = PythonCall.Wrap.min_length(x) |
| 23 | + len ≥ minlen |
| 24 | + else |
| 25 | + len == explen |
| 26 | + end |
| 27 | +end |
| 28 | + |
| 29 | +Base.IteratorSize(::Type{<:PyTuple}) = Base.HasLength() |
| 30 | + |
| 31 | +Base.length(x::PyTuple{T}) where {T<:Tuple} = |
| 32 | + @something(static_length(x), max(min_length(x), Int(pylen(x.py)))) |
| 33 | + |
| 34 | +Base.IteratorEltype(::Type{<:PyTuple}) = Base.HasEltype() |
| 35 | + |
| 36 | +Base.eltype(::Type{PyTuple{T}}) where {T<:Tuple} = eltype(T) |
| 37 | + |
| 38 | +Base.checkbounds(::Type{Bool}, x::PyTuple, i::Integer) = 1 ≤ i ≤ length(x) |
| 39 | + |
| 40 | +Base.checkbounds(x::PyTuple, i::Integer) = |
| 41 | + if !checkbounds(Bool, x, i) |
| 42 | + throw(BoundsError(x, i)) |
| 43 | + end |
| 44 | + |
| 45 | +Base.@propagate_inbounds function Base.getindex(x::PyTuple{T}, i::Integer) where {T<:Tuple} |
| 46 | + i = convert(Int, i)::Int |
| 47 | + @boundscheck checkbounds(x, i) |
| 48 | + E = fieldtype(T, i) |
| 49 | + return pyconvert(E, @py x[@jl(i - 1)]) |
| 50 | +end |
| 51 | + |
| 52 | +Base.@propagate_inbounds function Base.setindex!( |
| 53 | + x::PyTuple{T}, |
| 54 | + v, |
| 55 | + i::Integer, |
| 56 | +) where {T<:Tuple} |
| 57 | + i = convert(Int, i)::Int |
| 58 | + @boundscheck checkbounds(x, i) |
| 59 | + E = fieldtype(T, i) |
| 60 | + v = convert(E, v)::E |
| 61 | + @py x[@jl(i - 1)] = v |
| 62 | + x |
| 63 | +end |
| 64 | + |
| 65 | +function Base.iterate(x::PyTuple{T}, ni = (length(x), 1)) where {T<:Tuple} |
| 66 | + n, i = ni |
| 67 | + if i > @something(static_length(x), n) |
| 68 | + nothing |
| 69 | + else |
| 70 | + (x[i], (n, i + 1)) |
| 71 | + end |
| 72 | +end |
| 73 | + |
| 74 | +function Base.Tuple(x::PyTuple{T}) where {T<:Tuple} |
| 75 | + n = static_length(x) |
| 76 | + if n === nothing |
| 77 | + ntuple(i -> x[i], length(x))::T |
| 78 | + else |
| 79 | + ntuple(i -> x[i], Val(n))::T |
| 80 | + end |
| 81 | +end |
| 82 | + |
| 83 | +# Conversion rule for builtins:tuple -> PyTuple |
| 84 | +function pyconvert_rule_tuple( |
| 85 | + ::Type{T}, |
| 86 | + x::Py, |
| 87 | + ::Type{T1} = Utils._type_ub(T), |
| 88 | +) where {T<:PyTuple,T1} |
| 89 | + ans = @inbounds T1(x) |
| 90 | + if check_length(ans) |
| 91 | + pyconvert_return(ans) |
| 92 | + else |
| 93 | + pyconvert_unconverted() |
| 94 | + end |
| 95 | +end |
| 96 | + |
| 97 | +function Base.show(io::IO, mime::MIME"text/plain", x::PyTuple) |
| 98 | + if !(get(io, :typeinfo, Any) <: PyTuple) |
| 99 | + print(io, "PyTuple: ") |
| 100 | + end |
| 101 | + show(io, mime, Tuple(x)) |
| 102 | + nothing |
| 103 | +end |
0 commit comments