|
| 1 | +const PyJuliaAnyValue_Type__ref = Ref(PyPtr()) |
| 2 | +PyJuliaAnyValue_Type() = begin |
| 3 | + ptr = PyJuliaAnyValue_Type__ref[] |
| 4 | + if isnull(ptr) |
| 5 | + c = [] |
| 6 | + base = PyJuliaBaseValue_Type() |
| 7 | + isnull(base) && return PyPtr() |
| 8 | + t = fill(PyType_Create(c, |
| 9 | + name = "julia.AnyValue", |
| 10 | + base = base, |
| 11 | + repr = pyjlany_repr, |
| 12 | + str = pyjlany_str, |
| 13 | + getattro = pyjlany_getattro, |
| 14 | + setattro = pyjlany_setattro, |
| 15 | + call = pyjlany_call, |
| 16 | + iter = pyjlany_iter, |
| 17 | + richcompare = pyjlany_richcompare, |
| 18 | + as_mapping = ( |
| 19 | + length = pyjlany_length, |
| 20 | + subscript = pyjlany_getitem, |
| 21 | + ass_subscript = pyjlany_setitem, |
| 22 | + ), |
| 23 | + as_sequence = ( |
| 24 | + contains = pyjlany_contains, |
| 25 | + ), |
| 26 | + methods = [ |
| 27 | + (name="__dir__", flags=Py_METH_NOARGS, meth=pyjlany_dir), |
| 28 | + ], |
| 29 | + )) |
| 30 | + ptr = PyPtr(pointer(t)) |
| 31 | + err = PyType_Ready(ptr) |
| 32 | + ism1(err) && return PyPtr() |
| 33 | + PYJLGCCACHE[ptr] = push!(c, t) |
| 34 | + PyJuliaAnyValue_Type__ref[] = ptr |
| 35 | + end |
| 36 | + ptr |
| 37 | +end |
| 38 | + |
| 39 | +PyJuliaAnyValue_New(x) = PyJuliaValue_New(PyJuliaAnyValue_Type(), x) |
| 40 | +PyJuliaValue_From(x) = PyJuliaAnyValue_New(x) |
| 41 | + |
| 42 | +pyjlany_repr(xo::PyPtr) = try |
| 43 | + x = PyJuliaValue_GetValue(xo) |
| 44 | + s = "<jl $(repr(x))>" |
| 45 | + PyUnicode_From(s) |
| 46 | +catch err |
| 47 | + PyErr_SetJuliaError(err) |
| 48 | + PyPtr() |
| 49 | +end |
| 50 | + |
| 51 | +pyjlany_str(xo::PyPtr) = try |
| 52 | + x = PyJuliaValue_GetValue(xo) |
| 53 | + s = string(x) |
| 54 | + PyUnicode_From(s) |
| 55 | +catch err |
| 56 | + PyErr_SetJuliaError(err) |
| 57 | + return PyPtr() |
| 58 | +end |
| 59 | + |
| 60 | +pyjlany_getattro(xo::PyPtr, ko::PyPtr) = begin |
| 61 | + # Try generic lookup first |
| 62 | + ro = PyObject_GenericGetAttr(xo, ko) |
| 63 | + if isnull(ro) && PyErr_IsSet(PyExc_AttributeError()) |
| 64 | + PyErr_Clear() |
| 65 | + else |
| 66 | + return ro |
| 67 | + end |
| 68 | + # Now try to get the corresponding property |
| 69 | + x = PyJuliaValue_GetValue(xo) |
| 70 | + k = PyUnicode_AsString(ko) |
| 71 | + isempty(k) && PyErr_IsSet() && return PyPtr() |
| 72 | + k = pyjl_attr_py2jl(k) |
| 73 | + try |
| 74 | + v = getproperty(x, Symbol(k)) |
| 75 | + PyObject_From(v) |
| 76 | + catch err |
| 77 | + if (err isa UndefVarError && err.var === Symbol(k)) || (err isa ErrorException && occursin("has no field", err.msg)) |
| 78 | + PyErr_SetStringFromJuliaError(PyExc_AttributeError(), err) |
| 79 | + else |
| 80 | + PyErr_SetJuliaError(err) |
| 81 | + end |
| 82 | + PyPtr() |
| 83 | + end |
| 84 | +end |
| 85 | + |
| 86 | +propertytype(x, k) = |
| 87 | + propertiesarefields(typeof(x)) && hasfield(typeof(x), k) ? fieldtype(typeof(x), k) : Any |
| 88 | +@generated propertiesarefields(::Type{T}) where {T} = |
| 89 | + which(getproperty, Tuple{T,Symbol}) == which(getproperty, Tuple{Any,Symbol}) |
| 90 | + |
| 91 | +pyjlany_setattro(xo::PyPtr, ko::PyPtr, vo::PyPtr) = begin |
| 92 | + # Try generic lookup first |
| 93 | + ro = PyObject_GenericSetAttr(xo, ko, vo) |
| 94 | + if ism1(ro) && PyErr_IsSet(PyExc_AttributeError()) |
| 95 | + PyErr_Clear() |
| 96 | + else |
| 97 | + return ro |
| 98 | + end |
| 99 | + if isnull(vo) |
| 100 | + PyErr_SetString(PyExc_TypeError(), "attribute deletion not supported") |
| 101 | + return Cint(-1) |
| 102 | + end |
| 103 | + # Now try to set the corresponding property |
| 104 | + x = PyJuliaValue_GetValue(xo) |
| 105 | + k = PyUnicode_AsString(ko) |
| 106 | + isempty(k) && PyErr_IsSet() && return Cint(-1) |
| 107 | + k = pyjl_attr_py2jl(k) |
| 108 | + try |
| 109 | + V = propertytype(x, Symbol(k)) |
| 110 | + ism1(PyObject_Convert(vo, V)) && return Cint(-1) |
| 111 | + v = takeresult(V) |
| 112 | + setproperty!(x, Symbol(k), v) |
| 113 | + Cint(0) |
| 114 | + catch err |
| 115 | + if (err isa UndefVarError && err.var === Symbol(k)) || (err isa ErrorException && occursin("has no field", err.msg)) |
| 116 | + PyErr_SetStringFromJuliaError(PyExc_AttributeError(), err) |
| 117 | + else |
| 118 | + PyErr_SetJuliaError(err) |
| 119 | + end |
| 120 | + Cint(-1) |
| 121 | + end |
| 122 | +end |
| 123 | + |
| 124 | +pyjl_dir(x) = propertynames(x) |
| 125 | +pyjl_dir(x::Module) = begin |
| 126 | + r = Symbol[] |
| 127 | + append!(r, names(x, all=true, imported=true)) |
| 128 | + for m in ccall(:jl_module_usings, Any, (Any,), x)::Vector |
| 129 | + append!(r, names(m)) |
| 130 | + end |
| 131 | + r |
| 132 | +end |
| 133 | + |
| 134 | +pyjlany_dir(xo::PyPtr, _::PyPtr) = begin |
| 135 | + fo = PyObject_GetAttrString(PyJuliaBaseValue_Type(), "__dir__") |
| 136 | + isnull(fo) && return PyPtr() |
| 137 | + ro = PyObject_CallNice(fo, PyObjectRef(xo)) |
| 138 | + Py_DecRef(fo) |
| 139 | + isnull(ro) && return PyPtr() |
| 140 | + x = PyJuliaValue_GetValue(xo) |
| 141 | + ks = try |
| 142 | + collect(map(string, pyjl_dir(x))) |
| 143 | + catch err |
| 144 | + Py_DecRef(ro) |
| 145 | + PyErr_SetJuliaError(err) |
| 146 | + return PyPtr() |
| 147 | + end |
| 148 | + for k in ks |
| 149 | + ko = PyUnicode_From(pyjl_attr_jl2py(k)) |
| 150 | + isnull(ko) && (Py_DecRef(ro); return PyPtr()) |
| 151 | + err = PyList_Append(ro, ko) |
| 152 | + Py_DecRef(ko) |
| 153 | + ism1(err) && (Py_DecRef(ro); return PyPtr()) |
| 154 | + end |
| 155 | + return ro |
| 156 | +end |
| 157 | + |
| 158 | +pyjlany_call(fo::PyPtr, argso::PyPtr, kwargso::PyPtr) = begin |
| 159 | + f = PyJuliaValue_GetValue(fo) |
| 160 | + if isnull(argso) |
| 161 | + args = Vector{Any}() |
| 162 | + else |
| 163 | + ism1(PyObject_Convert(argso, Vector{Any})) && return PyPtr() |
| 164 | + args = takeresult(Vector{Any}) |
| 165 | + end |
| 166 | + if isnull(kwargso) |
| 167 | + kwargs = Dict{Symbol, Any}() |
| 168 | + else |
| 169 | + ism1(PyObject_Convert(kwargso, Dict{Symbol, Any})) && return PyPtr() |
| 170 | + kwargs = takeresult(Dict{Symbol, Any}) |
| 171 | + end |
| 172 | + try |
| 173 | + x = f(args...; kwargs...) |
| 174 | + PyObject_From(x) |
| 175 | + catch err |
| 176 | + if err isa MethodError && err.f === f |
| 177 | + PyErr_SetStringFromJuliaError(PyExc_TypeError(), err) |
| 178 | + else |
| 179 | + PyErr_SetJuliaError(err) |
| 180 | + end |
| 181 | + PyPtr() |
| 182 | + end |
| 183 | +end |
| 184 | + |
| 185 | +pyjlany_length(xo::PyPtr) = try |
| 186 | + x = PyJuliaValue_GetValue(xo) |
| 187 | + Py_ssize_t(length(x)) |
| 188 | +catch err |
| 189 | + if err isa MethodError && err.f === length |
| 190 | + PyErr_SetStringFromJuliaError(PyExc_TypeError(), err) |
| 191 | + else |
| 192 | + PyErr_SetJuliaError(err) |
| 193 | + end |
| 194 | + Py_ssize_t(-1) |
| 195 | +end |
| 196 | + |
| 197 | +@generated pyjl_keytype(::Type{T}) where {T} = try; keytype(T); catch; nothing; end; |
| 198 | +pyjl_hasvarindices(::Type) = true |
| 199 | +pyjl_hasvarindices(::Type{<:AbstractDict}) = false |
| 200 | + |
| 201 | +pyjl_getindices(x, ko) = |
| 202 | + if (K = pyjl_keytype(typeof(x))) !== nothing |
| 203 | + ism1(PyObject_Convert(ko, K)) ? PYERR() : (takeresult(K),) |
| 204 | + elseif pyjl_hasvarindices(typeof(x)) && PyTuple_Check(ko) |
| 205 | + ism1(PyObject_TryConvert(ko, Tuple)) ? PYERR() : takeresult(Tuple) |
| 206 | + else |
| 207 | + ism1(PyObject_TryConvert(ko, Any)) ? PYERR() : takeresult(Any) |
| 208 | + end |
| 209 | + |
| 210 | +pyjlany_getitem(xo::PyPtr, ko::PyPtr) = begin |
| 211 | + x = PyJuliaValue_GetValue(xo) |
| 212 | + k = pyjl_getindices(x, ko) |
| 213 | + k === PYERR() && return PyPtr() |
| 214 | + try |
| 215 | + PyObject_From(x[k...]) |
| 216 | + catch err |
| 217 | + if err isa BoundsError && err.a === x |
| 218 | + PyErr_SetStringFromJuliaError(PyExc_IndexError(), err) |
| 219 | + elseif err isa KeyError && (err.key === k || (err.key,) === k) |
| 220 | + PyErr_SetStringFromJuliaError(PyExc_KeyError(), err) |
| 221 | + else |
| 222 | + PyErr_SetJuliaError(err) |
| 223 | + end |
| 224 | + PyPtr() |
| 225 | + end |
| 226 | +end |
| 227 | + |
| 228 | +@generated pyjl_valtype(::Type{T}) where {T} = try; valtype(T); catch; try eltype(T); catch; nothing; end; end; |
| 229 | + |
| 230 | +pyjl_getvalue(x, vo) = |
| 231 | + if (V = pyjl_valtype(typeof(x))) !== nothing |
| 232 | + ism1(PyObject_Convert(vo, V)) ? PYERR() : takeresult(V) |
| 233 | + else |
| 234 | + ism1(PyObject_Convert(vo, Any)) ? PYERR() : takeresult(Any) |
| 235 | + end |
| 236 | + |
| 237 | +pyjlany_setitem(xo::PyPtr, ko::PyPtr, vo::PyPtr) = begin |
| 238 | + x = PyJuliaValue_GetValue(xo) |
| 239 | + k = pyjl_getindices(x, ko) |
| 240 | + k === PYERR() && return Cint(-1) |
| 241 | + try |
| 242 | + if isnull(vo) |
| 243 | + delete!(x, k...) |
| 244 | + Cint(0) |
| 245 | + else |
| 246 | + v = pyjl_getvalue(x, vo) |
| 247 | + v === PYERR() && return Cint(-1) |
| 248 | + x[k...] = v |
| 249 | + Cint(0) |
| 250 | + end |
| 251 | + catch |
| 252 | + if err isa BoundsError && err.a === x |
| 253 | + PyErr_SetStringFromJuliaError(PyExc_IndexError(), err) |
| 254 | + elseif err isa KeyError && (err.key === k || (err.key,) === k) |
| 255 | + PyErr_SetStringFromJuliaError(PyExc_KeyError(), err) |
| 256 | + elseif err isa MethodError && err.f === delete! |
| 257 | + PyErr_SetStringFromJuliaError(PyExc_TypeError(), err) |
| 258 | + else |
| 259 | + PyErr_SetJuliaError(err) |
| 260 | + end |
| 261 | + Cint(-1) |
| 262 | + end |
| 263 | +end |
| 264 | + |
| 265 | +pyjlany_iter(xo::PyPtr) = PyJuliaIteratorValue_New(Iterator(PyJuliaValue_GetValue(xo))) |
| 266 | + |
| 267 | +pyjlany_contains(xo::PyPtr, vo::PyPtr) = begin |
| 268 | + x = PyJuliaValue_GetValue(xo) |
| 269 | + r = PyObject_TryConvert(vo, eltype(x)) |
| 270 | + r == -1 && return Cint(-1) |
| 271 | + r == 0 && return Cint(0) |
| 272 | + v = takeresult(eltype(x)) |
| 273 | + try |
| 274 | + Cint(v in x) |
| 275 | + catch err |
| 276 | + if err isa MethodError && err.f === :in |
| 277 | + PyErr_SetStringFromJuliaError(PyExc_TypeError(), err) |
| 278 | + else |
| 279 | + PyErr_SetJuliaError(err) |
| 280 | + end |
| 281 | + Cint(-1) |
| 282 | + end |
| 283 | +end |
| 284 | + |
| 285 | +pyjlany_richcompare(xo::PyPtr, yo::PyPtr, op::Cint) = begin |
| 286 | + x = PyJuliaValue_GetValue(xo) |
| 287 | + r = PyObject_TryConvert(yo, Any) |
| 288 | + r == -1 && return PyPtr() |
| 289 | + r == 0 && return PyNotImplemented_New() |
| 290 | + y = takeresult() |
| 291 | + try |
| 292 | + if op == Py_EQ |
| 293 | + PyObject_From(x == y) |
| 294 | + elseif op == Py_NE |
| 295 | + PyObject_From(x != y) |
| 296 | + elseif op == Py_LE |
| 297 | + PyObject_From(x <= y) |
| 298 | + elseif op == Py_LT |
| 299 | + PyObject_From(x < y) |
| 300 | + elseif op == Py_GE |
| 301 | + PyObject_From(x >= y) |
| 302 | + elseif op == Py_GT |
| 303 | + PyObject_From(x > y) |
| 304 | + else |
| 305 | + PyErr_SetString(PyExc_ValueError(), "bad op given to richcompare: $op") |
| 306 | + PyPtr() |
| 307 | + end |
| 308 | + catch err |
| 309 | + if err isa MethodError && err.f in (==, !=, <=, <, >=, >) |
| 310 | + PyNotImplemented_New() |
| 311 | + else |
| 312 | + PyErr_SetJuliaError(err) |
| 313 | + PyPtr() |
| 314 | + end |
| 315 | + end |
| 316 | +end |
0 commit comments