Skip to content

Commit f93156c

Browse files
committed
Address pull review feedback from @jagerman
- Clear has_patients in clear_instance - Handle multiple inheritence - Use ConstructorStats.detail_reg_inst to do deep garbage collection and verify the instance tracking counts
1 parent d225c7f commit f93156c

File tree

3 files changed

+51
-22
lines changed

3 files changed

+51
-22
lines changed

include/pybind11/class_support.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ inline void clear_instance(PyObject *self) {
314314
// from the unordered_map first.
315315
auto patients = std::move(pos->second);
316316
internals.patients.erase(pos);
317+
instance->has_patients = false;
317318
for (PyObject *&patient : patients)
318319
Py_CLEAR(patient);
319320
}

include/pybind11/pybind11.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,8 +1334,8 @@ inline void keep_alive_impl(handle nurse, handle patient) {
13341334
if (patient.is_none() || nurse.is_none())
13351335
return; /* Nothing to keep alive or nothing to be kept alive by */
13361336

1337-
auto tinfo = get_type_info(Py_TYPE(nurse.ptr()));
1338-
if (tinfo) {
1337+
auto tinfo = all_type_info(Py_TYPE(nurse.ptr()));
1338+
if (!tinfo.empty()) {
13391339
/* It's a pybind-registered type, so we can store the patient in the
13401340
* internal list. */
13411341
add_patient(nurse.ptr(), patient.ptr());

tests/test_call_policies.py

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,67 +2,69 @@
22

33

44
def test_keep_alive_argument(capture):
5-
from pybind11_tests import Parent, Child
5+
from pybind11_tests import Parent, Child, ConstructorStats
66

7+
n_inst = ConstructorStats.detail_reg_inst()
78
with capture:
89
p = Parent()
910
assert capture == "Allocating parent."
1011
with capture:
1112
p.addChild(Child())
12-
pytest.gc_collect()
13+
assert ConstructorStats.detail_reg_inst() == n_inst + 1
1314
assert capture == """
1415
Allocating child.
1516
Releasing child.
1617
"""
1718
with capture:
1819
del p
19-
pytest.gc_collect()
20+
assert ConstructorStats.detail_reg_inst() == n_inst
2021
assert capture == "Releasing parent."
2122

2223
with capture:
2324
p = Parent()
2425
assert capture == "Allocating parent."
2526
with capture:
2627
p.addChildKeepAlive(Child())
27-
pytest.gc_collect()
28+
assert ConstructorStats.detail_reg_inst() == n_inst + 2
2829
assert capture == "Allocating child."
2930
with capture:
3031
del p
31-
pytest.gc_collect()
32+
assert ConstructorStats.detail_reg_inst() == n_inst
3233
assert capture == """
3334
Releasing parent.
3435
Releasing child.
3536
"""
3637

3738

3839
def test_keep_alive_return_value(capture):
39-
from pybind11_tests import Parent
40+
from pybind11_tests import Parent, ConstructorStats
4041

42+
n_inst = ConstructorStats.detail_reg_inst()
4143
with capture:
4244
p = Parent()
4345
assert capture == "Allocating parent."
4446
with capture:
4547
p.returnChild()
46-
pytest.gc_collect()
48+
assert ConstructorStats.detail_reg_inst() == n_inst + 1
4749
assert capture == """
4850
Allocating child.
4951
Releasing child.
5052
"""
5153
with capture:
5254
del p
53-
pytest.gc_collect()
55+
assert ConstructorStats.detail_reg_inst() == n_inst
5456
assert capture == "Releasing parent."
5557

5658
with capture:
5759
p = Parent()
5860
assert capture == "Allocating parent."
5961
with capture:
6062
p.returnChildKeepAlive()
61-
pytest.gc_collect()
63+
assert ConstructorStats.detail_reg_inst() == n_inst + 2
6264
assert capture == "Allocating child."
6365
with capture:
6466
del p
65-
pytest.gc_collect()
67+
assert ConstructorStats.detail_reg_inst() == n_inst
6668
assert capture == """
6769
Releasing parent.
6870
Releasing child.
@@ -72,66 +74,92 @@ def test_keep_alive_return_value(capture):
7274
# https://bitbucket.org/pypy/pypy/issues/2447
7375
@pytest.unsupported_on_pypy
7476
def test_alive_gc(capture):
75-
from pybind11_tests import ParentGC, Child
77+
from pybind11_tests import ParentGC, Child, ConstructorStats
7678

79+
n_inst = ConstructorStats.detail_reg_inst()
7780
p = ParentGC()
7881
p.addChildKeepAlive(Child())
82+
assert ConstructorStats.detail_reg_inst() == n_inst + 2
7983
lst = [p]
8084
lst.append(lst) # creates a circular reference
8185
with capture:
8286
del p, lst
83-
pytest.gc_collect()
87+
assert ConstructorStats.detail_reg_inst() == n_inst
8488
assert capture == """
8589
Releasing parent.
8690
Releasing child.
8791
"""
8892

8993

9094
def test_alive_gc_derived(capture):
91-
from pybind11_tests import Parent, Child
95+
from pybind11_tests import Parent, Child, ConstructorStats
9296

9397
class Derived(Parent):
9498
pass
9599

100+
n_inst = ConstructorStats.detail_reg_inst()
96101
p = Derived()
97102
p.addChildKeepAlive(Child())
103+
assert ConstructorStats.detail_reg_inst() == n_inst + 2
98104
lst = [p]
99105
lst.append(lst) # creates a circular reference
100106
with capture:
101107
del p, lst
102-
pytest.gc_collect()
103-
pytest.gc_collect() # Needed to make PyPy free the child
108+
assert ConstructorStats.detail_reg_inst() == n_inst
109+
assert capture == """
110+
Releasing parent.
111+
Releasing child.
112+
"""
113+
114+
115+
def test_alive_gc_multi_derived(capture):
116+
from pybind11_tests import Parent, Child, ConstructorStats
117+
118+
class Derived(Parent, Child):
119+
pass
120+
121+
n_inst = ConstructorStats.detail_reg_inst()
122+
p = Derived()
123+
p.addChildKeepAlive(Child())
124+
# +3 rather than +2 because Derived corresponds to two registered instances
125+
assert ConstructorStats.detail_reg_inst() == n_inst + 3
126+
lst = [p]
127+
lst.append(lst) # creates a circular reference
128+
with capture:
129+
del p, lst
130+
assert ConstructorStats.detail_reg_inst() == n_inst
104131
assert capture == """
105132
Releasing parent.
106133
Releasing child.
107134
"""
108135

109136

110137
def test_return_none(capture):
111-
from pybind11_tests import Parent
138+
from pybind11_tests import Parent, ConstructorStats
112139

140+
n_inst = ConstructorStats.detail_reg_inst()
113141
with capture:
114142
p = Parent()
115143
assert capture == "Allocating parent."
116144
with capture:
117145
p.returnNullChildKeepAliveChild()
118-
pytest.gc_collect()
146+
assert ConstructorStats.detail_reg_inst() == n_inst + 1
119147
assert capture == ""
120148
with capture:
121149
del p
122-
pytest.gc_collect()
150+
assert ConstructorStats.detail_reg_inst() == n_inst
123151
assert capture == "Releasing parent."
124152

125153
with capture:
126154
p = Parent()
127155
assert capture == "Allocating parent."
128156
with capture:
129157
p.returnNullChildKeepAliveParent()
130-
pytest.gc_collect()
158+
assert ConstructorStats.detail_reg_inst() == n_inst + 1
131159
assert capture == ""
132160
with capture:
133161
del p
134-
pytest.gc_collect()
162+
assert ConstructorStats.detail_reg_inst() == n_inst
135163
assert capture == "Releasing parent."
136164

137165

0 commit comments

Comments
 (0)