chiark / gitweb /
documentation/python: properly parse pybind's Callable annotations.
authorVladimír Vondruš <mosra@centrum.cz>
Sun, 14 Jul 2019 09:34:52 +0000 (11:34 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Sun, 14 Jul 2019 17:11:08 +0000 (19:11 +0200)
documentation/python.py
documentation/test_python/pybind_signatures/pybind_signatures.cpp
documentation/test_python/pybind_signatures/pybind_signatures.html
documentation/test_python/test_pybind.py

index 32c7cb5068906ff7c338db8cadaf118833dce2e0..39fb22ab9c69057800b9b923fe00047ff6e42ad3 100755 (executable)
@@ -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 += '['
index 39c3a48cbc2749c13ad768adee33b633d1f98993..62e2aaa8c846465abaa638efa5d60ca6d31fcfd4 100644 (file)
@@ -1,5 +1,7 @@
+#include <functional>
 #include <pybind11/pybind11.h>
 #include <pybind11/stl.h> /* needed for std::vector! */
+#include <pybind11/functional.h> /* 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<int(float, std::vector<float>&)>) {}
+
 struct MyClass {
     static MyClass staticFunction(int, float) { return {}; }
 
@@ -55,6 +60,7 @@ PYBIND11_MODULE(pybind_signatures, m) {
         .def("overloaded", static_cast<std::string(*)(int)>(&overloaded), "Overloaded for ints")
         .def("overloaded", static_cast<bool(*)(float)>(&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<float, float>, "Ten overloads of a function")
         .def("tenOverloads", &tenOverloads<int, float>, "Ten overloads of a function")
index f610009cef2a62c74152baa42d9a410d9ddc6bbd..32f91e2af413d1b09e7cd2f64aa0b2903831ba72 100644 (file)
               argument: float) -&gt; int</span>
             </dt>
             <dd>Scale an integer, kwargs</dd>
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#takes_a_function-b0069" class="m-doc-self" id="takes_a_function-b0069">takes_a_function</a>(</span><span class="m-doc-wrap">arg0: Callable[[float, List[float]], int]<span class="m-text m-dim">, /</span>)</span>
+            </dt>
+            <dd>A function taking a Callable</dd>
             <dt>
               <span class="m-doc-wrap-bumper">def <a href="#taking_a_list_returning_a_tuple-54d79" class="m-doc-self" id="taking_a_list_returning_a_tuple-54d79">taking_a_list_returning_a_tuple</a>(</span><span class="m-doc-wrap">arg0: List[float]<span class="m-text m-dim">, /</span>) -&gt; Tuple[int, int, int]</span>
             </dt>
index 192711663fdb9f407f4822e6a0386f7b9292758d..44bcfef8d6583e28e17b941030b3de82bdae2b66 100644 (file)
@@ -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)'),