Skip to content

Commit bd3e1a7

Browse files
test_keep_alive: Show that keep_alive cycle escapes GC
1 parent 8a9aec7 commit bd3e1a7

File tree

3 files changed

+42
-0
lines changed

3 files changed

+42
-0
lines changed

tests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ set(PYBIND11_TEST_FILES
124124
# test_tagbased_polymorphic.cpp
125125
# test_union.cpp
126126
# test_virtual_functions.cpp)
127+
test_keep_alive.cpp
127128
)
128129
# cmake-format: on
129130

tests/test_keep_alive.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include "pybind11_tests.h"
2+
3+
class SimpleClass {};
4+
5+
TEST_SUBMODULE(keep_alive, m) {
6+
m.def("keep_alive_impl", [](py::handle nurse, py::handle patient) {
7+
py::detail::keep_alive_impl(nurse, patient);
8+
});
9+
10+
py::class_<SimpleClass>(m, "SimpleClass")
11+
.def(py::init());
12+
}

tests/test_keep_alive.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# -*- coding: utf-8 -*-
2+
# N.B. This is only focused on CPython, so using gc directly, rather than
3+
# `pytest.gc_collect()`.
4+
import gc
5+
import weakref
6+
7+
from pybind11_tests import keep_alive as m
8+
9+
10+
def test_keep_alive_cycle():
11+
# See #2761.
12+
o1 = m.SimpleClass()
13+
wr1 = weakref.ref(o1)
14+
o2 = m.SimpleClass()
15+
wr2 = weakref.ref(o2)
16+
assert wr1() is not None
17+
assert wr2() is not None
18+
19+
# Add a direct cycle.
20+
m.keep_alive_impl(o1, o2)
21+
m.keep_alive_impl(o2, o1)
22+
23+
del o1
24+
del o2
25+
gc.collect()
26+
27+
# This shows that py::keep_alive will leak objects :(
28+
assert wr1() is not None
29+
assert wr2() is not None

0 commit comments

Comments
 (0)