From c12d6d7da4116d8231c57e349a469567f14f362c Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Wed, 21 Jun 2023 18:01:37 +0200 Subject: [PATCH] disable deepcopy on PyObject #757 (#1039) * disable deepcopy on PyObject #757 * Update src/PyCall.jl Co-authored-by: Steven G. Johnson * fix deepcopy and add tests * add another deepcopy test * v1.95.2 * Update Project.toml * Update test/runtests.jl --------- Co-authored-by: Steven G. Johnson --- Project.toml | 2 +- src/PyCall.jl | 10 ++++++++++ test/runtests.jl | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index f2462d16..133a026d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PyCall" uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" authors = ["Steven G. Johnson ", "Yichao Yu ", "Takafumi Arakaki ", "Simon Kornblith ", "Páll Haraldsson ", "Jon Malmaud ", "Jake Bolewski ", "Keno Fischer ", "Joel Mason ", "Jameson Nash ", "The JuliaPy development team"] -version = "1.95.2" +version = "1.96.0" [deps] Conda = "8f4d0f93-b110-5947-807f-2305c1781a2d" diff --git a/src/PyCall.jl b/src/PyCall.jl index 0c155fef..182abb13 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -979,6 +979,16 @@ include("serialize.jl") include("pyinit.jl") +const _deepcopy = PyNULL() + +function Base.deepcopy_internal(obj::PyObject, stackdict::Base.IdDict) + haskey(stackdict, obj) && return stackdict[obj] + ispynull(_deepcopy) && copy!(_deepcopy, pyimport("copy")["deepcopy"]) + ret = pycall(_deepcopy, PyObject, obj) + stackdict[obj] = ret + ret +end + ######################################################################### include("precompile.jl") diff --git a/test/runtests.jl b/test/runtests.jl index 2e5c915a..b718ee23 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -620,6 +620,45 @@ const PyInt = pyversion < v"3" ? Int : Clonglong @test_throws ArgumentError float(pybuiltin("type")) end +@testset "deepcopy #757" begin + l = py"[1,2,3]"o + l2 = deepcopy(l) + @test l == l2 + l2.append(4) + @test l != l2 + @test collect(l2) == [1,2,3,4] + @test collect(l) == [1,2,3] + + obj = py""" + class C757: + def __init__(self, a, b): + self.a = a + self.b = b + """ + obj = py"C757(C757(1,2), C757(3,4))"o + obj2 = deepcopy(obj) + @test PyPtr(obj) != PyPtr(obj2) # make sure a new Python object is created + @test obj.a.a == obj2.a.a + @test obj.a.b == obj2.a.b + @test obj.b.a == obj2.b.a + @test obj.b.b == obj2.b.b + obj.a = 3 + @test obj.a == 3 + @test obj2.a.a == 1 + @test obj2.a.b == 2 + + struct S;a;b;end + + c = py"C757(1,2)" + obj = S(c, c) + obj2 = deepcopy(obj) + @test obj.a === obj.b + @test obj2.a === obj2.b + obj.a.a = 4 + @test obj.a.a == 4 + @test obj2.a.a == 1 +end + ###################################################################### #@pydef tests: type declarations need to happen at top level @@ -855,3 +894,4 @@ end @test_throws PyCall.PyError a.a = 0 @test_throws KeyError a.a = 1 end +