From 2d06ad748825a89154d21c5e23dccb3538808ce0 Mon Sep 17 00:00:00 2001 From: Bryn Lloyd <12702862+dyollb@users.noreply.github.com> Date: Mon, 14 Apr 2025 19:57:40 +0200 Subject: [PATCH 1/8] add class doc string to native_enum --- include/pybind11/detail/native_enum_data.h | 6 +++++- include/pybind11/native_enum.h | 5 +++-- tests/test_native_enum.cpp | 6 +++--- tests/test_native_enum.py | 4 ++++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/include/pybind11/detail/native_enum_data.h b/include/pybind11/detail/native_enum_data.h index 5192ed51a6..75e0b47330 100644 --- a/include/pybind11/detail/native_enum_data.h +++ b/include/pybind11/detail/native_enum_data.h @@ -29,10 +29,12 @@ class native_enum_data { native_enum_data(const object &parent_scope, const char *enum_name, const char *native_type_name, + const char *enum_doc, const std::type_index &enum_type_index) : enum_name_encoded{enum_name}, native_type_name_encoded{native_type_name}, enum_type_index{enum_type_index}, parent_scope(parent_scope), enum_name{enum_name}, - native_type_name{native_type_name}, export_values_flag{false}, finalize_needed{false} {} + native_type_name{native_type_name}, enum_doc(enum_doc), export_values_flag{false}, + finalize_needed{false} {} void finalize(); @@ -70,6 +72,7 @@ class native_enum_data { object parent_scope; str enum_name; str native_type_name; + str enum_doc; protected: list members; @@ -191,6 +194,7 @@ inline void native_enum_data::finalize() { parent_scope.attr(member_name) = py_enum[member_name]; } } + py_enum.attr("__doc__") = enum_doc; for (auto doc : docs) { py_enum[doc[int_(0)]].attr("__doc__") = doc[int_(1)]; } diff --git a/include/pybind11/native_enum.h b/include/pybind11/native_enum.h index 759acd48db..9c08cdc01a 100644 --- a/include/pybind11/native_enum.h +++ b/include/pybind11/native_enum.h @@ -24,9 +24,10 @@ class native_enum : public detail::native_enum_data { native_enum(const object &parent_scope, const char *name, - const char *native_type_name = "enum.Enum") + const char *native_type_name, + const char *doc = "") : detail::native_enum_data( - parent_scope, name, native_type_name, std::type_index(typeid(EnumType))) { + parent_scope, name, native_type_name, doc, std::type_index(typeid(EnumType))) { if (detail::get_local_type_info(typeid(EnumType)) != nullptr || detail::get_global_type_info(typeid(EnumType)) != nullptr) { pybind11_fail( diff --git a/tests/test_native_enum.cpp b/tests/test_native_enum.cpp index 8ed3e40acb..c8fd34df03 100644 --- a/tests/test_native_enum.cpp +++ b/tests/test_native_enum.cpp @@ -76,7 +76,7 @@ PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) TEST_SUBMODULE(native_enum, m) { using namespace test_native_enum; - py::native_enum(m, "smallenum", "enum.IntEnum") + py::native_enum(m, "smallenum", "enum.IntEnum", "doc smallenum") .value("a", smallenum::a) .value("b", smallenum::b) .value("c", smallenum::c) @@ -89,7 +89,7 @@ TEST_SUBMODULE(native_enum, m) { .value("blue", color::blue) .finalize(); - py::native_enum(m, "altitude") + py::native_enum(m, "altitude", "enum.Enum") .value("high", altitude::high) .value("low", altitude::low) .finalize(); @@ -189,7 +189,7 @@ TEST_SUBMODULE(native_enum, m) { py::native_enum(m, "fake_double_registration_native_enum", "enum.IntEnum") .value("x", fake::x) .finalize(); - py::native_enum(m, "fake_double_registration_native_enum"); + py::native_enum(m, "fake_double_registration_native_enum", "enum.Enum"); }); m.def("native_enum_name_clash", [](py::module_ &m) { diff --git a/tests/test_native_enum.py b/tests/test_native_enum.py index f98ebd0459..a831ae8019 100644 --- a/tests/test_native_enum.py +++ b/tests/test_native_enum.py @@ -108,6 +108,10 @@ def test_export_values(): assert m.exv1 is m.export_values.exv1 +def test_class_doc(): + assert m.smallenum.__doc__ == "doc smallenum" + + def test_member_doc(): pure_native = enum.IntEnum("pure_native", (("mem", 0),)) assert m.member_doc.mem0.__doc__ == "docA" From a952009686fd7d4bf0b41d71a740331c254a6908 Mon Sep 17 00:00:00 2001 From: Bryn Lloyd <12702862+dyollb@users.noreply.github.com> Date: Mon, 14 Apr 2025 20:30:44 +0200 Subject: [PATCH 2/8] adapt doc argument name --- include/pybind11/native_enum.h | 4 ++-- tests/test_native_enum.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/pybind11/native_enum.h b/include/pybind11/native_enum.h index 9c08cdc01a..fd5690cc53 100644 --- a/include/pybind11/native_enum.h +++ b/include/pybind11/native_enum.h @@ -25,9 +25,9 @@ class native_enum : public detail::native_enum_data { native_enum(const object &parent_scope, const char *name, const char *native_type_name, - const char *doc = "") + const char *enum_doc = "") : detail::native_enum_data( - parent_scope, name, native_type_name, doc, std::type_index(typeid(EnumType))) { + parent_scope, name, native_type_name, enum_doc, std::type_index(typeid(EnumType))) { if (detail::get_local_type_info(typeid(EnumType)) != nullptr || detail::get_global_type_info(typeid(EnumType)) != nullptr) { pybind11_fail( diff --git a/tests/test_native_enum.py b/tests/test_native_enum.py index a831ae8019..6f4e2b5a38 100644 --- a/tests/test_native_enum.py +++ b/tests/test_native_enum.py @@ -110,6 +110,8 @@ def test_export_values(): def test_class_doc(): assert m.smallenum.__doc__ == "doc smallenum" + assert m.color.__doc__ == "" + assert m.member_doc.__doc__ == "" def test_member_doc(): From d9988092cfd84e29a7a7932390d6384744dc24f1 Mon Sep 17 00:00:00 2001 From: Bryn Lloyd <12702862+dyollb@users.noreply.github.com> Date: Mon, 14 Apr 2025 20:53:33 +0200 Subject: [PATCH 3/8] fix test, make class enum doc None by default --- include/pybind11/detail/native_enum_data.h | 5 +++-- tests/test_native_enum.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/pybind11/detail/native_enum_data.h b/include/pybind11/detail/native_enum_data.h index 75e0b47330..8a98bd900a 100644 --- a/include/pybind11/detail/native_enum_data.h +++ b/include/pybind11/detail/native_enum_data.h @@ -72,7 +72,7 @@ class native_enum_data { object parent_scope; str enum_name; str native_type_name; - str enum_doc; + std::string enum_doc; protected: list members; @@ -194,7 +194,8 @@ inline void native_enum_data::finalize() { parent_scope.attr(member_name) = py_enum[member_name]; } } - py_enum.attr("__doc__") = enum_doc; + if (!enum_doc.empty()) + py_enum.attr("__doc__") = enum_doc.c_str(); for (auto doc : docs) { py_enum[doc[int_(0)]].attr("__doc__") = doc[int_(1)]; } diff --git a/tests/test_native_enum.py b/tests/test_native_enum.py index 6f4e2b5a38..086090c9b4 100644 --- a/tests/test_native_enum.py +++ b/tests/test_native_enum.py @@ -110,8 +110,8 @@ def test_export_values(): def test_class_doc(): assert m.smallenum.__doc__ == "doc smallenum" - assert m.color.__doc__ == "" - assert m.member_doc.__doc__ == "" + assert m.color.__doc__ is None + assert m.member_doc.__doc__ is None def test_member_doc(): From 4b0e68c7c3f7786a6716d7942043b075bf86b83a Mon Sep 17 00:00:00 2001 From: Bryn Lloyd <12702862+dyollb@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:03:41 +0200 Subject: [PATCH 4/8] fix other python versions? --- tests/test_native_enum.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_native_enum.py b/tests/test_native_enum.py index 086090c9b4..b942fca0d5 100644 --- a/tests/test_native_enum.py +++ b/tests/test_native_enum.py @@ -109,9 +109,9 @@ def test_export_values(): def test_class_doc(): + pure_native = enum.IntEnum("pure_native", (("mem", 0),)) assert m.smallenum.__doc__ == "doc smallenum" - assert m.color.__doc__ is None - assert m.member_doc.__doc__ is None + assert m.color.__doc__ == pure_native.__doc__ def test_member_doc(): From eda2be7760eeb61823f30894ffca4e725a851be2 Mon Sep 17 00:00:00 2001 From: Bryn Lloyd <12702862+dyollb@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:10:19 +0200 Subject: [PATCH 5/8] make clang-tidy happy --- include/pybind11/detail/native_enum_data.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/pybind11/detail/native_enum_data.h b/include/pybind11/detail/native_enum_data.h index 8a98bd900a..f5fee81ec6 100644 --- a/include/pybind11/detail/native_enum_data.h +++ b/include/pybind11/detail/native_enum_data.h @@ -194,8 +194,9 @@ inline void native_enum_data::finalize() { parent_scope.attr(member_name) = py_enum[member_name]; } } - if (!enum_doc.empty()) + if (!enum_doc.empty()) { py_enum.attr("__doc__") = enum_doc.c_str(); + } for (auto doc : docs) { py_enum[doc[int_(0)]].attr("__doc__") = doc[int_(1)]; } From f53f1820995c452bcdfeb54f61838198a1f1fd0e Mon Sep 17 00:00:00 2001 From: Bryn Lloyd <12702862+dyollb@users.noreply.github.com> Date: Tue, 15 Apr 2025 06:50:00 +0200 Subject: [PATCH 6/8] rename 'enum_doc' to 'class_doc' --- include/pybind11/detail/native_enum_data.h | 10 +++++----- include/pybind11/native_enum.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/pybind11/detail/native_enum_data.h b/include/pybind11/detail/native_enum_data.h index f5fee81ec6..c02f003da7 100644 --- a/include/pybind11/detail/native_enum_data.h +++ b/include/pybind11/detail/native_enum_data.h @@ -29,11 +29,11 @@ class native_enum_data { native_enum_data(const object &parent_scope, const char *enum_name, const char *native_type_name, - const char *enum_doc, + const char *class_doc, const std::type_index &enum_type_index) : enum_name_encoded{enum_name}, native_type_name_encoded{native_type_name}, enum_type_index{enum_type_index}, parent_scope(parent_scope), enum_name{enum_name}, - native_type_name{native_type_name}, enum_doc(enum_doc), export_values_flag{false}, + native_type_name{native_type_name}, class_doc(class_doc), export_values_flag{false}, finalize_needed{false} {} void finalize(); @@ -72,7 +72,7 @@ class native_enum_data { object parent_scope; str enum_name; str native_type_name; - std::string enum_doc; + std::string class_doc; protected: list members; @@ -194,8 +194,8 @@ inline void native_enum_data::finalize() { parent_scope.attr(member_name) = py_enum[member_name]; } } - if (!enum_doc.empty()) { - py_enum.attr("__doc__") = enum_doc.c_str(); + if (!class_doc.empty()) { + py_enum.attr("__doc__") = class_doc.c_str(); } for (auto doc : docs) { py_enum[doc[int_(0)]].attr("__doc__") = doc[int_(1)]; diff --git a/include/pybind11/native_enum.h b/include/pybind11/native_enum.h index fd5690cc53..ca2a739d98 100644 --- a/include/pybind11/native_enum.h +++ b/include/pybind11/native_enum.h @@ -25,9 +25,9 @@ class native_enum : public detail::native_enum_data { native_enum(const object &parent_scope, const char *name, const char *native_type_name, - const char *enum_doc = "") + const char *class_doc = "") : detail::native_enum_data( - parent_scope, name, native_type_name, enum_doc, std::type_index(typeid(EnumType))) { + parent_scope, name, native_type_name, class_doc, std::type_index(typeid(EnumType))) { if (detail::get_local_type_info(typeid(EnumType)) != nullptr || detail::get_global_type_info(typeid(EnumType)) != nullptr) { pybind11_fail( From a8bdaebea7c4448bcab0d316df8c1ecac4957f75 Mon Sep 17 00:00:00 2001 From: Bryn Lloyd <12702862+dyollb@users.noreply.github.com> Date: Tue, 15 Apr 2025 07:13:55 +0200 Subject: [PATCH 7/8] update documentation --- docs/classes.rst | 9 +++++---- include/pybind11/detail/native_enum_data.h | 4 ++-- include/pybind11/native_enum.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/classes.rst b/docs/classes.rst index b6923963dc..84c0da7396 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -556,7 +556,7 @@ The binding code for this example looks as follows: .def_readwrite("type", &Pet::type) .def_readwrite("attr", &Pet::attr); - py::native_enum(pet, "Kind") + py::native_enum(pet, "Kind", "enum.Enum") .value("Dog", Pet::Kind::Dog) .value("Cat", Pet::Kind::Cat) .export_values() @@ -593,13 +593,14 @@ once. To achieve this, ``py::native_enum`` acts as a buffer to collect the name/value pairs. The ``.finalize()`` call uses the accumulated name/value pairs to build the arguments for constructing a native Python enum type. -The ``py::native_enum`` constructor supports a third optional -``native_type_name`` string argument, with default value ``"enum.Enum"``. +The ``py::native_enum`` constructor has a third ``native_type_name`` string +argument which specifies the used base class, e.g., ``"enum.Enum"``. The fourth +optional string argument ``class_doc`` specifies the class documentation string. Other types can be specified like this: .. code-block:: cpp - py::native_enum(pet, "Kind", "enum.IntEnum") + py::native_enum(pet, "Kind", "enum.IntEnum", "Constant specifying the kind of pet") Any fully-qualified Python name can be specified. The only requirement is that the named type is similar to diff --git a/include/pybind11/detail/native_enum_data.h b/include/pybind11/detail/native_enum_data.h index c02f003da7..b12ca6202b 100644 --- a/include/pybind11/detail/native_enum_data.h +++ b/include/pybind11/detail/native_enum_data.h @@ -76,7 +76,7 @@ class native_enum_data { protected: list members; - list docs; + list member_docs; bool export_values_flag : 1; // Attention: It is best to keep the bools together. private: @@ -197,7 +197,7 @@ inline void native_enum_data::finalize() { if (!class_doc.empty()) { py_enum.attr("__doc__") = class_doc.c_str(); } - for (auto doc : docs) { + for (auto doc : member_docs) { py_enum[doc[int_(0)]].attr("__doc__") = doc[int_(1)]; } global_internals_native_enum_type_map_set_item(enum_type_index, py_enum.release().ptr()); diff --git a/include/pybind11/native_enum.h b/include/pybind11/native_enum.h index ca2a739d98..5537218f21 100644 --- a/include/pybind11/native_enum.h +++ b/include/pybind11/native_enum.h @@ -54,7 +54,7 @@ class native_enum : public detail::native_enum_data { disarm_finalize_check("value after finalize"); members.append(make_tuple(name, static_cast(value))); if (doc) { - docs.append(make_tuple(name, doc)); + member_docs.append(make_tuple(name, doc)); } arm_finalize_check(); // There was no exception. return *this; From 0346498be069979936ffe998ac05a11136790438 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 14 Apr 2025 23:14:25 -0700 Subject: [PATCH 8/8] [skip ci] Polish changed documentation (mostly done by ChatGPT). --- docs/classes.rst | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/classes.rst b/docs/classes.rst index 84c0da7396..8788badcd5 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -593,17 +593,20 @@ once. To achieve this, ``py::native_enum`` acts as a buffer to collect the name/value pairs. The ``.finalize()`` call uses the accumulated name/value pairs to build the arguments for constructing a native Python enum type. -The ``py::native_enum`` constructor has a third ``native_type_name`` string -argument which specifies the used base class, e.g., ``"enum.Enum"``. The fourth -optional string argument ``class_doc`` specifies the class documentation string. -Other types can be specified like this: +The ``py::native_enum`` constructor takes a third argument, +``native_type_name``, which specifies the fully qualified name of the Python +base class to use — e.g., ``"enum.Enum"`` or ``"enum.IntEnum"``. A fourth +optional argument, ``class_doc``, provides the docstring for the generated +class. + +For example: .. code-block:: cpp py::native_enum(pet, "Kind", "enum.IntEnum", "Constant specifying the kind of pet") -Any fully-qualified Python name can be specified. The only requirement is -that the named type is similar to +You may use any fully qualified Python name for ``native_type_name``. +The only requirement is that the named type is similar to `enum.Enum `_ in these ways: