From f12e098f1d68d9e7445f84d9bf94ce7d0908c531 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 2 Jan 2023 03:46:17 -0800 Subject: [PATCH] Fix `detail::obj_class_name()` to work correctly for meta classes. (#4436) * Fix `detail::obj_class_name()` to work correctly for meta classes. * Adjust expected name for PyPy --- include/pybind11/pytypes.h | 2 +- tests/test_class.cpp | 2 ++ tests/test_class.py | 11 ++++++++++- tests/test_pytypes.cpp | 2 ++ tests/test_pytypes.py | 6 ++++++ 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 3660c180d..a2824a0e0 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -463,7 +463,7 @@ PYBIND11_NAMESPACE_BEGIN(detail) // Equivalent to obj.__class__.__name__ (or obj.__name__ if obj is a class). inline const char *obj_class_name(PyObject *obj) { - if (Py_TYPE(obj) == &PyType_Type) { + if (PyType_Check(obj)) { return reinterpret_cast(obj)->tp_name; } return Py_TYPE(obj)->tp_name; diff --git a/tests/test_class.cpp b/tests/test_class.cpp index 9ea10fae1..ca925917e 100644 --- a/tests/test_class.cpp +++ b/tests/test_class.cpp @@ -55,6 +55,8 @@ void bind_empty0(py::module_ &m) { } // namespace test_class TEST_SUBMODULE(class_, m) { + m.def("obj_class_name", [](py::handle obj) { return py::detail::obj_class_name(obj.ptr()); }); + // test_instance struct NoConstructor { NoConstructor() = default; diff --git a/tests/test_class.py b/tests/test_class.py index 7c1ed2060..9c964e001 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -1,10 +1,19 @@ import pytest -import env # noqa: F401 +import env from pybind11_tests import ConstructorStats, UserType from pybind11_tests import class_ as m +def test_obj_class_name(): + if env.PYPY: + expected_name = "UserType" + else: + expected_name = "pybind11_tests.UserType" + assert m.obj_class_name(UserType(1)) == expected_name + assert m.obj_class_name(UserType) == expected_name + + def test_repr(): assert "pybind11_type" in repr(type(UserType)) assert "UserType" in repr(UserType) diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index ea8d03958..1028bb58e 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -99,6 +99,8 @@ void m_defs(py::module_ &m) { } // namespace handle_from_move_only_type_with_operator_PyObject TEST_SUBMODULE(pytypes, m) { + m.def("obj_class_name", [](py::handle obj) { return py::detail::obj_class_name(obj.ptr()); }); + handle_from_move_only_type_with_operator_PyObject::m_defs(m); // test_bool diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 079ee7ca5..8f9f2987e 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -9,6 +9,12 @@ from pybind11_tests import detailed_error_messages_enabled from pybind11_tests import pytypes as m +def test_obj_class_name(): + assert m.obj_class_name(None) == "NoneType" + assert m.obj_class_name(list) == "list" + assert m.obj_class_name([]) == "list" + + def test_handle_from_move_only_type_with_operator_PyObject(): # noqa: N802 assert m.handle_from_move_only_type_with_operator_PyObject_ncnst() assert m.handle_from_move_only_type_with_operator_PyObject_const()