From: Vladimír Vondruš Date: Sun, 14 Jul 2019 09:34:52 +0000 (+0200) Subject: documentation/python: properly parse pybind's Callable annotations. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~cjwatson/git?a=commitdiff_plain;h=6c0de24028fc23bf848d3f70e4733341a5a60f54;p=blog.git documentation/python: properly parse pybind's Callable annotations. --- diff --git a/documentation/python.py b/documentation/python.py index 32c7cb50..39fb22ab 100755 --- a/documentation/python.py +++ b/documentation/python.py @@ -560,10 +560,19 @@ _pybind_type_rx = re.compile('[a-zA-Z0-9_.]+') _pybind_default_value_rx = re.compile('[^,)]+') def parse_pybind_type(state: State, referrer_path: List[str], signature: str) -> str: - input_type = _pybind_type_rx.match(signature).group(0) - signature = signature[len(input_type):] - type = map_name_prefix(state, input_type) - type_link = make_name_link(state, referrer_path, type) + # If this doesn't match, it's because we're in Callable[[arg, ...], retval] + match = _pybind_type_rx.match(signature) + if match: + input_type = match.group(0) + signature = signature[len(input_type):] + type = map_name_prefix(state, input_type) + type_link = make_name_link(state, referrer_path, type) + else: + assert signature[0] == '[' + type = '' + type_link = '' + + # This is a generic type (or the list in Callable) if signature and signature[0] == '[': type += '[' type_link += '[' diff --git a/documentation/test_python/pybind_signatures/pybind_signatures.cpp b/documentation/test_python/pybind_signatures/pybind_signatures.cpp index 39c3a48c..62e2aaa8 100644 --- a/documentation/test_python/pybind_signatures/pybind_signatures.cpp +++ b/documentation/test_python/pybind_signatures/pybind_signatures.cpp @@ -1,5 +1,7 @@ +#include #include #include /* needed for std::vector! */ +#include /* for std::function */ namespace py = pybind11; @@ -20,6 +22,9 @@ void crazySignature(const Crazy<3, int>&) {} std::string overloaded(int) { return {}; } bool overloaded(float) { return {}; } +// Doesn't work with just a plain function pointer, MEH +void takesAFunction(std::function&)>) {} + struct MyClass { static MyClass staticFunction(int, float) { return {}; } @@ -55,6 +60,7 @@ PYBIND11_MODULE(pybind_signatures, m) { .def("overloaded", static_cast(&overloaded), "Overloaded for ints") .def("overloaded", static_cast(&overloaded), "Overloaded for floats") .def("duck", &duck, "A function taking args/kwargs directly") + .def("takes_a_function", &takesAFunction, "A function taking a Callable") .def("tenOverloads", &tenOverloads, "Ten overloads of a function") .def("tenOverloads", &tenOverloads, "Ten overloads of a function") diff --git a/documentation/test_python/pybind_signatures/pybind_signatures.html b/documentation/test_python/pybind_signatures/pybind_signatures.html index f610009c..32f91e2a 100644 --- a/documentation/test_python/pybind_signatures/pybind_signatures.html +++ b/documentation/test_python/pybind_signatures/pybind_signatures.html @@ -73,6 +73,10 @@ argument: float) -> int
Scale an integer, kwargs
+
+ def takes_a_function(arg0: Callable[[float, List[float]], int], /) +
+
A function taking a Callable
def taking_a_list_returning_a_tuple(arg0: List[float], /) -> Tuple[int, int, int]
diff --git a/documentation/test_python/test_pybind.py b/documentation/test_python/test_pybind.py index 19271166..44bcfef8 100644 --- a/documentation/test_python/test_pybind.py +++ b/documentation/test_python/test_pybind.py @@ -90,6 +90,14 @@ class Signature(unittest.TestCase): ('another', 'float', 'float', None), ], 'Union[str, Any]')) + def test_callable(self): + self.assertEqual(parse_pybind_signature(State({}), [], + 'foo(a: Callable[[int, Tuple[int, int]], float], another: float)'), + ('foo', '', [ + ('a', 'Callable[[int, Tuple[int, int]], float]', 'Callable[[int, Tuple[int, int]], float]', None), + ('another', 'float', 'float', None), + ], None)) + def test_kwargs(self): self.assertEqual(parse_pybind_signature(State({}), [], 'foo(*args, **kwargs)'),