diff --git a/docs/classes.rst b/docs/classes.rst index b6923963dc..8788badcd5 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,16 +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 supports a third optional -``native_type_name`` string argument, with default value ``"enum.Enum"``. -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") + 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: diff --git a/include/pybind11/detail/native_enum_data.h b/include/pybind11/detail/native_enum_data.h index 5192ed51a6..b12ca6202b 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 *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}, export_values_flag{false}, finalize_needed{false} {} + native_type_name{native_type_name}, class_doc(class_doc), export_values_flag{false}, + finalize_needed{false} {} void finalize(); @@ -70,10 +72,11 @@ class native_enum_data { object parent_scope; str enum_name; str native_type_name; + std::string class_doc; protected: list members; - list docs; + list member_docs; bool export_values_flag : 1; // Attention: It is best to keep the bools together. private: @@ -191,7 +194,10 @@ inline void native_enum_data::finalize() { parent_scope.attr(member_name) = py_enum[member_name]; } } - for (auto doc : docs) { + if (!class_doc.empty()) { + py_enum.attr("__doc__") = class_doc.c_str(); + } + 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 759acd48db..5537218f21 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 *class_doc = "") : detail::native_enum_data( - parent_scope, name, native_type_name, 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( @@ -53,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; 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..b942fca0d5 100644 --- a/tests/test_native_enum.py +++ b/tests/test_native_enum.py @@ -108,6 +108,12 @@ def test_export_values(): assert m.exv1 is m.export_values.exv1 +def test_class_doc(): + pure_native = enum.IntEnum("pure_native", (("mem", 0),)) + assert m.smallenum.__doc__ == "doc smallenum" + assert m.color.__doc__ == pure_native.__doc__ + + def test_member_doc(): pure_native = enum.IntEnum("pure_native", (("mem", 0),)) assert m.member_doc.mem0.__doc__ == "docA"