gh-104683: Argument Clinic: Modernise parse_special_symbol() (#106837)

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Erlend E. Aasland 2023-07-18 00:37:11 +02:00 committed by GitHub
parent 1654916c48
commit 00e52acebd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -4864,11 +4864,21 @@ class DSLParser:
self.parameter_continuation = line[:-1] self.parameter_continuation = line[:-1]
return return
line = line.lstrip() func = self.function
match line.lstrip():
case '*':
self.parse_star(func)
case '[':
self.parse_opening_square_bracket(func)
case ']':
self.parse_closing_square_bracket(func)
case '/':
self.parse_slash(func)
case param:
self.parse_parameter(param)
if line in ('*', '/', '[', ']'): def parse_parameter(self, line: str) -> None:
self.parse_special_symbol(line) assert self.function is not None
return
match self.parameter_state: match self.parameter_state:
case ParamState.START | ParamState.REQUIRED: case ParamState.START | ParamState.REQUIRED:
@ -5146,57 +5156,71 @@ class DSLParser:
"Annotations must be either a name, a function call, or a string." "Annotations must be either a name, a function call, or a string."
) )
def parse_special_symbol(self, symbol): def parse_star(self, function: Function) -> None:
if symbol == '*': """Parse keyword-only parameter marker '*'."""
if self.keyword_only: if self.keyword_only:
fail("Function " + self.function.name + " uses '*' more than once.") fail(f"Function {function.name} uses '*' more than once.")
self.keyword_only = True self.keyword_only = True
elif symbol == '[':
match self.parameter_state: def parse_opening_square_bracket(self, function: Function) -> None:
case ParamState.START | ParamState.LEFT_SQUARE_BEFORE: """Parse opening parameter group symbol '['."""
self.parameter_state = ParamState.LEFT_SQUARE_BEFORE match self.parameter_state:
case ParamState.REQUIRED | ParamState.GROUP_AFTER: case ParamState.START | ParamState.LEFT_SQUARE_BEFORE:
self.parameter_state = ParamState.GROUP_AFTER self.parameter_state = ParamState.LEFT_SQUARE_BEFORE
case st: case ParamState.REQUIRED | ParamState.GROUP_AFTER:
fail(f"Function {self.function.name} has an unsupported group configuration. (Unexpected state {st}.b)") self.parameter_state = ParamState.GROUP_AFTER
self.group += 1 case st:
self.function.docstring_only = True fail(f"Function {function.name} has an unsupported group configuration. "
elif symbol == ']': f"(Unexpected state {st}.b)")
if not self.group: self.group += 1
fail("Function " + self.function.name + " has a ] without a matching [.") function.docstring_only = True
if not any(p.group == self.group for p in self.function.parameters.values()):
fail("Function " + self.function.name + " has an empty group.\nAll groups must contain at least one parameter.") def parse_closing_square_bracket(self, function: Function) -> None:
self.group -= 1 """Parse closing parameter group symbol ']'."""
match self.parameter_state: if not self.group:
case ParamState.LEFT_SQUARE_BEFORE | ParamState.GROUP_BEFORE: fail(f"Function {function.name} has a ] without a matching [.")
self.parameter_state = ParamState.GROUP_BEFORE if not any(p.group == self.group for p in function.parameters.values()):
case ParamState.GROUP_AFTER | ParamState.RIGHT_SQUARE_AFTER: fail(f"Function {function.name} has an empty group.\n"
self.parameter_state = ParamState.RIGHT_SQUARE_AFTER "All groups must contain at least one parameter.")
case st: self.group -= 1
fail(f"Function {self.function.name} has an unsupported group configuration. (Unexpected state {st}.c)") match self.parameter_state:
elif symbol == '/': case ParamState.LEFT_SQUARE_BEFORE | ParamState.GROUP_BEFORE:
if self.positional_only: self.parameter_state = ParamState.GROUP_BEFORE
fail("Function " + self.function.name + " uses '/' more than once.") case ParamState.GROUP_AFTER | ParamState.RIGHT_SQUARE_AFTER:
self.positional_only = True self.parameter_state = ParamState.RIGHT_SQUARE_AFTER
# REQUIRED and OPTIONAL are allowed here, that allows positional-only without option groups case st:
# to work (and have default values!) fail(f"Function {function.name} has an unsupported group configuration. "
allowed = { f"(Unexpected state {st}.c)")
ParamState.REQUIRED,
ParamState.OPTIONAL, def parse_slash(self, function: Function) -> None:
ParamState.RIGHT_SQUARE_AFTER, """Parse positional-only parameter marker '/'."""
ParamState.GROUP_BEFORE, if self.positional_only:
} fail(f"Function {function.name} uses '/' more than once.")
if (self.parameter_state not in allowed) or self.group: self.positional_only = True
fail(f"Function {self.function.name} has an unsupported group configuration. (Unexpected state {self.parameter_state}.d)") # REQUIRED and OPTIONAL are allowed here, that allows positional-only
if self.keyword_only: # without option groups to work (and have default values!)
fail("Function " + self.function.name + " mixes keyword-only and positional-only parameters, which is unsupported.") allowed = {
# fixup preceding parameters ParamState.REQUIRED,
for p in self.function.parameters.values(): ParamState.OPTIONAL,
if p.is_vararg(): ParamState.RIGHT_SQUARE_AFTER,
continue ParamState.GROUP_BEFORE,
if (p.kind != inspect.Parameter.POSITIONAL_OR_KEYWORD and not isinstance(p.converter, self_converter)): }
fail("Function " + self.function.name + " mixes keyword-only and positional-only parameters, which is unsupported.") if (self.parameter_state not in allowed) or self.group:
p.kind = inspect.Parameter.POSITIONAL_ONLY fail(f"Function {function.name} has an unsupported group configuration. "
f"(Unexpected state {self.parameter_state}.d)")
if self.keyword_only:
fail(f"Function {function.name} mixes keyword-only and "
"positional-only parameters, which is unsupported.")
# fixup preceding parameters
for p in function.parameters.values():
if p.is_vararg():
continue
if (p.kind is not inspect.Parameter.POSITIONAL_OR_KEYWORD and
not isinstance(p.converter, self_converter)
):
fail(f"Function {function.name} mixes keyword-only and "
"positional-only parameters, which is unsupported.")
p.kind = inspect.Parameter.POSITIONAL_ONLY
def state_parameter_docstring_start(self, line: str | None) -> None: def state_parameter_docstring_start(self, line: str | None) -> None:
self.parameter_docstring_indent = len(self.indent.margin) self.parameter_docstring_indent = len(self.indent.margin)