Skip to content

Commit 0cc91ca

Browse files
committed
Prepend all overload signatures to docstrings
1 parent 65ec5de commit 0cc91ca

File tree

5 files changed

+141
-9
lines changed

5 files changed

+141
-9
lines changed

docs/advanced/misc.rst

+71
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,77 @@ 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+
The docstring of an overloaded function is prepended with the signature of each overload.
309+
All overload docstrings are then concatenated together
310+
into sections that are separated by each function signature.
311+
The prepended signatures can be read by tools like Sphinx.
312+
313+
.. code-block:: cpp
314+
315+
PYBIND11_MODULE(example, m) {
316+
m.def("add", [](int a, int b)->int { return a + b; },
317+
"Add two integers together.");
318+
m.def("add", [](float a, float b)->float { return a + b; },
319+
"Add two floating point numbers together.");
320+
}
321+
322+
The above example would produce the following docstring:
323+
324+
.. code-block:: pycon
325+
326+
>>> help(example.add)
327+
328+
add(...)
329+
| add(arg0: int, arg1: int) -> int
330+
| add(arg0: float, arg1: float) -> float
331+
| Overloaded function.
332+
|
333+
| 1. add(arg0: int, arg1: int) -> int
334+
|
335+
| Add two integers together.
336+
|
337+
| 2. add(arg0: float, arg1: float) -> float
338+
|
339+
| Add two floating point numbers together.
340+
341+
Calling ``options.disable_function_signatures()`` as shown previously
342+
will cause the docstrings of overloaded functions to be generated without the section headings.
343+
The prepended overload signatures will remain:
344+
345+
.. code-block:: cpp
346+
347+
PYBIND11_MODULE(example, m) {
348+
py::options options;
349+
options.disable_function_signatures();
350+
351+
m.def("add", [](int a, int b)->int { return a + b; },
352+
"A function which adds two numbers.\n"); // Note the additional newline here.
353+
m.def("add", [](float a, float b)->float { return a + b; },
354+
"Internally, a simple addition is performed.");
355+
m.def("add", [](const py::none&, const py::none&)->py::none { return py::none(); },
356+
"Both numbers can be None, and None will be returned.");
357+
}
358+
359+
The above example would produce the following docstring:
360+
361+
.. code-block:: pycon
362+
363+
>>> help(example.add)
364+
add(...)
365+
| add(arg0: int, arg1: int) -> int
366+
| add(arg0: float, arg1: float) -> float
367+
| add(arg0: None, arg1: None) -> None
368+
| A function which adds two numbers.
369+
|
370+
| Internally, a simple addition is performed.
371+
| Both numbers can be None, and None will be returned.
372+
373+
Not every overload must supply a docstring.
374+
You may find it easier for a single overload to supply the entire docstring.
375+
305376
.. [#f4] http://www.sphinx-doc.org
306377
.. [#f5] http://github.com/pybind/python_example
307378

include/pybind11/pybind11.h

+9-5
Original file line numberDiff line numberDiff line change
@@ -550,11 +550,15 @@ class cpp_function : public function {
550550
int index = 0;
551551
/* Create a nice pydoc rec including all signatures and
552552
docstrings of the functions in the overload chain */
553-
if (chain && options::show_function_signatures()) {
554-
// First a generic signature
555-
signatures += rec->name;
556-
signatures += "(*args, **kwargs)\n";
557-
signatures += "Overloaded function.\n\n";
553+
if (chain) {
554+
for (auto it = chain_start; it != nullptr; it = it->next) {
555+
signatures += rec->name;
556+
signatures += it->signature;
557+
signatures += "\n";
558+
}
559+
if (options::show_function_signatures()) {
560+
signatures += "Overloaded function.\n\n";
561+
}
558562
}
559563
// Then specific overload signatures
560564
bool first_user_def = true;

tests/test_docstring_options.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,21 @@ TEST_SUBMODULE(docstring_options, m) {
8585
&DocstringTestFoo::setValue,
8686
"This is a property docstring");
8787
}
88+
89+
m.def("test_overloaded4", [](int a, int b)->int { return a + b; },
90+
"Add two integers together.");
91+
m.def("test_overloaded4", [](float a, float b)->float { return a + b; },
92+
"Add two floating point numbers together.");
93+
94+
{
95+
py::options options;
96+
options.disable_function_signatures();
97+
98+
m.def("test_overloaded5", [](int a, int b)->int { return a + b; },
99+
"A function which adds two numbers.\n");
100+
m.def("test_overloaded5", [](float a, float b)->float { return a + b; },
101+
"Internally, a simple addition is performed.");
102+
m.def("test_overloaded5", [](const py::none&, const py::none&)->py::none { return py::none(); },
103+
"Both numbers can be None, and None will be returned.");
104+
}
88105
}

tests/test_docstring_options.py

+40-3
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,26 @@ def test_docstring_options():
88
assert m.test_function2.__doc__ == "A custom docstring"
99

1010
# docstring specified on just the first overload definition:
11-
assert m.test_overloaded1.__doc__ == "Overload docstring"
11+
assert m.test_overloaded1.__doc__ == (
12+
"test_overloaded1(i: int) -> None\n"
13+
"test_overloaded1(d: float) -> None\n"
14+
"Overload docstring"
15+
)
1216

1317
# docstring on both overloads:
14-
assert m.test_overloaded2.__doc__ == "overload docstring 1\noverload docstring 2"
18+
assert m.test_overloaded2.__doc__ == (
19+
"test_overloaded2(i: int) -> None\n"
20+
"test_overloaded2(d: float) -> None\n"
21+
"overload docstring 1\n"
22+
"overload docstring 2"
23+
)
1524

1625
# docstring on only second overload:
17-
assert m.test_overloaded3.__doc__ == "Overload docstr"
26+
assert m.test_overloaded3.__doc__ == (
27+
"test_overloaded3(i: int) -> None\n"
28+
"test_overloaded3(d: float) -> None\n"
29+
"Overload docstr"
30+
)
1831

1932
# options.enable_function_signatures()
2033
assert m.test_function3.__doc__.startswith("test_function3(a: int, b: int) -> None")
@@ -39,3 +52,27 @@ def test_docstring_options():
3952
# Suppression of user-defined docstrings for non-function objects
4053
assert not m.DocstringTestFoo.__doc__
4154
assert not m.DocstringTestFoo.value_prop.__doc__
55+
56+
# Check overload configuration behaviour matches the documentation
57+
assert m.test_overloaded4.__doc__ == (
58+
"test_overloaded4(arg0: int, arg1: int) -> int\n"
59+
"test_overloaded4(arg0: float, arg1: float) -> float\n"
60+
"Overloaded function.\n"
61+
"\n"
62+
"1. test_overloaded4(arg0: int, arg1: int) -> int\n"
63+
"\n"
64+
"Add two integers together.\n"
65+
"\n"
66+
"2. test_overloaded4(arg0: float, arg1: float) -> float\n"
67+
"\n"
68+
"Add two floating point numbers together.\n"
69+
)
70+
71+
assert m.test_overloaded5.__doc__ == (
72+
"test_overloaded5(arg0: int, arg1: int) -> int\n"
73+
"test_overloaded5(arg0: float, arg1: float) -> float\n"
74+
"test_overloaded5(arg0: None, arg1: None) -> None\n"
75+
"A function which adds two numbers.\n\n"
76+
"Internally, a simple addition is performed.\n"
77+
"Both numbers can be None, and None will be returned."
78+
)

tests/test_factory_constructors.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ def test_init_factory_signature(msg):
8686
assert (
8787
msg(m.TestFactory1.__init__.__doc__)
8888
== """
89-
__init__(*args, **kwargs)
89+
__init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int) -> None
90+
__init__(self: m.factory_constructors.TestFactory1, arg0: str) -> None
91+
__init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.pointer_tag) -> None
92+
__init__(self: m.factory_constructors.TestFactory1, arg0: handle, arg1: int, arg2: handle) -> None
9093
Overloaded function.
9194
9295
1. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int) -> None

0 commit comments

Comments
 (0)