From 0ca66fdd9f5e9777ccba2aba537697b9b8f45bf6 Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Sat, 27 Oct 2018 18:08:35 +0200 Subject: [PATCH 01/15] switch to 0.7/1.0 properties --- benchmarks/pywrapfn.jl | 5 +-- src/PyCall.jl | 83 ++++++++++++++++++++++++++++++------------ src/callback.jl | 2 +- src/conversions.jl | 10 ++--- src/exception.jl | 5 +-- src/gui.jl | 8 ++-- src/io.jl | 2 +- src/pydates.jl | 6 +-- src/pyfncall.jl | 4 +- src/pyinit.jl | 2 +- src/pyoperators.jl | 10 ++--- src/pytype.jl | 12 +++--- src/serialize.jl | 2 +- test/runtests.jl | 12 +++++- 14 files changed, 101 insertions(+), 62 deletions(-) diff --git a/benchmarks/pywrapfn.jl b/benchmarks/pywrapfn.jl index 58f94d68..9fd2149b 100644 --- a/benchmarks/pywrapfn.jl +++ b/benchmarks/pywrapfn.jl @@ -9,10 +9,7 @@ end function PyWrapFn(o::Union{PyObject, PyPtr}, nargs::Int, returntype::Type=PyObject) pyargsptr = ccall((@pysym :PyTuple_New), PyPtr, (Int,), nargs) - ret = PyNULL() - optr = o isa PyPtr ? o : o.o - pyincref_(optr) - return PyWrapFn{nargs, returntype}(optr, pyargsptr, ret) + return PyWrapFn{nargs, returntype}(pyincref_(PyPtr(o)), pyargsptr, PyNULL()) end (pf::PyWrapFn{N, RT})(args...) where {N, RT} = diff --git a/src/PyCall.jl b/src/PyCall.jl index 8e5ccc46..610e528e 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -74,6 +74,18 @@ mutable struct PyObject end end +PyPtr(o::PyObject) = getfield(o, :o) + +""" + ≛(x, y) + +`PyPtr` based comparison of `x` and `y`, which can be of type `PyObject` or `PyPtr`. +""" +≛(o::PyObject, p::PyPtr) = PyPtr(o) == p +≛(p::PyPtr, o::PyObject) = o ≛ p +≛(o1::PyObject, o2::PyObject) = PyPtr(o1) == PyPtr(o2) +≛(p1::PyPtr, p2::PyPtr) = p1 == p2 + """ PyNULL() @@ -96,7 +108,7 @@ PyNULL() = PyObject(PyPtr_NULL) Test where `o` contains a `NULL` pointer to a Python object, i.e. whether it is equivalent to a `PyNULL()` object. """ -ispynull(o::PyObject) = o.o == PyPtr_NULL +ispynull(o::PyObject) = o ≛ PyPtr_NULL function pydecref_(o::Union{PyPtr,PyObject}) ccall(@pysym(:Py_DecRef), Cvoid, (PyPtr,), o) @@ -105,7 +117,7 @@ end function pydecref(o::PyObject) pydecref_(o) - o.o = PyPtr_NULL + setfield!(o, :o, PyPtr_NULL) return o end @@ -128,8 +140,8 @@ will be performed when `o` is garbage collected. (This means that you can no longer use `o`.) Used for passing objects to Python. """ function pystealref!(o::PyObject) - optr = o.o - o.o = PyPtr_NULL # don't decref when o is gc'ed + optr = PyPtr(o) + setfield!(o, :o, PyPtr_NULL) # don't decref when o is gc'ed return optr end @@ -141,16 +153,16 @@ a `PyObject`, the refcount is incremented. Otherwise a `PyObject` wrapping/converted from `x` is created. """ pyreturn(x::Any) = pystealref!(PyObject(x)) -pyreturn(x::PyObject) = pyincref_(x.o) +pyreturn(x::PyObject) = pyincref_(PyPtr(x)) function Base.copy!(dest::PyObject, src::PyObject) pydecref(dest) - dest.o = src.o + setfield!(dest, :o, PyPtr(src)) return pyincref(dest) end pyisinstance(o::PyObject, t::PyObject) = - !ispynull(t) && ccall((@pysym :PyObject_IsInstance), Cint, (PyPtr,PyPtr), o, t.o) == 1 + !ispynull(t) && ccall((@pysym :PyObject_IsInstance), Cint, (PyPtr,PyPtr), o, t) == 1 pyisinstance(o::PyObject, t::Union{Ptr{Cvoid},PyPtr}) = t != C_NULL && ccall((@pysym :PyObject_IsInstance), Cint, (PyPtr,PyPtr), o, t) == 1 @@ -159,7 +171,7 @@ pyquery(q::Ptr{Cvoid}, o::PyObject) = ccall(q, Cint, (PyPtr,), o) == 1 # conversion to pass PyObject as ccall arguments: -unsafe_convert(::Type{PyPtr}, po::PyObject) = po.o +unsafe_convert(::Type{PyPtr}, po::PyObject) = PyPtr(po) # use constructor for generic conversions to PyObject convert(::Type{PyObject}, o) = PyObject(o) @@ -230,7 +242,7 @@ function pystring(o::PyObject) s = ccall((@pysym :PyObject_Str), PyPtr, (PyPtr,), o) if (s == C_NULL) pyerr_clear() - return string(o.o) + return string(PyPtr(o)) end end return convert(AbstractString, PyObject(s)) @@ -242,9 +254,9 @@ function show(io::IO, o::PyObject) end function Base.Docs.doc(o::PyObject) - if haskey(o, "__doc__") + if hasproperty(o, "__doc__") d = o["__doc__"] - if d.o != pynothing[] + if !(PyPtr(d) ≛ pynothing[]) return Base.Docs.Text(convert(AbstractString, d)) end end @@ -263,23 +275,23 @@ function hash(o::PyObject) elseif is_pyjlwrap(o) # call native Julia hash directly on wrapped Julia objects, # since on 64-bit Windows the Python 2.x hash is only 32 bits - hashsalt(unsafe_pyjlwrap_to_objref(o.o)) + hashsalt(unsafe_pyjlwrap_to_objref(PyPtr(o))) else h = ccall((@pysym :PyObject_Hash), Py_hash_t, (PyPtr,), o) if h == -1 # error pyerr_clear() - return hashsalt(o.o) + return hashsalt(PyPtr(o)) end hashsalt(h) end end ######################################################################### -# For o::PyObject, make o["foo"] and o[:foo] equivalent to o.foo in Python, +# For o::PyObject, make o["foo"], o[:foo], and o.foo equivalent to o.foo in Python, # with the former returning an raw PyObject and the latter giving the PyAny # conversion. -function getindex(o::PyObject, s::AbstractString) +function Base.getproperty(o::PyObject, s::AbstractString) if ispynull(o) throw(ArgumentError("ref of NULL PyObject")) end @@ -291,8 +303,29 @@ function getindex(o::PyObject, s::AbstractString) return PyObject(p) end -getindex(o::PyObject, s::Symbol) = convert(PyAny, getindex(o, string(s))) +Base.getproperty(o::PyObject, s::Symbol) = convert(PyAny, getproperty(o, string(s))) + +Base.propertynames(o::PyObject) = map(x->Symbol(first(x)), + pycall(inspect["getmembers"], PyObject, o)) + +function Base.setproperty!(o::PyObject, s::Union{Symbol,AbstractString}, v) + if ispynull(o) + throw(ArgumentError("assign of NULL PyObject")) + end + if -1 == ccall((@pysym :PyObject_SetAttrString), Cint, + (PyPtr, Cstring, PyPtr), o, s, PyObject(v)) + pyerr_clear() + throw(KeyError(s)) + end + o +end + +getindex(o::PyObject, s::Union{Symbol, AbstractString}) = getproperty(o, s) + +# If I make this +# setindex!(o::PyObject, v, s::Union{Symbol,AbstractString}) = setproperty!(o, s, v) +# the tests (Pkg.test("PyCall")) won't go through. Don't know why..... function setindex!(o::PyObject, v, s::Union{Symbol,AbstractString}) if ispynull(o) throw(ArgumentError("assign of NULL PyObject")) @@ -305,9 +338,11 @@ function setindex!(o::PyObject, v, s::Union{Symbol,AbstractString}) o end -function haskey(o::PyObject, s::Union{Symbol,AbstractString}) +haskey(o::PyObject, s::Union{Symbol,AbstractString}) = hasproperty(o, s) + +function hasproperty(o::PyObject, s::Union{Symbol,AbstractString}) if ispynull(o) - throw(ArgumentError("haskey of NULL PyObject")) + throw(ArgumentError("hasproperty of NULL PyObject")) end return 1 == ccall((@pysym :PyObject_HasAttrString), Cint, (PyPtr, Cstring), o, s) @@ -795,7 +830,7 @@ end pushfirst!(a::PyObject, item) = insert!(a, 1, item) function prepend!(a::PyObject, items) - if isa(items,PyObject) && items.o == a.o + if isa(items,PyObject) && PyPtr(items) == PyPtr(a) # avoid infinite loop in prepending a to itself return prepend!(a, collect(items)) end @@ -837,16 +872,16 @@ for (mime, method) in ((MIME"text/html", "_repr_html_"), showable = VERSION < v"0.7.0-DEV.4047" ? :mimewritable : :showable @eval begin function show(io::IO, mime::$mime, o::PyObject) - if !ispynull(o) && haskey(o, $method) + if !ispynull(o) && hasproperty(o, $method) r = pycall(o[$method], PyObject) - r.o != pynothing[] && return write(io, convert($T, r)) + PyPtr(r) != pynothing[] && return write(io, convert($T, r)) end throw(MethodError(show, (io, mime, o))) end Base.$showable(::$mime, o::PyObject) = - !ispynull(o) && haskey(o, $method) && let meth = o[$method] - meth.o != pynothing[] && - pycall(meth, PyObject).o != pynothing[] + !ispynull(o) && hasproperty(o, $method) && let meth = o[$method] + PyPtr(meth) != pynothing[] && + PyPtr(pycall(meth, PyObject)) != pynothing[] end end end diff --git a/src/callback.jl b/src/callback.jl index 55d84936..b302c7d9 100644 --- a/src/callback.jl +++ b/src/callback.jl @@ -41,7 +41,7 @@ function _pyjlwrap_call(f, args_::PyPtr, kw_::PyPtr) catch e pyraise(e) finally - args.o = PyPtr_NULL # don't decref + setfield!(args, :o, PyPtr_NULL) # don't decref end return PyPtr_NULL end diff --git a/src/conversions.jl b/src/conversions.jl index ab681ae5..e009c482 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -244,7 +244,7 @@ PyVector(o::PyObject) = PyVector{PyAny}(o) PyObject(a::PyVector) = a.o convert(::Type{PyVector}, o::PyObject) = PyVector(o) convert(::Type{PyVector{T}}, o::PyObject) where {T} = PyVector{T}(o) -unsafe_convert(::Type{PyPtr}, a::PyVector) = a.o.o +unsafe_convert(::Type{PyPtr}, a::PyVector) = PyPtr(a.o) PyVector(a::PyVector) = a PyVector(a::AbstractVector{T}) where {T} = PyVector{T}(array2py(a)) @@ -456,7 +456,7 @@ PyDict(d::AbstractDict{Any,V}) where {V} = PyDict{PyAny,V}(PyObject(d)) PyDict(d::AbstractDict{K,Any}) where {K} = PyDict{K,PyAny}(PyObject(d)) convert(::Type{PyDict}, o::PyObject) = PyDict(o) convert(::Type{PyDict{K,V}}, o::PyObject) where {K,V} = PyDict{K,V}(o) -unsafe_convert(::Type{PyPtr}, d::PyDict) = d.o.o +unsafe_convert(::Type{PyPtr}, d::PyDict) = PyPtr(d.o) haskey(d::PyDict{K,V,true}, key) where {K,V} = 1 == ccall(@pysym(:PyDict_Contains), Cint, (PyPtr, PyPtr), d, PyObject(key)) keys(::Type{T}, d::PyDict{K,V,true}) where {T,K,V} = convert(Vector{T}, PyObject(@pycheckn ccall((@pysym :PyDict_Keys), PyPtr, (PyPtr,), d))) @@ -747,7 +747,7 @@ pystring_query(o::PyObject) = pyisinstance(o, @pyglobalobj PyString_Type) ? Abst # we never automatically convert to Function. pyfunction_query(o::PyObject) = Union{} -pynothing_query(o::PyObject) = o.o == pynothing[] ? Nothing : Union{} +pynothing_query(o::PyObject) = o ≛ pynothing[] ? Nothing : Union{} # We refrain from converting all objects that support the mapping protocol (PyMapping_Check) # to avoid converting types like Pandas `DataFrame` that are only lossily @@ -768,7 +768,7 @@ function pysequence_query(o::PyObject) return AbstractRange elseif ispybytearray(o) return Vector{UInt8} - elseif !haskey(o, "__array_interface__") + elseif !hasproperty(o, "__array_interface__") # only handle PyList for now return pyisinstance(o, @pyglobalobj :PyList_Type) ? Array : Union{} else @@ -844,7 +844,7 @@ function convert(::Type{PyAny}, o::PyObject) try T = pytype_query(o) if T == PyObject && is_pyjlwrap(o) - return unsafe_pyjlwrap_to_objref(o.o) + return unsafe_pyjlwrap_to_objref(o) end convert(T, o) catch diff --git a/src/exception.jl b/src/exception.jl index 8e706600..5ec44fa9 100644 --- a/src/exception.jl +++ b/src/exception.jl @@ -172,6 +172,5 @@ end function pyraise(e::PyError) ccall((@pysym :PyErr_Restore), Cvoid, (PyPtr, PyPtr, PyPtr), - e.T, e.val, e.traceback) - e.T.o = e.val.o = e.traceback.o = C_NULL # refs were stolen -end + pyincref(e.T), pyincref(e.val), pyincref(e.traceback)) +end \ No newline at end of file diff --git a/src/gui.jl b/src/gui.jl index 956b9098..b77afe2b 100644 --- a/src/gui.jl +++ b/src/gui.jl @@ -148,7 +148,7 @@ function qt_eventloop(QtCore::PyObject, sec::Real=50e-3) maxtime = PyObject(50) install_doevent(sec) do async app = pycall(instance, PyObject) - if app.o != pynothing[] + if PyPtr(app) != pynothing[] app["_in_event_loop"] = true pycall(processEvents, PyObject, AllEvents, maxtime) end @@ -176,7 +176,7 @@ function wx_eventloop(sec::Real=50e-3) EventLoopActivator = wx["EventLoopActivator"] install_doevent(sec) do async app = pycall(GetApp, PyObject) - if app.o != pynothing[] + if PyPtr(app) != pynothing[] app["_in_event_loop"] = true evtloop = pycall(EventLoop, PyObject) ea = pycall(EventLoopActivator, PyObject, evtloop) @@ -200,10 +200,10 @@ function Tk_eventloop(sec::Real=50e-3) root = PyObject(nothing) install_doevent(sec) do async new_root = Tk["_default_root"] - if new_root.o != pynothing[] + if PyPtr(new_root) != pynothing[] root = new_root end - if root.o != pynothing[] + if PyPtr(root) != pynothing[] while pycall(root["dooneevent"], Bool, flag) end end diff --git a/src/io.jl b/src/io.jl index 91c28625..5a5f9339 100644 --- a/src/io.jl +++ b/src/io.jl @@ -65,7 +65,7 @@ end ########################################################################## -pyio_jl(self::PyObject) = unsafe_pyjlwrap_to_objref(self["io"].o)::IO +pyio_jl(self::PyObject) = unsafe_pyjlwrap_to_objref(PyPtr(self["io"]))::IO const PyIO = PyNULL() diff --git a/src/pydates.jl b/src/pydates.jl index e8c62c9c..8119e8a6 100644 --- a/src/pydates.jl +++ b/src/pydates.jl @@ -133,7 +133,7 @@ end function convert(::Type{Dates.DateTime}, o::PyObject) if PyDate_Check(o) - dt = convert(Ptr{UInt8}, o.o) + PyDate_HEAD + dt = convert(Ptr{UInt8}, PyPtr(o)) + PyDate_HEAD if PyDateTime_Check(o) Dates.DateTime((UInt(unsafe_load(dt,1))<<8)|unsafe_load(dt,2), # Y unsafe_load(dt,3), unsafe_load(dt,4), # month, day @@ -153,7 +153,7 @@ end function convert(::Type{Dates.Date}, o::PyObject) if PyDate_Check(o) - dt = convert(Ptr{UInt8}, o.o) + PyDate_HEAD + dt = convert(Ptr{UInt8}, PyPtr(o)) + PyDate_HEAD Dates.Date((UInt(unsafe_load(dt,1)) << 8) | unsafe_load(dt,2), # Y unsafe_load(dt,3), unsafe_load(dt,4)) # month, day else @@ -163,7 +163,7 @@ end function delta_dsμ(o::PyObject) PyDelta_Check(o) || throw(ArgumentError("$o is not a timedelta instance")) - p = unsafe_load(convert(Ptr{PyDateTime_Delta{Py_hash_t}}, o.o)) + p = unsafe_load(convert(Ptr{PyDateTime_Delta{Py_hash_t}}, PyPtr(o))) return (p.days, p.seconds, p.microseconds) end diff --git a/src/pyfncall.jl b/src/pyfncall.jl index 95cdd5ad..b264ce57 100644 --- a/src/pyfncall.jl +++ b/src/pyfncall.jl @@ -43,8 +43,8 @@ function __pycall!(ret::PyObject, pyargsptr::PyPtr, o::Union{PyObject,PyPtr}, try retptr = @pycheckn ccall((@pysym :PyObject_Call), PyPtr, (PyPtr,PyPtr,PyPtr), o, pyargsptr, kw) - pydecref_(ret.o) - ret.o = retptr + pydecref_(PyPtr(ret)) + setfield!(ret, :o, retptr) finally sigatomic_end() end diff --git a/src/pyinit.jl b/src/pyinit.jl index 7515b12c..ed816e39 100644 --- a/src/pyinit.jl +++ b/src/pyinit.jl @@ -148,7 +148,7 @@ function __init__() # (e.g. Matplotlib: see PyPlot#79) if isinteractive() let sys = pyimport("sys") - if !haskey(sys, "ps1") + if !hasproperty(sys, "ps1") sys["ps1"] = ">>> " end end diff --git a/src/pyoperators.jl b/src/pyoperators.jl index 87f48f6e..a3047379 100644 --- a/src/pyoperators.jl +++ b/src/pyoperators.jl @@ -29,7 +29,7 @@ for (op,py) in ((:+,:PyNumber_InPlaceAdd), (:-,:PyNumber_InPlaceSubtract), (:*,: (:<<,:PyNumber_InPlaceLshift), (:>>,:PyNumber_InPlaceRshift), (:⊻,:PyNumber_InPlaceXor)) qpy = QuoteNode(py) @eval function Base.broadcast!(::typeof($op), a::PyObject, a′::PyObject, b) - a.o == a′.o || throw(MethodError(broadcast!, ($op, a, a', b))) + a ≛ a′ || throw(MethodError(broadcast!, ($op, a, a', b))) PyObject(@pycheckn ccall((@pysym $qpy), PyPtr, (PyPtr, PyPtr), a,PyObject(b))) end end @@ -58,10 +58,10 @@ for (op,py) in ((:<, Py_LT), (:<=, Py_LE), (:(==), Py_EQ), (:!=, Py_NE), (:>, Py_GT), (:>=, Py_GE), (:isequal, Py_EQ), (:isless, Py_LT)) @eval function $op(o1::PyObject, o2::PyObject) if ispynull(o1) || ispynull(o2) - return $(py==Py_EQ || py==Py_NE || op==:isless ? :($op(o1.o, o2.o)) : false) + return $(py==Py_EQ || py==Py_NE || op==:isless ? :($op(PyPtr(o1), PyPtr(o2))) : false) elseif is_pyjlwrap(o1) && is_pyjlwrap(o2) - return $op(unsafe_pyjlwrap_to_objref(o1.o), - unsafe_pyjlwrap_to_objref(o2.o)) + return $op(unsafe_pyjlwrap_to_objref(PyPtr(o1)), + unsafe_pyjlwrap_to_objref(PyPtr(o2))) else if $(op == :isless || op == :isequal) return Bool(@pycheckz ccall((@pysym :PyObject_RichCompareBool), Cint, @@ -80,5 +80,5 @@ for (op,py) in ((:<, Py_LT), (:<=, Py_LE), (:(==), Py_EQ), (:!=, Py_NE), end end # default to false since hash(x) != hash(PyObject(x)) in general -isequal(o1::PyObject, o2::Any) = !ispynull(o1) && is_pyjlwrap(o1) ? isequal(unsafe_pyjlwrap_to_objref(o1.o), o2) : false +isequal(o1::PyObject, o2::Any) = !ispynull(o1) && is_pyjlwrap(o1) ? isequal(unsafe_pyjlwrap_to_objref(PyPtr(o1)), o2) : false isequal(o1::Any, o2::PyObject) = isequal(o2, o1) diff --git a/src/pytype.jl b/src/pytype.jl index 058cc713..93ec807a 100644 --- a/src/pytype.jl +++ b/src/pytype.jl @@ -334,8 +334,8 @@ function pyjlwrap_dealloc(o::PyPtr) return nothing end -unsafe_pyjlwrap_to_objref(o::PyPtr) = - unsafe_pointer_to_objref(unsafe_load(convert(Ptr{Ptr{Cvoid}}, o), 3)) +unsafe_pyjlwrap_to_objref(o::Union{PyPtr, PyObject}) = + unsafe_pointer_to_objref(unsafe_load(convert(Ptr{Ptr{Cvoid}}, PyPtr(o)), 3)) function pyjlwrap_repr(o::PyPtr) try @@ -391,7 +391,7 @@ function pyjlwrap_getattr(self_::PyPtr, attr__::PyPtr) catch e pyraise(e) finally - attr_.o = PyPtr_NULL # don't decref + setfield!(attr_, :o, PyPtr_NULL) # don't decref end return PyPtr_NULL end @@ -426,7 +426,7 @@ function pyjlwrap_new(pyT::PyTypeObject, value::Any) # TODO change to `Ref{PyTypeObject}` when 0.6 is dropped. o = PyObject(@pycheckn ccall((@pysym :_PyObject_New), PyPtr, (Any,), pyT)) - p = convert(Ptr{Ptr{Cvoid}}, o.o) + p = convert(Ptr{Ptr{Cvoid}}, PyPtr(o)) if isimmutable(value) # It is undefined to call `pointer_from_objref` on immutable objects. # The compiler is free to return basically anything since the boxing is not @@ -434,10 +434,10 @@ function pyjlwrap_new(pyT::PyTypeObject, value::Any) # Below is a well defined way to get a pointer (`ptr`) and an object that defines # the lifetime of the pointer `ref`. ref = Ref{Any}(value) - pycall_gc[o.o] = ref + pycall_gc[PyPtr(o)] = ref ptr = unsafe_load(Ptr{Ptr{Cvoid}}(pointer_from_objref(ref))) else - pycall_gc[o.o] = value + pycall_gc[PyPtr(o)] = value ptr = pointer_from_objref(value) end unsafe_store!(p, ptr, 3) diff --git a/src/serialize.jl b/src/serialize.jl index 42b4563d..e097f06d 100644 --- a/src/serialize.jl +++ b/src/serialize.jl @@ -13,7 +13,7 @@ pickle() = ispynull(_pickle) ? copy!(_pickle, pyimport(PyCall.pyversion.major function serialize(s::AbstractSerializer, pyo::PyObject) serialize_type(s, PyObject) if ispynull(pyo) - serialize(s, pyo.o) + serialize(s, PyPtr(pyo)) else b = PyBuffer(pycall(pickle()["dumps"], PyObject, pyo)) serialize(s, unsafe_wrap(Array, Ptr{UInt8}(pointer(b)), sizeof(b))) diff --git a/test/runtests.jl b/test/runtests.jl index a1951214..e4eb08c7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -258,6 +258,14 @@ const PyInt = pyversion < v"3" ? Int : Clonglong end @test convert(BigInt, PyObject(1234)) == 1234 + # hasproperty, getproperty, and propertynames + @test PyCall.hasproperty(np, "random") + @test PyCall.hasproperty(np.random, :rand) + @test getproperty(np, "linalg") == np.linalg + @test getproperty(np.linalg, :eig) == np.linalg.eig + @test :rand in propertynames(np.random) + @test_throws KeyError np.whatever + # buffers let b = PyCall.PyBuffer(pyutf8("test string")) @test ndims(b) == 1 @@ -267,7 +275,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong end let o = PyObject(1+2im) - @test haskey(o, :real) + @test PyCall.hasproperty(o, :real) # replace by Base.hasproperty in the future @test :real in keys(o) @test o[:real] == 1 end @@ -437,7 +445,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong let o = PyObject(Any[1,2]), c = o broadcast!(+, o, o, Any[3,4]) # o .+= x doesn't work yet in 0.7 @test collect(o) == [1,2,3,4] - @test o.o == c.o # updated in-place + @test PyPtr(o) == PyPtr(c) # updated in-place end # more flexible bool conversions, matching Python "truth value testing" From c51bd592227341ae062e10e6fa2979f9be64818e Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Sat, 27 Oct 2018 18:45:07 +0200 Subject: [PATCH 02/15] still support 0.6 --- src/PyCall.jl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/PyCall.jl b/src/PyCall.jl index 610e528e..4d5f245a 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -18,6 +18,11 @@ import Base: size, ndims, similar, copy, getindex, setindex!, stride, eltype, get, delete!, empty!, length, isempty, filter!, hash, splice!, pop!, ==, isequal, push!, append!, insert!, prepend!, unsafe_convert + +@static if VERSION >= v"0.7-" + import Base: getproperty, setproperty!, propertynames +end + import Compat: pushfirst!, popfirst!, firstindex, lastindex # Python C API is not interrupt-safe. In principle, we should @@ -291,7 +296,7 @@ end # with the former returning an raw PyObject and the latter giving the PyAny # conversion. -function Base.getproperty(o::PyObject, s::AbstractString) +function getproperty(o::PyObject, s::AbstractString) if ispynull(o) throw(ArgumentError("ref of NULL PyObject")) end @@ -303,12 +308,12 @@ function Base.getproperty(o::PyObject, s::AbstractString) return PyObject(p) end -Base.getproperty(o::PyObject, s::Symbol) = convert(PyAny, getproperty(o, string(s))) +getproperty(o::PyObject, s::Symbol) = convert(PyAny, getproperty(o, string(s))) -Base.propertynames(o::PyObject) = map(x->Symbol(first(x)), +propertynames(o::PyObject) = map(x->Symbol(first(x)), pycall(inspect["getmembers"], PyObject, o)) -function Base.setproperty!(o::PyObject, s::Union{Symbol,AbstractString}, v) +function setproperty!(o::PyObject, s::Union{Symbol,AbstractString}, v) if ispynull(o) throw(ArgumentError("assign of NULL PyObject")) end From 2ebfa6a1d47555822ba24e3d3ef0bc1ca583d423 Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Sat, 27 Oct 2018 19:34:57 +0200 Subject: [PATCH 03/15] adjust runtests --- test/runtests.jl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index e4eb08c7..4d8fd6ce 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -258,13 +258,15 @@ const PyInt = pyversion < v"3" ? Int : Clonglong end @test convert(BigInt, PyObject(1234)) == 1234 - # hasproperty, getproperty, and propertynames - @test PyCall.hasproperty(np, "random") - @test PyCall.hasproperty(np.random, :rand) - @test getproperty(np, "linalg") == np.linalg - @test getproperty(np.linalg, :eig) == np.linalg.eig - @test :rand in propertynames(np.random) - @test_throws KeyError np.whatever + @static if VERSION >= v"0.7-" + # hasproperty, getproperty, and propertynames + @test PyCall.hasproperty(np, "random") + @test PyCall.hasproperty(np.random, :rand) + @test getproperty(np, "linalg") == np.linalg + @test getproperty(np.linalg, :eig) == np.linalg.eig + @test :rand in propertynames(np.random) + @test_throws KeyError np.whatever + end # buffers let b = PyCall.PyBuffer(pyutf8("test string")) From 89da2c5ba85062c743c9995d5a15f9a27de2649b Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Wed, 31 Oct 2018 17:07:46 +0100 Subject: [PATCH 04/15] updated tests --- test/runtests.jl | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 4d8fd6ce..7eb8bab7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,11 @@ using PyCall, Compat using Compat.Test, Compat.Dates, Compat.Serialization +using PyCall: hasproperty +@static if VERSION < v"0.7-" + using PyCall: getproperty, setproperty!, propertynames +end + filter(f, itr) = collect(Iterators.filter(f, itr)) filter(f, d::AbstractDict) = Base.filter(f, d) @@ -258,15 +263,26 @@ const PyInt = pyversion < v"3" ? Int : Clonglong end @test convert(BigInt, PyObject(1234)) == 1234 + # hasproperty, getproperty, and propertynames + py""" + class A: + class B: + C = 1 + """ + A = py"A" + @test hasproperty(A, "B") + @test getproperty(A, "B") == py"A.B" + @test_throws KeyError A.X + @test :B in propertynames(A) @static if VERSION >= v"0.7-" - # hasproperty, getproperty, and propertynames - @test PyCall.hasproperty(np, "random") - @test PyCall.hasproperty(np.random, :rand) - @test getproperty(np, "linalg") == np.linalg - @test getproperty(np.linalg, :eig) == np.linalg.eig - @test :rand in propertynames(np.random) - @test_throws KeyError np.whatever + @test A.B.C == 1 end + setproperty!(A.B, "C", 2) + @test py"A.B.C" == 2 + + pylogging = pyimport("logging") + @test hasproperty(pylogging.Logger, "info") + @test getproperty(pylogging, :Logger) == pylogging.Logger # buffers let b = PyCall.PyBuffer(pyutf8("test string")) From da5f08e769aaa1afb6d5fb13bd6eeba120bc6675 Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Wed, 31 Oct 2018 17:19:14 +0100 Subject: [PATCH 05/15] test fix (0.6) --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 7eb8bab7..6a5ceda4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -277,7 +277,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong @static if VERSION >= v"0.7-" @test A.B.C == 1 end - setproperty!(A.B, "C", 2) + setproperty!(py"A.B", "C", 2) @test py"A.B.C" == 2 pylogging = pyimport("logging") From 77736c8d43a8688498dc26bb40888404e86242cd Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Wed, 31 Oct 2018 17:28:45 +0100 Subject: [PATCH 06/15] modified tests --- test/runtests.jl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 6a5ceda4..0a4934f3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -272,18 +272,14 @@ const PyInt = pyversion < v"3" ? Int : Clonglong A = py"A" @test hasproperty(A, "B") @test getproperty(A, "B") == py"A.B" - @test_throws KeyError A.X @test :B in propertynames(A) @static if VERSION >= v"0.7-" @test A.B.C == 1 + @test_throws KeyError A.X end setproperty!(py"A.B", "C", 2) @test py"A.B.C" == 2 - pylogging = pyimport("logging") - @test hasproperty(pylogging.Logger, "info") - @test getproperty(pylogging, :Logger) == pylogging.Logger - # buffers let b = PyCall.PyBuffer(pyutf8("test string")) @test ndims(b) == 1 From 5fef94d2fc237a14939896bd7d1969c3ab548164 Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Sun, 20 Jan 2019 17:54:18 +0100 Subject: [PATCH 07/15] added getindex and haskey deprectations; fixed some internal warnings --- src/PyCall.jl | 33 +++++++++-------- src/conversions.jl | 16 ++++----- src/io.jl | 2 +- src/numpy.jl | 14 ++++---- src/pyfncall.jl | 2 +- src/pyinit.jl | 4 +-- src/serialize.jl | 4 +-- test/runtests.jl | 88 +++++++++++++++++++++++----------------------- 8 files changed, 83 insertions(+), 80 deletions(-) diff --git a/src/PyCall.jl b/src/PyCall.jl index 4d5f245a..fcb8fa0b 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -86,10 +86,7 @@ PyPtr(o::PyObject) = getfield(o, :o) `PyPtr` based comparison of `x` and `y`, which can be of type `PyObject` or `PyPtr`. """ -≛(o::PyObject, p::PyPtr) = PyPtr(o) == p -≛(p::PyPtr, o::PyObject) = o ≛ p -≛(o1::PyObject, o2::PyObject) = PyPtr(o1) == PyPtr(o2) -≛(p1::PyPtr, p2::PyPtr) = p1 == p2 +≛(o1::Union{PyObject,PyPtr}, o2::Union{PyObject,PyPtr}) = PyPtr(o1) == PyPtr(o2) """ PyNULL() @@ -260,8 +257,8 @@ end function Base.Docs.doc(o::PyObject) if hasproperty(o, "__doc__") - d = o["__doc__"] - if !(PyPtr(d) ≛ pynothing[]) + d = o."__doc__" + if !(d ≛ pynothing[]) return Base.Docs.Text(convert(AbstractString, d)) end end @@ -311,7 +308,7 @@ end getproperty(o::PyObject, s::Symbol) = convert(PyAny, getproperty(o, string(s))) propertynames(o::PyObject) = map(x->Symbol(first(x)), - pycall(inspect["getmembers"], PyObject, o)) + pycall(inspect."getmembers", PyObject, o)) function setproperty!(o::PyObject, s::Union{Symbol,AbstractString}, v) if ispynull(o) @@ -325,7 +322,10 @@ function setproperty!(o::PyObject, s::Union{Symbol,AbstractString}, v) o end -getindex(o::PyObject, s::Union{Symbol, AbstractString}) = getproperty(o, s) +function getindex(o::PyObject, s::Union{Symbol, AbstractString}) + Base.depwarn("`getindex(o::PyObject, s::Union{Symbol, AbstractString})` is deprecated in favor of dot overloading (`getproperty`) so elements should now be accessed as e.g. `o.s` instead of `o[:s]`.", :getindex) + return getproperty(o, s) +end # If I make this @@ -343,7 +343,10 @@ function setindex!(o::PyObject, v, s::Union{Symbol,AbstractString}) o end -haskey(o::PyObject, s::Union{Symbol,AbstractString}) = hasproperty(o, s) +function haskey(o::PyObject, s::Union{Symbol,AbstractString}) + Base.depwarn("`haskey(o::PyObject, s::Union{Symbol, AbstractString})` is deprecated, use `hasproperty(o, s)` instead.", :haskey) + return hasproperty(o, s) +end function hasproperty(o::PyObject, s::Union{Symbol,AbstractString}) if ispynull(o) @@ -376,12 +379,12 @@ If the Python module contains identifiers that are reserved words in Julia (e.g. """ function pywrap(o::PyObject, mname::Symbol=:__anon__) members = convert(Vector{Tuple{AbstractString,PyObject}}, - pycall(inspect["getmembers"], PyObject, o)) + pycall(inspect."getmembers", PyObject, o)) filter!(m -> !(m[1] in reserved), members) m = Module(mname, false) consts = [Expr(:const, Expr(:(=), Symbol(x[1]), convert(PyAny, x[2]))) for x in members] exports = try - convert(Vector{Symbol}, o["__all__"]) + convert(Vector{Symbol}, o."__all__") catch [Symbol(x[1]) for x in filter(x -> x[1][1] != '_', members)] end @@ -625,8 +628,8 @@ function _pywith(EXPR,VAR,TYPE,BLOCK) end end mgrT = pytypeof(mgr) - exit = mgrT["__exit__"] - value = @pycall mgrT["__enter__"](mgr)::$(esc(TYPE)) + exit = mgrT."__exit__" + value = @pycall mgrT."__enter__"(mgr)::$(esc(TYPE)) exc = true try try @@ -634,7 +637,7 @@ function _pywith(EXPR,VAR,TYPE,BLOCK) $(esc(BLOCK)) catch err exc = false - if !(@pycall exit(mgr, pyimport(:sys)[:exc_info]()...)::Bool) + if !(@pycall exit(mgr, pyimport(:sys).exc_info()...)::Bool) throw(err) end end @@ -835,7 +838,7 @@ end pushfirst!(a::PyObject, item) = insert!(a, 1, item) function prepend!(a::PyObject, items) - if isa(items,PyObject) && PyPtr(items) == PyPtr(a) + if isa(items,PyObject) && items ≛ a # avoid infinite loop in prepending a to itself return prepend!(a, collect(items)) end diff --git a/src/conversions.jl b/src/conversions.jl index e009c482..cf636b55 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -129,10 +129,10 @@ PyObject(p::Ptr) = pycall(c_void_p_Type, PyObject, UInt(p)) function convert(::Type{Ptr{Cvoid}}, po::PyObject) if pyisinstance(po, c_void_p_Type) - v = po["value"] + v = po."value" # ctypes stores the NULL pointer specially, grrr pynothing_query(v) == Nothing ? C_NULL : - convert(Ptr{Cvoid}, convert(UInt, po["value"])) + convert(Ptr{Cvoid}, convert(UInt, po."value")) elseif pyisinstance(po, @pyglobalobj(:PyCapsule_Type)) @pycheck ccall((@pysym :PyCapsule_GetPointer), Ptr{Cvoid}, (PyPtr,Ptr{UInt8}), @@ -657,13 +657,13 @@ const mpc = PyNULL() function mpmath_init() if ispynull(mpmath) copy!(mpmath, pyimport("mpmath")) - copy!(mpf, mpmath["mpf"]) - copy!(mpc, mpmath["mpc"]) + copy!(mpf, mpmath."mpf") + copy!(mpc, mpmath."mpc") end curprec = precision(BigFloat) if mpprec[1] != curprec mpprec[1] = curprec - mpmath["mp"]["prec"] = mpprec[1] + mpmath."mp"."prec" = mpprec[1] end end @@ -686,8 +686,8 @@ convert(::Type{BigFloat}, o::PyObject) = parse(BigFloat, pystr(o)) function convert(::Type{Complex{BigFloat}}, o::PyObject) try - Complex{BigFloat}(convert(BigFloat, o["real"]), - convert(BigFloat, o["imag"])) + Complex{BigFloat}(convert(BigFloat, o."real"), + convert(BigFloat, o."imag")) catch convert(Complex{BigFloat}, convert(Complex{Float64}, o)) end @@ -772,7 +772,7 @@ function pysequence_query(o::PyObject) # only handle PyList for now return pyisinstance(o, @pyglobalobj :PyList_Type) ? Array : Union{} else - otypestr = get(o["__array_interface__"], PyObject, "typestr") + otypestr = get(o."__array_interface__", PyObject, "typestr") typestr = convert(AbstractString, otypestr) # Could this just be String now? T = npy_typestrs[typestr[2:end]] if T == PyPtr diff --git a/src/io.jl b/src/io.jl index 5a5f9339..ce444084 100644 --- a/src/io.jl +++ b/src/io.jl @@ -65,7 +65,7 @@ end ########################################################################## -pyio_jl(self::PyObject) = unsafe_pyjlwrap_to_objref(PyPtr(self["io"]))::IO +pyio_jl(self::PyObject) = unsafe_pyjlwrap_to_objref(self."io")::IO const PyIO = PyNULL() diff --git a/src/numpy.jl b/src/numpy.jl index 542b8b9b..72c76c1f 100644 --- a/src/numpy.jl +++ b/src/numpy.jl @@ -62,14 +62,14 @@ function npyinitialize() numpy = pyimport("numpy") # directory for numpy include files to parse - inc = pycall(numpy["get_include"], AbstractString) + inc = pycall(numpy."get_include", AbstractString) # numpy.number types - copy!(npy_number, numpy["number"]) - copy!(npy_integer, numpy["integer"]) - copy!(npy_floating, numpy["floating"]) - copy!(npy_complexfloating, numpy["complexfloating"]) - copy!(npy_bool, numpy["bool_"]) + copy!(npy_number, numpy."number") + copy!(npy_integer, numpy."integer") + copy!(npy_floating, numpy."floating") + copy!(npy_complexfloating, numpy."complexfloating") + copy!(npy_bool, numpy."bool_") # Parse __multiarray_api.h to obtain length and meaning of PyArray_API try @@ -236,7 +236,7 @@ mutable struct PyArray_Info readonly::Bool function PyArray_Info(a::PyObject) - ai = PyDict{AbstractString,PyObject}(a["__array_interface__"]) + ai = PyDict{AbstractString,PyObject}(a."__array_interface__") typestr = convert(AbstractString, ai["typestr"]) T = npy_typestrs[typestr[2:end]] datatuple = convert(Tuple{Int,Bool}, ai["data"]) diff --git a/src/pyfncall.jl b/src/pyfncall.jl index b264ce57..1fe7e804 100644 --- a/src/pyfncall.jl +++ b/src/pyfncall.jl @@ -43,7 +43,7 @@ function __pycall!(ret::PyObject, pyargsptr::PyPtr, o::Union{PyObject,PyPtr}, try retptr = @pycheckn ccall((@pysym :PyObject_Call), PyPtr, (PyPtr,PyPtr,PyPtr), o, pyargsptr, kw) - pydecref_(PyPtr(ret)) + pydecref_(ret) setfield!(ret, :o, retptr) finally sigatomic_end() diff --git a/src/pyinit.jl b/src/pyinit.jl index ed816e39..eb6ce435 100644 --- a/src/pyinit.jl +++ b/src/pyinit.jl @@ -114,10 +114,10 @@ function __init__() pyxrange[] = @pyglobalobj(:PyRange_Type) # ctypes.c_void_p for Ptr types - copy!(c_void_p_Type, pyimport("ctypes")["c_void_p"]) + copy!(c_void_p_Type, pyimport("ctypes")."c_void_p") # traceback.format_tb function, for show(PyError) - copy!(format_traceback, pyimport("traceback")["format_tb"]) + copy!(format_traceback, pyimport("traceback")."format_tb") init_datetime() pyjlwrap_init() diff --git a/src/serialize.jl b/src/serialize.jl index e097f06d..e8a6229c 100644 --- a/src/serialize.jl +++ b/src/serialize.jl @@ -15,7 +15,7 @@ function serialize(s::AbstractSerializer, pyo::PyObject) if ispynull(pyo) serialize(s, PyPtr(pyo)) else - b = PyBuffer(pycall(pickle()["dumps"], PyObject, pyo)) + b = PyBuffer(pycall(pickle()."dumps", PyObject, pyo)) serialize(s, unsafe_wrap(Array, Ptr{UInt8}(pointer(b)), sizeof(b))) end end @@ -41,6 +41,6 @@ function deserialize(s::AbstractSerializer, t::Type{PyObject}) @assert b == C_NULL return PyNULL() else - return pycall(pickle()["loads"], PyObject, pybytes(b)) + return pycall(pickle()."loads", PyObject, pybytes(b)) end end diff --git a/test/runtests.jl b/test/runtests.jl index 0a4934f3..21ad1baf 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -34,7 +34,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong # conversion of NumPy scalars before npy_initialized by array conversions (#481) np = pyimport_e("numpy") if !ispynull(np) # numpy is installed, so test - let o = get(pycall(np["array"], PyObject, 1:3), PyObject, 2) + let o = get(pycall(np."array", PyObject, 1:3), PyObject, 2) @test convert(Int32, o) === Int32(3) @test convert(Int64, o) === Int64(3) @test convert(Float64, o) === Float64(3) @@ -84,7 +84,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong @test roundtrip(testkw)(314157, y=1) == 314159 # check type stability of pycall with an explicit return type - @inferred pycall(PyObject(1)[:__add__], Int, 2) + @inferred pycall(PyObject(1).__add__, Int, 2) if PyCall.npy_initialized @test PyArray(PyObject([1. 2 3;4 5 6])) == [1. 2 3;4 5 6] @@ -176,51 +176,51 @@ const PyInt = pyversion < v"3" ? Int : Clonglong # IO (issue #107) #@test roundtripeq(stdout) # No longer true since #250 let buf = Compat.IOBuffer(read=false, write=true), obuf = PyObject(buf) - @test !obuf[:isatty]() - @test obuf[:writable]() - @test !obuf[:readable]() - @test obuf[:seekable]() - obuf[:write](pyutf8("hello")) - obuf[:flush]() # should be a no-op, since there's no flushing IOBuffer - @test position(buf) == obuf[:tell]() == 5 - let p = obuf[:seek](-2, 1) + @test !obuf.isatty() + @test obuf.writable() + @test !obuf.readable() + @test obuf.seekable() + obuf.write(pyutf8("hello")) + obuf.flush() # should be a no-op, since there's no flushing IOBuffer + @test position(buf) == obuf.tell() == 5 + let p = obuf.seek(-2, 1) @test p == position(buf) == 3 end - let p = obuf[:seek](0, 0) + let p = obuf.seek(0, 0) @test p == position(buf) == 0 end @test String(take!(buf)) == "hello" - obuf[:writelines](["first\n", "second\n", "third"]) + obuf.writelines(["first\n", "second\n", "third"]) @test String(take!(buf)) == "first\nsecond\nthird" - obuf[:write](b"möre stuff") + obuf.write(b"möre stuff") @test String(take!(buf)) == "möre stuff" - @test isopen(buf) == !obuf[:closed] == true - obuf[:close]() - @test isopen(buf) == !obuf[:closed] == false + @test isopen(buf) == !obuf.closed == true + obuf.close() + @test isopen(buf) == !obuf.closed == false end let buf = IOBuffer("hello\nagain"), obuf = PyObject(buf) - @test !obuf[:writable]() - @test obuf[:readable]() - @test obuf[:readlines]() == ["hello\n", "again"] + @test !obuf.writable() + @test obuf.readable() + @test obuf.readlines() == ["hello\n", "again"] end let buf = IOBuffer("hello\nagain"), obuf = PyObject(buf) - @test codeunits(obuf[:read](5)) == b"hello" - @test codeunits(obuf[:readall]()) == b"\nagain" + @test codeunits(obuf.read(5)) == b"hello" + @test codeunits(obuf.readall()) == b"\nagain" end let buf = IOBuffer("hello\nagain"), obuf = PyTextIO(buf) - @test obuf[:encoding] == "UTF-8" - @test obuf[:read](3) == "hel" - @test obuf[:readall]() == "lo\nagain" + @test obuf.encoding == "UTF-8" + @test obuf.read(3) == "hel" + @test obuf.readall() == "lo\nagain" end let nm = tempname() open(nm, "w") do f # @test roundtripeq(f) # PR #250 pf = PyObject(f) - @test pf[:fileno]() == fd(f) - @test pf[:writable]() - @test !pf[:readable]() - pf[:write](pyutf8(nm)) - pf[:flush]() + @test pf.fileno() == fd(f) + @test pf.writable() + @test !pf.readable() + pf.write(pyutf8(nm)) + pf.flush() end @test read(nm, String) == nm end @@ -291,7 +291,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong let o = PyObject(1+2im) @test PyCall.hasproperty(o, :real) # replace by Base.hasproperty in the future @test :real in keys(o) - @test o[:real] == 1 + @test o.real == 1 end # []-based sequence access @@ -333,7 +333,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong end # issue #216: - @test length(collect(pyimport("itertools")[:combinations]([1,2,3],2))) == 3 + @test length(collect(pyimport("itertools").combinations([1,2,3],2))) == 3 # PyNULL and copy! let x = PyNULL(), y = copy!(x, PyObject(314159)) @@ -401,10 +401,10 @@ const PyInt = pyversion < v"3" ? Int : Clonglong # function attributes let o = PyObject(foobar) - @test o[:__name__] == o[:func_name] == string(foobar) - @test o[:__doc__] == o[:func_doc] == "foobar doc\n" - @test o[:__module__] == o[:__defaults__] == o[:func_defaults] == - o[:__closure__] == o[:func_closure] == nothing + @test o.__name__ == o.func_name == string(foobar) + @test o.__doc__ == o.func_doc == "foobar doc\n" + @test o.__module__ == o.__defaults__ == o.func_defaults == + o.__closure__ == o.func_closure == nothing end # issue #345 @@ -492,7 +492,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong try @test begin @pywith pybuiltin("open")(fname,"w") as f begin - f[:write]("test") + f.write("test") end open(io->read(io, String), fname)=="test" end @@ -522,20 +522,20 @@ const PyInt = pyversion < v"3" ? Int : Clonglong @test_throws BoundsError convert(NTuple{3,Int}, PyObject((3,34))) let p = PyCall.pickle(), buf = IOBuffer() - p[:dump]("hello world", buf) - p[:dump](314159, buf) - p[:dump](Any[1,1,2,3,5,8], buf) - @test p[:load](seekstart(buf)) == "hello world" - @test p[:load](buf) == 314159 - @test p[:load](buf) == [1,1,2,3,5,8] + p.dump("hello world", buf) + p.dump(314159, buf) + p.dump(Any[1,1,2,3,5,8], buf) + @test p.load(seekstart(buf)) == "hello world" + @test p.load(buf) == 314159 + @test p.load(buf) == [1,1,2,3,5,8] end # Test that we can call constructors on the python side @test pycall(PyObject(TestConstruct), PyAny, 1).x == 1 # Test getattr fallback - @test PyObject(TestConstruct(1))[:x] == 1 - @test_throws KeyError PyObject(TestConstruct(1))[:y] + @test PyObject(TestConstruct(1)).x == 1 + @test_throws KeyError PyObject(TestConstruct(1)).y # iterating over Julia objects in Python: @test py"[x**2 for x in $(PyCall.pyjlwrap_new(1:4))]" == From bfd11e9112795435c2966bb7f97c549d3dcf3d7b Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Sun, 20 Jan 2019 18:08:02 +0100 Subject: [PATCH 08/15] starequal in pynothing comparisons --- src/PyCall.jl | 6 +++--- src/gui.jl | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/PyCall.jl b/src/PyCall.jl index fcb8fa0b..1398d2ef 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -882,14 +882,14 @@ for (mime, method) in ((MIME"text/html", "_repr_html_"), function show(io::IO, mime::$mime, o::PyObject) if !ispynull(o) && hasproperty(o, $method) r = pycall(o[$method], PyObject) - PyPtr(r) != pynothing[] && return write(io, convert($T, r)) + !(r ≛ pynothing[]) && return write(io, convert($T, r)) end throw(MethodError(show, (io, mime, o))) end Base.$showable(::$mime, o::PyObject) = !ispynull(o) && hasproperty(o, $method) && let meth = o[$method] - PyPtr(meth) != pynothing[] && - PyPtr(pycall(meth, PyObject)) != pynothing[] + !(meth ≛ pynothing[]) && + !(pycall(meth, PyObject) ≛ pynothing[]) end end end diff --git a/src/gui.jl b/src/gui.jl index b77afe2b..d0ac5fbb 100644 --- a/src/gui.jl +++ b/src/gui.jl @@ -148,7 +148,7 @@ function qt_eventloop(QtCore::PyObject, sec::Real=50e-3) maxtime = PyObject(50) install_doevent(sec) do async app = pycall(instance, PyObject) - if PyPtr(app) != pynothing[] + if !(app ≛ pynothing[]) app["_in_event_loop"] = true pycall(processEvents, PyObject, AllEvents, maxtime) end @@ -176,7 +176,7 @@ function wx_eventloop(sec::Real=50e-3) EventLoopActivator = wx["EventLoopActivator"] install_doevent(sec) do async app = pycall(GetApp, PyObject) - if PyPtr(app) != pynothing[] + if !(app ≛ pynothing[]) app["_in_event_loop"] = true evtloop = pycall(EventLoop, PyObject) ea = pycall(EventLoopActivator, PyObject, evtloop) @@ -200,10 +200,10 @@ function Tk_eventloop(sec::Real=50e-3) root = PyObject(nothing) install_doevent(sec) do async new_root = Tk["_default_root"] - if PyPtr(new_root) != pynothing[] + if !(new_root ≛ pynothing[]) root = new_root end - if PyPtr(root) != pynothing[] + if !(root ≛ pynothing[]) while pycall(root["dooneevent"], Bool, flag) end end From a2a439414254dc21f3ed5879783745c660840b61 Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Tue, 22 Jan 2019 17:43:46 +0100 Subject: [PATCH 09/15] separate depwarns for getindex --- src/PyCall.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/PyCall.jl b/src/PyCall.jl index 1398d2ef..87aa1b02 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -322,8 +322,12 @@ function setproperty!(o::PyObject, s::Union{Symbol,AbstractString}, v) o end -function getindex(o::PyObject, s::Union{Symbol, AbstractString}) - Base.depwarn("`getindex(o::PyObject, s::Union{Symbol, AbstractString})` is deprecated in favor of dot overloading (`getproperty`) so elements should now be accessed as e.g. `o.s` instead of `o[:s]`.", :getindex) +function getindex(o::PyObject, s::T) where T<:Union{Symbol, AbstractString} + if T == Symbol + Base.depwarn("`getindex(o::PyObject, s::Symbol)` is deprecated in favor of dot overloading (`getproperty`) so elements should now be accessed as e.g. `o.s` instead of `o[:s]`.", :getindex) + else + Base.depwarn("`getindex(o::PyObject, s::AbstractString)` is deprecated in favor of dot overloading (`getproperty`) so elements should now be accessed as e.g. `o.\"s\"` instead of `o[\"s\"]`.", :getindex) + end return getproperty(o, s) end From c590d92492bdef37f038cf48664c5656192c17dc Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Tue, 22 Jan 2019 19:06:04 +0100 Subject: [PATCH 10/15] fix tests introduced in #594 --- test/runtests.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index bce8393d..6b54a217 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -732,8 +732,8 @@ else @test length(piter) == 2 @test length(collect(piter)) == 2 r1, r2 = collect(piter) - @test r1.o === i1.o - @test r2.o === i2.o + @test getfield(r1, :o) === getfield(i1, :o) + @test getfield(r2, :o) === getfield(i2, :o) @test Base.IteratorSize(PyCall.PyIterator(PyObject(1))) == Base.SizeUnknown() @test Base.IteratorSize(PyCall.PyIterator(PyObject([1]))) == Base.HasLength() From 27f6d108f04a10b451d74a3cd1a9783ab0fbda09 Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Tue, 22 Jan 2019 22:06:47 +0100 Subject: [PATCH 11/15] fixing deprecations... --- src/PyCall.jl | 2 +- src/conversions.jl | 2 +- src/numpy.jl | 4 ++-- test/runtests.jl | 6 +++--- test/testpybuffer.jl | 16 ++++++++-------- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/PyCall.jl b/src/PyCall.jl index 938d8ce0..ce3a97ff 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -890,7 +890,7 @@ end ######################################################################### # Expose Python docstrings to the Julia doc system -Docs.getdoc(o::PyObject) = Text(convert(String, o["__doc__"])) +Docs.getdoc(o::PyObject) = Text(convert(String, o."__doc__")) ######################################################################### diff --git a/src/conversions.jl b/src/conversions.jl index 4a35f841..ba4db31e 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -554,7 +554,7 @@ end # Our approach is to wrap an iterator over d.o["items"] # which necessitates including d.o["items"] in the state. function _start(d::PyDict{K,V,false}) where {K,V} - d_items = pycall(d.o["items"], PyObject) + d_items = pycall(d.o."items", PyObject) (d_items, iterate(d_items)) end function Base.iterate(d::PyDict{K,V,false}, itr=_start(d)) where {K,V} diff --git a/src/numpy.jl b/src/numpy.jl index 8b3ae572..6767b558 100644 --- a/src/numpy.jl +++ b/src/numpy.jl @@ -52,11 +52,11 @@ function npyinitialize() if pyversion.major < 3 PyArray_API = @pycheck ccall((@pysym :PyCObject_AsVoidPtr), Ptr{Ptr{Cvoid}}, (PyPtr,), - npy_multiarray["_ARRAY_API"]) + npy_multiarray."_ARRAY_API") else PyArray_API = @pycheck ccall((@pysym :PyCapsule_GetPointer), Ptr{Ptr{Cvoid}}, (PyPtr,Ptr{Cvoid}), - npy_multiarray["_ARRAY_API"], C_NULL) + npy_multiarray."_ARRAY_API", C_NULL) end numpy = pyimport("numpy") diff --git a/test/runtests.jl b/test/runtests.jl index 1c277e19..74b79f15 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -413,8 +413,8 @@ const PyInt = pyversion < v"3" ? Int : Clonglong bar = TestConstruct(1) o = PyObject(bar) @test PyCall.is_pyjlwrap(o) - r = weakref[:ref](o) - @test weakref[:getweakrefcount](o) == 1 + r = weakref.ref(o) + @test weakref.getweakrefcount(o) == 1 end # Expose python docs to Julia doc system @@ -743,7 +743,7 @@ end using PyCall - pyimport("atexit")[:register]() do + pyimport("atexit").register() do println("atexit called") end """ diff --git a/test/testpybuffer.jl b/test/testpybuffer.jl index 8650b0ad..a875470c 100644 --- a/test/testpybuffer.jl +++ b/test/testpybuffer.jl @@ -2,7 +2,7 @@ using Test, PyCall using PyCall: f_contiguous, PyBUF_ND_CONTIGUOUS, array_format, npy_initialized, NoCopyArray, isbuftype, setdata! -pyutf8(s::PyObject) = pycall(s["encode"], PyObject, "utf-8") +pyutf8(s::PyObject) = pycall(s."encode", PyObject, "utf-8") pyutf8(s::String) = pyutf8(PyObject(s)) @testset "PyBuffer" begin @@ -20,15 +20,15 @@ pyutf8(s::String) = pyutf8(PyObject(s)) np = pyimport("numpy") listpy = pybuiltin("list") arrpyo(args...; kwargs...) = - pycall(np["array"], PyObject, args...; kwargs...) + pycall(np."array", PyObject, args...; kwargs...) listpyo(args...) = pycall(listpy, PyObject, args...) pytestarray(sz::Int...; order="C") = - pycall(arrpyo(1.0:prod(sz), "d")["reshape"], PyObject, sz, order=order) + pycall(arrpyo(1.0:prod(sz), "d")."reshape", PyObject, sz, order=order) @testset "Non-native-endian" begin wrong_endian_str = ENDIAN_BOM == 0x01020304 ? "<" : ">" wrong_endian_arr = - pycall(np["ndarray"], PyObject, 2; buffer=UInt8[0,1,3,2], + pycall(np."ndarray", PyObject, 2; buffer=UInt8[0,1,3,2], dtype=wrong_endian_str*"i2") # Not supported, so throws @test_throws ArgumentError NoCopyArray(wrong_endian_arr) @@ -99,7 +99,7 @@ pyutf8(s::String) = pyutf8(PyObject(s)) @testset "Non contiguous PyArrays" begin @testset "1d non-contiguous" begin # create an array of four Int32s, with stride 8 - nparr = pycall(np["ndarray"], PyObject, 4, + nparr = pycall(np."ndarray", PyObject, 4, buffer=UInt32[1,0,1,0,1,0,1,0], dtype="i4", strides=(8,)) pyarr = PyArray(nparr) @@ -116,7 +116,7 @@ pyutf8(s::String) = pyutf8(PyObject(s)) end @testset "2d non-contiguous" begin - nparr = pycall(np["ndarray"], PyObject, + nparr = pycall(np."ndarray", PyObject, buffer=UInt32[1,0,2,0,1,0,2,0, 1,0,2,0,1,0,2,0], order="f", dtype="i4", shape=(2, 4), strides=(8,16)) @@ -202,8 +202,8 @@ pyutf8(s::String) = pyutf8(PyObject(s)) @testset "similar on PyArray PyVec getindex" begin jlarr1 = [1:10;] jlarr2 = hcat([1:10;], [1:10;]) - pyarr1 = pycall(np["array"], PyArray, jlarr1) - pyarr2 = pycall(np["array"], PyArray, jlarr2) + pyarr1 = pycall(np."array", PyArray, jlarr1) + pyarr2 = pycall(np."array", PyArray, jlarr2) @test all(pyarr1[1:10] .== jlarr1[1:10]) @test all(pyarr2[1:10, 2] .== jlarr2[1:10, 2]) @test all(pyarr2[1:10, 1:2] .== jlarr2) From a90dce2e628a167058da39139dddc45041c890be Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Tue, 22 Jan 2019 23:22:43 +0100 Subject: [PATCH 12/15] fixing deprecations again... --- src/PyCall.jl | 12 +++++++----- test/runtests.jl | 14 +++++++------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/PyCall.jl b/src/PyCall.jl index ce3a97ff..59ffcc1d 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -306,7 +306,11 @@ getproperty(o::PyObject, s::Symbol) = convert(PyAny, getproperty(o, string(s))) propertynames(o::PyObject) = map(x->Symbol(first(x)), pycall(inspect."getmembers", PyObject, o)) -function setproperty!(o::PyObject, s::Union{Symbol,AbstractString}, v) +# avoiding method ambiguity +setproperty!(o::PyObject, s::Symbol, v) = _setproperty!(o,s,v) +setproperty!(o::PyObject, s::AbstractString, v) = _setproperty!(o,s,v) + +function _setproperty!(o::PyObject, s::Union{Symbol,AbstractString}, v) if ispynull(o) throw(ArgumentError("assign of NULL PyObject")) end @@ -358,7 +362,7 @@ end ######################################################################### -keys(o::PyObject) = Symbol[m[1] for m in pycall(inspect["getmembers"], +keys(o::PyObject) = Symbol[m[1] for m in pycall(inspect."getmembers", PyVector{Tuple{Symbol,PyObject}}, o)] ######################################################################### @@ -731,9 +735,7 @@ end Look up a string or symbol `s` among the global Python builtins. If `s` is a string it returns a PyObject, while if `s` is a symbol it returns the builtin converted to `PyAny`. """ -function pybuiltin(name) - builtin[name] -end +pybuiltin(name) = getproperty(builtin, name) ######################################################################### include("pyfncall.jl") diff --git a/test/runtests.jl b/test/runtests.jl index 74b79f15..a44e3ae0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -163,7 +163,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong # in Python 3, we need a specific encoding to write strings or bufferize them # (http://stackoverflow.com/questions/5471158/typeerror-str-does-not-support-the-buffer-interface) - pyutf8(s::PyObject) = pycall(s["encode"], PyObject, "utf-8") + pyutf8(s::PyObject) = pycall(s."encode", PyObject, "utf-8") pyutf8(s::String) = pyutf8(PyObject(s)) # IO (issue #107) @@ -401,7 +401,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong end # issue #345 - let weakdict = pyimport("weakref")["WeakValueDictionary"] + let weakdict = pyimport("weakref")."WeakValueDictionary" # (use weakdict for the value, since Python supports # weak references to type objects) @test convert(Dict{Int,PyObject}, weakdict(Dict(3=>weakdict))) == Dict(3=>weakdict) @@ -607,10 +607,10 @@ end @testset "pydef" begin d = Doubler(5) - @test d[:x] == 5 - d[:x2] = 30 - @test d[:x] == 15 - @test d[:type_str](10) == string(PyInt) + @test d.x == 5 + d.x2 = 30 + @test d.x == 15 + @test d.type_str(10) == string(PyInt) @test PyCall.builtin[:isinstance](d, PyCall.builtin[:AssertionError]) @test_throws ErrorException @pywith IgnoreError(false) error() @@ -747,7 +747,7 @@ end println("atexit called") end """ - out = read(`$(Base.julia_cmd()) -e $script`, String) + out = read(`$(Base.julia_cmd()) --startup-file=no -e $script`, String) @test occursin("atexit called", out) end From d7955de0e91e4f12bc420a526b920eeac90f0c70 Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Tue, 22 Jan 2019 23:50:03 +0100 Subject: [PATCH 13/15] fixed all deprecation warnings --- src/io.jl | 8 ++++---- test/runtests.jl | 22 +++++++++++----------- test/testpybuffer.jl | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/io.jl b/src/io.jl index c614f511..145b2fea 100644 --- a/src/io.jl +++ b/src/io.jl @@ -75,8 +75,8 @@ function pyio_initialize() if !pyio_initialized::Bool copy!(PyIO, @pydef_object mutable struct PyIO function __init__(self, io::IO; istextio=false) - self[:io] = pyjlwrap_new(io) # avoid recursion - self[:istextio] = istextio + self.io = pyjlwrap_new(io) # avoid recursion + self.istextio = istextio end close(self) = @with_ioraise(close(pyio_jl(self))) closed.get(self) = @with_ioraise(!isopen(pyio_jl(self))) @@ -104,11 +104,11 @@ function pyio_initialize() writelines(self, seq) = @with_ioraise(for s in seq write(pyio_jl(self), s) end) read(self, nb=typemax(Int)) = - @with_ioraise(self[:istextio] ? + @with_ioraise(self.istextio ? String(read(pyio_jl(self), nb < 0 ? typemax(Int) : nb)) : pybytes(read(pyio_jl(self), nb < 0 ? typemax(Int) : nb))) readall(self) = - @with_ioraise(self[:istextio] ? read(pyio_jl(self), String) : + @with_ioraise(self.istextio ? read(pyio_jl(self), String) : pybytes(read(pyio_jl(self)))) readinto(self, b) = @with_ioraise(pybytes(readbytes!(pyio_jl(self), b))) write(self, b) = @with_ioraise(write(pyio_jl(self), b)) diff --git a/test/runtests.jl b/test/runtests.jl index a44e3ae0..95e496fe 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -97,7 +97,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong @test get(d.o, 1) == "hello" set!(d.o, 34, "goodbye") @test d[34] == "goodbye" - @test sort!(keys(Int, d)) == sort!(collect(d.o[:keys]())) == sort!(collect(keys(d))) == [1, 34] + @test sort!(keys(Int, d)) == sort!(collect(d.o.keys())) == sort!(collect(keys(d))) == [1, 34] @test eltype(d) == eltype(typeof(d)) == Pair{Int, String} end @@ -578,22 +578,22 @@ end # @pywith errors correctly handled @pydef mutable struct IgnoreError function __init__(self, ignore) - self[:ignore] = ignore + self.ignore = ignore end __enter__(self) = () - __exit__(self, typ, value, tb) = self[:ignore] + __exit__(self, typ, value, tb) = self.ignore end # @pydef example from README -@pydef mutable struct Doubler <: PyCall.builtin[:AssertionError] - __init__(self, x=10) = (self[:x] = x) +@pydef mutable struct Doubler <: PyCall.builtin.AssertionError + __init__(self, x=10) = (self.x = x) function my_method(self, arg1::Number) return arg1 + 20 end type_str(self, obj::T) where T = string(T) - x2.get(self) = self[:x] * 2 + x2.get(self) = self.x * 2 function x2.set!(self, new_val) - self[:x] = new_val / 2 + self.x = new_val / 2 end end @@ -601,7 +601,7 @@ end @pydef mutable struct ObjectCounter obj_count = 1 - 1 function __init__(::PyObject) - ObjectCounter[:obj_count] += 1 + ObjectCounter.obj_count += 1 end end @@ -611,14 +611,14 @@ end d.x2 = 30 @test d.x == 15 @test d.type_str(10) == string(PyInt) - @test PyCall.builtin[:isinstance](d, PyCall.builtin[:AssertionError]) + @test PyCall.builtin.isinstance(d, PyCall.builtin.AssertionError) @test_throws ErrorException @pywith IgnoreError(false) error() @test (@pywith IgnoreError(true) error(); true) - @test ObjectCounter[:obj_count] == 0 + @test ObjectCounter.obj_count == 0 a = ObjectCounter() - @test ObjectCounter[:obj_count] == 1 + @test ObjectCounter.obj_count == 1 end @testset "callback" begin diff --git a/test/testpybuffer.jl b/test/testpybuffer.jl index a875470c..6d3fafcd 100644 --- a/test/testpybuffer.jl +++ b/test/testpybuffer.jl @@ -42,7 +42,7 @@ pyutf8(s::String) = pyutf8(PyObject(s)) @testset "dtype $pytype should match eltype $jltype" begin jlarr = jltype[1:10;] nparr = arrpyo(jlarr, dtype=pytype) - @test pystr(nparr["dtype"]) == pytype + @test pystr(nparr."dtype") == pytype jlarr2 = convert(PyAny, nparr) @test eltype(jlarr2) == jltype @test jlarr2 == jlarr From 52e9b1382240eee848f4274b1a973188ed79dd77 Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Wed, 23 Jan 2019 00:24:25 +0100 Subject: [PATCH 14/15] deprecate setindex! and fix warnings etc --- src/PyCall.jl | 17 +++-------------- src/gui.jl | 38 +++++++++++++++++++------------------- src/pyclass.jl | 8 ++++---- src/pyinit.jl | 2 +- 4 files changed, 27 insertions(+), 38 deletions(-) diff --git a/src/PyCall.jl b/src/PyCall.jl index 59ffcc1d..63cfe6bf 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -331,20 +331,9 @@ function getindex(o::PyObject, s::T) where T<:Union{Symbol, AbstractString} return getproperty(o, s) end - -# If I make this -# setindex!(o::PyObject, v, s::Union{Symbol,AbstractString}) = setproperty!(o, s, v) -# the tests (Pkg.test("PyCall")) won't go through. Don't know why..... -function setindex!(o::PyObject, v, s::Union{Symbol,AbstractString}) - if ispynull(o) - throw(ArgumentError("assign of NULL PyObject")) - end - if -1 == ccall((@pysym :PyObject_SetAttrString), Cint, - (PyPtr, Cstring, PyPtr), o, s, PyObject(v)) - pyerr_clear() - throw(KeyError(s)) - end - o +function setindex!(o::PyObject, v, s::Union{Symbol, AbstractString}) + Base.depwarn("`setindex!(o::PyObject, v, s::Union{Symbol, AbstractString})` is deprecated in favor of `setproperty!(o, s, v)`.", :setindex!) + return _setproperty!(o, s, v) end function haskey(o::PyObject, s::Union{Symbol,AbstractString}) diff --git a/src/gui.jl b/src/gui.jl index 84293676..206912fa 100644 --- a/src/gui.jl +++ b/src/gui.jl @@ -76,8 +76,8 @@ end function gtk_requireversion(gtkmodule::AbstractString, vers::VersionNumber=v"3.0") if startswith(gtkmodule, "gi.") gi = pyimport("gi") - if gi[:get_required_version]("Gtk") === nothing - gi[:require_version]("Gtk", string(vers)) + if gi.get_required_version("Gtk") === nothing + gi.require_version("Gtk", string(vers)) end end end @@ -86,8 +86,8 @@ end function gtk_eventloop(gtkmodule::AbstractString, sec::Real=50e-3) gtk_requireversion(gtkmodule) gtk = pyimport(gtkmodule) - events_pending = gtk["events_pending"] - main_iteration = gtk["main_iteration"] + events_pending = gtk."events_pending" + main_iteration = gtk."main_iteration" install_doevent(sec) do async # handle all pending while pycall(events_pending, Bool) @@ -125,7 +125,7 @@ function fixqtpath(qtconf=joinpath(dirname(pyprogramname),"qt.conf")) # for some reason I don't understand, # if libpython has already been loaded, then # we need to use Python's setenv rather than Julia's - PyDict(pyimport("os")["environ"])["QT_PLUGIN_PATH"] = realpath(plugin_path) + PyDict(pyimport("os")."environ")."QT_PLUGIN_PATH" = realpath(plugin_path) return true end end @@ -137,15 +137,15 @@ end # Qt: (PyQt5, PyQt4, or PySide module) function qt_eventloop(QtCore::PyObject, sec::Real=50e-3) fixqtpath() - instance = QtCore["QCoreApplication"]["instance"] - AllEvents = QtCore["QEventLoop"]["AllEvents"] - processEvents = QtCore["QCoreApplication"]["processEvents"] + instance = QtCore."QCoreApplication"."instance" + AllEvents = QtCore."QEventLoop"."AllEvents" + processEvents = QtCore."QCoreApplication"."processEvents" pop!(ENV, "QT_PLUGIN_PATH", "") # clean up environment maxtime = PyObject(50) install_doevent(sec) do async app = pycall(instance, PyObject) if !(app ≛ pynothing[]) - app["_in_event_loop"] = true + app."_in_event_loop" = true pycall(processEvents, PyObject, AllEvents, maxtime) end end @@ -167,22 +167,22 @@ end # wx: (based on IPython/lib/inputhookwx.py, which is 3-clause BSD-licensed) function wx_eventloop(sec::Real=50e-3) wx = pyimport("wx") - GetApp = wx["GetApp"] - EventLoop = wx["EventLoop"] - EventLoopActivator = wx["EventLoopActivator"] + GetApp = wx."GetApp" + EventLoop = wx."EventLoop" + EventLoopActivator = wx."EventLoopActivator" install_doevent(sec) do async app = pycall(GetApp, PyObject) if !(app ≛ pynothing[]) - app["_in_event_loop"] = true + app."_in_event_loop" = true evtloop = pycall(EventLoop, PyObject) ea = pycall(EventLoopActivator, PyObject, evtloop) - Pending = evtloop["Pending"] - Dispatch = evtloop["Dispatch"] + Pending = evtloop."Pending" + Dispatch = evtloop."Dispatch" while pycall(Pending, Bool) pycall(Dispatch, PyObject) end pydecref(ea) # deactivate event loop - pycall(app["ProcessIdle"], PyObject) + pycall(app."ProcessIdle", PyObject) end end end @@ -192,15 +192,15 @@ end function Tk_eventloop(sec::Real=50e-3) Tk = pyimport(tkinter_name()) _tkinter = pyimport("_tkinter") - flag = _tkinter[:ALL_EVENTS] | _tkinter[:DONT_WAIT] + flag = _tkinter.ALL_EVENTS | _tkinter.DONT_WAIT root = PyObject(nothing) install_doevent(sec) do async - new_root = Tk["_default_root"] + new_root = Tk."_default_root" if !(new_root ≛ pynothing[]) root = new_root end if !(root ≛ pynothing[]) - while pycall(root["dooneevent"], Bool, flag) + while pycall(root."dooneevent", Bool, flag) end end end diff --git a/src/pyclass.jl b/src/pyclass.jl index 0302b084..e2211808 100644 --- a/src/pyclass.jl +++ b/src/pyclass.jl @@ -135,12 +135,12 @@ For instance, @pyimport numpy.polynomial as P @pydef type Doubler <: P.Polynomial - __init__(self, x=10) = (self[:x] = x) + __init__(self, x=10) = (self.x = x) my_method(self, arg1::Number) = arg1 + 20 - x2.get(self) = self[:x] * 2 - x2.set!(self, new_val) = (self[:x] = new_val / 2) + x2.get(self) = self.x * 2 + x2.set!(self, new_val) = (self.x = new_val / 2) end - Doubler()[:x2] + Doubler().x2 is essentially equivalent to the following Python code: diff --git a/src/pyinit.jl b/src/pyinit.jl index 1de9eecf..80a4b247 100644 --- a/src/pyinit.jl +++ b/src/pyinit.jl @@ -160,7 +160,7 @@ function __init__() if isinteractive() let sys = pyimport("sys") if !hasproperty(sys, "ps1") - sys["ps1"] = ">>> " + sys."ps1" = ">>> " end end end From 631bcb0d4e82e7c78a4b0f37334e2c0b98f8a8e6 Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Wed, 23 Jan 2019 00:40:56 +0100 Subject: [PATCH 15/15] first (insufficient) modification of README --- README.md | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 5b15fb25..66c835ec 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ without copying them. ## Installation Within Julia, just use the package manager to run `Pkg.add("PyCall")` to -install the files. Julia 0.7 or later is required. +install the files. Julia 0.7 or later is required. The latest development version of PyCall is available from . If you want to switch to @@ -139,7 +139,7 @@ we could call the Newton solver in scipy.optimize via: A macro exists for mimicking Python's "with statement". For example: @pywith pybuiltin("open")("file.txt","w") as f begin - f[:write]("hello") + f.write("hello") end The type of `f` can be specified with `f::T` (for example, to override automatic @@ -162,18 +162,13 @@ example, using [Biopython](http://biopython.org/wiki/Seq) we can do: @pyimport Bio.Seq as s @pyimport Bio.Alphabet as a my_dna = s.Seq("AGTACACTGGT", a.generic_dna) - my_dna[:find]("ACT") - -whereas in Python the last step would have been `my_dna.find("ACT")`. + my_dna.find("ACT") ## Troubleshooting Here are solutions to some common problems: -* As mentioned above, use `foo[:bar]` and `foo[:bar](...)` rather than `foo.bar` and `foo.bar(...)`, -respectively, to access attributes and methods of Python objects. - -* By default, PyCall [doesn't include the current directory in the Python search path](https://github.com/JuliaPy/PyCall.jl/issues/48). If you want to do that (in order to load a Python module from the current directory), just run `pushfirst!(PyVector(pyimport("sys")["path"]), "")`. +* By default, PyCall [doesn't include the current directory in the Python search path](https://github.com/JuliaPy/PyCall.jl/issues/48). If you want to do that (in order to load a Python module from the current directory), just run `pushfirst!(PyVector(pyimport("sys")."path"), "")`. ## Python object interfaces @@ -199,9 +194,9 @@ dates/periods, and functions, along with tuples and arrays/lists thereof, but more are planned. (Julia symbols are converted to Python strings.) -Given `o::PyObject`, `o[:attribute]` is equivalent to `o.attribute` +Given `o::PyObject`, `o.attribute` in Julia is equivalent to `o.attribute` in Python, with automatic type conversion. To get an attribute as a -`PyObject` without type conversion, do `o["attribute"]` instead. +`PyObject` without type conversion, do `o."attribute"` instead. The `keys(o::PyObject)` function returns an array of the available attribute symbols. @@ -338,10 +333,10 @@ and also by providing more type information to the Julia compiler. * `pyimport(s)`: Import the Python module `s` (a string or symbol) and return a pointer to it (a `PyObject`). Functions or other symbols - in the module may then be looked up by `s[name]` where `name` is a + in the module may then be looked up by `s.name` where `name` is a string (for the raw `PyObject`) or symbol (for automatic type-conversion). Unlike the `@pyimport` macro, this does not - define a Julia module and members cannot be accessed with `s.name`. + define a Julia module. * `py"..."` evaluates `"..."` as a Python string, equivalent to Python's [`eval`](https://docs.python.org/2/library/functions.html#eval) function, and returns the result @@ -377,7 +372,7 @@ and also by providing more type information to the Julia compiler. instead use `w.pymember(:member)` (for the `PyAny` conversion) or `w.pymember("member")` (for the raw `PyObject`). `pywrap` is rather inefficient since it converts *every* member of `o` at once; you - are generally encouraged to simply access members via `o[:member]` + are generally encouraged to simply access members via `o.member` rather than using `pywrap`. Occasionally, you may need to pass a keyword argument to Python that @@ -411,15 +406,15 @@ For instance, @pyimport numpy.polynomial as P @pydef mutable struct Doubler <: P.Polynomial function __init__(self, x=10) - self[:x] = x + self.x = x end my_method(self, arg1::Number) = arg1 + 20 - x2.get(self) = self[:x] * 2 + x2.get(self) = self.x * 2 function x2.set!(self, new_val) - self[:x] = new_val / 2 + self.x = new_val / 2 end end - Doubler()[:x2] + Doubler().x2 is essentially equivalent to the following Python code: @@ -450,14 +445,14 @@ Here's another example using [Tkinter](https://wiki.python.org/moin/TkInter): @pydef mutable struct SampleApp <: tk.Tk __init__(self, args...; kwargs...) = begin - tk.Tk[:__init__](self, args...; kwargs...) - self[:label] = tk.Label(text="Hello, world!") - self[:label][:pack](padx=10, pady=10) + tk.Tk.__init__(self, args...; kwargs...) + self.label = tk.Label(text="Hello, world!") + self.label.pack(padx=10, pady=10) end end app = SampleApp() - app[:mainloop]() + app.mainloop() Class variables are also supported: @@ -465,7 +460,7 @@ Class variables are also supported: @pydef mutable struct ObjectCounter obj_count = 0 # Class variable function __init__(::PyObject) - ObjectCounter[:obj_count] += 1 + ObjectCounter.obj_count += 1 end end