test_call_policies.py 6.4 KB
Newer Older
Wenzel Jakob's avatar
Wenzel Jakob committed
1
import pytest
2
3

import env  # noqa: F401
4
from pybind11_tests import ConstructorStats
5
from pybind11_tests import call_policies as m
Dean Moldovan's avatar
Dean Moldovan committed
6
7


8
@pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False)
Dean Moldovan's avatar
Dean Moldovan committed
9
def test_keep_alive_argument(capture):
10
    n_inst = ConstructorStats.detail_reg_inst()
Dean Moldovan's avatar
Dean Moldovan committed
11
    with capture:
12
        p = m.Parent()
Dean Moldovan's avatar
Dean Moldovan committed
13
14
    assert capture == "Allocating parent."
    with capture:
15
        p.addChild(m.Child())
16
        assert ConstructorStats.detail_reg_inst() == n_inst + 1
17
18
19
    assert (
        capture
        == """
Dean Moldovan's avatar
Dean Moldovan committed
20
21
22
        Allocating child.
        Releasing child.
    """
23
    )
Dean Moldovan's avatar
Dean Moldovan committed
24
25
    with capture:
        del p
26
        assert ConstructorStats.detail_reg_inst() == n_inst
Dean Moldovan's avatar
Dean Moldovan committed
27
28
29
    assert capture == "Releasing parent."

    with capture:
30
        p = m.Parent()
Dean Moldovan's avatar
Dean Moldovan committed
31
32
    assert capture == "Allocating parent."
    with capture:
33
        p.addChildKeepAlive(m.Child())
34
        assert ConstructorStats.detail_reg_inst() == n_inst + 2
Dean Moldovan's avatar
Dean Moldovan committed
35
36
37
    assert capture == "Allocating child."
    with capture:
        del p
38
        assert ConstructorStats.detail_reg_inst() == n_inst
39
40
41
    assert (
        capture
        == """
Dean Moldovan's avatar
Dean Moldovan committed
42
43
44
        Releasing parent.
        Releasing child.
    """
45
    )
Dean Moldovan's avatar
Dean Moldovan committed
46

47
48
49
50
51
52
53
54
55
56
57
58
59
    p = m.Parent()
    c = m.Child()
    assert ConstructorStats.detail_reg_inst() == n_inst + 2
    m.free_function(p, c)
    del c
    assert ConstructorStats.detail_reg_inst() == n_inst + 2
    del p
    assert ConstructorStats.detail_reg_inst() == n_inst

    with pytest.raises(RuntimeError) as excinfo:
        m.invalid_arg_index()
    assert str(excinfo.value) == "Could not activate keep_alive!"

Dean Moldovan's avatar
Dean Moldovan committed
60
61

def test_keep_alive_return_value(capture):
62
    n_inst = ConstructorStats.detail_reg_inst()
Dean Moldovan's avatar
Dean Moldovan committed
63
    with capture:
64
        p = m.Parent()
Dean Moldovan's avatar
Dean Moldovan committed
65
66
67
    assert capture == "Allocating parent."
    with capture:
        p.returnChild()
68
        assert ConstructorStats.detail_reg_inst() == n_inst + 1
69
70
71
    assert (
        capture
        == """
Dean Moldovan's avatar
Dean Moldovan committed
72
73
74
        Allocating child.
        Releasing child.
    """
75
    )
Dean Moldovan's avatar
Dean Moldovan committed
76
77
    with capture:
        del p
78
        assert ConstructorStats.detail_reg_inst() == n_inst
Dean Moldovan's avatar
Dean Moldovan committed
79
80
81
    assert capture == "Releasing parent."

    with capture:
82
        p = m.Parent()
Dean Moldovan's avatar
Dean Moldovan committed
83
84
85
    assert capture == "Allocating parent."
    with capture:
        p.returnChildKeepAlive()
86
        assert ConstructorStats.detail_reg_inst() == n_inst + 2
Dean Moldovan's avatar
Dean Moldovan committed
87
88
89
    assert capture == "Allocating child."
    with capture:
        del p
90
        assert ConstructorStats.detail_reg_inst() == n_inst
91
92
93
    assert (
        capture
        == """
94
95
96
        Releasing parent.
        Releasing child.
    """
97
    )
98

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
    p = m.Parent()
    assert ConstructorStats.detail_reg_inst() == n_inst + 1
    with capture:
        m.Parent.staticFunction(p)
        assert ConstructorStats.detail_reg_inst() == n_inst + 2
    assert capture == "Allocating child."
    with capture:
        del p
        assert ConstructorStats.detail_reg_inst() == n_inst
    assert (
        capture
        == """
        Releasing parent.
        Releasing child.
    """
    )

116

117
118
# https://foss.heptapod.net/pypy/pypy/-/issues/2447
@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented")
119
120
def test_alive_gc(capture):
    n_inst = ConstructorStats.detail_reg_inst()
121
122
    p = m.ParentGC()
    p.addChildKeepAlive(m.Child())
123
124
    assert ConstructorStats.detail_reg_inst() == n_inst + 2
    lst = [p]
125
    lst.append(lst)  # creates a circular reference
126
127
128
    with capture:
        del p, lst
        assert ConstructorStats.detail_reg_inst() == n_inst
129
130
131
    assert (
        capture
        == """
132
133
134
        Releasing parent.
        Releasing child.
    """
135
    )
136
137
138


def test_alive_gc_derived(capture):
139
    class Derived(m.Parent):
140
141
142
143
        pass

    n_inst = ConstructorStats.detail_reg_inst()
    p = Derived()
144
    p.addChildKeepAlive(m.Child())
145
146
    assert ConstructorStats.detail_reg_inst() == n_inst + 2
    lst = [p]
147
    lst.append(lst)  # creates a circular reference
148
149
150
    with capture:
        del p, lst
        assert ConstructorStats.detail_reg_inst() == n_inst
151
152
153
    assert (
        capture
        == """
154
155
156
        Releasing parent.
        Releasing child.
    """
157
    )
158
159
160


def test_alive_gc_multi_derived(capture):
161
    class Derived(m.Parent, m.Child):
162
        def __init__(self):
163
164
            m.Parent.__init__(self)
            m.Child.__init__(self)
165
166
167

    n_inst = ConstructorStats.detail_reg_inst()
    p = Derived()
168
    p.addChildKeepAlive(m.Child())
169
170
171
    # +3 rather than +2 because Derived corresponds to two registered instances
    assert ConstructorStats.detail_reg_inst() == n_inst + 3
    lst = [p]
172
    lst.append(lst)  # creates a circular reference
173
174
175
    with capture:
        del p, lst
        assert ConstructorStats.detail_reg_inst() == n_inst
176
177
178
    assert (
        capture
        == """
Dean Moldovan's avatar
Dean Moldovan committed
179
180
        Releasing parent.
        Releasing child.
181
        Releasing child.
Dean Moldovan's avatar
Dean Moldovan committed
182
    """
183
    )
Dean Moldovan's avatar
Dean Moldovan committed
184
185
186


def test_return_none(capture):
187
    n_inst = ConstructorStats.detail_reg_inst()
Dean Moldovan's avatar
Dean Moldovan committed
188
    with capture:
189
        p = m.Parent()
Dean Moldovan's avatar
Dean Moldovan committed
190
191
192
    assert capture == "Allocating parent."
    with capture:
        p.returnNullChildKeepAliveChild()
193
        assert ConstructorStats.detail_reg_inst() == n_inst + 1
Dean Moldovan's avatar
Dean Moldovan committed
194
195
196
    assert capture == ""
    with capture:
        del p
197
        assert ConstructorStats.detail_reg_inst() == n_inst
Dean Moldovan's avatar
Dean Moldovan committed
198
199
200
    assert capture == "Releasing parent."

    with capture:
201
        p = m.Parent()
Dean Moldovan's avatar
Dean Moldovan committed
202
203
204
    assert capture == "Allocating parent."
    with capture:
        p.returnNullChildKeepAliveParent()
205
        assert ConstructorStats.detail_reg_inst() == n_inst + 1
Dean Moldovan's avatar
Dean Moldovan committed
206
207
208
    assert capture == ""
    with capture:
        del p
209
        assert ConstructorStats.detail_reg_inst() == n_inst
Dean Moldovan's avatar
Dean Moldovan committed
210
    assert capture == "Releasing parent."
Dean Moldovan's avatar
Dean Moldovan committed
211
212


213
214
215
216
217
218
def test_keep_alive_constructor(capture):
    n_inst = ConstructorStats.detail_reg_inst()

    with capture:
        p = m.Parent(m.Child())
        assert ConstructorStats.detail_reg_inst() == n_inst + 2
219
220
221
    assert (
        capture
        == """
222
223
224
        Allocating child.
        Allocating parent.
    """
225
    )
226
227
228
    with capture:
        del p
        assert ConstructorStats.detail_reg_inst() == n_inst
229
230
231
    assert (
        capture
        == """
232
233
234
        Releasing parent.
        Releasing child.
    """
235
    )
236
237


Dean Moldovan's avatar
Dean Moldovan committed
238
def test_call_guard():
239
240
    assert m.unguarded_call() == "unguarded"
    assert m.guarded_call() == "guarded"
Dean Moldovan's avatar
Dean Moldovan committed
241

242
243
    assert m.multiple_guards_correct_order() == "guarded & guarded"
    assert m.multiple_guards_wrong_order() == "unguarded & guarded"
Dean Moldovan's avatar
Dean Moldovan committed
244

245
246
247
    if hasattr(m, "with_gil"):
        assert m.with_gil() == "GIL held"
        assert m.without_gil() == "GIL released"