Commit 24941cb7 authored by Josh Tobin's avatar Josh Tobin

posonly_args: bugfix & test

parent ca25dfd2
...@@ -3661,6 +3661,7 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3661,6 +3661,7 @@ class DefNodeWrapper(FuncDefNode):
positional_args = [] positional_args = []
required_kw_only_args = [] required_kw_only_args = []
optional_kw_only_args = [] optional_kw_only_args = []
num_pos_only_args = 0
for arg in args: for arg in args:
if arg.is_generic: if arg.is_generic:
if arg.default: if arg.default:
...@@ -3673,6 +3674,8 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3673,6 +3674,8 @@ class DefNodeWrapper(FuncDefNode):
required_kw_only_args.append(arg) required_kw_only_args.append(arg)
elif not arg.is_self_arg and not arg.is_type_arg: elif not arg.is_self_arg and not arg.is_type_arg:
positional_args.append(arg) positional_args.append(arg)
if arg.pos_only:
num_pos_only_args += 1
# sort required kw-only args before optional ones to avoid special # sort required kw-only args before optional ones to avoid special
# cases in the unpacking code # cases in the unpacking code
...@@ -3907,10 +3910,10 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3907,10 +3910,10 @@ class DefNodeWrapper(FuncDefNode):
has_fixed_positional_count, has_fixed_positional_count,
has_kw_only_args, all_args, argtuple_error_label, code): has_kw_only_args, all_args, argtuple_error_label, code):
# First we count how many arguments must be passed as positional # First we count how many arguments must be passed as positional
num_required_posonly_args = num_posonly_args = 0 num_required_posonly_args = num_pos_only_args = 0
for i, arg in enumerate(all_args): for i, arg in enumerate(all_args):
if arg.pos_only: if arg.pos_only:
num_posonly_args += 1 num_pos_only_args += 1
if not arg.default: if not arg.default:
num_required_posonly_args += 1 num_required_posonly_args += 1
...@@ -3964,11 +3967,11 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3964,11 +3967,11 @@ class DefNodeWrapper(FuncDefNode):
last_required_arg = i last_required_arg = i
if last_required_arg < max_positional_args: if last_required_arg < max_positional_args:
last_required_arg = max_positional_args-1 last_required_arg = max_positional_args-1
if max_positional_args > num_posonly_args: if max_positional_args > num_pos_only_args:
code.putln('switch (pos_args) {') code.putln('switch (pos_args) {')
for i, arg in enumerate(all_args[num_posonly_args:last_required_arg+1], num_posonly_args): for i, arg in enumerate(all_args[num_pos_only_args:last_required_arg+1], num_pos_only_args):
if max_positional_args > num_posonly_args and i <= max_positional_args: if max_positional_args > num_pos_only_args and i <= max_positional_args:
if i != num_posonly_args: if i != num_pos_only_args:
code.putln('CYTHON_FALLTHROUGH;') code.putln('CYTHON_FALLTHROUGH;')
if self.star_arg and i == max_positional_args: if self.star_arg and i == max_positional_args:
code.putln('default:') code.putln('default:')
...@@ -4013,7 +4016,7 @@ class DefNodeWrapper(FuncDefNode): ...@@ -4013,7 +4016,7 @@ class DefNodeWrapper(FuncDefNode):
self.name, pystring_cname)) self.name, pystring_cname))
code.putln(code.error_goto(self.pos)) code.putln(code.error_goto(self.pos))
code.putln('}') code.putln('}')
if max_positional_args > num_posonly_args: if max_positional_args > num_pos_only_args:
code.putln('}') code.putln('}')
if has_kw_only_args: if has_kw_only_args:
...@@ -4033,7 +4036,6 @@ class DefNodeWrapper(FuncDefNode): ...@@ -4033,7 +4036,6 @@ class DefNodeWrapper(FuncDefNode):
# ParseOptionalKeywords() needs to know how many of the arguments # ParseOptionalKeywords() needs to know how many of the arguments
# that could be passed as keywords have in fact been passed as # that could be passed as keywords have in fact been passed as
# positional args. # positional args.
num_pos_only_args = self.num_posonly_args
if num_pos_only_args > 0: if num_pos_only_args > 0:
# There are positional-only arguments which we don't want to count, # There are positional-only arguments which we don't want to count,
# since they cannot be keyword arguments. Subtract the number of # since they cannot be keyword arguments. Subtract the number of
...@@ -4077,14 +4079,17 @@ class DefNodeWrapper(FuncDefNode): ...@@ -4077,14 +4079,17 @@ class DefNodeWrapper(FuncDefNode):
def generate_optional_kwonly_args_unpacking_code(self, all_args, code): def generate_optional_kwonly_args_unpacking_code(self, all_args, code):
optional_args = [] optional_args = []
first_optional_arg = -1 first_optional_arg = -1
num_posonly_args = 0
for i, arg in enumerate(all_args): for i, arg in enumerate(all_args):
if arg.pos_only:
num_posonly_args += 1
if not arg.kw_only or not arg.default: if not arg.kw_only or not arg.default:
continue continue
if not optional_args: if not optional_args:
first_optional_arg = i first_optional_arg = i
optional_args.append(arg.name) optional_args.append(arg.name)
if self.num_posonly_args > 0: if num_posonly_args > 0:
posonly_correction = '-%d' % self.num_posonly_args posonly_correction = '-%d' % num_posonly_args
else: else:
posonly_correction = '' posonly_correction = ''
if optional_args: if optional_args:
......
...@@ -540,3 +540,19 @@ def test_empty_kwargs(a, b, /): ...@@ -540,3 +540,19 @@ def test_empty_kwargs(a, b, /):
TypeError: test_empty_kwargs() got an unexpected keyword argument 'c' TypeError: test_empty_kwargs() got an unexpected keyword argument 'c'
""" """
return (a,b) return (a,b)
cdef class TestExtensionClass:
"""
>>> t = TestExtensionClass()
>>> t.f(1,2)
(1, 2, 3)
>>> t.f(1,2,4)
(1, 2, 4)
>>> t.f(1, 2, c=4)
(1, 2, 4)
>>> t.f(1, 2, 5, c=6)
Traceback (most recent call last):
TypeError: f() got multiple values for keyword argument 'c'
"""
def f(self, a, b, /, c=3):
return (a,b,c)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment