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