Show kwargs in failed method invocation

With the previous commit, output can be very confusing because you only
see positional arguments in the "invoked with" line, but you can have a
failure from kwargs as well (in particular, when a value is invalidly
specified via both via positional and kwargs).  This commits adds
kwargs to the output, and updates the associated tests to match.
This commit is contained in:
Jason Rhinelander 2017-02-23 21:04:46 -05:00 committed by Wenzel Jakob
parent caa1379e92
commit 231e167854
2 changed files with 32 additions and 15 deletions

View File

@ -650,11 +650,26 @@ protected:
} }
msg += "\nInvoked with: "; msg += "\nInvoked with: ";
auto args_ = reinterpret_borrow<tuple>(args_in); auto args_ = reinterpret_borrow<tuple>(args_in);
bool some_args = false;
for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { 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]); msg += pybind11::repr(args_[ti]);
if ((ti + 1) != args_.size() )
msg += ", ";
} }
if (kwargs_in) {
auto kwargs = reinterpret_borrow<dict>(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()); PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr; return nullptr;
} else if (!result) { } else if (!result) {

View File

@ -34,12 +34,8 @@ def test_named_arguments(msg):
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
# noinspection PyArgumentList # noinspection PyArgumentList
kw_func2(x=5, y=10, z=12) kw_func2(x=5, y=10, z=12)
assert msg(excinfo.value) == """ assert excinfo.match(
kw_func2(): incompatible function arguments. The following argument types are supported: r'(?s)^kw_func2\(\): incompatible.*Invoked with: kwargs: ((x=5|y=10|z=12)(, |$)){3}$')
1. (x: int=100, y: int=200) -> str
Invoked with:
"""
assert kw_func4() == "{13 17}" assert kw_func4() == "{13 17}"
assert kw_func4(myList=[1, 2, 3]) == "{1 2 3}" 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 1. (arg0: int, arg1: float, *args) -> tuple
Invoked with: 1 Invoked with: 1
""" # noqa: E501 """ # noqa: E501 line too long
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
assert mpa() assert mpa()
assert msg(excinfo.value) == """ assert msg(excinfo.value) == """
@ -82,7 +78,7 @@ def test_mixed_args_and_kwargs(msg):
1. (arg0: int, arg1: float, *args) -> tuple 1. (arg0: int, arg1: float, *args) -> tuple
Invoked with: 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 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) == ( 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(k=42) == (1, 3.14159, (), {'k': 42})
assert mpakd(1, 1, 2, 3, 5, 8, then=13, followedby=21) == ( assert mpakd(1, 1, 2, 3, 5, 8, then=13, followedby=21) == (
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: with pytest.raises(TypeError) as excinfo:
assert mpakd(1, i=1) assert mpakd(1, i=1)
assert msg(excinfo.value) == """ 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: with pytest.raises(TypeError) as excinfo:
assert mpakd(1, 2, j=1) assert mpakd(1, 2, j=1)
assert msg(excinfo.value) == """ 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