From a2155c18e0a74e84b60c3bc7fe4bc23fd3719946 Mon Sep 17 00:00:00 2001 From: Seth Axen Date: Sat, 16 Jan 2021 03:23:52 -0800 Subject: [PATCH 1/7] Use dir instead of getmembers --- src/PyCall.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/PyCall.jl b/src/PyCall.jl index 48c28697..b6e6e68b 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -316,7 +316,7 @@ function trygetproperty(o::PyObject, s::AbstractString, d) return p == C_NULL ? d : PyObject(p) end -propertynames(o::PyObject) = ispynull(o) ? Symbol[] : map(x->Symbol(first(x)), PyIterator{PyObject}(pycall(inspect."getmembers", PyObject, o))) +propertynames(o::PyObject) = ispynull(o) ? Symbol[] : map(Symbol, PyIterator{PyObject}(pycall(py"dir", PyObject, o))) # avoiding method ambiguity setproperty!(o::PyObject, s::Symbol, v) = _setproperty!(o,s,v) @@ -366,8 +366,7 @@ hasproperty(o::PyObject, s::AbstractString) = pyhasproperty(o, s) ######################################################################### -keys(o::PyObject) = Symbol[m[1] for m in pycall(inspect."getmembers", - PyVector{Tuple{Symbol,PyObject}}, o)] +keys(o::PyObject) = map(Symbol, pycall(py"dir", PyVector{Tuple{Symbol,PyObject}}, o)) ######################################################################### # Create anonymous composite w = pywrap(o) wrapping the object o From d1534fd926a3b8ef1eda56980e23224c3cdb08b9 Mon Sep 17 00:00:00 2001 From: Seth Axen Date: Sat, 16 Jan 2021 03:36:38 -0800 Subject: [PATCH 2/7] Add property decorator tests --- test/runtests.jl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index dff36dbc..e8677481 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -295,17 +295,34 @@ const PyInt = pyversion < v"3" ? Int : Clonglong class A: class B: C = 1 + D_ = 2 + @property + def D(self): + return self.D_ + @D.setter + def D(self, Dnew): + self.D_ = Dnew + @property + def E(self): + raise NotImplementedError """ A = py"A" @test hasproperty(A, "B") @test getproperty(A, "B") == py"A.B" @test :B in propertynames(A) @static if VERSION >= v"0.7-" + @test :C in propertynames(A.B) + @test :D in propertynames(A.B) + @test :E in propertynames(A.B) @test A.B.C == 1 + @test A.B.D == 2 + @test_throws PyCall.PyError A.B.E @test_throws KeyError A.X end setproperty!(py"A.B", "C", 2) @test py"A.B.C" == 2 + setproperty!(py"A.B", "D", 3) + @test py"A.B.D" == 3 # buffers let b = PyCall.PyBuffer(pyutf8("test string")) From cebe67e4a9caa2ed2b09292afdde4cd1b6c0de13 Mon Sep 17 00:00:00 2001 From: Seth Axen Date: Sat, 16 Jan 2021 04:20:14 -0800 Subject: [PATCH 3/7] Use cpython api function for dir --- src/PyCall.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/PyCall.jl b/src/PyCall.jl index b6e6e68b..db848195 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -316,7 +316,10 @@ function trygetproperty(o::PyObject, s::AbstractString, d) return p == C_NULL ? d : PyObject(p) end -propertynames(o::PyObject) = ispynull(o) ? Symbol[] : map(Symbol, PyIterator{PyObject}(pycall(py"dir", PyObject, o))) +function propertynames(o::PyObject) + ispynull(o) && return Symbol[] + return convert(Vector{Symbol}, ccall((@pysym :PyObject_Dir), PyObject, (PyPtr,), o)) +end # avoiding method ambiguity setproperty!(o::PyObject, s::Symbol, v) = _setproperty!(o,s,v) @@ -366,7 +369,7 @@ hasproperty(o::PyObject, s::AbstractString) = pyhasproperty(o, s) ######################################################################### -keys(o::PyObject) = map(Symbol, pycall(py"dir", PyVector{Tuple{Symbol,PyObject}}, o)) +keys(o::PyObject) = convert(Vector{Symbol}, ccall((@pysym :PyObject_Dir), PyObject, (PyPtr,), o)) ######################################################################### # Create anonymous composite w = pywrap(o) wrapping the object o From 1785ee79698b2f93ab1ce1bc28ad93a8d151fe7c Mon Sep 17 00:00:00 2001 From: Seth Axen Date: Sat, 16 Jan 2021 04:20:43 -0800 Subject: [PATCH 4/7] Don't try to test properties --- test/runtests.jl | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index e8677481..419cdec7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -295,15 +295,8 @@ const PyInt = pyversion < v"3" ? Int : Clonglong class A: class B: C = 1 - D_ = 2 @property def D(self): - return self.D_ - @D.setter - def D(self, Dnew): - self.D_ = Dnew - @property - def E(self): raise NotImplementedError """ A = py"A" @@ -311,18 +304,12 @@ const PyInt = pyversion < v"3" ? Int : Clonglong @test getproperty(A, "B") == py"A.B" @test :B in propertynames(A) @static if VERSION >= v"0.7-" - @test :C in propertynames(A.B) @test :D in propertynames(A.B) - @test :E in propertynames(A.B) @test A.B.C == 1 - @test A.B.D == 2 - @test_throws PyCall.PyError A.B.E @test_throws KeyError A.X end setproperty!(py"A.B", "C", 2) @test py"A.B.C" == 2 - setproperty!(py"A.B", "D", 3) - @test py"A.B.D" == 3 # buffers let b = PyCall.PyBuffer(pyutf8("test string")) From 76be31a572c670cbd611a67d0153d52507857af6 Mon Sep 17 00:00:00 2001 From: Seth Axen Date: Sat, 16 Jan 2021 14:20:31 -0800 Subject: [PATCH 5/7] Check if return value is NULL --- src/PyCall.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PyCall.jl b/src/PyCall.jl index db848195..20f5c9ad 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -318,7 +318,8 @@ end function propertynames(o::PyObject) ispynull(o) && return Symbol[] - return convert(Vector{Symbol}, ccall((@pysym :PyObject_Dir), PyObject, (PyPtr,), o)) + names = @pycheckn ccall((@pysym :PyObject_Dir), PyObject, (PyPtr,), o) + return convert(Vector{Symbol}, names) end # avoiding method ambiguity From b8c6ec9fa2cb18b460809f7c2f766a2586c5abbe Mon Sep 17 00:00:00 2001 From: Seth Axen Date: Sat, 16 Jan 2021 14:20:38 -0800 Subject: [PATCH 6/7] Deprecate keys --- src/PyCall.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PyCall.jl b/src/PyCall.jl index 20f5c9ad..615f1171 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -370,7 +370,7 @@ hasproperty(o::PyObject, s::AbstractString) = pyhasproperty(o, s) ######################################################################### -keys(o::PyObject) = convert(Vector{Symbol}, ccall((@pysym :PyObject_Dir), PyObject, (PyPtr,), o)) +@deprecate keys(o::PyObject) propertynames(o) ######################################################################### # Create anonymous composite w = pywrap(o) wrapping the object o From 173542ecada6f7041e88c76453c44004f210a4d2 Mon Sep 17 00:00:00 2001 From: Seth Axen Date: Sat, 16 Jan 2021 14:44:43 -0800 Subject: [PATCH 7/7] Add deprecation test --- test/runtests.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 419cdec7..2e826080 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -310,6 +310,7 @@ const PyInt = pyversion < v"3" ? Int : Clonglong end setproperty!(py"A.B", "C", 2) @test py"A.B.C" == 2 + @test_deprecated keys(A) # buffers let b = PyCall.PyBuffer(pyutf8("test string")) @@ -321,7 +322,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 :real in propertynames(o) @test o.real == 1 end