From 6906b270d6fadfa22db4e15b9e59bf752ed43bd4 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Wed, 5 Apr 2017 10:51:02 -0400 Subject: [PATCH] Improve make_tuple error message under debugging When make_tuple fails (for example, when print() is called with a non-convertible argument, as in #778) the error message a less helpful than it could be: make_tuple(): unable to convert arguments of types 'std::tuple' to Python object There is no actual std::tuple involved (only a parameter pack and a Python tuple), but it also doesn't immediately reveal which type caused the problem. This commit changes the debugging mode output to show just the problematic type: make_tuple(): unable to convert argument of type 'type2' to Python object --- include/pybind11/cast.h | 11 ++++++----- tests/test_python_types.cpp | 8 ++++++++ tests/test_python_types.py | 10 +++++++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 0beaf6e48..c77dfd0c5 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1246,18 +1246,19 @@ NAMESPACE_END(detail) template tuple make_tuple(Args&&... args_) { - const size_t size = sizeof...(Args); + constexpr size_t size = sizeof...(Args); std::array args { { reinterpret_steal(detail::make_caster::cast( std::forward(args_), policy, nullptr))... } }; - for (auto &arg_value : args) { - if (!arg_value) { + for (size_t i = 0; i < args.size(); i++) { + if (!args[i]) { #if defined(NDEBUG) throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); #else - throw cast_error("make_tuple(): unable to convert arguments of types '" + - (std::string) type_id>() + "' to Python object"); + std::array argtypes { type_id()... }; + throw cast_error("make_tuple(): unable to convert argument of type '" + + argtypes[i] + "' to Python object"); #endif } } diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp index 5696239b4..6f2080994 100644 --- a/tests/test_python_types.cpp +++ b/tests/test_python_types.cpp @@ -186,6 +186,7 @@ struct MoveOutContainer { std::list move_list() const { return {{0}, {1}, {2}}; } }; +struct UnregisteredType { }; test_initializer python_types([](py::module &m) { /* No constructor is explicitly defined below. An exception is raised when @@ -235,6 +236,13 @@ test_initializer python_types([](py::module &m) { py::print("{a} + {b} = {c}"_s.format("a"_a="py::print", "b"_a="str.format", "c"_a="this")); }); + m.def("test_print_failure", []() { py::print(42, UnregisteredType()); }); +#if !defined(NDEBUG) + m.attr("debug_enabled") = true; +#else + m.attr("debug_enabled") = false; +#endif + m.def("test_str_format", []() { auto s1 = "{} + {} = {}"_s.format(1, 2, 3); auto s2 = "{a} + {b} = {c}"_s.format("a"_a=1, "b"_a=2, "c"_a=3); diff --git a/tests/test_python_types.py b/tests/test_python_types.py index 7956c7cc6..cf8c1476b 100644 --- a/tests/test_python_types.py +++ b/tests/test_python_types.py @@ -258,7 +258,7 @@ def test_module(): def test_print(capture): - from pybind11_tests import test_print_function + from pybind11_tests import test_print_function, test_print_failure, debug_enabled with capture: test_print_function() @@ -272,6 +272,14 @@ def test_print(capture): """ assert capture.stderr == "this goes to stderr" + with pytest.raises(RuntimeError) as excinfo: + test_print_failure() + assert str(excinfo.value) == "make_tuple(): unable to convert " + ( + "argument of type 'UnregisteredType' to Python object" + if debug_enabled else + "arguments to Python object (compile in debug mode for details)" + ) + def test_str_api(): from pybind11_tests import test_str_format