diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 74c3138c7..ec3a5b0be 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -650,11 +650,26 @@ protected: } msg += "\nInvoked with: "; auto args_ = reinterpret_borrow(args_in); + bool some_args = false; for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { + if (!some_args) some_args = true; + else msg += ", "; msg += pybind11::repr(args_[ti]); - if ((ti + 1) != args_.size() ) - msg += ", "; } + if (kwargs_in) { + auto kwargs = reinterpret_borrow(kwargs_in); + if (kwargs.size() > 0) { + if (some_args) msg += "; "; + msg += "kwargs: "; + bool first = true; + for (auto kwarg : kwargs) { + if (first) first = false; + else msg += ", "; + msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second); + } + } + } + PyErr_SetString(PyExc_TypeError, msg.c_str()); return nullptr; } else if (!result) { diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index d1777a4ee..e3cb0f6bc 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -34,12 +34,8 @@ def test_named_arguments(msg): with pytest.raises(TypeError) as excinfo: # noinspection PyArgumentList kw_func2(x=5, y=10, z=12) - assert msg(excinfo.value) == """ - kw_func2(): incompatible function arguments. The following argument types are supported: - 1. (x: int=100, y: int=200) -> str - - Invoked with: - """ + assert excinfo.match( + r'(?s)^kw_func2\(\): incompatible.*Invoked with: kwargs: ((x=5|y=10|z=12)(, |$)){3}$') assert kw_func4() == "{13 17}" assert kw_func4(myList=[1, 2, 3]) == "{1 2 3}" @@ -74,7 +70,7 @@ def test_mixed_args_and_kwargs(msg): 1. (arg0: int, arg1: float, *args) -> tuple Invoked with: 1 - """ # noqa: E501 + """ # noqa: E501 line too long with pytest.raises(TypeError) as excinfo: assert mpa() assert msg(excinfo.value) == """ @@ -82,7 +78,7 @@ def test_mixed_args_and_kwargs(msg): 1. (arg0: int, arg1: float, *args) -> tuple Invoked with: - """ # noqa: E501 + """ # noqa: E501 line too long assert mpk(-2, 3.5, pi=3.14159, e=2.71828) == (-2, 3.5, {'e': 2.71828, 'pi': 3.14159}) assert mpak(7, 7.7, 7.77, 7.777, 7.7777, minusseven=-7) == ( @@ -93,14 +89,20 @@ def test_mixed_args_and_kwargs(msg): assert mpakd(k=42) == (1, 3.14159, (), {'k': 42}) assert mpakd(1, 1, 2, 3, 5, 8, then=13, followedby=21) == ( 1, 1, (2, 3, 5, 8), {'then': 13, 'followedby': 21}) - # Arguments specified both positionally and via kwargs is an error: + # Arguments specified both positionally and via kwargs should fail: with pytest.raises(TypeError) as excinfo: assert mpakd(1, i=1) assert msg(excinfo.value) == """ - mixed_plus_args_kwargs_defaults(): got multiple values for argument 'i' - """ + mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported: + 1. (i: int=1, j: float=3.14159, *args, **kwargs) -> tuple + + Invoked with: 1; kwargs: i=1 + """ # noqa: E501 line too long with pytest.raises(TypeError) as excinfo: assert mpakd(1, 2, j=1) assert msg(excinfo.value) == """ - mixed_plus_args_kwargs_defaults(): got multiple values for argument 'j' - """ + mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported: + 1. (i: int=1, j: float=3.14159, *args, **kwargs) -> tuple + + Invoked with: 1, 2; kwargs: j=1 + """ # noqa: E501 line too long