Repository: JuliaPy/PyCall.jl
Pull request #1039 (2023-06-09 19:14:16)

disable deepcopy on PyObject #757

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/JuliaPy/PyCall.jl/pull/1039?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
Patch coverage: **`100.00`**% and project coverage change: **`-0.06`** :warning:
> Comparison is base [(`bcaba00`)](https://app.codecov.io/gh/JuliaPy/PyCall.jl/commit/bcaba00d1e2c412b2f61d33343ef5a9ab1b9258a?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) 67.03% compared to head [(`dac72c6`)](https://app.codecov.io/gh/JuliaPy/PyCall.jl/pull/1039?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) 66.98%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #1039      +/-   ##
==========================================
- Coverage   67.03%   66.98%   -0.06%     
==========================================
  Files          20       20              
  Lines        2017     2020       +3     
==========================================
+ Hits         1352     1353       +1     
- Misses        665      667       +2     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `66.98% <100.00%> (-0.06%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://app.codecov.io/gh/JuliaPy/PyCall.jl/pull/1039?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [src/PyCall.jl](https://app.codecov.io/gh/JuliaPy/PyCall.jl/pull/1039?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-c3JjL1B5Q2FsbC5qbA==) | `64.48% <100.00%> (-0.61%)` | :arrow_down: |

... and [1 file with indirect coverage changes](https://app.codecov.io/gh/JuliaPy/PyCall.jl/pull/1039/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/JuliaPy/PyCall.jl/pull/1039?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).

stevengj: Why not just increment the refcount as proposed in #757?
jw3126: @stevengj Does this PR look good to you?
jw3126: @stevengj LGTY?

Patch:
diff --git a/Project.toml b/Project.toml
index f2462d1..133a026 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
 name = "PyCall"
 uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
 authors = ["Steven G. Johnson <stevenj@mit.edu>", "Yichao Yu <yyc1992@gmail.com>", "Takafumi Arakaki <aka.tkf@gmail.com>", "Simon Kornblith <simon@simonster.com>", "Páll Haraldsson <Pall.Haraldsson@gmail.com>", "Jon Malmaud <malmaud@gmail.com>", "Jake Bolewski <jakebolewski@gmail.com>", "Keno Fischer <keno@alumni.harvard.edu>", "Joel Mason <jobba1@hotmail.com>", "Jameson Nash <vtjnash@gmail.com>", "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 0c155fe..182abb1 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")


Test patch:
diff --git a/test/runtests.jl b/test/runtests.jl
index 2e5c915..b718ee2 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
+