Skip to content

Commit be221b9

Browse files
committed
Fix debugging output for nameless py::arg annotations
This fixes a couple bugs with nameless py::arg() (introduced in pybind#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'".
1 parent bc6a24d commit be221b9

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

include/pybind11/attr.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,19 +303,21 @@ template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
303303

304304
if (!a.value) {
305305
#if !defined(NDEBUG)
306-
auto descr = "'" + std::string(a.name ? a.name : "arg") + ": " + a.type + "'";
306+
std::string descr("'");
307+
if (a.name) descr += std::string(a.name) + ": ";
308+
descr += a.type + "'";
307309
if (r->is_method) {
308310
if (r->name)
309311
descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'";
310312
else
311313
descr += " in method of '" + (std::string) str(r->scope) + "'";
312314
} else if (r->name) {
313-
descr += " in function named '" + (std::string) r->name + "'";
315+
descr += " in function '" + (std::string) r->name + "'";
314316
}
315-
pybind11_fail("arg(): could not convert default keyword argument "
317+
pybind11_fail("arg(): could not convert default argument "
316318
+ descr + " into a Python object (type not registered yet?)");
317319
#else
318-
pybind11_fail("arg(): could not convert default keyword argument "
320+
pybind11_fail("arg(): could not convert default argument "
319321
"into a Python object (type not registered yet?). "
320322
"Compile in debug mode for more information.");
321323
#endif

tests/test_issues.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ namespace std {
7474
template <> struct hash<TplConstrClass> { size_t operator()(const TplConstrClass &t) const { return std::hash<std::string>()(t.str); } };
7575
}
7676

77+
/// Issue/PR #648: bad arg default debugging output
78+
class NotRegistered {};
7779

7880
void init_issues(py::module &m) {
7981
py::module m2 = m.def_submodule("issues");
@@ -395,7 +397,22 @@ void init_issues(py::module &m) {
395397
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
396398
m2.def("tpl_constr_optional", [](std::experimental::optional<TplConstrClass> &) {});
397399
#endif
400+
401+
/// Issue/PR #648: bad arg default debugging output
402+
#if !defined(NDEBUG)
403+
m2.attr("debug_enabled") = true;
404+
#else
405+
m2.attr("debug_enabled") = false;
406+
#endif
407+
m2.def("bad_arg_def_named", []{
408+
auto m = py::module::import("pybind11_tests.issues");
409+
m.def("should_fail", [](int, NotRegistered) {}, py::arg(), py::arg("a") = NotRegistered());
410+
});
411+
m2.def("bad_arg_def_unnamed", []{
412+
auto m = py::module::import("pybind11_tests.issues");
413+
m.def("should_fail", [](int, NotRegistered) {}, py::arg(), py::arg() = NotRegistered());
414+
});
398415
}
399416

400-
// MSVC workaround: trying to use a lambda here crashes MSCV
417+
// MSVC workaround: trying to use a lambda here crashes MSVC
401418
test_initializer issues(&init_issues);

tests/test_issues.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,3 +249,27 @@ def test_inheritance_override_def_static():
249249
assert isinstance(b, MyBase)
250250
assert isinstance(d1, MyDerived)
251251
assert isinstance(d2, MyDerived)
252+
253+
254+
def test_bad_arg_default(msg):
255+
from pybind11_tests.issues import debug_enabled, bad_arg_def_named, bad_arg_def_unnamed
256+
257+
with pytest.raises(RuntimeError) as excinfo:
258+
bad_arg_def_named()
259+
assert msg(excinfo.value) == (
260+
"arg(): could not convert default argument 'a: NotRegistered' in function 'should_fail'"
261+
"into a Python object (type not registered yet?)"
262+
if debug_enabled else
263+
"arg(): could not convert default argument into a Python object (type not registered "
264+
"yet?). Compile in debug mode for more information."
265+
)
266+
267+
with pytest.raises(RuntimeError) as excinfo:
268+
bad_arg_def_unnamed()
269+
assert msg(excinfo.value) == (
270+
"arg(): could not convert default argument 'NotRegistered' in function 'should_fail'"
271+
"into a Python object (type not registered yet?)"
272+
if debug_enabled else
273+
"arg(): could not convert default argument into a Python object (type not registered "
274+
"yet?). Compile in debug mode for more information."
275+
)

0 commit comments

Comments
 (0)