Skip to content

Commit b6ec0e9

Browse files
functions: Add doc on incorrect argument index (#2979)
test_call_policies: Explicitly check free-functions and static methods
1 parent 417fd12 commit b6ec0e9

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed

docs/advanced/functions.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ relies on the ability to create a *weak reference* to the nurse object. When
182182
the nurse object is not a pybind11-registered type and does not support weak
183183
references, an exception will be thrown.
184184

185+
If you use an incorrect argument index, you will get a ``RuntimeError`` saying
186+
``Could not activate keep_alive!``. You should review the indices you're using.
187+
185188
Consider the following example: here, the binding code for a list append
186189
operation ties the lifetime of the newly added element to the underlying
187190
container:

tests/test_call_policies.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ TEST_SUBMODULE(call_policies, m) {
5151
void addChild(Child *) { }
5252
Child *returnChild() { return new Child(); }
5353
Child *returnNullChild() { return nullptr; }
54+
static Child *staticFunction(Parent*) { return new Child(); }
5455
};
5556
py::class_<Parent>(m, "Parent")
5657
.def(py::init<>())
@@ -60,7 +61,12 @@ TEST_SUBMODULE(call_policies, m) {
6061
.def("returnChild", &Parent::returnChild)
6162
.def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>())
6263
.def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>())
63-
.def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>());
64+
.def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>())
65+
.def_static(
66+
"staticFunction", &Parent::staticFunction, py::keep_alive<1, 0>());
67+
68+
m.def("free_function", [](Parent*, Child*) {}, py::keep_alive<1, 2>());
69+
m.def("invalid_arg_index", []{}, py::keep_alive<0, 1>());
6470

6571
#if !defined(PYPY_VERSION)
6672
// test_alive_gc

tests/test_call_policies.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ def test_keep_alive_argument(capture):
4646
"""
4747
)
4848

49+
p = m.Parent()
50+
c = m.Child()
51+
assert ConstructorStats.detail_reg_inst() == n_inst + 2
52+
m.free_function(p, c)
53+
del c
54+
assert ConstructorStats.detail_reg_inst() == n_inst + 2
55+
del p
56+
assert ConstructorStats.detail_reg_inst() == n_inst
57+
58+
with pytest.raises(RuntimeError) as excinfo:
59+
m.invalid_arg_index()
60+
assert str(excinfo.value) == "Could not activate keep_alive!"
61+
4962

5063
def test_keep_alive_return_value(capture):
5164
n_inst = ConstructorStats.detail_reg_inst()
@@ -85,6 +98,23 @@ def test_keep_alive_return_value(capture):
8598
"""
8699
)
87100

101+
p = m.Parent()
102+
assert ConstructorStats.detail_reg_inst() == n_inst + 1
103+
with capture:
104+
m.Parent.staticFunction(p)
105+
assert ConstructorStats.detail_reg_inst() == n_inst + 2
106+
assert capture == "Allocating child."
107+
with capture:
108+
del p
109+
assert ConstructorStats.detail_reg_inst() == n_inst
110+
assert (
111+
capture
112+
== """
113+
Releasing parent.
114+
Releasing child.
115+
"""
116+
)
117+
88118

89119
# https://foss.heptapod.net/pypy/pypy/-/issues/2447
90120
@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented")

0 commit comments

Comments
 (0)