File: test_class_sh_trampoline_shared_ptr_cpp_arg.py

package info (click to toggle)
pybind11 3.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,448 kB
  • sloc: cpp: 27,239; python: 13,512; ansic: 4,244; makefile: 204; sh: 36
file content (154 lines) | stat: -rw-r--r-- 4,214 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
from __future__ import annotations

import pytest

import env  # noqa: F401
import pybind11_tests.class_sh_trampoline_shared_ptr_cpp_arg as m


@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
def test_shared_ptr_cpp_arg():
    import weakref

    class PyChild(m.SpBase):
        def is_base_used(self):
            return False

    tester = m.SpBaseTester()

    obj = PyChild()
    objref = weakref.ref(obj)

    # Pass the last python reference to the C++ function
    tester.set_object(obj)
    del obj
    pytest.gc_collect()

    # python reference is still around since C++ has it now
    assert objref() is not None
    assert tester.is_base_used() is False
    assert tester.obj.is_base_used() is False
    assert tester.get_object() is objref()


@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
def test_shared_ptr_cpp_prop():
    class PyChild(m.SpBase):
        def is_base_used(self):
            return False

    tester = m.SpBaseTester()

    # Set the last python reference as a property of the C++ object
    tester.obj = PyChild()
    pytest.gc_collect()

    # python reference is still around since C++ has it now
    assert tester.is_base_used() is False
    assert tester.has_python_instance() is True
    assert tester.obj.is_base_used() is False
    assert tester.obj.has_python_instance() is True


@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
def test_shared_ptr_arg_identity():
    import weakref

    tester = m.SpBaseTester()

    obj = m.SpBase()
    objref = weakref.ref(obj)

    tester.set_object(obj)
    del obj
    pytest.gc_collect()

    # NOTE: the behavior below is DIFFERENT from PR #2839
    # python reference is gone because it is not an Alias instance
    assert objref() is None
    assert tester.has_python_instance() is False


@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
def test_shared_ptr_alias_nonpython():
    tester = m.SpBaseTester()

    # C++ creates the object, a python instance shouldn't exist
    tester.set_nonpython_instance()
    assert tester.is_base_used() is True
    assert tester.has_instance() is True
    assert tester.has_python_instance() is False

    # Now a python instance exists
    cobj = tester.get_object()
    assert cobj.has_python_instance()
    assert tester.has_instance() is True
    assert tester.has_python_instance() is True

    # Now it's gone
    del cobj
    pytest.gc_collect()
    assert tester.has_instance() is True
    assert tester.has_python_instance() is False

    # When we pass it as an arg to a new tester the python instance should
    # disappear because it wasn't created with an alias
    new_tester = m.SpBaseTester()

    cobj = tester.get_object()
    assert cobj.has_python_instance()

    new_tester.set_object(cobj)
    assert tester.has_python_instance() is True
    assert new_tester.has_python_instance() is True

    del cobj
    pytest.gc_collect()

    # Gone!
    assert tester.has_instance() is True
    assert tester.has_python_instance() is False
    assert new_tester.has_instance() is True
    assert new_tester.has_python_instance() is False


@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
def test_shared_ptr_goaway():
    import weakref

    tester = m.SpGoAwayTester()

    obj = m.SpGoAway()
    objref = weakref.ref(obj)

    assert tester.obj is None

    tester.obj = obj
    del obj
    pytest.gc_collect()

    # python reference is no longer around
    assert objref() is None
    # C++ reference is still around
    assert tester.obj is not None


def test_infinite():
    tester = m.SpBaseTester()
    while True:
        tester.set_object(m.SpBase())
        break  # Comment out for manual leak checking (use `top` command).


@pytest.mark.parametrize(
    "pass_through_func", [m.pass_through_shd_ptr, m.pass_through_shd_ptr_release_gil]
)
def test_std_make_shared_factory(pass_through_func):
    class PyChild(m.SpBase):
        def __init__(self):
            super().__init__(0)

    obj = PyChild()
    while True:
        assert pass_through_func(obj) is obj
        break  # Comment out for manual leak checking (use `top` command).