diff --git a/docs/advanced/misc.rst b/docs/advanced/misc.rst index edab15fcb7..a68a50bb85 100644 --- a/docs/advanced/misc.rst +++ b/docs/advanced/misc.rst @@ -302,6 +302,77 @@ Note that changes to the settings affect only function bindings created during t lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function, the default settings are restored to prevent unwanted side effects. +Overloaded functions +-------------------- + +The docstring of an overloaded function is prepended with the signature of each overload. +All overload docstrings are then concatenated together +into sections that are separated by each function signature. +The prepended signatures can be read by tools like Sphinx. + +.. code-block:: cpp + + PYBIND11_MODULE(example, m) { + m.def("add", [](int a, int b)->int { return a + b; }, + "Add two integers together."); + m.def("add", [](float a, float b)->float { return a + b; }, + "Add two floating point numbers together."); + } + +The above example would produce the following docstring: + +.. code-block:: pycon + + >>> help(example.add) + + add(...) + | add(arg0: int, arg1: int) -> int + | add(arg0: float, arg1: float) -> float + | Overloaded function. + | + | 1. add(arg0: int, arg1: int) -> int + | + | Add two integers together. + | + | 2. add(arg0: float, arg1: float) -> float + | + | Add two floating point numbers together. + +Calling ``options.disable_function_signatures()`` as shown previously +will cause the docstrings of overloaded functions to be generated without the section headings. +The prepended overload signatures will remain: + +.. code-block:: cpp + + PYBIND11_MODULE(example, m) { + py::options options; + options.disable_function_signatures(); + + m.def("add", [](int a, int b)->int { return a + b; }, + "A function which adds two numbers.\n"); // Note the additional newline here. + m.def("add", [](float a, float b)->float { return a + b; }, + "Internally, a simple addition is performed."); + m.def("add", [](const py::none&, const py::none&)->py::none { return py::none(); }, + "Both numbers can be None, and None will be returned."); + } + +The above example would produce the following docstring: + +.. code-block:: pycon + + >>> help(example.add) + add(...) + | add(arg0: int, arg1: int) -> int + | add(arg0: float, arg1: float) -> float + | add(arg0: None, arg1: None) -> None + | A function which adds two numbers. + | + | Internally, a simple addition is performed. + | Both numbers can be None, and None will be returned. + +Not every overload must supply a docstring. +You may find it easier for a single overload to supply the entire docstring. + .. [#f4] http://www.sphinx-doc.org .. [#f5] http://github.com/pybind/python_example diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index fa018b509d..9285ee0771 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -550,11 +550,15 @@ class cpp_function : public function { int index = 0; /* Create a nice pydoc rec including all signatures and docstrings of the functions in the overload chain */ - if (chain && options::show_function_signatures()) { - // First a generic signature - signatures += rec->name; - signatures += "(*args, **kwargs)\n"; - signatures += "Overloaded function.\n\n"; + if (chain) { + for (auto it = chain_start; it != nullptr; it = it->next) { + signatures += rec->name; + signatures += it->signature; + signatures += "\n"; + } + if (options::show_function_signatures()) { + signatures += "Overloaded function.\n\n"; + } } // Then specific overload signatures bool first_user_def = true; diff --git a/tests/test_docstring_options.cpp b/tests/test_docstring_options.cpp index 4d44f4e20d..3ab2aecef6 100644 --- a/tests/test_docstring_options.cpp +++ b/tests/test_docstring_options.cpp @@ -85,4 +85,21 @@ TEST_SUBMODULE(docstring_options, m) { &DocstringTestFoo::setValue, "This is a property docstring"); } + + m.def("test_overloaded4", [](int a, int b)->int { return a + b; }, + "Add two integers together."); + m.def("test_overloaded4", [](float a, float b)->float { return a + b; }, + "Add two floating point numbers together."); + + { + py::options options; + options.disable_function_signatures(); + + m.def("test_overloaded5", [](int a, int b)->int { return a + b; }, + "A function which adds two numbers.\n"); + m.def("test_overloaded5", [](float a, float b)->float { return a + b; }, + "Internally, a simple addition is performed."); + m.def("test_overloaded5", [](const py::none&, const py::none&)->py::none { return py::none(); }, + "Both numbers can be None, and None will be returned."); + } } diff --git a/tests/test_docstring_options.py b/tests/test_docstring_options.py index fcd16b89fd..7283421ddb 100644 --- a/tests/test_docstring_options.py +++ b/tests/test_docstring_options.py @@ -8,13 +8,26 @@ def test_docstring_options(): assert m.test_function2.__doc__ == "A custom docstring" # docstring specified on just the first overload definition: - assert m.test_overloaded1.__doc__ == "Overload docstring" + assert m.test_overloaded1.__doc__ == ( + "test_overloaded1(i: int) -> None\n" + "test_overloaded1(d: float) -> None\n" + "Overload docstring" + ) # docstring on both overloads: - assert m.test_overloaded2.__doc__ == "overload docstring 1\noverload docstring 2" + assert m.test_overloaded2.__doc__ == ( + "test_overloaded2(i: int) -> None\n" + "test_overloaded2(d: float) -> None\n" + "overload docstring 1\n" + "overload docstring 2" + ) # docstring on only second overload: - assert m.test_overloaded3.__doc__ == "Overload docstr" + assert m.test_overloaded3.__doc__ == ( + "test_overloaded3(i: int) -> None\n" + "test_overloaded3(d: float) -> None\n" + "Overload docstr" + ) # options.enable_function_signatures() assert m.test_function3.__doc__.startswith("test_function3(a: int, b: int) -> None") @@ -39,3 +52,27 @@ def test_docstring_options(): # Suppression of user-defined docstrings for non-function objects assert not m.DocstringTestFoo.__doc__ assert not m.DocstringTestFoo.value_prop.__doc__ + + # Check overload configuration behaviour matches the documentation + assert m.test_overloaded4.__doc__ == ( + "test_overloaded4(arg0: int, arg1: int) -> int\n" + "test_overloaded4(arg0: float, arg1: float) -> float\n" + "Overloaded function.\n" + "\n" + "1. test_overloaded4(arg0: int, arg1: int) -> int\n" + "\n" + "Add two integers together.\n" + "\n" + "2. test_overloaded4(arg0: float, arg1: float) -> float\n" + "\n" + "Add two floating point numbers together.\n" + ) + + assert m.test_overloaded5.__doc__ == ( + "test_overloaded5(arg0: int, arg1: int) -> int\n" + "test_overloaded5(arg0: float, arg1: float) -> float\n" + "test_overloaded5(arg0: None, arg1: None) -> None\n" + "A function which adds two numbers.\n\n" + "Internally, a simple addition is performed.\n" + "Both numbers can be None, and None will be returned." + ) diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index 120a587c45..14ff149ce6 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -86,7 +86,10 @@ def test_init_factory_signature(msg): assert ( msg(m.TestFactory1.__init__.__doc__) == """ - __init__(*args, **kwargs) + __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int) -> None + __init__(self: m.factory_constructors.TestFactory1, arg0: str) -> None + __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.pointer_tag) -> None + __init__(self: m.factory_constructors.TestFactory1, arg0: handle, arg1: int, arg2: handle) -> None Overloaded function. 1. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int) -> None