예제 #1
0
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)),
    )
예제 #2
0
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,
    )
예제 #3
0
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,
    )