From 1bd6875d4dedde96fcb4bf65e4a62d79f7868c38 Mon Sep 17 00:00:00 2001 From: Ashley Whetter Date: Fri, 3 Jul 2020 16:55:10 -0700 Subject: [PATCH] Operator methods include a type signature of the additional overload Closes #2269 --- include/pybind11/pybind11.h | 25 ++++++++++++++++++++++--- tests/test_operator_overloading.py | 12 ++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index da682ea704..fe3d581fc5 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -257,6 +257,7 @@ class cpp_function : public function { /* Generate a proper function signature */ std::string signature; size_t type_index = 0, arg_index = 0; + std::string self_type("object"); for (auto *pc = text; *pc != '\0'; ++pc) { const auto c = *pc; @@ -293,9 +294,13 @@ class cpp_function : public function { pybind11_fail("Internal error while parsing type signature (1)"); if (auto tinfo = detail::get_type_info(*t)) { handle th((PyObject *) tinfo->type); - signature += + std::string tname = th.attr("__module__").cast() + "." + th.attr("__qualname__").cast(); // Python 3.3+, but we backport it to earlier versions + signature += tname; + if (arg_index == 0 && rec->is_method) { + self_type = tname; + } } else if (rec->is_new_style_constructor && arg_index == 0) { // A new-style `__init__` takes `self` as `value_and_holder`. // Rewrite it to the proper class type. @@ -306,6 +311,9 @@ class cpp_function : public function { std::string tname(t->name()); detail::clean_type_id(tname); signature += tname; + if (arg_index == 0 && rec->is_method) { + self_type = tname; + } } } else { signature += c; @@ -404,9 +412,10 @@ class cpp_function : public function { std::string signatures; int index = 0; + const bool has_overloads = (chain || rec->is_operator); /* Create a nice pydoc rec including all signatures and docstrings of the functions in the overload chain */ - if (chain && options::show_function_signatures()) { + if (has_overloads && options::show_function_signatures()) { // First a generic signature signatures += rec->name; signatures += "(*args, **kwargs)\n"; @@ -417,7 +426,7 @@ class cpp_function : public function { for (auto it = chain_start; it != nullptr; it = it->next) { if (options::show_function_signatures()) { if (index > 0) signatures += "\n"; - if (chain) + if (has_overloads) signatures += std::to_string(++index) + ". "; signatures += rec->name; signatures += it->signature; @@ -435,6 +444,16 @@ class cpp_function : public function { if (options::show_function_signatures()) signatures += "\n"; } } + if (rec->is_operator) { + signatures += "\n"; + signatures += std::to_string(++index) + ". "; + signatures += rec->name; + signatures += "("; + if (rec->is_method) { + signatures += "self: " + self_type + ", "; + } + signatures += "*args, **kwargs) -> NotImplemented\n"; + } /* Install docstring */ auto *func = (PyCFunctionObject *) m_ptr; diff --git a/tests/test_operator_overloading.py b/tests/test_operator_overloading.py index 39e3aee271..8f3bd81b54 100644 --- a/tests/test_operator_overloading.py +++ b/tests/test_operator_overloading.py @@ -143,3 +143,15 @@ def test_overriding_eq_reset_hash(): assert hash(hashable(15)) == 15 assert hash(hashable(15)) == hash(hashable(15)) + + +def test_docstring_includes_type_signature(msg): + """#2269: Docstring includes a type signature of the additional overload""" + assert msg(m.Vector2.__eq__.__doc__) == """ + __eq__(*args, **kwargs) + Overloaded function. + + 1. __eq__(self: m.operators.Vector2, arg0: m.operators.Vector2) -> bool + + 2. __eq__(self: m.operators.Vector2, *args, **kwargs) -> NotImplemented + """