Fix debugging output for nameless py::arg_v annotations (#648)

* Fix debugging output for nameless py::arg annotations

This fixes a couple bugs with nameless py::arg() (introduced in #634)
annotations:

- the argument name was being used in debug mode without checking that
  it exists (which would result in the std::string construction throwing
  an exception for being invoked with a nullptr)
- the error output says "keyword arguments", but py::arg_v() can now
  also be used for positional argument defaults.
- the debugging output "in function named 'blah'" was overly verbose:
  changed it to just "in function 'blah'".

* Fix missing space in debug test string

* Moved tests from issues to methods_and_attributes
This commit is contained in:
Jason Rhinelander 2017-02-08 02:45:51 -05:00 committed by Wenzel Jakob
parent 93cc4bd6d7
commit 1eaacd19f6
4 changed files with 48 additions and 6 deletions

View File

@ -303,19 +303,21 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
if (!a.value) { if (!a.value) {
#if !defined(NDEBUG) #if !defined(NDEBUG)
auto descr = "'" + std::string(a.name) + ": " + a.type + "'"; std::string descr("'");
if (a.name) descr += std::string(a.name) + ": ";
descr += a.type + "'";
if (r->is_method) { if (r->is_method) {
if (r->name) if (r->name)
descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'";
else else
descr += " in method of '" + (std::string) str(r->scope) + "'"; descr += " in method of '" + (std::string) str(r->scope) + "'";
} else if (r->name) { } else if (r->name) {
descr += " in function named '" + (std::string) r->name + "'"; descr += " in function '" + (std::string) r->name + "'";
} }
pybind11_fail("arg(): could not convert default keyword argument " pybind11_fail("arg(): could not convert default argument "
+ descr + " into a Python object (type not registered yet?)"); + descr + " into a Python object (type not registered yet?)");
#else #else
pybind11_fail("arg(): could not convert default keyword argument " pybind11_fail("arg(): could not convert default argument "
"into a Python object (type not registered yet?). " "into a Python object (type not registered yet?). "
"Compile in debug mode for more information."); "Compile in debug mode for more information.");
#endif #endif

View File

@ -74,7 +74,6 @@ namespace std {
template <> struct hash<TplConstrClass> { size_t operator()(const TplConstrClass &t) const { return std::hash<std::string>()(t.str); } }; template <> struct hash<TplConstrClass> { size_t operator()(const TplConstrClass &t) const { return std::hash<std::string>()(t.str); } };
} }
void init_issues(py::module &m) { void init_issues(py::module &m) {
py::module m2 = m.def_submodule("issues"); py::module m2 = m.def_submodule("issues");
@ -397,5 +396,5 @@ void init_issues(py::module &m) {
#endif #endif
} }
// MSVC workaround: trying to use a lambda here crashes MSCV // MSVC workaround: trying to use a lambda here crashes MSVC
test_initializer issues(&init_issues); test_initializer issues(&init_issues);

View File

@ -150,6 +150,9 @@ public:
}; };
}} }}
/// Issue/PR #648: bad arg default debugging output
class NotRegistered {};
test_initializer methods_and_attributes([](py::module &m) { test_initializer methods_and_attributes([](py::module &m) {
py::class_<ExampleMandA>(m, "ExampleMandA") py::class_<ExampleMandA>(m, "ExampleMandA")
.def(py::init<>()) .def(py::init<>())
@ -270,4 +273,18 @@ test_initializer methods_and_attributes([](py::module &m) {
m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f")); m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f"));
m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert()); m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
/// Issue/PR #648: bad arg default debugging output
#if !defined(NDEBUG)
m.attr("debug_enabled") = true;
#else
m.attr("debug_enabled") = false;
#endif
m.def("bad_arg_def_named", []{
auto m = py::module::import("pybind11_tests.issues");
m.def("should_fail", [](int, NotRegistered) {}, py::arg(), py::arg("a") = NotRegistered());
});
m.def("bad_arg_def_unnamed", []{
auto m = py::module::import("pybind11_tests.issues");
m.def("should_fail", [](int, NotRegistered) {}, py::arg(), py::arg() = NotRegistered());
});
}); });

View File

@ -255,3 +255,27 @@ def test_noconvert_args(msg):
Invoked with: 4 Invoked with: 4
""" """
def test_bad_arg_default(msg):
from pybind11_tests import debug_enabled, bad_arg_def_named, bad_arg_def_unnamed
with pytest.raises(RuntimeError) as excinfo:
bad_arg_def_named()
assert msg(excinfo.value) == (
"arg(): could not convert default argument 'a: NotRegistered' in function 'should_fail' "
"into a Python object (type not registered yet?)"
if debug_enabled else
"arg(): could not convert default argument into a Python object (type not registered "
"yet?). Compile in debug mode for more information."
)
with pytest.raises(RuntimeError) as excinfo:
bad_arg_def_unnamed()
assert msg(excinfo.value) == (
"arg(): could not convert default argument 'NotRegistered' in function 'should_fail' "
"into a Python object (type not registered yet?)"
if debug_enabled else
"arg(): could not convert default argument into a Python object (type not registered "
"yet?). Compile in debug mode for more information."
)