From ecced6c5ae399d7c0214b57a5b24ba8c1fbebd1b Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Sun, 31 Jul 2016 20:03:18 +0200 Subject: [PATCH] Use generic arg names for functions without explicitly named arguments Example signatures (old => new): foo(int) => foo(arg0: int) bar(Object, int) => bar(self: Object, arg0: int) The change makes the signatures uniform for named and unnamed arguments and it helps static analysis tools reconstruct function signatures from docstrings. This also tweaks the signature whitespace style to better conform to PEP 8 for annotations and default arguments: " : " => ": " " = " => "=" --- example/example-arg-keywords-and-defaults.cpp | 11 +++++- example/example-arg-keywords-and-defaults.py | 17 ++++++--- example/example-arg-keywords-and-defaults.ref | 38 ++++++++++++++----- example/example-callbacks.ref | 2 +- example/issues.ref | 6 +-- include/pybind11/pybind11.h | 25 +++++++----- 6 files changed, 71 insertions(+), 28 deletions(-) diff --git a/example/example-arg-keywords-and-defaults.cpp b/example/example-arg-keywords-and-defaults.cpp index 4e620de52..9c58605fc 100644 --- a/example/example-arg-keywords-and-defaults.cpp +++ b/example/example-arg-keywords-and-defaults.cpp @@ -40,8 +40,13 @@ void args_kwargs_function(py::args args, py::kwargs kwargs) { } } +struct KWClass { + void foo(int, float) {} +}; + void init_ex_arg_keywords_and_defaults(py::module &m) { - m.def("kw_func", &kw_func, py::arg("x"), py::arg("y")); + m.def("kw_func0", &kw_func); + m.def("kw_func1", &kw_func, py::arg("x"), py::arg("y")); m.def("kw_func2", &kw_func, py::arg("x") = 100, py::arg("y") = 200); m.def("kw_func3", [](const char *) { }, py::arg("data") = std::string("Hello world!")); @@ -59,4 +64,8 @@ void init_ex_arg_keywords_and_defaults(py::module &m) { using namespace py::literals; m.def("kw_func_udl", &kw_func, "x"_a, "y"_a=300); m.def("kw_func_udl_z", &kw_func, "x"_a, "y"_a=0); + + py::class_(m, "KWClass") + .def("foo0", &KWClass::foo) + .def("foo1", &KWClass::foo, "x"_a, "y"_a); } diff --git a/example/example-arg-keywords-and-defaults.py b/example/example-arg-keywords-and-defaults.py index 09ba13e6c..2536782fc 100755 --- a/example/example-arg-keywords-and-defaults.py +++ b/example/example-arg-keywords-and-defaults.py @@ -5,19 +5,26 @@ import pydoc sys.path.append('.') -from example import kw_func, kw_func2, kw_func3, kw_func4, call_kw_func +from example import kw_func0, kw_func1, kw_func2, kw_func3, kw_func4, call_kw_func from example import args_function, args_kwargs_function, kw_func_udl, kw_func_udl_z +from example import KWClass -print(pydoc.render_doc(kw_func, "Help on %s")) +print(pydoc.render_doc(kw_func0, "Help on %s")) +print(pydoc.render_doc(kw_func1, "Help on %s")) print(pydoc.render_doc(kw_func2, "Help on %s")) print(pydoc.render_doc(kw_func3, "Help on %s")) print(pydoc.render_doc(kw_func4, "Help on %s")) print(pydoc.render_doc(kw_func_udl, "Help on %s")) print(pydoc.render_doc(kw_func_udl_z, "Help on %s")) +print(pydoc.render_doc(args_function, "Help on %s")) +print(pydoc.render_doc(args_kwargs_function, "Help on %s")) -kw_func(5, 10) -kw_func(5, y=10) -kw_func(y=10, x=5) +print(KWClass.foo0.__doc__) +print(KWClass.foo1.__doc__) + +kw_func1(5, 10) +kw_func1(5, y=10) +kw_func1(y=10, x=5) kw_func2() diff --git a/example/example-arg-keywords-and-defaults.ref b/example/example-arg-keywords-and-defaults.ref index a693b6ceb..68895743e 100644 --- a/example/example-arg-keywords-and-defaults.ref +++ b/example/example-arg-keywords-and-defaults.ref @@ -1,32 +1,52 @@ -Help on built-in function kw_func in module example +Help on built-in function kw_func0 in module example -kkww__ffuunncc(...) - kw_func(x : int, y : int) -> NoneType +kkww__ffuunncc00(...) + kw_func0(arg0: int, arg1: int) -> NoneType + +Help on built-in function kw_func1 in module example + +kkww__ffuunncc11(...) + kw_func1(x: int, y: int) -> NoneType Help on built-in function kw_func2 in module example kkww__ffuunncc22(...) - kw_func2(x : int = 100L, y : int = 200L) -> NoneType + kw_func2(x: int=100L, y: int=200L) -> NoneType Help on built-in function kw_func3 in module example kkww__ffuunncc33(...) - kw_func3(data : unicode = u'Hello world!') -> NoneType + kw_func3(data: unicode=u'Hello world!') -> NoneType Help on built-in function kw_func4 in module example kkww__ffuunncc44(...) - kw_func4(myList : list = [13L, 17L]) -> NoneType + kw_func4(myList: list=[13L, 17L]) -> NoneType Help on built-in function kw_func_udl in module example kkww__ffuunncc__uuddll(...) - kw_func_udl(x : int, y : int = 300L) -> NoneType + kw_func_udl(x: int, y: int=300L) -> NoneType Help on built-in function kw_func_udl_z in module example kkww__ffuunncc__uuddll__zz(...) - kw_func_udl_z(x : int, y : int = 0L) -> NoneType + kw_func_udl_z(x: int, y: int=0L) -> NoneType + +Help on built-in function args_function in module example + +aarrggss__ffuunnccttiioonn(...) + args_function(*args) -> NoneType + +Help on built-in function args_kwargs_function in module example + +aarrggss__kkwwaarrggss__ffuunnccttiioonn(...) + args_kwargs_function(*args, **kwargs) -> NoneType + + +foo0(self: KWClass, arg0: int, arg1: float) -> NoneType +foo1(self: KWClass, x: int, y: float) -> NoneType + kw_func(x=5, y=10) kw_func(x=5, y=10) @@ -38,7 +58,7 @@ kw_func(x=100, y=10) kw_func(x=5, y=10) kw_func(x=5, y=10) Caught expected exception: Incompatible function arguments. The following argument types are supported: - 1. (x : int = 100L, y : int = 200L) -> NoneType + 1. (x: int=100L, y: int=200L) -> NoneType Invoked with: kw_func4: 13 17 kw_func4: 1 2 3 diff --git a/example/example-callbacks.ref b/example/example-callbacks.ref index c2e8eef5d..d6ab75eb6 100644 --- a/example/example-callbacks.ref +++ b/example/example-callbacks.ref @@ -6,7 +6,7 @@ Molly is a dog Molly is a dog Woof! The following error is expected: Incompatible function arguments. The following argument types are supported: - 1. (example.Dog) -> NoneType + 1. (arg0: example.Dog) -> NoneType Invoked with: Callback function 1 called! False diff --git a/example/issues.ref b/example/issues.ref index 0386d2c66..90d8ce17f 100644 --- a/example/issues.ref +++ b/example/issues.ref @@ -6,10 +6,10 @@ Yay.. [3, 5, 7, 9, 11, 13, 15] 0==0, 1==1, 2==2, 3==3, 4==4, 5==5, 6==6, 7==7, 8==8, 9==9, Failed as expected: Incompatible function arguments. The following argument types are supported: - 1. (example.issues.ElementA) -> NoneType + 1. (arg0: example.issues.ElementA) -> NoneType Invoked with: None Failed as expected: Incompatible function arguments. The following argument types are supported: - 1. (int) -> int + 1. (arg0: int) -> int Invoked with: 5.2 12.0 C++ version @@ -21,6 +21,6 @@ In python f() StrIssue.__str__ called StrIssue[3] Failed as expected: Incompatible constructor arguments. The following argument types are supported: - 1. example.issues.StrIssue(int) + 1. example.issues.StrIssue(arg0: int) 2. example.issues.StrIssue() Invoked with: no, such, constructor diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index bfb0f07ad..26124ae98 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -157,7 +157,7 @@ protected: /// Register a function call with Python (generic non-templated code goes here) void initialize_generic(detail::function_record *rec, const char *text, - const std::type_info *const *types, int args) { + const std::type_info *const *types, size_t args) { /* Create copies of all referenced C-style strings */ rec->name = strdup(rec->name ? rec->name : ""); @@ -182,16 +182,23 @@ protected: break; if (c == '{') { - if (type_depth == 1 && arg_index < rec->args.size()) { - signature += rec->args[arg_index].name; - signature += " : "; + // Write arg name for everything except *args, **kwargs and return type. + if (type_depth == 1 && text[char_index] != '*' && arg_index < args) { + if (!rec->args.empty()) { + signature += rec->args[arg_index].name; + } else if (arg_index == 0 && rec->class_) { + signature += "self"; + } else { + signature += "arg" + std::to_string(arg_index - (rec->class_ ? 1 : 0)); + } + signature += ": "; } ++type_depth; } else if (c == '}') { --type_depth; - if (type_depth == 1 && arg_index < rec->args.size()) { - if (rec->args[arg_index].descr) { - signature += " = "; + if (type_depth == 1) { + if (arg_index < rec->args.size() && rec->args[arg_index].descr) { + signature += "="; signature += rec->args[arg_index].descr; } arg_index++; @@ -453,9 +460,9 @@ protected: bool wrote_sig = false; if (overloads->is_constructor) { - // For a constructor, rewrite `(Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` + // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` std::string sig = it2->signature; - size_t start = sig.find('(') + 1; + size_t start = sig.find('(') + 7; // skip "(self: " if (start < sig.size()) { // End at the , for the next argument size_t end = sig.find(", "), next = end + 2;