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
5

import env  # noqa: F401

6
7
from pybind11_tests import call_policies as m
from pybind11_tests import ConstructorStats
Dean Moldovan's avatar
Dean Moldovan committed
8
9


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

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

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

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

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

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

118

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


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

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


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

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


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

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


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


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

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

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