Skip to content

Commit 8cf1cdb

Browse files
Incomplete attempt to address regression introduced in #5381
1 parent 7e418f4 commit 8cf1cdb

File tree

3 files changed

+60
-2
lines changed

3 files changed

+60
-2
lines changed

include/pybind11/cast.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,9 +1570,9 @@ class argument_loader {
15701570
using indices = make_index_sequence<sizeof...(Args)>;
15711571

15721572
template <typename Arg>
1573-
using argument_is_args = std::is_base_of<args, intrinsic_t<Arg>>;
1573+
using argument_is_args = all_of<std::is_base_of<args, intrinsic_t<Arg>>, negation<std::is_pointer<Arg>>>;
15741574
template <typename Arg>
1575-
using argument_is_kwargs = std::is_base_of<kwargs, intrinsic_t<Arg>>;
1575+
using argument_is_kwargs = all_of<std::is_base_of<kwargs, intrinsic_t<Arg>>, negation<std::is_pointer<Arg>>>;
15761576
// Get kwargs argument position, or -1 if not present:
15771577
static constexpr auto kwargs_pos = constexpr_last<argument_is_kwargs, Args...>();
15781578

tests/test_kwargs_and_defaults.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,33 @@ class ArgsSubclass : public py::args {
2121
class KWArgsSubclass : public py::kwargs {
2222
using py::kwargs::kwargs;
2323
};
24+
class MoveOrCopyInt {
25+
public:
26+
MoveOrCopyInt() { print_default_created(this); }
27+
explicit MoveOrCopyInt(int v) : value{v} { print_created(this, value); }
28+
MoveOrCopyInt(MoveOrCopyInt &&m) noexcept {
29+
print_move_created(this, m.value);
30+
std::swap(value, m.value);
31+
}
32+
MoveOrCopyInt &operator=(MoveOrCopyInt &&m) noexcept {
33+
print_move_assigned(this, m.value);
34+
std::swap(value, m.value);
35+
return *this;
36+
}
37+
MoveOrCopyInt(const MoveOrCopyInt &c) {
38+
print_copy_created(this, c.value);
39+
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
40+
value = c.value;
41+
}
42+
MoveOrCopyInt &operator=(const MoveOrCopyInt &c) {
43+
print_copy_assigned(this, c.value);
44+
value = c.value;
45+
return *this;
46+
}
47+
~MoveOrCopyInt() { print_destroyed(this); }
48+
49+
int value;
50+
};
2451
namespace pybind11 {
2552
namespace detail {
2653
template <>
@@ -31,6 +58,19 @@ template <>
3158
struct handle_type_name<KWArgsSubclass> {
3259
static constexpr auto name = const_name("**KWArgs");
3360
};
61+
template <>
62+
struct type_caster<MoveOrCopyInt> {
63+
PYBIND11_TYPE_CASTER(MoveOrCopyInt*, const_name("MoveOrCopyInt"));
64+
bool load(handle src, bool) {
65+
auto as_class = MoveOrCopyInt(src.cast<int>());
66+
value = &as_class;
67+
return true;
68+
}
69+
static handle cast(int v, return_value_policy r, handle p) {
70+
auto as_class = MoveOrCopyInt(v);
71+
return pybind11::handle(as_class, r, p);
72+
}
73+
};
3474
} // namespace detail
3575
} // namespace pybind11
3676

@@ -348,4 +388,10 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
348388
[](const ArgsSubclass &args, const KWArgsSubclass &kwargs) {
349389
return py::make_tuple(args, kwargs);
350390
});
391+
392+
// Test that support for args and kwargs subclasses skips checking arguments passed in as pointers
393+
m.def("args_kwargs_subclass_function_with_pointer_arg",
394+
[](MoveOrCopyInt* pointer, const ArgsSubclass &args, const KWArgsSubclass &kwargs) {
395+
return py::make_tuple(pointer->value, args, kwargs);
396+
});
351397
}

tests/test_kwargs_and_defaults.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ def test_function_signatures(doc):
2121
doc(m.args_kwargs_subclass_function)
2222
== "args_kwargs_subclass_function(*Args, **KWArgs) -> tuple"
2323
)
24+
assert (
25+
doc(m.args_kwargs_subclass_function_with_pointer_arg)
26+
== "args_kwargs_subclass_function_with_pointer_arg(arg0: NotArgsOrKWArgsClass, *Args, **KWArgs) -> tuple"
27+
)
2428
assert (
2529
doc(m.KWClass.foo0)
2630
== "foo0(self: m.kwargs_and_defaults.KWClass, arg0: int, arg1: float) -> None"
@@ -103,6 +107,7 @@ def test_arg_and_kwargs():
103107
kwargs = {"arg3": "a3", "arg4": 4}
104108
assert m.args_kwargs_function(*args, **kwargs) == (args, kwargs)
105109
assert m.args_kwargs_subclass_function(*args, **kwargs) == (args, kwargs)
110+
assert m.args_kwargs_subclass_function_with_pointer_arg(10, *args, **kwargs) == (10, args, kwargs)
106111

107112

108113
def test_mixed_args_and_kwargs(msg):
@@ -424,6 +429,13 @@ def test_args_refcount():
424429
)
425430
assert refcount(myval) == expected
426431

432+
assert m.args_kwargs_subclass_function_with_pointer_arg(7, 8, myval, a=1, b=myval) == (
433+
7,
434+
(8, myval),
435+
{"a": 1, "b": myval},
436+
)
437+
assert refcount(myval) == expected
438+
427439
exp3 = refcount(myval, myval, myval)
428440
assert m.args_refcount(myval, myval, myval) == (exp3, exp3, exp3)
429441
assert refcount(myval) == expected

0 commit comments

Comments
 (0)