test_call_policies.py 6.42 KB
Newer Older
1
# -*- coding: utf-8 -*-
Wenzel Jakob's avatar
Wenzel Jakob committed
2
import pytest
3
4

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


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

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

48
49
50
51
52
53
54
55
56
57
58
59
60
    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
61
62

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

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

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
    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.
    """
    )

117

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


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

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


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

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


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

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


214
215
216
217
218
219
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
220
221
222
    assert (
        capture
        == """
223
224
225
        Allocating child.
        Allocating parent.
    """
226
    )
227
228
229
    with capture:
        del p
        assert ConstructorStats.detail_reg_inst() == n_inst
230
231
232
    assert (
        capture
        == """
233
234
235
        Releasing parent.
        Releasing child.
    """
236
    )
237
238


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

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

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