Skip to content

Commit a4b246a

Browse files
committed
Prepend all overload signatures to docstrings
1 parent a1d0091 commit a4b246a

File tree

5 files changed

+136
-9
lines changed

5 files changed

+136
-9
lines changed

docs/advanced/misc.rst

+71
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,77 @@ Note that changes to the settings affect only function bindings created during t
365365
lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function,
366366
the default settings are restored to prevent unwanted side effects.
367367

368+
Overloaded functions
369+
--------------------
370+
371+
The docstring of an overloaded function is prepended with the signature of each overload.
372+
All overload docstrings are then concatenated together
373+
into sections that are separated by each function signature.
374+
The prepended signatures can be read by tools like Sphinx.
375+
376+
.. code-block:: cpp
377+
378+
PYBIND11_MODULE(example, m) {
379+
m.def("add", [](int a, int b)->int { return a + b; },
380+
"Add two integers together.");
381+
m.def("add", [](float a, float b)->float { return a + b; },
382+
"Add two floating point numbers together.");
383+
}
384+
385+
The above example would produce the following docstring:
386+
387+
.. code-block:: pycon
388+
389+
>>> help(example.add)
390+
391+
add(...)
392+
| add(arg0: int, arg1: int) -> int
393+
| add(arg0: float, arg1: float) -> float
394+
| Overloaded function.
395+
|
396+
| 1. add(arg0: int, arg1: int) -> int
397+
|
398+
| Add two integers together.
399+
|
400+
| 2. add(arg0: float, arg1: float) -> float
401+
|
402+
| Add two floating point numbers together.
403+
404+
Calling ``options.disable_function_signatures()`` as shown previously
405+
will cause the docstrings of overloaded functions to be generated without the section headings.
406+
The prepended overload signatures will remain:
407+
408+
.. code-block:: cpp
409+
410+
PYBIND11_MODULE(example, m) {
411+
py::options options;
412+
options.disable_function_signatures();
413+
414+
m.def("add", [](int a, int b)->int { return a + b; },
415+
"A function which adds two numbers.\n"); // Note the additional newline here.
416+
m.def("add", [](float a, float b)->float { return a + b; },
417+
"Internally, a simple addition is performed.");
418+
m.def("add", [](const py::none&, const py::none&)->py::none { return py::none(); },
419+
"Both numbers can be None, and None will be returned.");
420+
}
421+
422+
The above example would produce the following docstring:
423+
424+
.. code-block:: pycon
425+
426+
>>> help(example.add)
427+
add(...)
428+
| add(arg0: int, arg1: int) -> int
429+
| add(arg0: float, arg1: float) -> float
430+
| add(arg0: None, arg1: None) -> None
431+
| A function which adds two numbers.
432+
|
433+
| Internally, a simple addition is performed.
434+
| Both numbers can be None, and None will be returned.
435+
436+
Not every overload must supply a docstring.
437+
You may find it easier for a single overload to supply the entire docstring.
438+
368439
.. [#f4] http://www.sphinx-doc.org
369440
.. [#f5] http://github.com/pybind/python_example
370441

include/pybind11/pybind11.h

+9-5
Original file line numberDiff line numberDiff line change
@@ -610,11 +610,15 @@ class cpp_function : public function {
610610
int index = 0;
611611
/* Create a nice pydoc rec including all signatures and
612612
docstrings of the functions in the overload chain */
613-
if (chain && options::show_function_signatures()) {
614-
// First a generic signature
615-
signatures += rec->name;
616-
signatures += "(*args, **kwargs)\n";
617-
signatures += "Overloaded function.\n\n";
613+
if (chain) {
614+
for (auto *it = chain_start; it != nullptr; it = it->next) {
615+
signatures += rec->name;
616+
signatures += it->signature;
617+
signatures += "\n";
618+
}
619+
if (options::show_function_signatures()) {
620+
signatures += "Overloaded function.\n\n";
621+
}
618622
}
619623
// Then specific overload signatures
620624
bool first_user_def = true;

tests/test_docstring_options.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,20 @@ TEST_SUBMODULE(docstring_options, m) {
2727
m.def("test_overloaded3", [](int) {}, py::arg("i"));
2828
m.def("test_overloaded3", [](double) {}, py::arg("d"), "Overload docstr");
2929

30+
m.def("test_overloaded4", [](int a, int b)->int { return a + b; },
31+
"A function which adds two numbers.\n");
32+
m.def("test_overloaded4", [](float a, float b)->float { return a + b; },
33+
"Internally, a simple addition is performed.");
34+
m.def("test_overloaded4", [](const py::none&, const py::none&)->py::none { return py::none(); },
35+
"Both numbers can be None, and None will be returned.");
36+
3037
options.enable_function_signatures();
3138

39+
m.def("test_overloaded5", [](int a, int b)->int { return a + b; },
40+
"Add two integers together.");
41+
m.def("test_overloaded5", [](float a, float b)->float { return a + b; },
42+
"Add two floating point numbers together.");
43+
3244
m.def("test_function3", [](int, int) {}, py::arg("a"), py::arg("b"));
3345
m.def("test_function4", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
3446

tests/test_docstring_options.py

+40-3
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,50 @@ def test_docstring_options():
1010
assert m.test_function2.__doc__ == "A custom docstring"
1111

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

1519
# docstring on both overloads:
16-
assert m.test_overloaded2.__doc__ == "overload docstring 1\noverload docstring 2"
20+
assert m.test_overloaded2.__doc__ == (
21+
"test_overloaded2(i: int) -> None\n"
22+
"test_overloaded2(d: float) -> None\n"
23+
"overload docstring 1\n"
24+
"overload docstring 2"
25+
)
1726

1827
# docstring on only second overload:
19-
assert m.test_overloaded3.__doc__ == "Overload docstr"
28+
assert m.test_overloaded3.__doc__ == (
29+
"test_overloaded3(i: int) -> None\n"
30+
"test_overloaded3(d: float) -> None\n"
31+
"Overload docstr"
32+
)
33+
34+
# Check overload configuration behaviour matches the documentation
35+
assert m.test_overloaded4.__doc__ == (
36+
"test_overloaded4(arg0: int, arg1: int) -> int\n"
37+
"test_overloaded4(arg0: float, arg1: float) -> float\n"
38+
"test_overloaded4(arg0: None, arg1: None) -> None\n"
39+
"A function which adds two numbers.\n\n"
40+
"Internally, a simple addition is performed.\n"
41+
"Both numbers can be None, and None will be returned."
42+
)
43+
44+
assert m.test_overloaded5.__doc__ == (
45+
"test_overloaded5(arg0: int, arg1: int) -> int\n"
46+
"test_overloaded5(arg0: float, arg1: float) -> float\n"
47+
"Overloaded function.\n"
48+
"\n"
49+
"1. test_overloaded5(arg0: int, arg1: int) -> int\n"
50+
"\n"
51+
"Add two integers together.\n"
52+
"\n"
53+
"2. test_overloaded5(arg0: float, arg1: float) -> float\n"
54+
"\n"
55+
"Add two floating point numbers together.\n"
56+
)
2057

2158
# options.enable_function_signatures()
2259
assert m.test_function3.__doc__.startswith("test_function3(a: int, b: int) -> None")

tests/test_factory_constructors.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ def test_init_factory_signature(msg):
8888
assert (
8989
msg(m.TestFactory1.__init__.__doc__)
9090
== """
91-
__init__(*args, **kwargs)
91+
__init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int) -> None
92+
__init__(self: m.factory_constructors.TestFactory1, arg0: str) -> None
93+
__init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.pointer_tag) -> None
94+
__init__(self: m.factory_constructors.TestFactory1, arg0: object, arg1: int, arg2: object) -> None
9295
Overloaded function.
9396
9497
1. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int) -> None

0 commit comments

Comments
 (0)