mirror of
https://github.com/pybind/pybind11.git
synced 2026-04-20 06:49:25 +00:00
Support keyword-only arguments
This adds support for a `py::args_kw_only()` annotation that can be
specified between `py::arg` annotations to indicate that any following
arguments are keyword-only. This allows you to write:
m.def("f", [](int a, int b) { /* ... */ },
py::arg("a"), py::args_kw_only(), py::arg("b"));
and have it work like Python 3's:
def f(a, *, b):
# ...
with respect to how `a` and `b` arguments are accepted (that is, `a` can
be positional or by keyword; `b` can only be specified by keyword).
This commit is contained in:
committed by
Wenzel Jakob
parent
03f9e4a8ec
commit
be0d804523
@@ -94,6 +94,30 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
|
||||
// m.def("bad_args6", [](py::args, py::args) {});
|
||||
// m.def("bad_args7", [](py::kwargs, py::kwargs) {});
|
||||
|
||||
// test_keyword_only_args
|
||||
m.def("kwonly_all", [](int i, int j) { return py::make_tuple(i, j); },
|
||||
py::args_kw_only(), py::arg("i"), py::arg("j"));
|
||||
m.def("kwonly_some", [](int i, int j, int k) { return py::make_tuple(i, j, k); },
|
||||
py::arg(), py::args_kw_only(), py::arg("j"), py::arg("k"));
|
||||
m.def("kwonly_with_defaults", [](int i, int j, int k, int z) { return py::make_tuple(i, j, k, z); },
|
||||
py::arg() = 3, "j"_a = 4, py::args_kw_only(), "k"_a = 5, "z"_a);
|
||||
m.def("kwonly_mixed", [](int i, int j) { return py::make_tuple(i, j); },
|
||||
"i"_a, py::args_kw_only(), "j"_a);
|
||||
m.def("kwonly_plus_more", [](int i, int j, int k, py::kwargs kwargs) {
|
||||
return py::make_tuple(i, j, k, kwargs); },
|
||||
py::arg() /* positional */, py::arg("j") = -1 /* both */, py::args_kw_only(), py::arg("k") /* kw-only */);
|
||||
|
||||
m.def("register_invalid_kwonly", [](py::module m) {
|
||||
m.def("bad_kwonly", [](int i, int j) { return py::make_tuple(i, j); },
|
||||
py::args_kw_only(), py::arg() /* invalid unnamed argument */, "j"_a);
|
||||
});
|
||||
|
||||
// These should fail to compile:
|
||||
// argument annotations are required when using args_kw_only
|
||||
// m.def("bad_kwonly1", [](int) {}, py::args_kw_only());
|
||||
// can't specify both `py::args_kw_only` and a `py::args` argument
|
||||
// m.def("bad_kwonly2", [](int i, py::args) {}, py::args_kw_only(), "i"_a);
|
||||
|
||||
// test_function_signatures (along with most of the above)
|
||||
struct KWClass { void foo(int, float) {} };
|
||||
py::class_<KWClass>(m, "KWClass")
|
||||
|
||||
@@ -107,6 +107,44 @@ def test_mixed_args_and_kwargs(msg):
|
||||
""" # noqa: E501 line too long
|
||||
|
||||
|
||||
def test_keyword_only_args(msg):
|
||||
assert m.kwonly_all(i=1, j=2) == (1, 2)
|
||||
assert m.kwonly_all(j=1, i=2) == (2, 1)
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
assert m.kwonly_all(i=1) == (1,)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
assert m.kwonly_all(1, 2) == (1, 2)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
assert m.kwonly_some(1, k=3, j=2) == (1, 2, 3)
|
||||
|
||||
assert m.kwonly_with_defaults(z=8) == (3, 4, 5, 8)
|
||||
assert m.kwonly_with_defaults(2, z=8) == (2, 4, 5, 8)
|
||||
assert m.kwonly_with_defaults(2, j=7, k=8, z=9) == (2, 7, 8, 9)
|
||||
assert m.kwonly_with_defaults(2, 7, z=9, k=8) == (2, 7, 8, 9)
|
||||
|
||||
assert m.kwonly_mixed(1, j=2) == (1, 2)
|
||||
assert m.kwonly_mixed(j=2, i=3) == (3, 2)
|
||||
assert m.kwonly_mixed(i=2, j=3) == (2, 3)
|
||||
|
||||
assert m.kwonly_plus_more(4, 5, k=6, extra=7) == (4, 5, 6, {'extra': 7})
|
||||
assert m.kwonly_plus_more(3, k=5, j=4, extra=6) == (3, 4, 5, {'extra': 6})
|
||||
assert m.kwonly_plus_more(2, k=3, extra=4) == (2, -1, 3, {'extra': 4})
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
assert m.kwonly_mixed(i=1) == (1,)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.register_invalid_kwonly(m)
|
||||
assert msg(excinfo.value) == """
|
||||
arg(): cannot specify an unnamed argument after an args_kw_only() annotation
|
||||
"""
|
||||
|
||||
|
||||
def test_args_refcount():
|
||||
"""Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular
|
||||
arguments"""
|
||||
|
||||
Reference in New Issue
Block a user