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.
This commit is contained in:
Ralf W. Grosse-Kunstleve 2024-03-27 12:39:05 -07:00 committed by GitHub
parent 705efccecd
commit 0efff79f01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 156 additions and 5 deletions

View File

@ -114,6 +114,7 @@ jobs:
run: > run: >
cmake -S . -B . cmake -S . -B .
-DPYBIND11_WERROR=ON -DPYBIND11_WERROR=ON
-DPYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION=ON
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON
-DPYBIND11_NUMPY_1_ONLY=ON -DPYBIND11_NUMPY_1_ONLY=ON
-DDOWNLOAD_CATCH=ON -DDOWNLOAD_CATCH=ON

View File

@ -107,6 +107,8 @@ endif()
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
option(PYBIND11_NOPYTHON "Disable search for Python" OFF) option(PYBIND11_NOPYTHON "Disable search for Python" OFF)
option(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION
"To enforce that a handle_type_name<> specialization exists" OFF)
option(PYBIND11_SIMPLE_GIL_MANAGEMENT option(PYBIND11_SIMPLE_GIL_MANAGEMENT
"Use simpler GIL management logic that does not support disassociation" OFF) "Use simpler GIL management logic that does not support disassociation" OFF)
option(PYBIND11_NUMPY_1_ONLY option(PYBIND11_NUMPY_1_ONLY
@ -115,6 +117,9 @@ set(PYBIND11_INTERNALS_VERSION
"" ""
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.") CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
if(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION)
add_compile_definitions(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION)
endif()
if(PYBIND11_SIMPLE_GIL_MANAGEMENT) if(PYBIND11_SIMPLE_GIL_MANAGEMENT)
add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT) add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT)
endif() endif()

View File

@ -881,10 +881,53 @@ struct is_holder_type
template <typename base, typename deleter> template <typename base, typename deleter>
struct is_holder_type<base, std::unique_ptr<base, deleter>> : std::true_type {}; struct is_holder_type<base, std::unique_ptr<base, deleter>> : std::true_type {};
#ifdef PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION // See PR #4888
// This leads to compilation errors if a specialization is missing.
template <typename T>
struct handle_type_name;
#else
template <typename T> template <typename T>
struct handle_type_name { struct handle_type_name {
static constexpr auto name = const_name<T>(); static constexpr auto name = const_name<T>();
}; };
#endif
template <>
struct handle_type_name<object> {
static constexpr auto name = const_name("object");
};
template <>
struct handle_type_name<list> {
static constexpr auto name = const_name("list");
};
template <>
struct handle_type_name<dict> {
static constexpr auto name = const_name("dict");
};
template <>
struct handle_type_name<anyset> {
static constexpr auto name = const_name("Union[set, frozenset]");
};
template <>
struct handle_type_name<set> {
static constexpr auto name = const_name("set");
};
template <>
struct handle_type_name<frozenset> {
static constexpr auto name = const_name("frozenset");
};
template <>
struct handle_type_name<str> {
static constexpr auto name = const_name("str");
};
template <>
struct handle_type_name<tuple> {
static constexpr auto name = const_name("tuple");
};
template <> template <>
struct handle_type_name<bool_> { struct handle_type_name<bool_> {
static constexpr auto name = const_name("bool"); static constexpr auto name = const_name("bool");
@ -930,6 +973,34 @@ struct handle_type_name<sequence> {
static constexpr auto name = const_name("Sequence"); static constexpr auto name = const_name("Sequence");
}; };
template <> template <>
struct handle_type_name<bytearray> {
static constexpr auto name = const_name("bytearray");
};
template <>
struct handle_type_name<memoryview> {
static constexpr auto name = const_name("memoryview");
};
template <>
struct handle_type_name<slice> {
static constexpr auto name = const_name("slice");
};
template <>
struct handle_type_name<type> {
static constexpr auto name = const_name("type");
};
template <>
struct handle_type_name<capsule> {
static constexpr auto name = const_name("capsule");
};
template <>
struct handle_type_name<ellipsis> {
static constexpr auto name = const_name("ellipsis");
};
template <>
struct handle_type_name<weakref> {
static constexpr auto name = const_name("weakref");
};
template <>
struct handle_type_name<args> { struct handle_type_name<args> {
static constexpr auto name = const_name("*args"); static constexpr auto name = const_name("*args");
}; };
@ -937,6 +1008,30 @@ template <>
struct handle_type_name<kwargs> { struct handle_type_name<kwargs> {
static constexpr auto name = const_name("**kwargs"); static constexpr auto name = const_name("**kwargs");
}; };
template <>
struct handle_type_name<obj_attr_accessor> {
static constexpr auto name = const_name<obj_attr_accessor>();
};
template <>
struct handle_type_name<str_attr_accessor> {
static constexpr auto name = const_name<str_attr_accessor>();
};
template <>
struct handle_type_name<item_accessor> {
static constexpr auto name = const_name<item_accessor>();
};
template <>
struct handle_type_name<sequence_accessor> {
static constexpr auto name = const_name<sequence_accessor>();
};
template <>
struct handle_type_name<list_accessor> {
static constexpr auto name = const_name<list_accessor>();
};
template <>
struct handle_type_name<tuple_accessor> {
static constexpr auto name = const_name<tuple_accessor>();
};
template <typename type> template <typename type>
struct pyobject_caster { struct pyobject_caster {

View File

@ -1201,13 +1201,17 @@ protected:
static Constructor make_move_constructor(...) { return nullptr; } static Constructor make_move_constructor(...) { return nullptr; }
}; };
inline std::string quote_cpp_type_name(const std::string &cpp_type_name) {
return cpp_type_name; // No-op for now. See PR #4888
}
PYBIND11_NOINLINE std::string type_info_description(const std::type_info &ti) { PYBIND11_NOINLINE std::string type_info_description(const std::type_info &ti) {
if (auto *type_data = get_type_info(ti)) { if (auto *type_data = get_type_info(ti)) {
handle th((PyObject *) type_data->type); handle th((PyObject *) type_data->type);
return th.attr("__module__").cast<std::string>() + '.' return th.attr("__module__").cast<std::string>() + '.'
+ th.attr("__qualname__").cast<std::string>(); + th.attr("__qualname__").cast<std::string>();
} }
return clean_type_id(ti.name()); return quote_cpp_type_name(clean_type_id(ti.name()));
} }
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)

View File

@ -46,10 +46,16 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_WARNING_DISABLE_MSVC(4127) PYBIND11_WARNING_DISABLE_MSVC(4127)
class dtype; // Forward declaration
class array; // Forward declaration class array; // Forward declaration
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <>
struct handle_type_name<dtype> {
static constexpr auto name = const_name("numpy.dtype");
};
template <> template <>
struct handle_type_name<array> { struct handle_type_name<array> {
static constexpr auto name = const_name("numpy.ndarray"); static constexpr auto name = const_name("numpy.ndarray");

View File

@ -492,9 +492,7 @@ protected:
signature += rec->scope.attr("__module__").cast<std::string>() + "." signature += rec->scope.attr("__module__").cast<std::string>() + "."
+ rec->scope.attr("__qualname__").cast<std::string>(); + rec->scope.attr("__qualname__").cast<std::string>();
} else { } else {
std::string tname(t->name()); signature += detail::quote_cpp_type_name(detail::clean_type_id(t->name()));
detail::clean_type_id(tname);
signature += tname;
} }
} else { } else {
signature += c; signature += c;
@ -1192,6 +1190,15 @@ protected:
} }
}; };
PYBIND11_NAMESPACE_BEGIN(detail)
template <>
struct handle_type_name<cpp_function> {
static constexpr auto name = const_name("Callable");
};
PYBIND11_NAMESPACE_END(detail)
/// Wrapper for Python extension modules /// Wrapper for Python extension modules
class module_ : public object { class module_ : public object {
public: public:
@ -1319,6 +1326,15 @@ public:
} }
}; };
PYBIND11_NAMESPACE_BEGIN(detail)
template <>
struct handle_type_name<module_> {
static constexpr auto name = const_name("module");
};
PYBIND11_NAMESPACE_END(detail)
// When inside a namespace (or anywhere as long as it's not the first item on a line), // When inside a namespace (or anywhere as long as it's not the first item on a line),
// C++20 allows "module" to be used. This is provided for backward compatibility, and for // C++20 allows "module" to be used. This is provided for backward compatibility, and for
// simplicity, if someone wants to use py::module for example, that is perfectly safe. // simplicity, if someone wants to use py::module for example, that is perfectly safe.
@ -2611,6 +2627,11 @@ public:
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
template <>
struct handle_type_name<exception<void>> {
static constexpr auto name = const_name("Exception");
};
// Helper function for register_exception and register_local_exception // Helper function for register_exception and register_local_exception
template <typename CppException> template <typename CppException>
exception<CppException> & exception<CppException> &

View File

@ -59,6 +59,7 @@ struct sequence_item;
struct list_item; struct list_item;
struct tuple_item; struct tuple_item;
} // namespace accessor_policies } // namespace accessor_policies
// PLEASE KEEP handle_type_name SPECIALIZATIONS IN SYNC.
using obj_attr_accessor = accessor<accessor_policies::obj_attr>; using obj_attr_accessor = accessor<accessor_policies::obj_attr>;
using str_attr_accessor = accessor<accessor_policies::str_attr>; using str_attr_accessor = accessor<accessor_policies::str_attr>;
using item_accessor = accessor<accessor_policies::generic_item>; using item_accessor = accessor<accessor_policies::generic_item>;

View File

@ -382,4 +382,7 @@ TEST_SUBMODULE(exceptions, m) {
// function returns None instead of int, should give a useful error message // function returns None instead of int, should give a useful error message
fn().cast<int>(); fn().cast<int>();
}); });
// m.def("pass_exception_void", [](const py::exception<void>&) {}); // Does not compile.
m.def("return_exception_void", []() { return py::exception<void>(); });
} }

View File

@ -424,3 +424,9 @@ def test_fn_cast_int_exception():
assert str(excinfo.value).startswith( assert str(excinfo.value).startswith(
"Unable to cast Python instance of type <class 'NoneType'> to C++ type" "Unable to cast Python instance of type <class 'NoneType'> to C++ type"
) )
def test_return_exception_void():
with pytest.raises(TypeError) as excinfo:
m.return_exception_void()
assert "Exception" in str(excinfo.value)

View File

@ -41,6 +41,15 @@ class float_ : public py::object {
}; };
} // namespace external } // namespace external
namespace pybind11 {
namespace detail {
template <>
struct handle_type_name<external::float_> {
static constexpr auto name = const_name("float");
};
} // namespace detail
} // namespace pybind11
namespace implicit_conversion_from_0_to_handle { namespace implicit_conversion_from_0_to_handle {
// Uncomment to trigger compiler error. Note: Before PR #4008 this used to compile successfully. // Uncomment to trigger compiler error. Note: Before PR #4008 this used to compile successfully.
// void expected_to_trigger_compiler_error() { py::handle(0); } // void expected_to_trigger_compiler_error() { py::handle(0); }

View File

@ -121,7 +121,7 @@ def test_set(capture, doc):
assert m.anyset_contains({"foo"}, "foo") assert m.anyset_contains({"foo"}, "foo")
assert doc(m.get_set) == "get_set() -> set" assert doc(m.get_set) == "get_set() -> set"
assert doc(m.print_anyset) == "print_anyset(arg0: anyset) -> None" assert doc(m.print_anyset) == "print_anyset(arg0: Union[set, frozenset]) -> None"
def test_frozenset(capture, doc): def test_frozenset(capture, doc):