From 3d2d6105e86f885c6d1d60c1ca1428dbee850ca1 Mon Sep 17 00:00:00 2001 From: Christopher Doris Date: Wed, 13 Jan 2021 13:43:59 +0000 Subject: [PATCH] adds julia.SetValue --- src/cpython/CPython.jl | 1 + src/cpython/collections.jl | 43 +++++++ src/cpython/complex.jl | 2 +- src/cpython/juliaset.jl | 250 +++++++++++++++++++++++++++++++++++++ src/cpython/newtype.jl | 48 ++++++- src/cpython/object.jl | 5 +- 6 files changed, 345 insertions(+), 4 deletions(-) create mode 100644 src/cpython/juliaset.jl diff --git a/src/cpython/CPython.jl b/src/cpython/CPython.jl index 06aa3db0..0e6af2e7 100644 --- a/src/cpython/CPython.jl +++ b/src/cpython/CPython.jl @@ -60,6 +60,7 @@ include("juliaany.jl") include("juliaiterator.jl") include("juliatype.jl") include("juliadict.jl") +include("juliaset.jl") include("juliaarray.jl") include("juliavector.jl") include("juliamodule.jl") diff --git a/src/cpython/collections.jl b/src/cpython/collections.jl index e416da52..2fb4a0ab 100644 --- a/src/cpython/collections.jl +++ b/src/cpython/collections.jl @@ -34,6 +34,49 @@ for n in [:Container, :Hashable, :Iterable, :Iterator, :Reversible, :Generator, end end +""" + PyIterable_Collect(xs::PyPtr, T::Type, [skip=false]) :: Vector{T} + +Convert the elements of `xs` to type `T` and collect them into a vector. +On error, an empty vector is returned. + +If `skip` then elements which cannot be converted to a `T` are skipped over, +instead of raising an error. Other errors are still propagated. +""" +PyIterable_Collect(xso::PyPtr, ::Type{T}, skip::Bool=false) where {T} = begin + xs = T[] + it = PyObject_GetIter(xso) + isnull(it) && return xs + try + while true + xo = PyIter_Next(it) + if !isnull(xo) + if skip + r = PyObject_TryConvert(xo, T) + Py_DecRef(xo) + r == -1 && (empty!(xs); break) + r == 0 && continue + else + r = PyObject_Convert(xo, T) + Py_DecRef(xo) + r == -1 && (empty!(xs); break) + end + x = takeresult(T) + push!(xs, x) + else + PyErr_IsSet() && empty!(xs) + break + end + end + catch err + empty!(xs) + PyErr_SetJuliaError(err) + finally + Py_DecRef(it) + end + return xs +end + PyIterable_ConvertRule_vector(o, ::Type{S}) where {S<:Vector} = begin it = PyObject_GetIter(o) isnull(it) && return -1 diff --git a/src/cpython/complex.jl b/src/cpython/complex.jl index 9d3777d8..d1119bf7 100644 --- a/src/cpython/complex.jl +++ b/src/cpython/complex.jl @@ -26,7 +26,7 @@ PyComplexable_TryConvertRule_convert(o, ::Type{S}) where {S} = begin end PyComplexable_TryConvertRule_tryconvert(o, ::Type{S}) where {S} = begin - x = PyComplexable_AsComplex(o) + x = PyComplex_AsComplex(o) ism1(x) && PyErr_IsSet() && return -1 putresult(tryconvert(S, x)) end diff --git a/src/cpython/juliaset.jl b/src/cpython/juliaset.jl new file mode 100644 index 00000000..9e1c720e --- /dev/null +++ b/src/cpython/juliaset.jl @@ -0,0 +1,250 @@ +const PyJuliaSetValue_Type__ref = Ref(PyPtr()) +PyJuliaSetValue_Type() = begin + ptr = PyJuliaSetValue_Type__ref[] + if isnull(ptr) + c = [] + base = PyJuliaAnyValue_Type() + isnull(base) && return PyPtr() + t = fill(PyType_Create(c, + name = "julia.SetValue", + base = base, + methods = [ + (name="add", flags=Py_METH_O, meth=pyjlset_add), + (name="clear", flags=Py_METH_NOARGS, meth=pyjlset_clear), + (name="copy", flags=Py_METH_NOARGS, meth=pyjlset_copy), + (name="difference", flags=Py_METH_O, meth=pyjlset_difference), + (name="difference_update", flags=Py_METH_O, meth=pyjlset_difference_update), + (name="discard", flags=Py_METH_O, meth=pyjlset_discard), + (name="intersection", flags=Py_METH_O, meth=pyjlset_intersection), + (name="intersection_update", flags=Py_METH_O, meth=pyjlset_intersection_update), + # (name="isdisjoint", flags=Py_METH_O, meth=pyjlset_isdisjoint), + # (name="issubset", flags=Py_METH_O, meth=pyjlset_issubset), + # (name="issuperset", flags=Py_METH_O, meth=pyjlset_issuperset), + (name="pop", flags=Py_METH_NOARGS, meth=pyjlset_pop), + (name="remove", flags=Py_METH_O, meth=pyjlset_remove), + (name="symmetric_difference", flags=Py_METH_O, meth=pyjlset_symmetric_difference), + (name="symmetric_difference_update", flags=Py_METH_O, meth=pyjlset_symmetric_difference_update), + (name="union", flags=Py_METH_O, meth=pyjlset_union), + (name="update", flags=Py_METH_O, meth=pyjlset_update), + ], + as_number = ( + or = pyjlset_or, + and = pyjlset_and, + xor = pyjlset_xor, + subtract = pyjlset_sub, + inplace_or = pyjlset_ior, + inplace_and = pyjlset_iand, + inplace_xor = pyjlset_ixor, + inplace_subtract = pyjlset_isub, + ), + )) + ptr = PyPtr(pointer(t)) + err = PyType_Ready(ptr) + ism1(err) && return PyPtr() + abc = PyMutableSetABC_Type() + isnull(abc) && return PyPtr() + ism1(PyABC_Register(ptr, abc)) && return PyPtr() + PYJLGCCACHE[ptr] = push!(c, t) + PyJuliaSetValue_Type__ref[] = ptr + end + ptr +end + +PyJuliaSetValue_New(x::AbstractSet) = PyJuliaValue_New(PyJuliaSetValue_Type(), x) +PyJuliaValue_From(x::AbstractSet) = PyJuliaSetValue_New(x) + +pyjlset_add(xo::PyPtr, vo::PyPtr) = begin + x = PyJuliaValue_GetValue(xo)::AbstractSet + ism1(PyObject_Convert(vo, eltype(x))) && return PyPtr() + v = takeresult(eltype(x)) + try + push!(x, v) + PyNone_New() + catch err + PyErr_SetJuliaError(err) + PyPtr() + end +end + +pyjlset_discard(xo::PyPtr, vo::PyPtr) = begin + x = PyJuliaValue_GetValue(xo)::AbstractSet + r = PyObject_TryConvert(vo, eltype(x)) + r == -1 && return PyPtr() + r == 0 && return PyNone_New() + v = takeresult(eltype(x)) + try + delete!(x, v) + PyNone_New() + catch err + PyErr_SetJuliaError(err) + PyPtr() + end +end + +pyjlset_clear(xo::PyPtr, _::PyPtr) = begin + x = PyJuliaValue_GetValue(xo)::AbstractSet + try + empty!(x) + PyNone_New() + catch err + PyErr_SetJuliaError(err) + PyPtr() + end +end + +pyjlset_copy(xo::PyPtr, _::PyPtr) = begin + x = PyJuliaValue_GetValue(xo)::AbstractSet + try + PyObject_From(copy(x)) + catch err + PyErr_SetJuliaError(err) + PyPtr() + end +end + +pyjlset_pop(xo::PyPtr, _::PyPtr) = begin + x = PyJuliaValue_GetValue(xo)::AbstractSet + try + if isempty(x) + PyErr_SetString(PyExc_KeyError(), "pop from an empty set") + PyPtr() + else + PyObject_From(pop!(x)) + end + catch err + PyErr_SetJuliaError(err) + PyPtr() + end +end + +pyjlset_remove(xo::PyPtr, vo::PyPtr) = begin + x = PyJuliaValue_GetValue(xo)::AbstractSet + r = PyObject_TryConvert(vo, eltype(x)) + if r == -1 + return PyPtr() + elseif r == 0 + PyErr_SetObject(PyExc_KeyError(), vo) + return PyPtr() + end + v = takeresult(eltype(x)) + try + if v in x + delete!(x, v) + PyNone_New() + else + PyErr_SetObject(PyExc_KeyError(), vo) + PyPtr() + end + catch err + PyErr_SetJuliaError(err) + PyPtr() + end +end + +pyjlset_ibinop_named(xo, yo, op, skip) = begin + x = PyJuliaValue_GetValue(xo)::AbstractSet + y = PyIterable_Collect(yo, eltype(x), skip) + isempty(y) && PyErr_IsSet() && return PyPtr() + try + op(x, y) + PyNone_New() + catch err + PyErr_SetJuliaError(err) + PyPtr() + end +end +pyjlset_update(xo, yo) = pyjlset_ibinop_named(xo, yo, union!, false) +pyjlset_difference_update(xo, yo) = pyjlset_ibinop_named(xo, yo, setdiff!, true) +pyjlset_symmetric_difference_update(xo, yo) = pyjlset_ibinop_named(xo, yo, symdiff!, false) +pyjlset_intersection_update(xo, yo) = pyjlset_ibinop_named(xo, yo, intersect!, true) + +pyjlset_ibinop_operator(xo, yo, op, skip) = begin + r = PySetABC_Check(yo) + r == -1 && return PyPtr() + r == 0 && return PyNotImplemented_New() + x = PyJuliaValue_GetValue(xo)::AbstractSet + y = PyIterable_Collect(yo, eltype(x), skip) + isempty(y) && PyErr_IsSet() && return PyPtr() + try + PyObject_From(op(x, y)) + catch err + PyErr_SetJuliaError(err) + PyPtr() + end +end +pyjlset_ior(xo, yo) = pyjlset_ibinop_operator(xo, yo, union!, false) +pyjlset_isub(xo, yo) = pyjlset_ibinop_operator(xo, yo, setdiff!, true) +pyjlset_ixor(xo, yo) = pyjlset_ibinop_operator(xo, yo, symdiff!, false) +pyjlset_iand(xo, yo) = pyjlset_ibinop_operator(xo, yo, intersect!, true) + +pyjlset_binop_generic(xo::PyPtr, yo::PyPtr, op) = begin + xo2 = PySet_New(xo) + isnull(xo2) && return PyPtr() + yo2 = PySet_New(yo) + isnull(yo2) && (Py_DecRef(xo2); return PyPtr()) + ro = op(xo2, yo2) + Py_DecRef(xo2) + Py_DecRef(yo2) + ro +end +pyjlset_or_generic(xo, yo) = pyjlset_binop_generic(xo, yo, PyNumber_InPlaceOr) +pyjlset_xor_generic(xo, yo) = pyjlset_binop_generic(xo, yo, PyNumber_InPlaceXor) +pyjlset_and_generic(xo, yo) = pyjlset_binop_generic(xo, yo, PyNumber_InPlaceAnd) +pyjlset_sub_generic(xo, yo) = pyjlset_binop_generic(xo, yo, PyNumber_InPlaceSubtract) + +pyjlset_binop_special(xo::PyPtr, yo::PyPtr, op) = begin + x = PyJuliaValue_GetValue(xo)::AbstractSet + y = PyJuliaValue_GetValue(yo)::AbstractSet + try + PyObject_From(op(x, y)) + catch err + PyErr_SetJuliaError(err) + PyPtr() + end +end +pyjlset_or_special(xo, yo) = pyjlset_binop_special(xo, yo, union) +pyjlset_xor_special(xo, yo) = pyjlset_binop_special(xo, yo, symdiff) +pyjlset_and_special(xo, yo) = pyjlset_binop_special(xo, yo, intersect) +pyjlset_sub_special(xo, yo) = pyjlset_binop_special(xo, yo, setdiff) + +pyjlset_binop_operator(xo::PyPtr, yo::PyPtr, gop, sop) = begin + t = PyJuliaSetValue_Type() + isnull(t) && return PyPtr() + if (r=PyObject_IsInstance(xo, t); ism1(r) && return PyPtr(); r!=0) + if (r=PyObject_IsInstance(yo, t); ism1(r) && return PyPtr(); r!=0) + sop(xo, yo) + elseif (r=PySetABC_Check(yo); ism1(r) && return PyPtr(); r!=0) + gop(xo, yo) + else + PyNotImplemented_New() + end + elseif (r=PyObject_IsInstance(yo, t); ism1(r) && return PyPtr(); r!=0) + if (r=PySetABC_Check(xo); ism1(r) && return PyPtr(); r!=0) + gop(xo, yo) + else + PyNotImplemented_New() + end + else + PyNotImplemented_New() + end +end +pyjlset_or(xo, yo) = pyjlset_binop_operator(xo, yo, pyjlset_or_generic, pyjlset_or_special) +pyjlset_xor(xo, yo) = pyjlset_binop_operator(xo, yo, pyjlset_xor_generic, pyjlset_xor_special) +pyjlset_and(xo, yo) = pyjlset_binop_operator(xo, yo, pyjlset_and_generic, pyjlset_and_special) +pyjlset_sub(xo, yo) = pyjlset_binop_operator(xo, yo, pyjlset_sub_generic, pyjlset_sub_special) + +pyjlset_binop_named(xo, yo, gop, sop) = begin + t = PyJuliaSetValue_Type() + isnull(t) && return PyPtr() + if (r=PyObject_IsInstance(yo, t); ism1(r) && return PyPtr(); r!=0) + sop(xo, yo) + elseif (r=PySetABC_Check(yo); ism1(r) && return PyPtr(); r!=0) + gop(xo, yo) + else + PyNotImplemented_New() + end +end +pyjlset_union(xo, yo) = pyjlset_binop_named(xo, yo, pyjlset_or_generic, pyjlset_or_special) +pyjlset_symmetric_difference(xo, yo) = pyjlset_binop_named(xo, yo, pyjlset_xor_generic, pyjlset_xor_special) +pyjlset_intersection(xo, yo) = pyjlset_binop_named(xo, yo, pyjlset_and_generic, pyjlset_and_special) +pyjlset_difference(xo, yo) = pyjlset_binop_named(xo, yo, pyjlset_sub_generic, pyjlset_sub_special) diff --git a/src/cpython/newtype.jl b/src/cpython/newtype.jl index 74571038..f38b883a 100644 --- a/src/cpython/newtype.jl +++ b/src/cpython/newtype.jl @@ -28,7 +28,53 @@ end ### PROTOCOLS PyNumberMethods_Create(c, x::PyNumberMethods) = x -PyNumberMethods_Create(c; opts...) = C.PyNumberMethods(; [k => (v isa Ptr ? v : v isa Base.CFunction ? cacheptr!(c, v) : error()) for (k,v) in pairs(opts)]...) +PyNumberMethods_Create(c; + add=C_NULL, subtract=C_NULL, multiply=C_NULL, remainder=C_NULL, divmod=C_NULL, power=C_NULL, + negative=C_NULL, positive=C_NULL, absolute=C_NULL, bool=C_NULL, invert=C_NULL, + lshift=C_NULL, rshift=C_NULL, and=C_NULL, or=C_NULL, xor=C_NULL, int=C_NULL, float=C_NULL, + inplace_add=C_NULL, inplace_subtract=C_NULL, inplace_multiply=C_NULL, + inplace_remainder=C_NULL, inplace_power=C_NULL, inplace_lshift=C_NULL, inplace_rshift=C_NULL, + inplace_and=C_NULL, inplace_xor=C_NULL, inplace_or=C_NULL, floordivide=C_NULL, + truedivide=C_NULL, inplace_floordivide=C_NULL, inplace_truedivide=C_NULL, index=C_NULL, + matrixmultiply=C_NULL, inplace_matrixmultiply=C_NULL +) = + PyNumberMethods( + add = @cachefuncptr!(c, add, PyPtr, (PyPtr, PyPtr)), + subtract = @cachefuncptr!(c, subtract, PyPtr, (PyPtr, PyPtr)), + multiply = @cachefuncptr!(c, multiply, PyPtr, (PyPtr, PyPtr)), + remainder = @cachefuncptr!(c, remainder, PyPtr, (PyPtr, PyPtr)), + divmod = @cachefuncptr!(c, divmod, PyPtr, (PyPtr, PyPtr)), + power = @cachefuncptr!(c, power, PyPtr, (PyPtr, PyPtr, PyPtr)), + negative = @cachefuncptr!(c, negative, PyPtr, (PyPtr,)), + positive = @cachefuncptr!(c, positive, PyPtr, (PyPtr,)), + absolute = @cachefuncptr!(c, absolute, PyPtr, (PyPtr,)), + bool = @cachefuncptr!(c, bool, Cint, (PyPtr,)), + invert = @cachefuncptr!(c, invert, PyPtr, (PyPtr,)), + lshift = @cachefuncptr!(c, lshift, PyPtr, (PyPtr, PyPtr)), + rshift = @cachefuncptr!(c, rshift, PyPtr, (PyPtr, PyPtr)), + and = @cachefuncptr!(c, and, PyPtr, (PyPtr, PyPtr)), + xor = @cachefuncptr!(c, xor, PyPtr, (PyPtr, PyPtr)), + or = @cachefuncptr!(c, or, PyPtr, (PyPtr, PyPtr)), + int = @cachefuncptr!(c, int, PyPtr, (PyPtr,)), + float = @cachefuncptr!(c, float, PyPtr, (PyPtr,)), + inplace_add = @cachefuncptr!(c, inplace_add, PyPtr, (PyPtr, PyPtr)), + inplace_subtract = @cachefuncptr!(c, inplace_subtract, PyPtr, (PyPtr, PyPtr)), + inplace_multiply = @cachefuncptr!(c, inplace_multiply, PyPtr, (PyPtr, PyPtr)), + inplace_remainder = @cachefuncptr!(c, inplace_remainder, PyPtr, (PyPtr, PyPtr)), + inplace_power = @cachefuncptr!(c, inplace_power, PyPtr, (PyPtr, PyPtr, PyPtr)), + inplace_lshift = @cachefuncptr!(c, inplace_lshift, PyPtr, (PyPtr, PyPtr)), + inplace_rshift = @cachefuncptr!(c, inplace_rshift, PyPtr, (PyPtr, PyPtr)), + inplace_and = @cachefuncptr!(c, inplace_and, PyPtr, (PyPtr, PyPtr)), + inplace_xor = @cachefuncptr!(c, inplace_xor, PyPtr, (PyPtr, PyPtr)), + inplace_or = @cachefuncptr!(c, inplace_or, PyPtr, (PyPtr, PyPtr)), + floordivide = @cachefuncptr!(c, floordivide, PyPtr, (PyPtr, PyPtr)), + truedivide = @cachefuncptr!(c, truedivide, PyPtr, (PyPtr, PyPtr)), + inplace_floordivide = @cachefuncptr!(c, inplace_floordivide, PyPtr, (PyPtr, PyPtr)), + inplace_truedivide = @cachefuncptr!(c, inplace_truedivide, PyPtr, (PyPtr, PyPtr)), + index = @cachefuncptr!(c, index, PyPtr, (PyPtr,)), + matrixmultiply = @cachefuncptr!(c, matrixmultiply, PyPtr, (PyPtr, PyPtr)), + inplace_matrixmultiply = @cachefuncptr!(c, inplace_matrixmultiply, PyPtr, (PyPtr, PyPtr)), + ) PyNumberMethods_Create(c, x::Dict) = PyNumberMethods_Create(c; x...) PyNumberMethods_Create(c, x::NamedTuple) = PyNumberMethods_Create(c; x...) diff --git a/src/cpython/object.jl b/src/cpython/object.jl index 466113d5..ff7cd4c0 100644 --- a/src/cpython/object.jl +++ b/src/cpython/object.jl @@ -155,7 +155,8 @@ PyObject_TryConvert_CompileRule(::Type{T}, t::PyPtr) where {T} = begin end if xb != PyPtr() push!(basetypes, xt) - push!(basemros, [xb; PyType_MROAsVector(xt)]) + xmro = PyType_MROAsVector(xt) + push!(basemros, xb in xmro ? xmro : [xb; xmro]) end end for b in basetypes[2:end] @@ -176,7 +177,7 @@ PyObject_TryConvert_CompileRule(::Type{T}, t::PyPtr) where {T} = begin break end end - ok || error("Fatal inheritence error: could not merge MROs") + ok || error("Fatal inheritence error: could not merge MROs (alltypes=$alltypes, basemros=$basemros)") # add it to the list push!(alltypes, b) # remove it from consideration