Skip to content

Commit c715c70

Browse files
jagermanwjakob
authored andcommitted
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 53e0aa0 commit c715c70

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
@@ -1802,6 +1802,10 @@ struct function_call {
18021802
/// The `convert` value the arguments should be loaded with
18031803
std::vector<bool> args_convert;
18041804

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

include/pybind11/pybind11.h

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

578578
// 4a. If we have a py::args argument, create a new tuple with leftovers
579-
tuple extra_args;
580579
if (func.has_args) {
580+
tuple extra_args;
581581
if (args_to_copy == 0) {
582582
// We didn't copy out any position arguments from the args_in tuple, so we
583583
// can reuse it directly without copying:
@@ -594,6 +594,7 @@ class cpp_function : public function {
594594
}
595595
call.args.push_back(extra_args);
596596
call.args_convert.push_back(false);
597+
call.args_ref = std::move(extra_args);
597598
}
598599

599600
// 4b. If we have a py::kwargs, pass on any remaining kwargs
@@ -602,6 +603,7 @@ class cpp_function : public function {
602603
kwargs = dict(); // If we didn't get one, send an empty one
603604
call.args.push_back(kwargs);
604605
call.args_convert.push_back(false);
606+
call.kwargs_ref = std::move(kwargs);
605607
}
606608

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

0 commit comments

Comments
 (0)