Skip to content

Commit f0b9f75

Browse files
authored
Replace error printing code gated by NDEBUG with a new flag: PYBIND11_DETAILED_ERROR_MESSAGES (#3913)
* Update cast.h * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Move definition to detail/common, change name, apply everywhere * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rename debug_enabled in tests to detailed_error_messages_enabled
1 parent 75007dd commit f0b9f75

File tree

10 files changed

+82
-66
lines changed

10 files changed

+82
-66
lines changed

include/pybind11/attr.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#pragma once
1212

13+
#include "detail/common.h"
1314
#include "cast.h"
1415

1516
#include <functional>
@@ -477,7 +478,7 @@ struct process_attribute<arg_v> : process_attribute_default<arg_v> {
477478
}
478479

479480
if (!a.value) {
480-
#if !defined(NDEBUG)
481+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
481482
std::string descr("'");
482483
if (a.name) {
483484
descr += std::string(a.name) + ": ";
@@ -498,7 +499,8 @@ struct process_attribute<arg_v> : process_attribute_default<arg_v> {
498499
#else
499500
pybind11_fail("arg(): could not convert default argument "
500501
"into a Python object (type not registered yet?). "
501-
"Compile in debug mode for more information.");
502+
"#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for "
503+
"more information.");
502504
#endif
503505
}
504506
r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none);

include/pybind11/cast.h

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -777,8 +777,9 @@ struct copyable_holder_caster : public type_caster_base<type> {
777777
return true;
778778
}
779779
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
780-
#if defined(NDEBUG)
781-
"(compile in debug mode for type information)");
780+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
781+
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for "
782+
"type information)");
782783
#else
783784
"of type '"
784785
+ type_id<holder_type>() + "''");
@@ -1001,9 +1002,9 @@ struct return_value_policy_override<
10011002
template <typename T, typename SFINAE>
10021003
type_caster<T, SFINAE> &load_type(type_caster<T, SFINAE> &conv, const handle &handle) {
10031004
if (!conv.load(handle, true)) {
1004-
#if defined(NDEBUG)
1005-
throw cast_error(
1006-
"Unable to cast Python instance to C++ type (compile in debug mode for details)");
1005+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
1006+
throw cast_error("Unable to cast Python instance to C++ type (#define "
1007+
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
10071008
#else
10081009
throw cast_error("Unable to cast Python instance of type "
10091010
+ (std::string) str(type::handle_of(handle)) + " to C++ type '"
@@ -1068,10 +1069,10 @@ inline void handle::cast() const {
10681069
template <typename T>
10691070
detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
10701071
if (obj.ref_count() > 1) {
1071-
#if defined(NDEBUG)
1072+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
10721073
throw cast_error(
10731074
"Unable to cast Python instance to C++ rvalue: instance has multiple references"
1074-
" (compile in debug mode for details)");
1075+
" (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
10751076
#else
10761077
throw cast_error("Unable to move from Python " + (std::string) str(type::handle_of(obj))
10771078
+ " instance to C++ " + type_id<T>()
@@ -1172,10 +1173,10 @@ PYBIND11_NAMESPACE_END(detail)
11721173

11731174
// The overloads could coexist, i.e. the #if is not strictly speaking needed,
11741175
// but it is an easy minor optimization.
1175-
#if defined(NDEBUG)
1176+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
11761177
inline cast_error cast_error_unable_to_convert_call_arg() {
1177-
return cast_error(
1178-
"Unable to convert call argument to Python object (compile in debug mode for details)");
1178+
return cast_error("Unable to convert call argument to Python object (#define "
1179+
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
11791180
}
11801181
#else
11811182
inline cast_error cast_error_unable_to_convert_call_arg(const std::string &name,
@@ -1197,7 +1198,7 @@ tuple make_tuple(Args &&...args_) {
11971198
detail::make_caster<Args>::cast(std::forward<Args>(args_), policy, nullptr))...}};
11981199
for (size_t i = 0; i < args.size(); i++) {
11991200
if (!args[i]) {
1200-
#if defined(NDEBUG)
1201+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
12011202
throw cast_error_unable_to_convert_call_arg();
12021203
#else
12031204
std::array<std::string, size> argtypes{{type_id<Args>()...}};
@@ -1249,7 +1250,7 @@ struct arg_v : arg {
12491250
: arg(base), value(reinterpret_steal<object>(detail::make_caster<T>::cast(
12501251
std::forward<T>(x), return_value_policy::automatic, {}))),
12511252
descr(descr)
1252-
#if !defined(NDEBUG)
1253+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
12531254
,
12541255
type(type_id<T>())
12551256
#endif
@@ -1289,7 +1290,7 @@ struct arg_v : arg {
12891290
object value;
12901291
/// The (optional) description of the default value
12911292
const char *descr;
1292-
#if !defined(NDEBUG)
1293+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
12931294
/// The C++ type name of the default value (only available when compiled in debug mode)
12941295
std::string type;
12951296
#endif
@@ -1487,7 +1488,7 @@ class unpacking_collector {
14871488
auto o = reinterpret_steal<object>(
14881489
detail::make_caster<T>::cast(std::forward<T>(x), policy, {}));
14891490
if (!o) {
1490-
#if defined(NDEBUG)
1491+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
14911492
throw cast_error_unable_to_convert_call_arg();
14921493
#else
14931494
throw cast_error_unable_to_convert_call_arg(std::to_string(args_list.size()),
@@ -1505,21 +1506,21 @@ class unpacking_collector {
15051506

15061507
void process(list & /*args_list*/, arg_v a) {
15071508
if (!a.name) {
1508-
#if defined(NDEBUG)
1509+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
15091510
nameless_argument_error();
15101511
#else
15111512
nameless_argument_error(a.type);
15121513
#endif
15131514
}
15141515
if (m_kwargs.contains(a.name)) {
1515-
#if defined(NDEBUG)
1516+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
15161517
multiple_values_error();
15171518
#else
15181519
multiple_values_error(a.name);
15191520
#endif
15201521
}
15211522
if (!a.value) {
1522-
#if defined(NDEBUG)
1523+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
15231524
throw cast_error_unable_to_convert_call_arg();
15241525
#else
15251526
throw cast_error_unable_to_convert_call_arg(a.name, a.type);
@@ -1534,7 +1535,7 @@ class unpacking_collector {
15341535
}
15351536
for (auto k : reinterpret_borrow<dict>(kp)) {
15361537
if (m_kwargs.contains(k.first)) {
1537-
#if defined(NDEBUG)
1538+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
15381539
multiple_values_error();
15391540
#else
15401541
multiple_values_error(str(k.first));
@@ -1545,18 +1546,20 @@ class unpacking_collector {
15451546
}
15461547

15471548
[[noreturn]] static void nameless_argument_error() {
1548-
throw type_error("Got kwargs without a name; only named arguments "
1549-
"may be passed via py::arg() to a python function call. "
1550-
"(compile in debug mode for details)");
1549+
throw type_error(
1550+
"Got kwargs without a name; only named arguments "
1551+
"may be passed via py::arg() to a python function call. "
1552+
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
15511553
}
15521554
[[noreturn]] static void nameless_argument_error(const std::string &type) {
15531555
throw type_error("Got kwargs without a name of type '" + type
15541556
+ "'; only named "
15551557
"arguments may be passed via py::arg() to a python function call. ");
15561558
}
15571559
[[noreturn]] static void multiple_values_error() {
1558-
throw type_error("Got multiple values for keyword argument "
1559-
"(compile in debug mode for details)");
1560+
throw type_error(
1561+
"Got multiple values for keyword argument "
1562+
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
15601563
}
15611564

15621565
[[noreturn]] static void multiple_values_error(const std::string &name) {
@@ -1603,7 +1606,7 @@ unpacking_collector<policy> collect_arguments(Args &&...args) {
16031606
template <typename Derived>
16041607
template <return_value_policy policy, typename... Args>
16051608
object object_api<Derived>::operator()(Args &&...args) const {
1606-
#ifndef NDEBUG
1609+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
16071610
if (!PyGILState_Check()) {
16081611
pybind11_fail("pybind11::object_api<>::operator() PyGILState_Check() failure.");
16091612
}

include/pybind11/detail/common.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,5 +1157,12 @@ constexpr inline bool silence_msvc_c4127(bool cond) { return cond; }
11571157
# define PYBIND11_SILENCE_MSVC_C4127(...) __VA_ARGS__
11581158
#endif
11591159

1160+
// Pybind offers detailed error messages by default for all builts that are debug (through the
1161+
// negation of ndebug). This can also be manually enabled by users, for any builds, through
1162+
// defining PYBIND11_DETAILED_ERROR_MESSAGES.
1163+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES) && !defined(NDEBUG)
1164+
# define PYBIND11_DETAILED_ERROR_MESSAGES
1165+
#endif
1166+
11601167
PYBIND11_NAMESPACE_END(detail)
11611168
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

include/pybind11/detail/type_caster_base.h

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -394,15 +394,16 @@ instance::get_value_and_holder(const type_info *find_type /*= nullptr default in
394394
return value_and_holder();
395395
}
396396

397-
#if defined(NDEBUG)
398-
pybind11_fail("pybind11::detail::instance::get_value_and_holder: "
399-
"type is not a pybind11 base of the given instance "
400-
"(compile in debug mode for type details)");
401-
#else
397+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
402398
pybind11_fail("pybind11::detail::instance::get_value_and_holder: `"
403399
+ get_fully_qualified_tp_name(find_type->type)
404400
+ "' is not a pybind11 base of the given `"
405401
+ get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance");
402+
#else
403+
pybind11_fail(
404+
"pybind11::detail::instance::get_value_and_holder: "
405+
"type is not a pybind11 base of the given instance "
406+
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for type details)");
406407
#endif
407408
}
408409

@@ -612,14 +613,15 @@ class type_caster_generic {
612613
if (copy_constructor) {
613614
valueptr = copy_constructor(src);
614615
} else {
615-
#if defined(NDEBUG)
616-
throw cast_error("return_value_policy = copy, but type is "
617-
"non-copyable! (compile in debug mode for details)");
618-
#else
616+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
619617
std::string type_name(tinfo->cpptype->name());
620618
detail::clean_type_id(type_name);
621619
throw cast_error("return_value_policy = copy, but type " + type_name
622620
+ " is non-copyable!");
621+
#else
622+
throw cast_error("return_value_policy = copy, but type is "
623+
"non-copyable! (#define PYBIND11_DETAILED_ERROR_MESSAGES or "
624+
"compile in debug mode for details)");
623625
#endif
624626
}
625627
wrapper->owned = true;
@@ -631,15 +633,16 @@ class type_caster_generic {
631633
} else if (copy_constructor) {
632634
valueptr = copy_constructor(src);
633635
} else {
634-
#if defined(NDEBUG)
635-
throw cast_error("return_value_policy = move, but type is neither "
636-
"movable nor copyable! "
637-
"(compile in debug mode for details)");
638-
#else
636+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
639637
std::string type_name(tinfo->cpptype->name());
640638
detail::clean_type_id(type_name);
641639
throw cast_error("return_value_policy = move, but type " + type_name
642640
+ " is neither movable nor copyable!");
641+
#else
642+
throw cast_error("return_value_policy = move, but type is neither "
643+
"movable nor copyable! "
644+
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in "
645+
"debug mode for details)");
643646
#endif
644647
}
645648
wrapper->owned = true;

include/pybind11/gil.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class gil_scoped_acquire {
6262

6363
if (!tstate) {
6464
tstate = PyThreadState_New(internals.istate);
65-
# if !defined(NDEBUG)
65+
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
6666
if (!tstate) {
6767
pybind11_fail("scoped_acquire: could not create thread state!");
6868
}
@@ -84,7 +84,7 @@ class gil_scoped_acquire {
8484

8585
PYBIND11_NOINLINE void dec_ref() {
8686
--tstate->gilstate_counter;
87-
# if !defined(NDEBUG)
87+
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
8888
if (detail::get_thread_state_unchecked() != tstate) {
8989
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
9090
}
@@ -93,7 +93,7 @@ class gil_scoped_acquire {
9393
}
9494
# endif
9595
if (tstate->gilstate_counter == 0) {
96-
# if !defined(NDEBUG)
96+
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
9797
if (!release) {
9898
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
9999
}

include/pybind11/pybind11.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ class cpp_function : public function {
370370
rec->is_constructor = (std::strcmp(rec->name, "__init__") == 0)
371371
|| (std::strcmp(rec->name, "__setstate__") == 0);
372372

373-
#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING)
373+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING)
374374
if (rec->is_constructor && !rec->is_new_style_constructor) {
375375
const auto class_name
376376
= detail::get_fully_qualified_tp_name((PyTypeObject *) rec->scope.ptr());
@@ -518,8 +518,9 @@ class cpp_function : public function {
518518
if (chain->is_method != rec->is_method) {
519519
pybind11_fail(
520520
"overloading a method with both static and instance methods is not supported; "
521-
#if defined(NDEBUG)
522-
"compile in debug mode for more details"
521+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
522+
"#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more "
523+
"details"
523524
#else
524525
"error while attempting to bind "
525526
+ std::string(rec->is_method ? "instance" : "static") + " method "
@@ -906,7 +907,7 @@ class cpp_function : public function {
906907

907908
// 5. Put everything in a vector. Not technically step 5, we've been building it
908909
// in `call.args` all along.
909-
#if !defined(NDEBUG)
910+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
910911
if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) {
911912
pybind11_fail("Internal error: function call dispatcher inserted wrong number "
912913
"of arguments!");

tests/pybind11_tests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,10 @@ PYBIND11_MODULE(pybind11_tests, m) {
6767

6868
bind_ConstructorStats(m);
6969

70-
#if !defined(NDEBUG)
71-
m.attr("debug_enabled") = true;
70+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
71+
m.attr("detailed_error_messages_enabled") = true;
7272
#else
73-
m.attr("debug_enabled") = false;
73+
m.attr("detailed_error_messages_enabled") = false;
7474
#endif
7575

7676
py::class_<UserType>(m, "UserType", "A `py::class_` type for testing")

tests/test_methods_and_attributes.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,10 +361,10 @@ TEST_SUBMODULE(methods_and_attributes, m) {
361361

362362
// test_bad_arg_default
363363
// Issue/PR #648: bad arg default debugging output
364-
#if !defined(NDEBUG)
365-
m.attr("debug_enabled") = true;
364+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
365+
m.attr("detailed_error_messages_enabled") = true;
366366
#else
367-
m.attr("debug_enabled") = false;
367+
m.attr("detailed_error_messages_enabled") = false;
368368
#endif
369369
m.def("bad_arg_def_named", [] {
370370
auto m = py::module_::import("pybind11_tests");

0 commit comments

Comments
 (0)