• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# -*- coding: utf-8 -*-
2import pytest
3
4import env  # noqa: F401
5
6from pybind11_tests import call_policies as m
7from pybind11_tests import ConstructorStats
8
9
10@pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False)
11def test_keep_alive_argument(capture):
12    n_inst = ConstructorStats.detail_reg_inst()
13    with capture:
14        p = m.Parent()
15    assert capture == "Allocating parent."
16    with capture:
17        p.addChild(m.Child())
18        assert ConstructorStats.detail_reg_inst() == n_inst + 1
19    assert (
20        capture
21        == """
22        Allocating child.
23        Releasing child.
24    """
25    )
26    with capture:
27        del p
28        assert ConstructorStats.detail_reg_inst() == n_inst
29    assert capture == "Releasing parent."
30
31    with capture:
32        p = m.Parent()
33    assert capture == "Allocating parent."
34    with capture:
35        p.addChildKeepAlive(m.Child())
36        assert ConstructorStats.detail_reg_inst() == n_inst + 2
37    assert capture == "Allocating child."
38    with capture:
39        del p
40        assert ConstructorStats.detail_reg_inst() == n_inst
41    assert (
42        capture
43        == """
44        Releasing parent.
45        Releasing child.
46    """
47    )
48
49
50def test_keep_alive_return_value(capture):
51    n_inst = ConstructorStats.detail_reg_inst()
52    with capture:
53        p = m.Parent()
54    assert capture == "Allocating parent."
55    with capture:
56        p.returnChild()
57        assert ConstructorStats.detail_reg_inst() == n_inst + 1
58    assert (
59        capture
60        == """
61        Allocating child.
62        Releasing child.
63    """
64    )
65    with capture:
66        del p
67        assert ConstructorStats.detail_reg_inst() == n_inst
68    assert capture == "Releasing parent."
69
70    with capture:
71        p = m.Parent()
72    assert capture == "Allocating parent."
73    with capture:
74        p.returnChildKeepAlive()
75        assert ConstructorStats.detail_reg_inst() == n_inst + 2
76    assert capture == "Allocating child."
77    with capture:
78        del p
79        assert ConstructorStats.detail_reg_inst() == n_inst
80    assert (
81        capture
82        == """
83        Releasing parent.
84        Releasing child.
85    """
86    )
87
88
89# https://foss.heptapod.net/pypy/pypy/-/issues/2447
90@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented")
91def test_alive_gc(capture):
92    n_inst = ConstructorStats.detail_reg_inst()
93    p = m.ParentGC()
94    p.addChildKeepAlive(m.Child())
95    assert ConstructorStats.detail_reg_inst() == n_inst + 2
96    lst = [p]
97    lst.append(lst)  # creates a circular reference
98    with capture:
99        del p, lst
100        assert ConstructorStats.detail_reg_inst() == n_inst
101    assert (
102        capture
103        == """
104        Releasing parent.
105        Releasing child.
106    """
107    )
108
109
110def test_alive_gc_derived(capture):
111    class Derived(m.Parent):
112        pass
113
114    n_inst = ConstructorStats.detail_reg_inst()
115    p = Derived()
116    p.addChildKeepAlive(m.Child())
117    assert ConstructorStats.detail_reg_inst() == n_inst + 2
118    lst = [p]
119    lst.append(lst)  # creates a circular reference
120    with capture:
121        del p, lst
122        assert ConstructorStats.detail_reg_inst() == n_inst
123    assert (
124        capture
125        == """
126        Releasing parent.
127        Releasing child.
128    """
129    )
130
131
132def test_alive_gc_multi_derived(capture):
133    class Derived(m.Parent, m.Child):
134        def __init__(self):
135            m.Parent.__init__(self)
136            m.Child.__init__(self)
137
138    n_inst = ConstructorStats.detail_reg_inst()
139    p = Derived()
140    p.addChildKeepAlive(m.Child())
141    # +3 rather than +2 because Derived corresponds to two registered instances
142    assert ConstructorStats.detail_reg_inst() == n_inst + 3
143    lst = [p]
144    lst.append(lst)  # creates a circular reference
145    with capture:
146        del p, lst
147        assert ConstructorStats.detail_reg_inst() == n_inst
148    assert (
149        capture
150        == """
151        Releasing parent.
152        Releasing child.
153        Releasing child.
154    """
155    )
156
157
158def test_return_none(capture):
159    n_inst = ConstructorStats.detail_reg_inst()
160    with capture:
161        p = m.Parent()
162    assert capture == "Allocating parent."
163    with capture:
164        p.returnNullChildKeepAliveChild()
165        assert ConstructorStats.detail_reg_inst() == n_inst + 1
166    assert capture == ""
167    with capture:
168        del p
169        assert ConstructorStats.detail_reg_inst() == n_inst
170    assert capture == "Releasing parent."
171
172    with capture:
173        p = m.Parent()
174    assert capture == "Allocating parent."
175    with capture:
176        p.returnNullChildKeepAliveParent()
177        assert ConstructorStats.detail_reg_inst() == n_inst + 1
178    assert capture == ""
179    with capture:
180        del p
181        assert ConstructorStats.detail_reg_inst() == n_inst
182    assert capture == "Releasing parent."
183
184
185def test_keep_alive_constructor(capture):
186    n_inst = ConstructorStats.detail_reg_inst()
187
188    with capture:
189        p = m.Parent(m.Child())
190        assert ConstructorStats.detail_reg_inst() == n_inst + 2
191    assert (
192        capture
193        == """
194        Allocating child.
195        Allocating parent.
196    """
197    )
198    with capture:
199        del p
200        assert ConstructorStats.detail_reg_inst() == n_inst
201    assert (
202        capture
203        == """
204        Releasing parent.
205        Releasing child.
206    """
207    )
208
209
210def test_call_guard():
211    assert m.unguarded_call() == "unguarded"
212    assert m.guarded_call() == "guarded"
213
214    assert m.multiple_guards_correct_order() == "guarded & guarded"
215    assert m.multiple_guards_wrong_order() == "unguarded & guarded"
216
217    if hasattr(m, "with_gil"):
218        assert m.with_gil() == "GIL held"
219        assert m.without_gil() == "GIL released"
220