Skip to content

Commit 7c306b1

Browse files
committed
Fix premature destruction of args/kwargs arguments
The `py::args` or `py::kwargs` arguments aren't properly referenced when added to the function_call arguments list: their reference counts drop to zero if the first (non-converting) function call fails, which means they might be cleaned up before the second pass call runs. This commit adds a couple of extra `object`s to the `function_call` where we can stash a reference to them when needed to tie their lifetime to the function_call object's lifetime. (Credit to YannickJadoul for catching and proposing a fix in #1223).
1 parent a303c6f commit 7c306b1

File tree

2 files changed

+7
-1
lines changed

2 files changed

+7
-1
lines changed

include/pybind11/cast.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,6 +1801,10 @@ struct function_call {
18011801
/// The `convert` value the arguments should be loaded with
18021802
std::vector<bool> args_convert;
18031803

1804+
/// Extra references for the optional `py::args` and/or `py::kwargs` arguments (which, if
1805+
/// present, are also in `args` but without a reference).
1806+
object args_ref, kwargs_ref;
1807+
18041808
/// The parent, if any
18051809
handle parent;
18061810

include/pybind11/pybind11.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,8 @@ class cpp_function : public function {
568568
continue; // Unconsumed kwargs, but no py::kwargs argument to accept them
569569

570570
// 4a. If we have a py::args argument, create a new tuple with leftovers
571-
tuple extra_args;
572571
if (func.has_args) {
572+
tuple extra_args;
573573
if (args_to_copy == 0) {
574574
// We didn't copy out any position arguments from the args_in tuple, so we
575575
// can reuse it directly without copying:
@@ -586,6 +586,7 @@ class cpp_function : public function {
586586
}
587587
call.args.push_back(extra_args);
588588
call.args_convert.push_back(false);
589+
call.args_ref = std::move(extra_args);
589590
}
590591

591592
// 4b. If we have a py::kwargs, pass on any remaining kwargs
@@ -594,6 +595,7 @@ class cpp_function : public function {
594595
kwargs = dict(); // If we didn't get one, send an empty one
595596
call.args.push_back(kwargs);
596597
call.args_convert.push_back(false);
598+
call.kwargs_ref = std::move(kwargs);
597599
}
598600

599601
// 5. Put everything in a vector. Not technically step 5, we've been building it

0 commit comments

Comments
 (0)