test_call_policies.py 5.59 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


def test_keep_alive_return_value(capture):
51
    n_inst = ConstructorStats.detail_reg_inst()
Dean Moldovan's avatar
Dean Moldovan committed
52
    with capture:
53
        p = m.Parent()
Dean Moldovan's avatar
Dean Moldovan committed
54
55
56
    assert capture == "Allocating parent."
    with capture:
        p.returnChild()
57
        assert ConstructorStats.detail_reg_inst() == n_inst + 1
58
59
60
    assert (
        capture
        == """
Dean Moldovan's avatar
Dean Moldovan committed
61
62
63
        Allocating child.
        Releasing child.
    """
64
    )
Dean Moldovan's avatar
Dean Moldovan committed
65
66
    with capture:
        del p
67
        assert ConstructorStats.detail_reg_inst() == n_inst
Dean Moldovan's avatar
Dean Moldovan committed
68
69
70
    assert capture == "Releasing parent."

    with capture:
71
        p = m.Parent()
Dean Moldovan's avatar
Dean Moldovan committed
72
73
74
    assert capture == "Allocating parent."
    with capture:
        p.returnChildKeepAlive()
75
        assert ConstructorStats.detail_reg_inst() == n_inst + 2
Dean Moldovan's avatar
Dean Moldovan committed
76
77
78
    assert capture == "Allocating child."
    with capture:
        del p
79
        assert ConstructorStats.detail_reg_inst() == n_inst
80
81
82
    assert (
        capture
        == """
83
84
85
        Releasing parent.
        Releasing child.
    """
86
    )
87
88


89
90
# https://foss.heptapod.net/pypy/pypy/-/issues/2447
@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented")
91
92
def test_alive_gc(capture):
    n_inst = ConstructorStats.detail_reg_inst()
93
94
    p = m.ParentGC()
    p.addChildKeepAlive(m.Child())
95
96
    assert ConstructorStats.detail_reg_inst() == n_inst + 2
    lst = [p]
97
    lst.append(lst)  # creates a circular reference
98
99
100
    with capture:
        del p, lst
        assert ConstructorStats.detail_reg_inst() == n_inst
101
102
103
    assert (
        capture
        == """
104
105
106
        Releasing parent.
        Releasing child.
    """
107
    )
108
109
110


def test_alive_gc_derived(capture):
111
    class Derived(m.Parent):
112
113
114
115
        pass

    n_inst = ConstructorStats.detail_reg_inst()
    p = Derived()
116
    p.addChildKeepAlive(m.Child())
117
118
    assert ConstructorStats.detail_reg_inst() == n_inst + 2
    lst = [p]
119
    lst.append(lst)  # creates a circular reference
120
121
122
    with capture:
        del p, lst
        assert ConstructorStats.detail_reg_inst() == n_inst
123
124
125
    assert (
        capture
        == """
126
127
128
        Releasing parent.
        Releasing child.
    """
129
    )
130
131
132


def test_alive_gc_multi_derived(capture):
133
    class Derived(m.Parent, m.Child):
134
        def __init__(self):
135
136
            m.Parent.__init__(self)
            m.Child.__init__(self)
137
138
139

    n_inst = ConstructorStats.detail_reg_inst()
    p = Derived()
140
    p.addChildKeepAlive(m.Child())
141
142
143
    # +3 rather than +2 because Derived corresponds to two registered instances
    assert ConstructorStats.detail_reg_inst() == n_inst + 3
    lst = [p]
144
    lst.append(lst)  # creates a circular reference
145
146
147
    with capture:
        del p, lst
        assert ConstructorStats.detail_reg_inst() == n_inst
148
149
150
    assert (
        capture
        == """
Dean Moldovan's avatar
Dean Moldovan committed
151
152
        Releasing parent.
        Releasing child.
153
        Releasing child.
Dean Moldovan's avatar
Dean Moldovan committed
154
    """
155
    )
Dean Moldovan's avatar
Dean Moldovan committed
156
157
158


def test_return_none(capture):
159
    n_inst = ConstructorStats.detail_reg_inst()
Dean Moldovan's avatar
Dean Moldovan committed
160
    with capture:
161
        p = m.Parent()
Dean Moldovan's avatar
Dean Moldovan committed
162
163
164
    assert capture == "Allocating parent."
    with capture:
        p.returnNullChildKeepAliveChild()
165
        assert ConstructorStats.detail_reg_inst() == n_inst + 1
Dean Moldovan's avatar
Dean Moldovan committed
166
167
168
    assert capture == ""
    with capture:
        del p
169
        assert ConstructorStats.detail_reg_inst() == n_inst
Dean Moldovan's avatar
Dean Moldovan committed
170
171
172
    assert capture == "Releasing parent."

    with capture:
173
        p = m.Parent()
Dean Moldovan's avatar
Dean Moldovan committed
174
175
176
    assert capture == "Allocating parent."
    with capture:
        p.returnNullChildKeepAliveParent()
177
        assert ConstructorStats.detail_reg_inst() == n_inst + 1
Dean Moldovan's avatar
Dean Moldovan committed
178
179
180
    assert capture == ""
    with capture:
        del p
181
        assert ConstructorStats.detail_reg_inst() == n_inst
Dean Moldovan's avatar
Dean Moldovan committed
182
    assert capture == "Releasing parent."
Dean Moldovan's avatar
Dean Moldovan committed
183
184


185
186
187
188
189
190
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
191
192
193
    assert (
        capture
        == """
194
195
196
        Allocating child.
        Allocating parent.
    """
197
    )
198
199
200
    with capture:
        del p
        assert ConstructorStats.detail_reg_inst() == n_inst
201
202
203
    assert (
        capture
        == """
204
205
206
        Releasing parent.
        Releasing child.
    """
207
    )
208
209


Dean Moldovan's avatar
Dean Moldovan committed
210
def test_call_guard():
211
212
    assert m.unguarded_call() == "unguarded"
    assert m.guarded_call() == "guarded"
Dean Moldovan's avatar
Dean Moldovan committed
213

214
215
    assert m.multiple_guards_correct_order() == "guarded & guarded"
    assert m.multiple_guards_wrong_order() == "unguarded & guarded"
Dean Moldovan's avatar
Dean Moldovan committed
216

217
218
219
    if hasattr(m, "with_gil"):
        assert m.with_gil() == "GIL held"
        assert m.without_gil() == "GIL released"