chiark / gitweb /
documentation/python: don't try to parse pybind objects as enums.
authorVladimír Vondruš <mosra@centrum.cz>
Mon, 3 Jan 2022 16:42:57 +0000 (17:42 +0100)
committerVladimír Vondruš <mosra@centrum.cz>
Mon, 3 Jan 2022 17:03:50 +0000 (18:03 +0100)
Followup to c6707e1c85a46e8848ebca077ec60eeb27eb5aab, forgot to test on
a real-world codebase early enough. And then of course forgot to handle
a corner case when accounting for that real-world scenario.

documentation/python.py
documentation/test_python/test_pybind.py

index 0955da752a459613835e17e177f2894444c97d58..c3b059e866df6484888fae50ffb4198247871bcb 100755 (executable)
@@ -822,16 +822,15 @@ def _pybind11_extract_default_argument(string):
         # everything until the colon and discard the rest. It can however be a
         # part of a rogue C++ type name, so pick the < only if directly at the
         # start or after a space or bracket, and the > only if at the end
-        # again.
-        if c == '<' and (i == 0 or string[i - 1] in [' ', '(', '[']):
-            name_end = string.find(':', i)
-            if name_end == -1:
-                raise SyntaxError("Enum expected to have a value: `{}`".format(string))
+        # again. Also ignore stuff like <FooBar object at 0x1234> -- a : has to
+        # be before a >. Ugh maybe I should just use a regex here.
+        if c == '<' and (i == 0 or string[i - 1] in [' ', '(', '[']) and -1 < string.find(':', i + 1) < string.find('>', i + 1):
+            name_end = string.index(':', i + 1)
 
             default += string[i + 1:name_end]
-            i = string.find('>', name_end + 1) + 1
+            i = string.index('>', name_end + 1) + 1
 
-            if i == -1 or (i < len(string) and string[i] not in [',', ')', ']']):
+            if i < len(string) and string[i] not in [',', ')', ']']:
                 raise SyntaxError("Unexpected content after enum value: `{}`".format(string[i:]))
 
             continue
index 47574ca80f93098d784b4b4f5c2c5d5c58f47931..e8dfe967a50816274d03005093f3742aced5789b 100644 (file)
@@ -244,12 +244,26 @@ class Signature(unittest.TestCase):
                 ('a', 'Enum', 'Enum', 'Enum_MISSING_DOT')
             ], None, None))
 
+        # This is how pybind prints various objects, should be passed as-is.
+        # It should not corrupt any parameters after.
+        self.assertEqual(parse_pybind_signature(self.state, [],
+            'foo(a: FooBar = <FooBar object at 0xabcd>, b: int = 3)'),
+            ('foo', '', [
+                ('a', 'FooBar', 'FooBar', '<FooBar object at 0xabcd>'),
+                ('b', 'int', 'int', '3')
+            ], None, None))
+
+        # This is weird and so will be passed as-is.
+        self.assertEqual(parse_pybind_signature(self.state, [],
+            'foo(a: Enum = <Enum_MISSING_GT: -1)'),
+            ('foo', '', [
+                ('a', 'Enum', 'Enum', '<Enum_MISSING_GT: -1')
+            ], None, None))
+
         # This will fail
         bad_signature = ('foo', '', [('…', None, None, None)], None, None)
-        self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: Enum = <Enum.MISSING_COLON>)'), bad_signature)
-        self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: Enum = <Enum.MISSING_GT)'), bad_signature)
-        self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: Enum = <Enum.CHARACTERS_AFTER>a)'), bad_signature)
-        self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: Enum = <Enum.CHARACTERS_AFTER><)'), bad_signature)
+        self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: Enum = <Enum.CHARACTERS_AFTER: 17>a)'), bad_signature)
+        self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: Enum = <Enum.CHARACTERS_AFTER: 89><)'), bad_signature)
 
     def test_bad_return_type(self):
         bad_signature = ('foo', '', [('…', None, None, None)], None, None)