def convert_parameters(config: ParserConfig, children: Sequence[Any]) -> Any: lpar, *paramlist, rpar = children return FuncdefPartial( lpar=LeftParen(whitespace_after=parse_parenthesizable_whitespace( config, lpar.whitespace_after)), params=Parameters() if not paramlist else paramlist[0], rpar=RightParen(whitespace_before=parse_parenthesizable_whitespace( config, rpar.whitespace_before)), )
def convert_argslist(config: ParserConfig, children: Sequence[Any]) -> Any: params: List[Param] = [] default_params: List[Param] = [] star_arg: Union[Param, ParamStar, MaybeSentinel] = MaybeSentinel.DEFAULT kwonly_params: List[Param] = [] star_kwarg: Optional[Param] = None def add_param(current_param: Optional[List[Param]], param: Union[Param, ParamStar]) -> Optional[List[Param]]: nonlocal star_arg nonlocal star_kwarg if isinstance(param, ParamStar): # Only can add this if we don't already have a "*" or a "*param". if current_param in [params, default_params]: star_arg = param current_param = kwonly_params else: # Example code: # def fn(*abc, *): ... # This should be unreachable, the grammar already disallows it. raise Exception( "Cannot have multiple star ('*') markers in a single argument " + "list.") # pyre-ignore Pyre seems to think param.star.__eq__ is not callable elif isinstance(param.star, str) and param.star == "" and param.default is None: # Can only add this if we're in the params or kwonly_params section if current_param is params: params.append(param) elif current_param is kwonly_params: kwonly_params.append(param) else: # Example code: # def fn(first=None, second): ... # This code is reachable, so we should use a PartialParserSyntaxError. raise PartialParserSyntaxError( "Cannot have a non-default argument following a default argument." ) elif (isinstance(param.star, str) # pyre-ignore Pyre seems to think param.star.__eq__ is not callable and param.star == "" and param.default is not None): if current_param is params: current_param = default_params # Can only add this if we're not yet at star args. if current_param is default_params: default_params.append(param) elif current_param is kwonly_params: kwonly_params.append(param) else: # Example code: # def fn(**kwargs, trailing=None) # This should be unreachable, the grammar already disallows it. raise Exception( "Cannot have any arguments after a kwargs expansion.") elif (isinstance(param.star, str) # pyre-ignore Pyre seems to think param.star.__eq__ is not callable and param.star == "*" and param.default is None): # Can only add this if we're in params/default_params, since # we only allow one of "*" or "*param". if current_param in [params, default_params]: star_arg = param current_param = kwonly_params else: # Example code: # def fn(*first, *second): ... # This should be unreachable, the grammar already disallows it. raise Exception( "Expected a keyword argument but found a starred positional " + "argument expansion.") elif (isinstance(param.star, str) # pyre-ignore Pyre seems to think param.star.__eq__ is not callable and param.star == "**" and param.default is None): # Can add this in all cases where we don't have a star_kwarg # yet. if current_param is not None: star_kwarg = param current_param = None else: # Example code: # def fn(**first, **second) # This should be unreachable, the grammar already disallows it. raise Exception( "Multiple starred keyword argument expansions are not allowed in a " + "single argument list") else: # The state machine should never end up here. raise Exception("Logic error!") return current_param # The parameter list we are adding to current: Optional[List[Param]] = params # We should have every other item in the group as a param or a comma by now, # so split them up, add commas and then put them in the appropriate group. for parameter, comma in grouper(children, 2): if comma is None: if isinstance(parameter, ParamStarPartial): # Example: # def fn(abc, *): ... # # There's also the case where we have bare * with a trailing comma. # That's handled later. # # It's not valid to construct a ParamStar object without a comma, so we # need to catch the non-comma case separately. raise PartialParserSyntaxError( "Named (keyword) arguments must follow a bare *.") else: current = add_param(current, parameter) else: comma = Comma( whitespace_before=parse_parenthesizable_whitespace( config, comma.whitespace_before), whitespace_after=parse_parenthesizable_whitespace( config, comma.whitespace_after), ) if isinstance(parameter, ParamStarPartial): current = add_param(current, ParamStar(comma=comma)) else: current = add_param(current, parameter.with_changes(comma=comma)) if isinstance(star_arg, ParamStar) and len(kwonly_params) == 0: # Example: # def fn(abc, *,): ... # # This will raise a validation error, but we want to make sure to raise a syntax # error instead. # # The case where there's no trailing comma is already handled by this point, so # this conditional is only for the case where we have a trailing comma. raise PartialParserSyntaxError( "Named (keyword) arguments must follow a bare *.") return Parameters( params=tuple(params), default_params=tuple(default_params), star_arg=star_arg, kwonly_params=tuple(kwonly_params), star_kwarg=star_kwarg, )
def convert_lambda(config: ParserConfig, children: typing.Sequence[typing.Any]) -> typing.Any: lambdatoken, *params, colontoken, test = children # Grab the whitespace around the colon. If there are no params, then # the colon owns the whitespace before and after it. If there are # any params, then the last param owns the whitespace before the colon. # We handle the parameter movement below. colon = Colon( whitespace_before=parse_parenthesizable_whitespace( config, colontoken.whitespace_before), whitespace_after=parse_parenthesizable_whitespace( config, colontoken.whitespace_after), ) # Unpack optional parameters if len(params) == 0: parameters = Parameters() whitespace_after_lambda = MaybeSentinel.DEFAULT else: (parameters, ) = params whitespace_after_lambda = parse_parenthesizable_whitespace( config, lambdatoken.whitespace_after) # Handle pre-colon whitespace if parameters.star_kwarg is not None: if parameters.star_kwarg.comma == MaybeSentinel.DEFAULT: parameters = parameters.with_changes( star_kwarg=parameters.star_kwarg.with_changes( whitespace_after_param=colon.whitespace_before)) elif parameters.kwonly_params: if parameters.kwonly_params[-1].comma == MaybeSentinel.DEFAULT: parameters = parameters.with_changes(kwonly_params=( *parameters.kwonly_params[:-1], parameters.kwonly_params[-1].with_changes( whitespace_after_param=colon.whitespace_before), )) elif isinstance(parameters.star_arg, Param): if parameters.star_arg.comma == MaybeSentinel.DEFAULT: parameters = parameters.with_changes( star_arg=parameters.star_arg.with_changes( whitespace_after_param=colon.whitespace_before)) elif parameters.default_params: if parameters.default_params[-1].comma == MaybeSentinel.DEFAULT: parameters = parameters.with_changes(default_params=( *parameters.default_params[:-1], parameters.default_params[-1].with_changes( whitespace_after_param=colon.whitespace_before), )) elif parameters.params: if parameters.params[-1].comma == MaybeSentinel.DEFAULT: parameters = parameters.with_changes(params=( *parameters.params[:-1], parameters.params[-1].with_changes( whitespace_after_param=colon.whitespace_before), )) # Colon doesn't own its own pre-whitespace now. colon = colon.with_changes(whitespace_before=SimpleWhitespace("")) # Return a lambda return WithLeadingWhitespace( Lambda( whitespace_after_lambda=whitespace_after_lambda, params=parameters, body=test.value, colon=colon, ), lambdatoken.whitespace_before, )