Skip to content

Commit 09b144f

Browse files
AWhetterrwgk
authored andcommitted
Prepend all function signatures to docstrings and disable headings
1 parent 7e7c558 commit 09b144f

File tree

5 files changed

+208
-13
lines changed

5 files changed

+208
-13
lines changed

docs/advanced/misc.rst

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,106 @@ Note that changes to the settings affect only function bindings created during t
302302
lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function,
303303
the default settings are restored to prevent unwanted side effects.
304304

305+
Overloaded functions
306+
--------------------
307+
308+
For overloaded functions, a generic function signature is prepended to the
309+
docstring of a function, and all overload docstrings are concatenated together
310+
into sections that are separated by each function signature.
311+
312+
For example:
313+
314+
.. code-block:: pycon
315+
316+
>>> help(example.add)
317+
318+
add(...)
319+
| add(*args, **kwargs)
320+
| Overloaded function.
321+
|
322+
| 1. add(arg0: int, arg1: int) -> int
323+
|
324+
| Add two integers together.
325+
|
326+
| 2. add(arg0: float, arg1: float) -> float
327+
|
328+
| Add two floating point numbers together.
329+
330+
Calling ``options.enable_prepended_overload_signatures()`` will cause docstrings to be
331+
prepended with the signature of each overload instead of a generic function signature.
332+
The prepended signatures can be read by tools like Sphinx.
333+
334+
.. code-block:: cpp
335+
336+
PYBIND11_MODULE(example, m) {
337+
py::options options;
338+
options.enable_prepended_overload_signatures();
339+
340+
m.def("add", [](int a, int b)->int { return a + b; },
341+
"Add two integers together.");
342+
m.def("add", [](float a, float b)->float { return a + b; },
343+
"Add two floating point numbers together.");
344+
}
345+
346+
The above example would produce the following docstring:
347+
348+
.. code-block:: pycon
349+
350+
>>> help(example.add)
351+
352+
add(...)
353+
| add(arg0: int, arg1: int) -> int
354+
| add(arg0: float, arg1: float) -> float
355+
|
356+
| Overloaded function.
357+
|
358+
| 1. add(arg0: int, arg1: int) -> int
359+
|
360+
| Add two integers together.
361+
|
362+
| 2. add(arg0: float, arg1: float) -> float
363+
|
364+
| Add two floating point numbers together.
365+
366+
Calling ``options.disable_function_signatures()`` as shown previously,
367+
will cause docstrings to be generated without the prepended function signatures
368+
and without the section headings.
369+
To disable only the sections headings, use ``options.disable_section_headings()``:
370+
371+
.. code-block:: cpp
372+
373+
PYBIND11_MODULE(example, m) {
374+
py::options options;
375+
options.disable_section_headings();
376+
options.enable_prepended_overload_signatures();
377+
378+
m.def("add", [](int a, int b)->int { return a + b; },
379+
"A function which adds two numbers.\n"); // Note the additional newline here.
380+
m.def("add", [](float a, float b)->float { return a + b; },
381+
"Internally, a simple addition is performed.");
382+
m.def("add", [](const py::none&, const py::none&)->py::none { return py::none(); },
383+
"Both numbers can be None, and None will be returned.");
384+
}
385+
386+
The above example would produce the following docstring:
387+
388+
.. code-block:: pycon
389+
390+
>>> help(example.add)
391+
392+
add(...)
393+
| add(arg0: int, arg1: int) -> int
394+
| add(arg0: float, arg1: float) -> float
395+
| add(arg0: None, arg1: None) -> None
396+
|
397+
| A function which adds two numbers.
398+
|
399+
| Internally, a simple addition is performed.
400+
| Both numbers can be None, and None will be returned.
401+
402+
Not every overload must supply a docstring.
403+
You may find it easier for a single overload to supply the entire docstring.
404+
305405
.. [#f4] http://www.sphinx-doc.org
306406
.. [#f5] http://github.com/pybind/python_example
307407

include/pybind11/options.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,24 @@ class options {
3838

3939
options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; }
4040

41+
options& disable_section_headings() & { global_state().show_section_headings = false; return *this; }
42+
43+
options& enable_section_headings() & { global_state().show_section_headings = true; return *this; }
44+
45+
options& disable_prepended_overload_signatures() & { global_state().prepend_overload_signatures = false; return *this; }
46+
47+
options& enable_prepended_overload_signatures() & { global_state().prepend_overload_signatures = true; return *this; }
48+
4149
// Getter methods (return the global state):
4250

4351
static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; }
4452

4553
static bool show_function_signatures() { return global_state().show_function_signatures; }
4654

55+
static bool show_section_headings() { return global_state().show_section_headings; }
56+
57+
static bool prepend_overload_signatures() { return global_state().prepend_overload_signatures; }
58+
4759
// This type is not meant to be allocated on the heap.
4860
void* operator new(size_t) = delete;
4961

@@ -52,6 +64,8 @@ class options {
5264
struct state {
5365
bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
5466
bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings.
67+
bool show_section_headings = true; //< Include section headings in docstrings.
68+
bool prepend_overload_signatures = false; //< Prepend all signatures of overloads in docstrings.
5569
};
5670

5771
static state &global_state() {

include/pybind11/pybind11.h

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -502,18 +502,32 @@ class cpp_function : public function {
502502

503503
std::string signatures;
504504
int index = 0;
505+
bool first_user_def = true;
505506
/* Create a nice pydoc rec including all signatures and
506507
docstrings of the functions in the overload chain */
507508
if (chain && options::show_function_signatures()) {
508-
// First a generic signature
509-
signatures += rec->name;
510-
signatures += "(*args, **kwargs)\n";
511-
signatures += "Overloaded function.\n\n";
509+
if (options::prepend_overload_signatures()) {
510+
for (auto it = chain_start; it != nullptr; it = it->next) {
511+
signatures += rec->name;
512+
signatures += it->signature;
513+
signatures += "\n";
514+
}
515+
}
516+
else {
517+
// Use a single generic signature
518+
signatures += rec->name;
519+
signatures += "(*args, **kwargs)";
520+
}
521+
if (options::show_section_headings())
522+
signatures += "\nOverloaded function.\n\n";
523+
else
524+
first_user_def = false;
512525
}
513526
// Then specific overload signatures
514-
bool first_user_def = true;
527+
const bool show_signature_headings = options::show_function_signatures()
528+
&& options::show_section_headings();
515529
for (auto it = chain_start; it != nullptr; it = it->next) {
516-
if (options::show_function_signatures()) {
530+
if (show_signature_headings) {
517531
if (index > 0) signatures += "\n";
518532
if (chain)
519533
signatures += std::to_string(++index) + ". ";
@@ -522,15 +536,14 @@ class cpp_function : public function {
522536
signatures += "\n";
523537
}
524538
if (it->doc && it->doc[0] != '\0' && options::show_user_defined_docstrings()) {
525-
// If we're appending another docstring, and aren't printing function signatures, we
539+
// If we're appending another docstring, and aren't printing signature headings, we
526540
// need to append a newline first:
527-
if (!options::show_function_signatures()) {
528-
if (first_user_def) first_user_def = false;
529-
else signatures += "\n";
530-
}
531-
if (options::show_function_signatures()) signatures += "\n";
541+
if (!show_signature_headings && first_user_def)
542+
first_user_def = false;
543+
else
544+
signatures += "\n";
532545
signatures += it->doc;
533-
if (options::show_function_signatures()) signatures += "\n";
546+
if (show_signature_headings) signatures += "\n";
534547
}
535548
}
536549

tests/test_docstring_options.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,32 @@ TEST_SUBMODULE(docstring_options, m) {
6666
.def_property("value_prop", &DocstringTestFoo::getValue, &DocstringTestFoo::setValue, "This is a property docstring")
6767
;
6868
}
69+
70+
m.def("test_overloaded4", [](int a, int b)->int { return a + b; },
71+
"Add two integers together.");
72+
m.def("test_overloaded4", [](float a, float b)->float { return a + b; },
73+
"Add two floating point numbers together.");
74+
75+
{
76+
py::options options;
77+
options.enable_prepended_overload_signatures();
78+
79+
m.def("test_overloaded5", [](int a, int b)->int { return a + b; },
80+
"Add two integers together.");
81+
m.def("test_overloaded5", [](float a, float b)->float { return a + b; },
82+
"Add two floating point numbers together.");
83+
}
84+
85+
{
86+
py::options options;
87+
options.disable_section_headings();
88+
options.enable_prepended_overload_signatures();
89+
90+
m.def("test_overloaded6", [](int a, int b)->int { return a + b; },
91+
"A function which adds two numbers.\n");
92+
m.def("test_overloaded6", [](float a, float b)->float { return a + b; },
93+
"Internally, a simple addition is performed.");
94+
m.def("test_overloaded6", [](const py::none&, const py::none&)->py::none { return py::none(); },
95+
"Both numbers can be None, and None will be returned.");
96+
}
6997
}

tests/test_docstring_options.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,43 @@ def test_docstring_options():
4040
# Suppression of user-defined docstrings for non-function objects
4141
assert not m.DocstringTestFoo.__doc__
4242
assert not m.DocstringTestFoo.value_prop.__doc__
43+
44+
# Check overload configuration behaviour matches the documentation
45+
assert m.test_overloaded4.__doc__ == (
46+
"test_overloaded4(*args, **kwargs)\n"
47+
"Overloaded function.\n"
48+
"\n"
49+
"1. test_overloaded4(arg0: int, arg1: int) -> int\n"
50+
"\n"
51+
"Add two integers together.\n"
52+
"\n"
53+
"2. test_overloaded4(arg0: float, arg1: float) -> float\n"
54+
"\n"
55+
"Add two floating point numbers together.\n"
56+
)
57+
58+
assert m.test_overloaded5.__doc__ == (
59+
"test_overloaded5(arg0: int, arg1: int) -> int\n"
60+
"test_overloaded5(arg0: float, arg1: float) -> float\n"
61+
"\n"
62+
"Overloaded function.\n"
63+
"\n"
64+
"1. test_overloaded5(arg0: int, arg1: int) -> int\n"
65+
"\n"
66+
"Add two integers together.\n"
67+
"\n"
68+
"2. test_overloaded5(arg0: float, arg1: float) -> float\n"
69+
"\n"
70+
"Add two floating point numbers together.\n"
71+
)
72+
73+
assert m.test_overloaded6.__doc__ == (
74+
"test_overloaded6(arg0: int, arg1: int) -> int\n"
75+
"test_overloaded6(arg0: float, arg1: float) -> float\n"
76+
"test_overloaded6(arg0: None, arg1: None) -> None\n"
77+
"\n"
78+
"A function which adds two numbers.\n"
79+
"\n"
80+
"Internally, a simple addition is performed.\n"
81+
"Both numbers can be None, and None will be returned."
82+
)

0 commit comments

Comments
 (0)