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:
  " : " => ": "
  " = " => "="
This commit is contained in:
Dean Moldovan 2016-07-31 20:03:18 +02:00
parent 52d77d9db9
commit ecced6c5ae
6 changed files with 71 additions and 28 deletions

View File

@ -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_<KWClass>(m, "KWClass")
.def("foo0", &KWClass::foo)
.def("foo1", &KWClass::foo, "x"_a, "y"_a);
}

View File

@ -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()

View File

@ -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<int> = [13L, 17L]) -> NoneType
kw_func4(myList: list<int>=[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

View File

@ -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: <example.Pet object at 0>
Callback function 1 called!
False

View File

@ -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

View File

@ -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;