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