Skip to content

Commit 3c58634

Browse files
dyollbrwgk
andauthored
Add class doc string to native_enum (#5617)
* add class doc string to native_enum * adapt doc argument name * fix test, make class enum doc None by default * fix other python versions? * make clang-tidy happy * rename 'enum_doc' to 'class_doc' * update documentation * [skip ci] Polish changed documentation (mostly done by ChatGPT). --------- Co-authored-by: Bryn Lloyd <[email protected]> Co-authored-by: Ralf W. Grosse-Kunstleve <[email protected]>
1 parent b3bb31c commit 3c58634

File tree

5 files changed

+33
-16
lines changed

5 files changed

+33
-16
lines changed

docs/classes.rst

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ The binding code for this example looks as follows:
556556
.def_readwrite("type", &Pet::type)
557557
.def_readwrite("attr", &Pet::attr);
558558
559-
py::native_enum<Pet::Kind>(pet, "Kind")
559+
py::native_enum<Pet::Kind>(pet, "Kind", "enum.Enum")
560560
.value("Dog", Pet::Kind::Dog)
561561
.value("Cat", Pet::Kind::Cat)
562562
.export_values()
@@ -593,16 +593,20 @@ once. To achieve this, ``py::native_enum`` acts as a buffer to collect the
593593
name/value pairs. The ``.finalize()`` call uses the accumulated name/value
594594
pairs to build the arguments for constructing a native Python enum type.
595595

596-
The ``py::native_enum`` constructor supports a third optional
597-
``native_type_name`` string argument, with default value ``"enum.Enum"``.
598-
Other types can be specified like this:
596+
The ``py::native_enum`` constructor takes a third argument,
597+
``native_type_name``, which specifies the fully qualified name of the Python
598+
base class to use — e.g., ``"enum.Enum"`` or ``"enum.IntEnum"``. A fourth
599+
optional argument, ``class_doc``, provides the docstring for the generated
600+
class.
601+
602+
For example:
599603

600604
.. code-block:: cpp
601605
602-
py::native_enum<Pet::Kind>(pet, "Kind", "enum.IntEnum")
606+
py::native_enum<Pet::Kind>(pet, "Kind", "enum.IntEnum", "Constant specifying the kind of pet")
603607
604-
Any fully-qualified Python name can be specified. The only requirement is
605-
that the named type is similar to
608+
You may use any fully qualified Python name for ``native_type_name``.
609+
The only requirement is that the named type is similar to
606610
`enum.Enum <https://docs.python.org/3/library/enum.html#enum.Enum>`_
607611
in these ways:
608612

include/pybind11/detail/native_enum_data.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ class native_enum_data {
2929
native_enum_data(const object &parent_scope,
3030
const char *enum_name,
3131
const char *native_type_name,
32+
const char *class_doc,
3233
const std::type_index &enum_type_index)
3334
: enum_name_encoded{enum_name}, native_type_name_encoded{native_type_name},
3435
enum_type_index{enum_type_index}, parent_scope(parent_scope), enum_name{enum_name},
35-
native_type_name{native_type_name}, export_values_flag{false}, finalize_needed{false} {}
36+
native_type_name{native_type_name}, class_doc(class_doc), export_values_flag{false},
37+
finalize_needed{false} {}
3638

3739
void finalize();
3840

@@ -70,10 +72,11 @@ class native_enum_data {
7072
object parent_scope;
7173
str enum_name;
7274
str native_type_name;
75+
std::string class_doc;
7376

7477
protected:
7578
list members;
76-
list docs;
79+
list member_docs;
7780
bool export_values_flag : 1; // Attention: It is best to keep the bools together.
7881

7982
private:
@@ -191,7 +194,10 @@ inline void native_enum_data::finalize() {
191194
parent_scope.attr(member_name) = py_enum[member_name];
192195
}
193196
}
194-
for (auto doc : docs) {
197+
if (!class_doc.empty()) {
198+
py_enum.attr("__doc__") = class_doc.c_str();
199+
}
200+
for (auto doc : member_docs) {
195201
py_enum[doc[int_(0)]].attr("__doc__") = doc[int_(1)];
196202
}
197203
global_internals_native_enum_type_map_set_item(enum_type_index, py_enum.release().ptr());

include/pybind11/native_enum.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ class native_enum : public detail::native_enum_data {
2424

2525
native_enum(const object &parent_scope,
2626
const char *name,
27-
const char *native_type_name = "enum.Enum")
27+
const char *native_type_name,
28+
const char *class_doc = "")
2829
: detail::native_enum_data(
29-
parent_scope, name, native_type_name, std::type_index(typeid(EnumType))) {
30+
parent_scope, name, native_type_name, class_doc, std::type_index(typeid(EnumType))) {
3031
if (detail::get_local_type_info(typeid(EnumType)) != nullptr
3132
|| detail::get_global_type_info(typeid(EnumType)) != nullptr) {
3233
pybind11_fail(
@@ -53,7 +54,7 @@ class native_enum : public detail::native_enum_data {
5354
disarm_finalize_check("value after finalize");
5455
members.append(make_tuple(name, static_cast<Underlying>(value)));
5556
if (doc) {
56-
docs.append(make_tuple(name, doc));
57+
member_docs.append(make_tuple(name, doc));
5758
}
5859
arm_finalize_check(); // There was no exception.
5960
return *this;

tests/test_native_enum.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
7676
TEST_SUBMODULE(native_enum, m) {
7777
using namespace test_native_enum;
7878

79-
py::native_enum<smallenum>(m, "smallenum", "enum.IntEnum")
79+
py::native_enum<smallenum>(m, "smallenum", "enum.IntEnum", "doc smallenum")
8080
.value("a", smallenum::a)
8181
.value("b", smallenum::b)
8282
.value("c", smallenum::c)
@@ -89,7 +89,7 @@ TEST_SUBMODULE(native_enum, m) {
8989
.value("blue", color::blue)
9090
.finalize();
9191

92-
py::native_enum<altitude>(m, "altitude")
92+
py::native_enum<altitude>(m, "altitude", "enum.Enum")
9393
.value("high", altitude::high)
9494
.value("low", altitude::low)
9595
.finalize();
@@ -189,7 +189,7 @@ TEST_SUBMODULE(native_enum, m) {
189189
py::native_enum<fake>(m, "fake_double_registration_native_enum", "enum.IntEnum")
190190
.value("x", fake::x)
191191
.finalize();
192-
py::native_enum<fake>(m, "fake_double_registration_native_enum");
192+
py::native_enum<fake>(m, "fake_double_registration_native_enum", "enum.Enum");
193193
});
194194

195195
m.def("native_enum_name_clash", [](py::module_ &m) {

tests/test_native_enum.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ def test_export_values():
108108
assert m.exv1 is m.export_values.exv1
109109

110110

111+
def test_class_doc():
112+
pure_native = enum.IntEnum("pure_native", (("mem", 0),))
113+
assert m.smallenum.__doc__ == "doc smallenum"
114+
assert m.color.__doc__ == pure_native.__doc__
115+
116+
111117
def test_member_doc():
112118
pure_native = enum.IntEnum("pure_native", (("mem", 0),))
113119
assert m.member_doc.mem0.__doc__ == "docA"

0 commit comments

Comments
 (0)