Skip to content

Commit 0012685

Browse files
knarfSpre-commit-ci[bot]rwgk
authored
Add option for enable/disable enum members in docstring. (#2768)
* Add option for enable/disable enum members in docstring * Add tests for disable enum members docstring option * Add docstring options to documentation * style: pre-commit fixes * Fix typos in documentation * Improve documentation wording * Apply suggestions by @Skylion007 Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ralf W. Grosse-Kunstleve <rwgk@google.com>
1 parent 65374c8 commit 0012685

File tree

5 files changed

+129
-22
lines changed

5 files changed

+129
-22
lines changed

Diff for: docs/advanced/misc.rst

+9
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,15 @@ The class ``options`` allows you to selectively suppress auto-generated signatur
324324
m.def("add", [](int a, int b) { return a + b; }, "A function which adds two numbers");
325325
}
326326
327+
pybind11 also appends all members of an enum to the resulting enum docstring.
328+
This default behavior can be disabled by using the ``disable_enum_members_docstring()``
329+
function of the ``options`` class.
330+
331+
With ``disable_user_defined_docstrings()`` all user defined docstrings of
332+
``module_::def()``, ``class_::def()`` and ``enum_()`` are disabled, but the
333+
function signatures and enum members are included in the docstring, unless they
334+
are disabled separately.
335+
327336
Note that changes to the settings affect only function bindings created during the
328337
lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function,
329338
the default settings are restored to prevent unwanted side effects.

Diff for: include/pybind11/options.h

+16
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ class options {
4747
return *this;
4848
}
4949

50+
options &disable_enum_members_docstring() & {
51+
global_state().show_enum_members_docstring = false;
52+
return *this;
53+
}
54+
55+
options &enable_enum_members_docstring() & {
56+
global_state().show_enum_members_docstring = true;
57+
return *this;
58+
}
59+
5060
// Getter methods (return the global state):
5161

5262
static bool show_user_defined_docstrings() {
@@ -55,6 +65,10 @@ class options {
5565

5666
static bool show_function_signatures() { return global_state().show_function_signatures; }
5767

68+
static bool show_enum_members_docstring() {
69+
return global_state().show_enum_members_docstring;
70+
}
71+
5872
// This type is not meant to be allocated on the heap.
5973
void *operator new(size_t) = delete;
6074

@@ -63,6 +77,8 @@ class options {
6377
bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
6478
bool show_function_signatures = true; //< Include auto-generated function signatures
6579
// in docstrings.
80+
bool show_enum_members_docstring = true; //< Include auto-generated member list in enum
81+
// docstrings.
6682
};
6783

6884
static state &global_state() {

Diff for: include/pybind11/pybind11.h

+28-22
Original file line numberDiff line numberDiff line change
@@ -1972,29 +1972,35 @@ struct enum_base {
19721972
name("name"),
19731973
is_method(m_base));
19741974

1975-
m_base.attr("__doc__") = static_property(
1976-
cpp_function(
1977-
[](handle arg) -> std::string {
1978-
std::string docstring;
1979-
dict entries = arg.attr("__entries");
1980-
if (((PyTypeObject *) arg.ptr())->tp_doc) {
1981-
docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n";
1982-
}
1983-
docstring += "Members:";
1984-
for (auto kv : entries) {
1985-
auto key = std::string(pybind11::str(kv.first));
1986-
auto comment = kv.second[int_(1)];
1987-
docstring += "\n\n " + key;
1988-
if (!comment.is_none()) {
1989-
docstring += " : " + (std::string) pybind11::str(comment);
1975+
if (options::show_enum_members_docstring()) {
1976+
m_base.attr("__doc__") = static_property(
1977+
cpp_function(
1978+
[](handle arg) -> std::string {
1979+
std::string docstring;
1980+
dict entries = arg.attr("__entries");
1981+
if (((PyTypeObject *) arg.ptr())->tp_doc) {
1982+
docstring += std::string(
1983+
reinterpret_cast<PyTypeObject *>(arg.ptr())->tp_doc);
1984+
docstring += "\n\n";
19901985
}
1991-
}
1992-
return docstring;
1993-
},
1994-
name("__doc__")),
1995-
none(),
1996-
none(),
1997-
"");
1986+
docstring += "Members:";
1987+
for (auto kv : entries) {
1988+
auto key = std::string(pybind11::str(kv.first));
1989+
auto comment = kv.second[int_(1)];
1990+
docstring += "\n\n ";
1991+
docstring += key;
1992+
if (!comment.is_none()) {
1993+
docstring += " : ";
1994+
docstring += pybind11::str(comment).cast<std::string>();
1995+
}
1996+
}
1997+
return docstring;
1998+
},
1999+
name("__doc__")),
2000+
none(),
2001+
none(),
2002+
"");
2003+
}
19982004

19992005
m_base.attr("__members__") = static_property(cpp_function(
20002006
[](handle arg) -> dict {

Diff for: tests/test_docstring_options.cpp

+53
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,57 @@ TEST_SUBMODULE(docstring_options, m) {
8585
&DocstringTestFoo::setValue,
8686
"This is a property docstring");
8787
}
88+
89+
{
90+
enum class DocstringTestEnum1 { Member1, Member2 };
91+
92+
py::enum_<DocstringTestEnum1>(m, "DocstringTestEnum1", "Enum docstring")
93+
.value("Member1", DocstringTestEnum1::Member1)
94+
.value("Member2", DocstringTestEnum1::Member2);
95+
}
96+
97+
{
98+
py::options options;
99+
options.enable_enum_members_docstring();
100+
101+
enum class DocstringTestEnum2 { Member1, Member2 };
102+
103+
py::enum_<DocstringTestEnum2>(m, "DocstringTestEnum2", "Enum docstring")
104+
.value("Member1", DocstringTestEnum2::Member1)
105+
.value("Member2", DocstringTestEnum2::Member2);
106+
}
107+
108+
{
109+
py::options options;
110+
options.disable_enum_members_docstring();
111+
112+
enum class DocstringTestEnum3 { Member1, Member2 };
113+
114+
py::enum_<DocstringTestEnum3>(m, "DocstringTestEnum3", "Enum docstring")
115+
.value("Member1", DocstringTestEnum3::Member1)
116+
.value("Member2", DocstringTestEnum3::Member2);
117+
}
118+
119+
{
120+
py::options options;
121+
options.disable_user_defined_docstrings();
122+
123+
enum class DocstringTestEnum4 { Member1, Member2 };
124+
125+
py::enum_<DocstringTestEnum4>(m, "DocstringTestEnum4", "Enum docstring")
126+
.value("Member1", DocstringTestEnum4::Member1)
127+
.value("Member2", DocstringTestEnum4::Member2);
128+
}
129+
130+
{
131+
py::options options;
132+
options.disable_user_defined_docstrings();
133+
options.disable_enum_members_docstring();
134+
135+
enum class DocstringTestEnum5 { Member1, Member2 };
136+
137+
py::enum_<DocstringTestEnum5>(m, "DocstringTestEnum5", "Enum docstring")
138+
.value("Member1", DocstringTestEnum5::Member1)
139+
.value("Member2", DocstringTestEnum5::Member2);
140+
}
88141
}

Diff for: tests/test_docstring_options.py

+23
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,26 @@ def test_docstring_options():
3939
# Suppression of user-defined docstrings for non-function objects
4040
assert not m.DocstringTestFoo.__doc__
4141
assert not m.DocstringTestFoo.value_prop.__doc__
42+
43+
# Check existig behaviour of enum docstings
44+
assert (
45+
m.DocstringTestEnum1.__doc__
46+
== "Enum docstring\n\nMembers:\n\n Member1\n\n Member2"
47+
)
48+
49+
# options.enable_enum_members_docstring()
50+
assert (
51+
m.DocstringTestEnum2.__doc__
52+
== "Enum docstring\n\nMembers:\n\n Member1\n\n Member2"
53+
)
54+
55+
# options.disable_enum_members_docstring()
56+
assert m.DocstringTestEnum3.__doc__ == "Enum docstring"
57+
58+
# options.disable_user_defined_docstrings()
59+
assert m.DocstringTestEnum4.__doc__ == "Members:\n\n Member1\n\n Member2"
60+
61+
# options.disable_user_defined_docstrings()
62+
# options.disable_enum_members_docstring()
63+
# When all options are disabled, no docstring (instead of an empty one) should be generated
64+
assert m.DocstringTestEnum5.__doc__ is None

0 commit comments

Comments
 (0)