_pybind_arg_name_rx = re.compile('[*a-zA-Z0-9_]+')
_pybind_type_rx = re.compile('[a-zA-Z0-9_.]+')
-def _pybind11_default_argument_length(string):
- """Returns length of balanced []()-expression at begin of input string until `,` or `)`"""
+def _pybind11_extract_default_argument(string):
+ """Consumes a balanced []()-expression at begin of input string until `,`
+ or `)`, while also replacing all `<Enum.FOO: -12354>` with just
+ `Enum.FOO`."""
stack = []
- for i, c in enumerate(string):
+ default = ''
+ i = 0
+ while i < len(string):
+ c = string[i]
+
+ # At the end, what follows is the next argument or end of the argument
+ # list, exit with what we got so far
if len(stack) == 0 and (c == ',' or c == ')'):
- return i
+ return string[i:], default
+
+ # Pybind 2.6+ enum in the form of <Enum.FOO_BAR: -2986>, extract
+ # 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))
+
+ default += string[i + 1:name_end]
+ i = string.find('>', name_end + 1) + 1
+
+ if i == -1 or (i < len(string) and string[i] not in [',', ')', ']']):
+ raise SyntaxError("Unexpected content after enum value: `{}`".format(string[i:]))
+
+ continue
+
+ # Brackets
if c == '(':
stack.append(')')
elif c == '[':
elif c == ')' or c == ']':
if len(stack) == 0 or c != stack.pop():
raise SyntaxError("Unmatched {} at pos {} in `{}`".format(c, i, string))
+
+ # If there would be find_first_not_of(), I wouldn't have to iterate
+ # byte by byte LIKE A CAVEMAN
+ default += string[i]
+ i += 1
+
raise SyntaxError("Unexpected end of `{}`".format(string))
def _pybind_map_name_prefix_or_add_typing_suffix(state: State, input_type: str):
self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: float = ))'), bad_signature)
self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: float = ])'), bad_signature)
+ # https://github.com/pybind/pybind11/pull/2126, extremely stupid and
+ # annoying but what can I do. Want to support both this and the original
+ # behavior in case they revert the insanity again, so test that both
+ # variants give the same output.
+ def test_default_values_pybind26(self):
+ # Before the insane change
+ self.assertEqual(parse_pybind_signature(self.state, [],
+ 'foo(a: bar.Enum = Enum.FOO_BAR)'),
+ ('foo', '', [
+ ('a', 'bar.Enum', 'bar.Enum', 'Enum.FOO_BAR')
+ ], None, None))
+
+ # After the insane change
+ self.assertEqual(parse_pybind_signature(self.state, [],
+ 'foo(a: bar.Enum = <Enum.FOO_BAR: -13376>)'),
+ ('foo', '', [
+ ('a', 'bar.Enum', 'bar.Enum', 'Enum.FOO_BAR')
+ ], None, None))
+
+ # Nested
+ self.assertEqual(parse_pybind_signature(self.state, [],
+ 'foo(a: bar.Enum = (4, [<Enum.FOO_BAR: -13376>], <Enum.FIZZ_PISS: 1>))'),
+ ('foo', '', [
+ ('a', 'bar.Enum', 'bar.Enum', '(4, [Enum.FOO_BAR], Enum.FIZZ_PISS)')
+ ], None, None))
+
+ # This isn't really expected to happen but yeah it still treats it as
+ # an enum
+ self.assertEqual(parse_pybind_signature(self.state, [],
+ 'foo(a: Enum = <Enum_MISSING_DOT:>)'),
+ ('foo', '', [
+ ('a', 'Enum', 'Enum', 'Enum_MISSING_DOT')
+ ], 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)
+
def test_bad_return_type(self):
bad_signature = ('foo', '', [('…', None, None, None)], None, None)
self.assertEqual(parse_pybind_signature(self.state, [], 'foo() -> List[[]'), bad_signature)