Skip to content

Commit 0efff79

Browse files
authored
Bug fixes: Add missing handle_type_name specializations. (#5073)
* Transfer bug fixes from #4888 wholesale. Full test coverage for all fixes is still missing. * Add cmake option(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION) and use in some tests.
1 parent 705efcc commit 0efff79

File tree

11 files changed

+156
-5
lines changed

11 files changed

+156
-5
lines changed

.github/workflows/ci.yml

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ jobs:
114114
run: >
115115
cmake -S . -B .
116116
-DPYBIND11_WERROR=ON
117+
-DPYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION=ON
117118
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON
118119
-DPYBIND11_NUMPY_1_ONLY=ON
119120
-DDOWNLOAD_CATCH=ON

CMakeLists.txt

+5
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ endif()
107107
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
108108
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
109109
option(PYBIND11_NOPYTHON "Disable search for Python" OFF)
110+
option(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION
111+
"To enforce that a handle_type_name<> specialization exists" OFF)
110112
option(PYBIND11_SIMPLE_GIL_MANAGEMENT
111113
"Use simpler GIL management logic that does not support disassociation" OFF)
112114
option(PYBIND11_NUMPY_1_ONLY
@@ -115,6 +117,9 @@ set(PYBIND11_INTERNALS_VERSION
115117
""
116118
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
117119

120+
if(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION)
121+
add_compile_definitions(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION)
122+
endif()
118123
if(PYBIND11_SIMPLE_GIL_MANAGEMENT)
119124
add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT)
120125
endif()

include/pybind11/cast.h

+95
Original file line numberDiff line numberDiff line change
@@ -881,10 +881,53 @@ struct is_holder_type
881881
template <typename base, typename deleter>
882882
struct is_holder_type<base, std::unique_ptr<base, deleter>> : std::true_type {};
883883

884+
#ifdef PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION // See PR #4888
885+
886+
// This leads to compilation errors if a specialization is missing.
887+
template <typename T>
888+
struct handle_type_name;
889+
890+
#else
891+
884892
template <typename T>
885893
struct handle_type_name {
886894
static constexpr auto name = const_name<T>();
887895
};
896+
897+
#endif
898+
899+
template <>
900+
struct handle_type_name<object> {
901+
static constexpr auto name = const_name("object");
902+
};
903+
template <>
904+
struct handle_type_name<list> {
905+
static constexpr auto name = const_name("list");
906+
};
907+
template <>
908+
struct handle_type_name<dict> {
909+
static constexpr auto name = const_name("dict");
910+
};
911+
template <>
912+
struct handle_type_name<anyset> {
913+
static constexpr auto name = const_name("Union[set, frozenset]");
914+
};
915+
template <>
916+
struct handle_type_name<set> {
917+
static constexpr auto name = const_name("set");
918+
};
919+
template <>
920+
struct handle_type_name<frozenset> {
921+
static constexpr auto name = const_name("frozenset");
922+
};
923+
template <>
924+
struct handle_type_name<str> {
925+
static constexpr auto name = const_name("str");
926+
};
927+
template <>
928+
struct handle_type_name<tuple> {
929+
static constexpr auto name = const_name("tuple");
930+
};
888931
template <>
889932
struct handle_type_name<bool_> {
890933
static constexpr auto name = const_name("bool");
@@ -930,13 +973,65 @@ struct handle_type_name<sequence> {
930973
static constexpr auto name = const_name("Sequence");
931974
};
932975
template <>
976+
struct handle_type_name<bytearray> {
977+
static constexpr auto name = const_name("bytearray");
978+
};
979+
template <>
980+
struct handle_type_name<memoryview> {
981+
static constexpr auto name = const_name("memoryview");
982+
};
983+
template <>
984+
struct handle_type_name<slice> {
985+
static constexpr auto name = const_name("slice");
986+
};
987+
template <>
988+
struct handle_type_name<type> {
989+
static constexpr auto name = const_name("type");
990+
};
991+
template <>
992+
struct handle_type_name<capsule> {
993+
static constexpr auto name = const_name("capsule");
994+
};
995+
template <>
996+
struct handle_type_name<ellipsis> {
997+
static constexpr auto name = const_name("ellipsis");
998+
};
999+
template <>
1000+
struct handle_type_name<weakref> {
1001+
static constexpr auto name = const_name("weakref");
1002+
};
1003+
template <>
9331004
struct handle_type_name<args> {
9341005
static constexpr auto name = const_name("*args");
9351006
};
9361007
template <>
9371008
struct handle_type_name<kwargs> {
9381009
static constexpr auto name = const_name("**kwargs");
9391010
};
1011+
template <>
1012+
struct handle_type_name<obj_attr_accessor> {
1013+
static constexpr auto name = const_name<obj_attr_accessor>();
1014+
};
1015+
template <>
1016+
struct handle_type_name<str_attr_accessor> {
1017+
static constexpr auto name = const_name<str_attr_accessor>();
1018+
};
1019+
template <>
1020+
struct handle_type_name<item_accessor> {
1021+
static constexpr auto name = const_name<item_accessor>();
1022+
};
1023+
template <>
1024+
struct handle_type_name<sequence_accessor> {
1025+
static constexpr auto name = const_name<sequence_accessor>();
1026+
};
1027+
template <>
1028+
struct handle_type_name<list_accessor> {
1029+
static constexpr auto name = const_name<list_accessor>();
1030+
};
1031+
template <>
1032+
struct handle_type_name<tuple_accessor> {
1033+
static constexpr auto name = const_name<tuple_accessor>();
1034+
};
9401035

9411036
template <typename type>
9421037
struct pyobject_caster {

include/pybind11/detail/type_caster_base.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -1201,13 +1201,17 @@ class type_caster_base : public type_caster_generic {
12011201
static Constructor make_move_constructor(...) { return nullptr; }
12021202
};
12031203

1204+
inline std::string quote_cpp_type_name(const std::string &cpp_type_name) {
1205+
return cpp_type_name; // No-op for now. See PR #4888
1206+
}
1207+
12041208
PYBIND11_NOINLINE std::string type_info_description(const std::type_info &ti) {
12051209
if (auto *type_data = get_type_info(ti)) {
12061210
handle th((PyObject *) type_data->type);
12071211
return th.attr("__module__").cast<std::string>() + '.'
12081212
+ th.attr("__qualname__").cast<std::string>();
12091213
}
1210-
return clean_type_id(ti.name());
1214+
return quote_cpp_type_name(clean_type_id(ti.name()));
12111215
}
12121216

12131217
PYBIND11_NAMESPACE_END(detail)

include/pybind11/numpy.h

+6
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,16 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
4646

4747
PYBIND11_WARNING_DISABLE_MSVC(4127)
4848

49+
class dtype; // Forward declaration
4950
class array; // Forward declaration
5051

5152
PYBIND11_NAMESPACE_BEGIN(detail)
5253

54+
template <>
55+
struct handle_type_name<dtype> {
56+
static constexpr auto name = const_name("numpy.dtype");
57+
};
58+
5359
template <>
5460
struct handle_type_name<array> {
5561
static constexpr auto name = const_name("numpy.ndarray");

include/pybind11/pybind11.h

+24-3
Original file line numberDiff line numberDiff line change
@@ -492,9 +492,7 @@ class cpp_function : public function {
492492
signature += rec->scope.attr("__module__").cast<std::string>() + "."
493493
+ rec->scope.attr("__qualname__").cast<std::string>();
494494
} else {
495-
std::string tname(t->name());
496-
detail::clean_type_id(tname);
497-
signature += tname;
495+
signature += detail::quote_cpp_type_name(detail::clean_type_id(t->name()));
498496
}
499497
} else {
500498
signature += c;
@@ -1192,6 +1190,15 @@ class cpp_function : public function {
11921190
}
11931191
};
11941192

1193+
PYBIND11_NAMESPACE_BEGIN(detail)
1194+
1195+
template <>
1196+
struct handle_type_name<cpp_function> {
1197+
static constexpr auto name = const_name("Callable");
1198+
};
1199+
1200+
PYBIND11_NAMESPACE_END(detail)
1201+
11951202
/// Wrapper for Python extension modules
11961203
class module_ : public object {
11971204
public:
@@ -1319,6 +1326,15 @@ class module_ : public object {
13191326
}
13201327
};
13211328

1329+
PYBIND11_NAMESPACE_BEGIN(detail)
1330+
1331+
template <>
1332+
struct handle_type_name<module_> {
1333+
static constexpr auto name = const_name("module");
1334+
};
1335+
1336+
PYBIND11_NAMESPACE_END(detail)
1337+
13221338
// When inside a namespace (or anywhere as long as it's not the first item on a line),
13231339
// C++20 allows "module" to be used. This is provided for backward compatibility, and for
13241340
// simplicity, if someone wants to use py::module for example, that is perfectly safe.
@@ -2611,6 +2627,11 @@ class exception : public object {
26112627

26122628
PYBIND11_NAMESPACE_BEGIN(detail)
26132629

2630+
template <>
2631+
struct handle_type_name<exception<void>> {
2632+
static constexpr auto name = const_name("Exception");
2633+
};
2634+
26142635
// Helper function for register_exception and register_local_exception
26152636
template <typename CppException>
26162637
exception<CppException> &

include/pybind11/pytypes.h

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct sequence_item;
5959
struct list_item;
6060
struct tuple_item;
6161
} // namespace accessor_policies
62+
// PLEASE KEEP handle_type_name SPECIALIZATIONS IN SYNC.
6263
using obj_attr_accessor = accessor<accessor_policies::obj_attr>;
6364
using str_attr_accessor = accessor<accessor_policies::str_attr>;
6465
using item_accessor = accessor<accessor_policies::generic_item>;

tests/test_exceptions.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -382,4 +382,7 @@ TEST_SUBMODULE(exceptions, m) {
382382
// function returns None instead of int, should give a useful error message
383383
fn().cast<int>();
384384
});
385+
386+
// m.def("pass_exception_void", [](const py::exception<void>&) {}); // Does not compile.
387+
m.def("return_exception_void", []() { return py::exception<void>(); });
385388
}

tests/test_exceptions.py

+6
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,9 @@ def test_fn_cast_int_exception():
424424
assert str(excinfo.value).startswith(
425425
"Unable to cast Python instance of type <class 'NoneType'> to C++ type"
426426
)
427+
428+
429+
def test_return_exception_void():
430+
with pytest.raises(TypeError) as excinfo:
431+
m.return_exception_void()
432+
assert "Exception" in str(excinfo.value)

tests/test_pytypes.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ class float_ : public py::object {
4141
};
4242
} // namespace external
4343

44+
namespace pybind11 {
45+
namespace detail {
46+
template <>
47+
struct handle_type_name<external::float_> {
48+
static constexpr auto name = const_name("float");
49+
};
50+
} // namespace detail
51+
} // namespace pybind11
52+
4453
namespace implicit_conversion_from_0_to_handle {
4554
// Uncomment to trigger compiler error. Note: Before PR #4008 this used to compile successfully.
4655
// void expected_to_trigger_compiler_error() { py::handle(0); }

tests/test_pytypes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def test_set(capture, doc):
121121
assert m.anyset_contains({"foo"}, "foo")
122122

123123
assert doc(m.get_set) == "get_set() -> set"
124-
assert doc(m.print_anyset) == "print_anyset(arg0: anyset) -> None"
124+
assert doc(m.print_anyset) == "print_anyset(arg0: Union[set, frozenset]) -> None"
125125

126126

127127
def test_frozenset(capture, doc):

0 commit comments

Comments
 (0)