[3.13] gh-122688: Fix support of var-positional parameter in Argument Clinic (GH-122689) (#122852)

* Parameters after the var-positional parameter are now keyword-only
  instead of positional-or-keyword.
* Correctly calculate min_kw_only.
* Raise errors for invalid combinations of the var-positional parameter
  with "*", "/" and deprecation markers.
(cherry picked from commit 8393608dd9)
This commit is contained in:
Serhiy Storchaka 2024-09-02 14:03:04 +03:00 committed by GitHub
parent 60e4c3f710
commit 8b6dd92db7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 203 additions and 96 deletions

View file

@ -915,8 +915,8 @@ class DSLParser:
f"invalid parameter declaration (**kwargs?): {line!r}")
if function_args.vararg:
if any(p.is_vararg() for p in self.function.parameters.values()):
fail("Too many var args")
self.check_previous_star()
self.check_remaining_star()
is_vararg = True
parameter = function_args.vararg
else:
@ -1124,6 +1124,9 @@ class DSLParser:
key = f"{parameter_name}_as_{c_name}" if c_name else parameter_name
self.function.parameters[key] = p
if is_vararg:
self.keyword_only = True
@staticmethod
def parse_converter(
annotation: ast.expr | None
@ -1165,8 +1168,6 @@ class DSLParser:
the marker will take effect (None means it is already in effect).
"""
if version is None:
if self.keyword_only:
fail(f"Function {function.name!r} uses '*' more than once.")
self.check_previous_star()
self.check_remaining_star()
self.keyword_only = True
@ -1456,6 +1457,7 @@ class DSLParser:
if p.is_vararg():
p_lines.append("*")
added_star = True
name = p.converter.signature_name or p.name
p_lines.append(name)
@ -1565,7 +1567,8 @@ class DSLParser:
for p in reversed(self.function.parameters.values()):
if self.keyword_only:
if p.kind == inspect.Parameter.KEYWORD_ONLY:
if (p.kind == inspect.Parameter.KEYWORD_ONLY or
p.kind == inspect.Parameter.VAR_POSITIONAL):
return
elif self.deprecated_positional:
if p.deprecated_positional == self.deprecated_positional:
@ -1575,12 +1578,11 @@ class DSLParser:
fail(f"Function {self.function.name!r} specifies {symbol!r} "
f"without following parameters.", line_number=lineno)
def check_previous_star(self, lineno: int | None = None) -> None:
def check_previous_star(self) -> None:
assert isinstance(self.function, Function)
for p in self.function.parameters.values():
if p.kind == inspect.Parameter.VAR_POSITIONAL:
fail(f"Function {self.function.name!r} uses '*' more than once.")
if self.keyword_only:
fail(f"Function {self.function.name!r} uses '*' more than once.")
def do_post_block_processing_cleanup(self, lineno: int) -> None:

View file

@ -262,7 +262,7 @@ class ParseArgsCodeGen:
if p.is_keyword_only():
assert not p.is_positional_only()
if not p.is_optional():
self.min_kw_only = i - self.max_pos
self.min_kw_only = i - self.max_pos - int(self.vararg != NO_VARARG)
elif p.is_vararg():
self.pseudo_args += 1
self.vararg = i - 1