Ejemplo n.º 1
0
 def collapse_isinstance_checks(self, _, updated_node):
     left_target, left_type = updated_node.left.args
     right_target, right_type = updated_node.right.args
     if left_target.deep_equals(right_target):
         merged_type = cst.Arg(
             cst.Tuple([
                 cst.Element(left_type.value),
                 cst.Element(right_type.value)
             ]))
         return updated_node.left.with_changes(
             args=[left_target, merged_type])
     return updated_node
    def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode:
        try:
            key = original.func.attr.value
            kword_params = self.METHOD_TO_PARAMS[key]
        except (AttributeError, KeyError):
            # Either not a method from the API or too convoluted to be sure.
            return updated

        # If the existing code is valid, keyword args come after positional args.
        # Therefore, all positional args must map to the first parameters.
        args, kwargs = partition(lambda a: not bool(a.keyword), updated.args)
        if any(k.keyword.value == "request" for k in kwargs):
            # We've already fixed this file, don't fix it again.
            return updated

        kwargs, ctrl_kwargs = partition(
            lambda a: not a.keyword.value in self.CTRL_PARAMS, kwargs)

        args, ctrl_args = args[:len(kword_params)], args[len(kword_params):]
        ctrl_kwargs.extend(
            cst.Arg(value=a.value, keyword=cst.Name(value=ctrl))
            for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS))

        request_arg = cst.Arg(
            value=cst.Dict([
                cst.DictElement(cst.SimpleString("'{}'".format(name)),
                                cst.Element(value=arg.value))
                # Note: the args + kwargs looks silly, but keep in mind that
                # the control parameters had to be stripped out, and that
                # those could have been passed positionally or by keyword.
                for name, arg in zip(kword_params, args + kwargs)
            ]),
            keyword=cst.Name("request"))

        return updated.with_changes(args=[request_arg] + ctrl_kwargs)
Ejemplo n.º 3
0
def _get_match_if_true(oldtype: cst.BaseExpression) -> cst.SubscriptElement:
    """
    Construct a MatchIfTrue type node appropriate for going into a Union.
    """
    return cst.SubscriptElement(
        cst.Index(
            cst.Subscript(
                cst.Name("MatchIfTrue"),
                slice=(
                    cst.SubscriptElement(
                        cst.Index(
                            cst.Subscript(
                                cst.Name("Callable"),
                                slice=(
                                    cst.SubscriptElement(
                                        cst.Index(
                                            cst.List([
                                                cst.Element(
                                                    # MatchIfTrue takes in the original node type,
                                                    # and returns a boolean. So, lets convert our
                                                    # quoted classes (forward refs to other
                                                    # matchers) back to the CSTNode they refer to.
                                                    # We can do this because there's always a 1:1
                                                    # name mapping.
                                                    _convert_match_nodes_to_cst_nodes(
                                                        oldtype))
                                            ]))),
                                    cst.SubscriptElement(
                                        cst.Index(cst.Name("bool"))),
                                ),
                            ))), ),
            )))
Ejemplo n.º 4
0
    def visit_BooleanOperation(self, node: cst.BooleanOperation) -> None:
        if node in self.seen_boolean_operations:
            return None

        stack = tuple(self.unwrap(node))
        operands, targets = self.collect_targets(stack)

        # If nothing gets collapsed, just exit from this short-path
        if len(operands) == len(stack):
            return None

        replacement = None
        for operand in operands:
            if operand in targets:
                matches = targets[operand]
                if len(matches) == 1:
                    arg = cst.Arg(value=matches[0])
                else:
                    arg = cst.Arg(cst.Tuple([cst.Element(match) for match in matches]))
                operand = cst.Call(cst.Name("isinstance"), [cst.Arg(operand), arg])

            if replacement is None:
                replacement = operand
            else:
                replacement = cst.BooleanOperation(
                    left=replacement, right=operand, operator=cst.Or()
                )

        if replacement is not None:
            self.report(node, replacement=replacement)
Ejemplo n.º 5
0
class TupleTest(CSTNodeTest):
    @data_provider(
        [
            # zero-element tuple
            {"node": cst.Tuple([]), "code": "()", "parser": parse_expression},
            # one-element tuple, sentinel comma value
            {
                "node": cst.Tuple([cst.Element(cst.Name("single_element"))]),
                "code": "(single_element,)",
                "parser": None,
            },
            {
                "node": cst.Tuple([cst.StarredElement(cst.Name("single_element"))]),
                "code": "(*single_element,)",
                "parser": None,
            },
            # two-element tuple, sentinel comma value
            {
                "node": cst.Tuple(
                    [cst.Element(cst.Name("one")), cst.Element(cst.Name("two"))]
                ),
                "code": "(one, two)",
                "parser": None,
            },
            # remove parenthesis
            {
                "node": cst.Tuple(
                    [cst.Element(cst.Name("one")), cst.Element(cst.Name("two"))],
                    lpar=[],
                    rpar=[],
                ),
                "code": "one, two",
                "parser": None,
            },
            # add extra parenthesis
            {
                "node": cst.Tuple(
                    [cst.Element(cst.Name("one")), cst.Element(cst.Name("two"))],
                    lpar=[cst.LeftParen(), cst.LeftParen()],
                    rpar=[cst.RightParen(), cst.RightParen()],
                ),
                "code": "((one, two))",
                "parser": None,
            },
            # starred element
            {
                "node": cst.Tuple(
                    [
                        cst.StarredElement(cst.Name("one")),
                        cst.StarredElement(cst.Name("two")),
                    ]
                ),
                "code": "(*one, *two)",
                "parser": None,
            },
            # custom comma on Element
            {
                "node": cst.Tuple(
                    [
                        cst.Element(cst.Name("one"), comma=cst.Comma()),
                        cst.Element(cst.Name("two"), comma=cst.Comma()),
                    ]
                ),
                "code": "(one,two,)",
                "parser": parse_expression,
            },
            # custom comma on StarredElement
            {
                "node": cst.Tuple(
                    [
                        cst.StarredElement(cst.Name("one"), comma=cst.Comma()),
                        cst.StarredElement(cst.Name("two"), comma=cst.Comma()),
                    ]
                ),
                "code": "(*one,*two,)",
                "parser": parse_expression,
                "expected_position": CodeRange((1, 1), (1, 11)),
            },
            # custom parenthesis on StarredElement
            {
                "node": cst.Tuple(
                    [
                        cst.StarredElement(
                            cst.Name("abc"),
                            lpar=[cst.LeftParen()],
                            rpar=[cst.RightParen()],
                            comma=cst.Comma(),
                        )
                    ]
                ),
                "code": "((*abc),)",
                "parser": parse_expression,
                "expected_position": CodeRange((1, 1), (1, 8)),
            },
            # custom whitespace on StarredElement
            {
                "node": cst.Tuple(
                    [
                        cst.Element(cst.Name("one"), comma=cst.Comma()),
                        cst.StarredElement(
                            cst.Name("two"),
                            whitespace_before_value=cst.SimpleWhitespace("  "),
                            lpar=[cst.LeftParen()],
                            rpar=[cst.RightParen()],
                        ),
                    ],
                    lpar=[],
                    rpar=[],  # rpar can't own the trailing whitespace if it's not there
                ),
                "code": "one,(*  two)",
                "parser": parse_expression,
                "expected_position": CodeRange((1, 0), (1, 12)),
            },
            # missing spaces around tuple, okay with parenthesis
            {
                "node": cst.For(
                    target=cst.Tuple(
                        [
                            cst.Element(cst.Name("k"), comma=cst.Comma()),
                            cst.Element(cst.Name("v")),
                        ]
                    ),
                    iter=cst.Name("abc"),
                    body=cst.SimpleStatementSuite([cst.Pass()]),
                    whitespace_after_for=cst.SimpleWhitespace(""),
                    whitespace_before_in=cst.SimpleWhitespace(""),
                ),
                "code": "for(k,v)in abc: pass\n",
                "parser": parse_statement,
            },
            # no spaces around tuple, but using values that are parenthesized
            {
                "node": cst.For(
                    target=cst.Tuple(
                        [
                            cst.Element(
                                cst.Name(
                                    "k", lpar=[cst.LeftParen()], rpar=[cst.RightParen()]
                                ),
                                comma=cst.Comma(),
                            ),
                            cst.Element(
                                cst.Name(
                                    "v", lpar=[cst.LeftParen()], rpar=[cst.RightParen()]
                                )
                            ),
                        ],
                        lpar=[],
                        rpar=[],
                    ),
                    iter=cst.Name("abc"),
                    body=cst.SimpleStatementSuite([cst.Pass()]),
                    whitespace_after_for=cst.SimpleWhitespace(""),
                    whitespace_before_in=cst.SimpleWhitespace(""),
                ),
                "code": "for(k),(v)in abc: pass\n",
                "parser": parse_statement,
            },
            # starred elements are safe to use without a space before them
            {
                "node": cst.For(
                    target=cst.Tuple(
                        [cst.StarredElement(cst.Name("foo"), comma=cst.Comma())],
                        lpar=[],
                        rpar=[],
                    ),
                    iter=cst.Name("bar"),
                    body=cst.SimpleStatementSuite([cst.Pass()]),
                    whitespace_after_for=cst.SimpleWhitespace(""),
                ),
                "code": "for*foo, in bar: pass\n",
                "parser": parse_statement,
            },
            # a trailing comma doesn't mess up TrailingWhitespace
            {
                "node": cst.SimpleStatementLine(
                    [
                        cst.Expr(
                            cst.Tuple(
                                [
                                    cst.Element(cst.Name("one"), comma=cst.Comma()),
                                    cst.Element(cst.Name("two"), comma=cst.Comma()),
                                ],
                                lpar=[],
                                rpar=[],
                            )
                        )
                    ],
                    trailing_whitespace=cst.TrailingWhitespace(
                        whitespace=cst.SimpleWhitespace("  "),
                        comment=cst.Comment("# comment"),
                    ),
                ),
                "code": "one,two,  # comment\n",
                "parser": parse_statement,
            },
        ]
    )
    def test_valid(self, **kwargs: Any) -> None:
        self.validate_node(**kwargs)

    @data_provider(
        (
            (
                lambda: cst.Tuple([], lpar=[], rpar=[]),
                "A zero-length tuple must be wrapped in parentheses.",
            ),
            (
                lambda: cst.Tuple(
                    [cst.Element(cst.Name("mismatched"))],
                    lpar=[cst.LeftParen(), cst.LeftParen()],
                    rpar=[cst.RightParen()],
                ),
                "unbalanced parens",
            ),
            (
                lambda: cst.For(
                    target=cst.Tuple([cst.Element(cst.Name("el"))], lpar=[], rpar=[]),
                    iter=cst.Name("it"),
                    body=cst.SimpleStatementSuite([cst.Pass()]),
                    whitespace_after_for=cst.SimpleWhitespace(""),
                ),
                "Must have at least one space after 'for' keyword.",
            ),
            (
                lambda: cst.For(
                    target=cst.Tuple([cst.Element(cst.Name("el"))], lpar=[], rpar=[]),
                    iter=cst.Name("it"),
                    body=cst.SimpleStatementSuite([cst.Pass()]),
                    whitespace_before_in=cst.SimpleWhitespace(""),
                ),
                "Must have at least one space before 'in' keyword.",
            ),
            # an additional check for StarredElement, since it's a separate codepath
            (
                lambda: cst.For(
                    target=cst.Tuple(
                        [cst.StarredElement(cst.Name("el"))], lpar=[], rpar=[]
                    ),
                    iter=cst.Name("it"),
                    body=cst.SimpleStatementSuite([cst.Pass()]),
                    whitespace_before_in=cst.SimpleWhitespace(""),
                ),
                "Must have at least one space before 'in' keyword.",
            ),
        )
    )
    def test_invalid(
        self, get_node: Callable[[], cst.CSTNode], expected_re: str
    ) -> None:
        self.assert_invalid(get_node, expected_re)
Ejemplo n.º 6
0
class TryTest(CSTNodeTest):
    @data_provider(
        (
            # Simple try/except block
            {
                "node": cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    handlers=(
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            whitespace_after_except=cst.SimpleWhitespace(""),
                        ),
                    ),
                ),
                "code": "try: pass\nexcept: pass\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (2, 12)),
            },
            # Try/except with a class
            {
                "node": cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    handlers=(
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            type=cst.Name("Exception"),
                        ),
                    ),
                ),
                "code": "try: pass\nexcept Exception: pass\n",
                "parser": parse_statement,
            },
            # Try/except with a named class
            {
                "node": cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    handlers=(
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            type=cst.Name("Exception"),
                            name=cst.AsName(cst.Name("exc")),
                        ),
                    ),
                ),
                "code": "try: pass\nexcept Exception as exc: pass\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (2, 29)),
            },
            # Try/except with multiple clauses
            {
                "node": cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    handlers=(
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            type=cst.Name("TypeError"),
                            name=cst.AsName(cst.Name("e")),
                        ),
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            type=cst.Name("KeyError"),
                            name=cst.AsName(cst.Name("e")),
                        ),
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            whitespace_after_except=cst.SimpleWhitespace(""),
                        ),
                    ),
                ),
                "code": "try: pass\n"
                + "except TypeError as e: pass\n"
                + "except KeyError as e: pass\n"
                + "except: pass\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (4, 12)),
            },
            # Simple try/finally block
            {
                "node": cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))),
                ),
                "code": "try: pass\nfinally: pass\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (2, 13)),
            },
            # Simple try/except/finally block
            {
                "node": cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    handlers=(
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            whitespace_after_except=cst.SimpleWhitespace(""),
                        ),
                    ),
                    finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))),
                ),
                "code": "try: pass\nexcept: pass\nfinally: pass\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (3, 13)),
            },
            # Simple try/except/else block
            {
                "node": cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    handlers=(
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            whitespace_after_except=cst.SimpleWhitespace(""),
                        ),
                    ),
                    orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(),))),
                ),
                "code": "try: pass\nexcept: pass\nelse: pass\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (3, 10)),
            },
            # Simple try/except/else block/finally
            {
                "node": cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    handlers=(
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            whitespace_after_except=cst.SimpleWhitespace(""),
                        ),
                    ),
                    orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(),))),
                    finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))),
                ),
                "code": "try: pass\nexcept: pass\nelse: pass\nfinally: pass\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (4, 13)),
            },
            # Verify whitespace in various locations
            {
                "node": cst.Try(
                    leading_lines=(cst.EmptyLine(comment=cst.Comment("# 1")),),
                    body=cst.SimpleStatementSuite((cst.Pass(),)),
                    handlers=(
                        cst.ExceptHandler(
                            leading_lines=(cst.EmptyLine(comment=cst.Comment("# 2")),),
                            type=cst.Name("TypeError"),
                            name=cst.AsName(
                                cst.Name("e"),
                                whitespace_before_as=cst.SimpleWhitespace("  "),
                                whitespace_after_as=cst.SimpleWhitespace("  "),
                            ),
                            whitespace_after_except=cst.SimpleWhitespace("  "),
                            whitespace_before_colon=cst.SimpleWhitespace(" "),
                            body=cst.SimpleStatementSuite((cst.Pass(),)),
                        ),
                    ),
                    orelse=cst.Else(
                        leading_lines=(cst.EmptyLine(comment=cst.Comment("# 3")),),
                        body=cst.SimpleStatementSuite((cst.Pass(),)),
                        whitespace_before_colon=cst.SimpleWhitespace(" "),
                    ),
                    finalbody=cst.Finally(
                        leading_lines=(cst.EmptyLine(comment=cst.Comment("# 4")),),
                        body=cst.SimpleStatementSuite((cst.Pass(),)),
                        whitespace_before_colon=cst.SimpleWhitespace(" "),
                    ),
                    whitespace_before_colon=cst.SimpleWhitespace(" "),
                ),
                "code": "# 1\ntry : pass\n# 2\nexcept  TypeError  as  e : pass\n# 3\nelse : pass\n# 4\nfinally : pass\n",
                "parser": parse_statement,
                "expected_position": CodeRange((2, 0), (8, 14)),
            },
            # Please don't write code like this
            {
                "node": cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    handlers=(
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            type=cst.Name("TypeError"),
                            name=cst.AsName(cst.Name("e")),
                        ),
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            type=cst.Name("KeyError"),
                            name=cst.AsName(cst.Name("e")),
                        ),
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            whitespace_after_except=cst.SimpleWhitespace(""),
                        ),
                    ),
                    orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(),))),
                    finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))),
                ),
                "code": "try: pass\n"
                + "except TypeError as e: pass\n"
                + "except KeyError as e: pass\n"
                + "except: pass\n"
                + "else: pass\n"
                + "finally: pass\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (6, 13)),
            },
            # Verify indentation
            {
                "node": DummyIndentedBlock(
                    "    ",
                    cst.Try(
                        cst.SimpleStatementSuite((cst.Pass(),)),
                        handlers=(
                            cst.ExceptHandler(
                                cst.SimpleStatementSuite((cst.Pass(),)),
                                type=cst.Name("TypeError"),
                                name=cst.AsName(cst.Name("e")),
                            ),
                            cst.ExceptHandler(
                                cst.SimpleStatementSuite((cst.Pass(),)),
                                type=cst.Name("KeyError"),
                                name=cst.AsName(cst.Name("e")),
                            ),
                            cst.ExceptHandler(
                                cst.SimpleStatementSuite((cst.Pass(),)),
                                whitespace_after_except=cst.SimpleWhitespace(""),
                            ),
                        ),
                        orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(),))),
                        finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))),
                    ),
                ),
                "code": "    try: pass\n"
                + "    except TypeError as e: pass\n"
                + "    except KeyError as e: pass\n"
                + "    except: pass\n"
                + "    else: pass\n"
                + "    finally: pass\n",
                "parser": None,
            },
            # Verify indentation in bodies
            {
                "node": DummyIndentedBlock(
                    "    ",
                    cst.Try(
                        cst.IndentedBlock((cst.SimpleStatementLine((cst.Pass(),)),)),
                        handlers=(
                            cst.ExceptHandler(
                                cst.IndentedBlock(
                                    (cst.SimpleStatementLine((cst.Pass(),)),)
                                ),
                                whitespace_after_except=cst.SimpleWhitespace(""),
                            ),
                        ),
                        orelse=cst.Else(
                            cst.IndentedBlock((cst.SimpleStatementLine((cst.Pass(),)),))
                        ),
                        finalbody=cst.Finally(
                            cst.IndentedBlock((cst.SimpleStatementLine((cst.Pass(),)),))
                        ),
                    ),
                ),
                "code": "    try:\n"
                + "        pass\n"
                + "    except:\n"
                + "        pass\n"
                + "    else:\n"
                + "        pass\n"
                + "    finally:\n"
                + "        pass\n",
                "parser": None,
            },
            # No space when using grouping parens
            {
                "node": cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    handlers=(
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            whitespace_after_except=cst.SimpleWhitespace(""),
                            type=cst.Name(
                                "Exception",
                                lpar=(cst.LeftParen(),),
                                rpar=(cst.RightParen(),),
                            ),
                        ),
                    ),
                ),
                "code": "try: pass\nexcept(Exception): pass\n",
                "parser": parse_statement,
            },
            # No space when using tuple
            {
                "node": cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    handlers=(
                        cst.ExceptHandler(
                            cst.SimpleStatementSuite((cst.Pass(),)),
                            whitespace_after_except=cst.SimpleWhitespace(""),
                            type=cst.Tuple(
                                [
                                    cst.Element(
                                        cst.Name("IOError"),
                                        comma=cst.Comma(
                                            whitespace_after=cst.SimpleWhitespace(" ")
                                        ),
                                    ),
                                    cst.Element(cst.Name("ImportError")),
                                ]
                            ),
                        ),
                    ),
                ),
                "code": "try: pass\nexcept(IOError, ImportError): pass\n",
                "parser": parse_statement,
            },
        )
    )
    def test_valid(self, **kwargs: Any) -> None:
        self.validate_node(**kwargs)

    @data_provider(
        (
            {
                "get_node": lambda: cst.AsName(cst.Name("")),
                "expected_re": "empty name identifier",
            },
            {
                "get_node": lambda: cst.AsName(
                    cst.Name("bla"), whitespace_after_as=cst.SimpleWhitespace("")
                ),
                "expected_re": "between 'as'",
            },
            {
                "get_node": lambda: cst.AsName(
                    cst.Name("bla"), whitespace_before_as=cst.SimpleWhitespace("")
                ),
                "expected_re": "before 'as'",
            },
            {
                "get_node": lambda: cst.ExceptHandler(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    name=cst.AsName(cst.Name("bla")),
                ),
                "expected_re": "name for an empty type",
            },
            {
                "get_node": lambda: cst.ExceptHandler(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    type=cst.Name("TypeError"),
                    whitespace_after_except=cst.SimpleWhitespace(""),
                ),
                "expected_re": "at least one space after except",
            },
            {
                "get_node": lambda: cst.Try(cst.SimpleStatementSuite((cst.Pass(),))),
                "expected_re": "at least one ExceptHandler or Finally",
            },
            {
                "get_node": lambda: cst.Try(
                    cst.SimpleStatementSuite((cst.Pass(),)),
                    orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(),))),
                    finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))),
                ),
                "expected_re": "at least one ExceptHandler in order to have an Else",
            },
        )
    )
    def test_invalid(self, **kwargs: Any) -> None:
        self.assert_invalid(**kwargs)
Ejemplo n.º 7
0
class ListTest(CSTNodeTest):

    # A lot of Element/StarredElement tests are provided by the tests for Tuple, so we
    # we don't need to duplicate them here.
    @data_provider([
        # one-element list, sentinel comma value
        {
            "node": cst.Set([cst.Element(cst.Name("single_element"))]),
            "code": "{single_element}",
            "parser": parse_expression,
        },
        # custom whitespace between brackets
        {
            "node":
            cst.Set(
                [cst.Element(cst.Name("single_element"))],
                lbrace=cst.LeftCurlyBrace(
                    whitespace_after=cst.SimpleWhitespace("\t")),
                rbrace=cst.RightCurlyBrace(
                    whitespace_before=cst.SimpleWhitespace("    ")),
            ),
            "code":
            "{\tsingle_element    }",
            "parser":
            parse_expression,
        },
        # two-element list, sentinel comma value
        {
            "node":
            cst.Set(
                [cst.Element(cst.Name("one")),
                 cst.Element(cst.Name("two"))]),
            "code":
            "{one, two}",
            "parser":
            None,
        },
        # with parenthesis
        {
            "node":
            cst.Set(
                [cst.Element(cst.Name("one"))],
                lpar=[cst.LeftParen()],
                rpar=[cst.RightParen()],
            ),
            "code":
            "({one})",
            "parser":
            None,
        },
        # starred element
        {
            "node":
            cst.Set([
                cst.StarredElement(cst.Name("one")),
                cst.StarredElement(cst.Name("two")),
            ]),
            "code":
            "{*one, *two}",
            "parser":
            None,
        },
        # missing spaces around set, always okay
        {
            "node":
            cst.GeneratorExp(
                cst.Name("elt"),
                cst.CompFor(
                    target=cst.Name("elt"),
                    iter=cst.Set([
                        cst.Element(
                            cst.Name("one"),
                            cst.Comma(
                                whitespace_after=cst.SimpleWhitespace(" ")),
                        ),
                        cst.Element(cst.Name("two")),
                    ]),
                    ifs=[
                        cst.CompIf(
                            cst.Name("test"),
                            whitespace_before=cst.SimpleWhitespace(""),
                        )
                    ],
                    whitespace_after_in=cst.SimpleWhitespace(""),
                ),
            ),
            "code":
            "(elt for elt in{one, two}if test)",
            "parser":
            parse_expression,
        },
    ])
    def test_valid(self, **kwargs: Any) -> None:
        self.validate_node(**kwargs)

    @data_provider((
        (
            lambda: cst.Set(
                [cst.Element(cst.Name("mismatched"))],
                lpar=[cst.LeftParen(), cst.LeftParen()],
                rpar=[cst.RightParen()],
            ),
            "unbalanced parens",
        ),
        (lambda: cst.Set([]), "at least one element"),
    ))
    def test_invalid(self, get_node: Callable[[], cst.CSTNode],
                     expected_re: str) -> None:
        self.assert_invalid(get_node, expected_re)

    @data_provider((
        {
            "code": "{*x, 2}",
            "parser": parse_expression_as(python_version="3.5"),
            "expect_success": True,
        },
        {
            "code": "{*x, 2}",
            "parser": parse_expression_as(python_version="3.3"),
            "expect_success": False,
        },
    ))
    def test_versions(self, **kwargs: Any) -> None:
        if is_native() and not kwargs.get("expect_success", True):
            self.skipTest("parse errors are disabled for native parser")
        self.assert_parses(**kwargs)
Ejemplo n.º 8
0
 def to_element_cst(value):
     return cst.Element(value)
Ejemplo n.º 9
0
class ListTest(CSTNodeTest):

    # A lot of Element/StarredElement tests are provided by the tests for Tuple, so we
    # we don't need to duplicate them here.
    @data_provider([
        # zero-element list
        {
            "node": cst.List([]),
            "code": "[]",
            "parser": parse_expression
        },
        # one-element list, sentinel comma value
        {
            "node": cst.List([cst.Element(cst.Name("single_element"))]),
            "code": "[single_element]",
            "parser": parse_expression,
        },
        # custom whitespace between brackets
        {
            "node":
            cst.List(
                [cst.Element(cst.Name("single_element"))],
                lbracket=cst.LeftSquareBracket(
                    whitespace_after=cst.SimpleWhitespace("\t")),
                rbracket=cst.RightSquareBracket(
                    whitespace_before=cst.SimpleWhitespace("    ")),
            ),
            "code":
            "[\tsingle_element    ]",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange.create((1, 0), (1, 21)),
        },
        # two-element list, sentinel comma value
        {
            "node":
            cst.List(
                [cst.Element(cst.Name("one")),
                 cst.Element(cst.Name("two"))]),
            "code":
            "[one, two]",
            "parser":
            None,
        },
        # with parenthesis
        {
            "node":
            cst.List(
                [cst.Element(cst.Name("one"))],
                lpar=[cst.LeftParen()],
                rpar=[cst.RightParen()],
            ),
            "code":
            "([one])",
            "parser":
            None,
            "expected_position":
            CodeRange.create((1, 1), (1, 6)),
        },
        # starred element
        {
            "node":
            cst.List([
                cst.StarredElement(cst.Name("one")),
                cst.StarredElement(cst.Name("two")),
            ]),
            "code":
            "[*one, *two]",
            "parser":
            None,
            "expected_position":
            CodeRange.create((1, 0), (1, 12)),
        },
        # missing spaces around list, always okay
        {
            "node":
            cst.For(
                target=cst.List([
                    cst.Element(cst.Name("k"), comma=cst.Comma()),
                    cst.Element(cst.Name("v")),
                ]),
                iter=cst.Name("abc"),
                body=cst.SimpleStatementSuite([cst.Pass()]),
                whitespace_after_for=cst.SimpleWhitespace(""),
                whitespace_before_in=cst.SimpleWhitespace(""),
            ),
            "code":
            "for[k,v]in abc: pass\n",
            "parser":
            parse_statement,
        },
    ])
    def test_valid(self, **kwargs: Any) -> None:
        self.validate_node(**kwargs)

    @data_provider(((
        lambda: cst.List(
            [cst.Element(cst.Name("mismatched"))],
            lpar=[cst.LeftParen(), cst.LeftParen()],
            rpar=[cst.RightParen()],
        ),
        "unbalanced parens",
    ), ))
    def test_invalid(self, get_node: Callable[[], cst.CSTNode],
                     expected_re: str) -> None:
        self.assert_invalid(get_node, expected_re)
Ejemplo n.º 10
0
    def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode:
        try:
            key = original.func.attr.value
            kword_params = self.METHOD_TO_PARAMS[key]
        except (AttributeError, KeyError):
            # Either not a method from the API or too convoluted to be sure.
            return updated

        # If the existing code is valid, keyword args come after positional args.
        # Therefore, all positional args must map to the first parameters.
        args, kwargs = partition(lambda a: not bool(a.keyword), updated.args)
        if any(k.keyword.value == "request" for k in kwargs):
            # We've already fixed this file, don't fix it again.
            return updated

        kwargs, ctrl_kwargs = partition(
            lambda a: not a.keyword.value in self.CTRL_PARAMS, kwargs)

        args, ctrl_args = args[:len(kword_params)], args[len(kword_params):]
        ctrl_kwargs.extend(
            cst.Arg(
                value=a.value,
                keyword=cst.Name(value=ctrl),
                equal=cst.AssignEqual(
                    whitespace_before=cst.SimpleWhitespace(""),
                    whitespace_after=cst.SimpleWhitespace(""),
                ),
            ) for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS))

        if self._use_keywords:
            new_kwargs = [
                cst.Arg(
                    value=arg.value,
                    keyword=cst.Name(value=name),
                    equal=cst.AssignEqual(
                        whitespace_before=cst.SimpleWhitespace(""),
                        whitespace_after=cst.SimpleWhitespace(""),
                    ),
                ) for name, arg in zip(kword_params, args + kwargs)
            ]
            new_kwargs.extend([
                cst.Arg(
                    value=arg.value,
                    keyword=cst.Name(value=arg.keyword.value),
                    equal=cst.AssignEqual(
                        whitespace_before=cst.SimpleWhitespace(""),
                        whitespace_after=cst.SimpleWhitespace(""),
                    ),
                ) for arg in ctrl_kwargs
            ])
            return updated.with_changes(args=new_kwargs)
        else:
            request_arg = cst.Arg(
                value=cst.Dict([
                    cst.DictElement(
                        cst.SimpleString('"{}"'.format(name)),
                        cst.Element(value=arg.value),
                    ) for name, arg in zip(kword_params, args + kwargs)
                ] + [
                    cst.DictElement(
                        cst.SimpleString('"{}"'.format(arg.keyword.value)),
                        cst.Element(value=arg.value),
                    ) for arg in ctrl_kwargs
                ]),
                keyword=cst.Name("request"),
                equal=cst.AssignEqual(
                    whitespace_before=cst.SimpleWhitespace(""),
                    whitespace_after=cst.SimpleWhitespace(""),
                ),
            )

            return updated.with_changes(args=[request_arg])
Ejemplo n.º 11
0
def make_list(elts):
    return cst.List(elements=[cst.Element(value=elt) for elt in elts])
Ejemplo n.º 12
0
class AtomTest(CSTNodeTest):
    @data_provider((
        # Simple identifier
        {
            "node": cst.Name("test"),
            "code": "test",
            "parser": parse_expression,
            "expected_position": None,
        },
        # Parenthesized identifier
        {
            "node":
            cst.Name("test",
                     lpar=(cst.LeftParen(), ),
                     rpar=(cst.RightParen(), )),
            "code":
            "(test)",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 1), (1, 5)),
        },
        # Decimal integers
        {
            "node": cst.Integer("12345"),
            "code": "12345",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Integer("0000"),
            "code": "0000",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Integer("1_234_567"),
            "code": "1_234_567",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Integer("0_000"),
            "code": "0_000",
            "parser": parse_expression,
            "expected_position": None,
        },
        # Binary integers
        {
            "node": cst.Integer("0b0000"),
            "code": "0b0000",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Integer("0B1011_0100"),
            "code": "0B1011_0100",
            "parser": parse_expression,
            "expected_position": None,
        },
        # Octal integers
        {
            "node": cst.Integer("0o12345"),
            "code": "0o12345",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Integer("0O12_345"),
            "code": "0O12_345",
            "parser": parse_expression,
            "expected_position": None,
        },
        # Hex numbers
        {
            "node": cst.Integer("0x123abc"),
            "code": "0x123abc",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Integer("0X12_3ABC"),
            "code": "0X12_3ABC",
            "parser": parse_expression,
            "expected_position": None,
        },
        # Parenthesized integers
        {
            "node":
            cst.Integer("123",
                        lpar=(cst.LeftParen(), ),
                        rpar=(cst.RightParen(), )),
            "code":
            "(123)",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 1), (1, 4)),
        },
        # Non-exponent floats
        {
            "node": cst.Float("12345."),
            "code": "12345.",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float("00.00"),
            "code": "00.00",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float("12.21"),
            "code": "12.21",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float(".321"),
            "code": ".321",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float("1_234_567."),
            "code": "1_234_567.",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float("0.000_000"),
            "code": "0.000_000",
            "parser": parse_expression,
            "expected_position": None,
        },
        # Exponent floats
        {
            "node": cst.Float("12345.e10"),
            "code": "12345.e10",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float("00.00e10"),
            "code": "00.00e10",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float("12.21e10"),
            "code": "12.21e10",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float(".321e10"),
            "code": ".321e10",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float("1_234_567.e10"),
            "code": "1_234_567.e10",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float("0.000_000e10"),
            "code": "0.000_000e10",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float("1e+10"),
            "code": "1e+10",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Float("1e-10"),
            "code": "1e-10",
            "parser": parse_expression,
            "expected_position": None,
        },
        # Parenthesized floats
        {
            "node":
            cst.Float("123.4",
                      lpar=(cst.LeftParen(), ),
                      rpar=(cst.RightParen(), )),
            "code":
            "(123.4)",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 1), (1, 6)),
        },
        # Imaginary numbers
        {
            "node": cst.Imaginary("12345j"),
            "code": "12345j",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Imaginary("1_234_567J"),
            "code": "1_234_567J",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Imaginary("12345.e10j"),
            "code": "12345.e10j",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.Imaginary(".321J"),
            "code": ".321J",
            "parser": parse_expression,
            "expected_position": None,
        },
        # Parenthesized imaginary
        {
            "node":
            cst.Imaginary("123.4j",
                          lpar=(cst.LeftParen(), ),
                          rpar=(cst.RightParen(), )),
            "code":
            "(123.4j)",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 1), (1, 7)),
        },
        # Simple elipses
        {
            "node": cst.Ellipsis(),
            "code": "...",
            "parser": parse_expression,
            "expected_position": None,
        },
        # Parenthesized elipses
        {
            "node":
            cst.Ellipsis(lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )),
            "code":
            "(...)",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 1), (1, 4)),
        },
        # Simple strings
        {
            "node": cst.SimpleString('""'),
            "code": '""',
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.SimpleString("''"),
            "code": "''",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.SimpleString('"test"'),
            "code": '"test"',
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.SimpleString('b"test"'),
            "code": 'b"test"',
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.SimpleString('r"test"'),
            "code": 'r"test"',
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.SimpleString('"""test"""'),
            "code": '"""test"""',
            "parser": parse_expression,
            "expected_position": None,
        },
        # Validate parens
        {
            "node":
            cst.SimpleString('"test"',
                             lpar=(cst.LeftParen(), ),
                             rpar=(cst.RightParen(), )),
            "code":
            '("test")',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        {
            "node":
            cst.SimpleString('rb"test"',
                             lpar=(cst.LeftParen(), ),
                             rpar=(cst.RightParen(), )),
            "code":
            '(rb"test")',
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 1), (1, 9)),
        },
        # Test that _safe_to_use_with_word_operator allows no space around quotes
        {
            "node":
            cst.Comparison(
                cst.SimpleString('"a"'),
                [
                    cst.ComparisonTarget(
                        cst.In(
                            whitespace_before=cst.SimpleWhitespace(""),
                            whitespace_after=cst.SimpleWhitespace(""),
                        ),
                        cst.SimpleString('"abc"'),
                    )
                ],
            ),
            "code":
            '"a"in"abc"',
            "parser":
            parse_expression,
        },
        {
            "node":
            cst.Comparison(
                cst.SimpleString('"a"'),
                [
                    cst.ComparisonTarget(
                        cst.In(
                            whitespace_before=cst.SimpleWhitespace(""),
                            whitespace_after=cst.SimpleWhitespace(""),
                        ),
                        cst.ConcatenatedString(cst.SimpleString('"a"'),
                                               cst.SimpleString('"bc"')),
                    )
                ],
            ),
            "code":
            '"a"in"a""bc"',
            "parser":
            parse_expression,
        },
        # Parenthesis make no spaces around a prefix okay
        {
            "node":
            cst.Comparison(
                cst.SimpleString('b"a"'),
                [
                    cst.ComparisonTarget(
                        cst.In(
                            whitespace_before=cst.SimpleWhitespace(""),
                            whitespace_after=cst.SimpleWhitespace(""),
                        ),
                        cst.SimpleString(
                            'b"abc"',
                            lpar=[cst.LeftParen()],
                            rpar=[cst.RightParen()],
                        ),
                    )
                ],
            ),
            "code":
            'b"a"in(b"abc")',
            "parser":
            parse_expression,
        },
        {
            "node":
            cst.Comparison(
                cst.SimpleString('b"a"'),
                [
                    cst.ComparisonTarget(
                        cst.In(
                            whitespace_before=cst.SimpleWhitespace(""),
                            whitespace_after=cst.SimpleWhitespace(""),
                        ),
                        cst.ConcatenatedString(
                            cst.SimpleString('b"a"'),
                            cst.SimpleString('b"bc"'),
                            lpar=[cst.LeftParen()],
                            rpar=[cst.RightParen()],
                        ),
                    )
                ],
            ),
            "code":
            'b"a"in(b"a"b"bc")',
            "parser":
            parse_expression,
        },
        # Empty formatted strings
        {
            "node": cst.FormattedString(start='f"', parts=(), end='"'),
            "code": 'f""',
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.FormattedString(start="f'", parts=(), end="'"),
            "code": "f''",
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.FormattedString(start='f"""', parts=(), end='"""'),
            "code": 'f""""""',
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node": cst.FormattedString(start="f'''", parts=(), end="'''"),
            "code": "f''''''",
            "parser": parse_expression,
            "expected_position": None,
        },
        # Non-empty formatted strings
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringText("foo"), )),
            "code": 'f"foo"',
            "parser": parse_expression,
            "expected_position": None,
        },
        {
            "node":
            cst.FormattedString(
                parts=(cst.FormattedStringExpression(cst.Name("foo")), )),
            "code":
            'f"{foo}"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(
                cst.FormattedStringText("foo "),
                cst.FormattedStringExpression(cst.Name("bar")),
                cst.FormattedStringText(" baz"),
            )),
            "code":
            'f"foo {bar} baz"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(
                cst.FormattedStringText("foo "),
                cst.FormattedStringExpression(cst.Call(cst.Name("bar"))),
                cst.FormattedStringText(" baz"),
            )),
            "code":
            'f"foo {bar()} baz"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        # Formatted strings with conversions and format specifiers
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Name("foo"), conversion="s"), )),
            "code":
            'f"{foo!s}"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Name("foo"), format_spec=()), )),
            "code":
            'f"{foo:}"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Name("today"),
                format_spec=(cst.FormattedStringText("%B %d, %Y"), ),
            ), )),
            "code":
            'f"{today:%B %d, %Y}"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Name("foo"),
                format_spec=(cst.FormattedStringExpression(cst.Name("bar")), ),
            ), )),
            "code":
            'f"{foo:{bar}}"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Name("foo"),
                format_spec=(
                    cst.FormattedStringExpression(cst.Name("bar")),
                    cst.FormattedStringText("."),
                    cst.FormattedStringExpression(cst.Name("baz")),
                ),
            ), )),
            "code":
            'f"{foo:{bar}.{baz}}"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Name("foo"),
                conversion="s",
                format_spec=(cst.FormattedStringExpression(cst.Name("bar")), ),
            ), )),
            "code":
            'f"{foo!s:{bar}}"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        # Test equality expression added in 3.8.
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Name("foo"),
                equal=cst.AssignEqual(
                    whitespace_before=cst.SimpleWhitespace(""),
                    whitespace_after=cst.SimpleWhitespace(""),
                ),
            ), ), ),
            "code":
            'f"{foo=}"',
            "parser":
            _parse_expression_force_38,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Name("foo"),
                equal=cst.AssignEqual(
                    whitespace_before=cst.SimpleWhitespace(""),
                    whitespace_after=cst.SimpleWhitespace(""),
                ),
                conversion="s",
            ), ), ),
            "code":
            'f"{foo=!s}"',
            "parser":
            _parse_expression_force_38,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Name("foo"),
                equal=cst.AssignEqual(
                    whitespace_before=cst.SimpleWhitespace(""),
                    whitespace_after=cst.SimpleWhitespace(""),
                ),
                conversion="s",
                format_spec=(cst.FormattedStringExpression(cst.Name("bar")), ),
            ), ), ),
            "code":
            'f"{foo=!s:{bar}}"',
            "parser":
            _parse_expression_force_38,
            "expected_position":
            None,
        },
        # Test that equality support doesn't break existing support
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Comparison(
                    left=cst.Name(value="a", ),
                    comparisons=[
                        cst.ComparisonTarget(
                            operator=cst.Equal(),
                            comparator=cst.Name(value="b", ),
                        ),
                    ],
                ), ), ), ),
            "code":
            'f"{a == b}"',
            "parser":
            _parse_expression_force_38,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Comparison(
                    left=cst.Name(value="a", ),
                    comparisons=[
                        cst.ComparisonTarget(
                            operator=cst.NotEqual(),
                            comparator=cst.Name(value="b", ),
                        ),
                    ],
                ), ), ), ),
            "code":
            'f"{a != b}"',
            "parser":
            _parse_expression_force_38,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.NamedExpr(
                    target=cst.Name(value="a", ),
                    value=cst.Integer(value="5", ),
                    lpar=(cst.LeftParen(), ),
                    rpar=(cst.RightParen(), ),
                ), ), ), ),
            "code":
            'f"{(a := 5)}"',
            "parser":
            _parse_expression_force_38,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(cst.FormattedStringExpression(
                cst.Yield(
                    value=cst.Integer("1"),
                    whitespace_after_yield=cst.SimpleWhitespace(" "),
                ), ), ), ),
            "code":
            'f"{yield 1}"',
            "parser":
            _parse_expression_force_38,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(parts=(
                cst.FormattedStringText("\\N{X Y}"),
                cst.FormattedStringExpression(cst.Name(value="Z"), ),
            ), ),
            "code":
            'f"\\N{X Y}{Z}"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        {
            "node":
            cst.FormattedString(
                parts=(
                    cst.FormattedStringText("\\"),
                    cst.FormattedStringExpression(cst.Name(value="a"), ),
                ),
                start='fr"',
            ),
            "code":
            'fr"\\{a}"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        # Validate parens
        {
            "node":
            cst.FormattedString(
                start='f"',
                parts=(),
                end='"',
                lpar=(cst.LeftParen(), ),
                rpar=(cst.RightParen(), ),
            ),
            "code":
            '(f"")',
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 1), (1, 4)),
        },
        # Generator expression (doesn't make sense, but legal syntax)
        {
            "node":
            cst.FormattedString(
                start='f"',
                parts=[
                    cst.FormattedStringExpression(expression=cst.GeneratorExp(
                        elt=cst.Name(value="x", ),
                        for_in=cst.CompFor(
                            target=cst.Name(value="x", ),
                            iter=cst.Name(value="y", ),
                        ),
                        lpar=[],
                        rpar=[],
                    ), ),
                ],
                end='"',
            ),
            "code":
            'f"{x for x in y}"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        # Unpacked tuple
        {
            "node":
            cst.FormattedString(
                parts=[
                    cst.FormattedStringExpression(expression=cst.Tuple(
                        elements=[
                            cst.Element(
                                value=cst.Name(value="a", ),
                                comma=cst.Comma(
                                    whitespace_before=cst.SimpleWhitespace(
                                        value="", ),
                                    whitespace_after=cst.SimpleWhitespace(
                                        value=" ", ),
                                ),
                            ),
                            cst.Element(value=cst.Name(value="b", ), ),
                        ],
                        lpar=[],
                        rpar=[],
                    ), ),
                ],
                start="f'",
                end="'",
            ),
            "code":
            "f'{a, b}'",
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        # Conditional expression
        {
            "node":
            cst.FormattedString(
                parts=[
                    cst.FormattedStringExpression(expression=cst.IfExp(
                        test=cst.Name(value="b", ),
                        body=cst.Name(value="a", ),
                        orelse=cst.Name(value="c", ),
                    ), ),
                ],
                start="f'",
                end="'",
            ),
            "code":
            "f'{a if b else c}'",
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        # Concatenated strings
        {
            "node":
            cst.ConcatenatedString(cst.SimpleString('"ab"'),
                                   cst.SimpleString('"c"')),
            "code":
            '"ab""c"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        {
            "node":
            cst.ConcatenatedString(
                cst.SimpleString('"ab"'),
                cst.ConcatenatedString(cst.SimpleString('"c"'),
                                       cst.SimpleString('"d"')),
            ),
            "code":
            '"ab""c""d"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        # mixed SimpleString and FormattedString
        {
            "node":
            cst.ConcatenatedString(
                cst.FormattedString([cst.FormattedStringText("ab")]),
                cst.SimpleString('"c"'),
            ),
            "code":
            'f"ab""c"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        {
            "node":
            cst.ConcatenatedString(
                cst.SimpleString('"ab"'),
                cst.FormattedString([cst.FormattedStringText("c")]),
            ),
            "code":
            '"ab"f"c"',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        # Concatenated parenthesized strings
        {
            "node":
            cst.ConcatenatedString(
                lpar=(cst.LeftParen(), ),
                left=cst.SimpleString('"ab"'),
                right=cst.SimpleString('"c"'),
                rpar=(cst.RightParen(), ),
            ),
            "code":
            '("ab""c")',
            "parser":
            parse_expression,
            "expected_position":
            None,
        },
        # Validate spacing
        {
            "node":
            cst.ConcatenatedString(
                lpar=(cst.LeftParen(
                    whitespace_after=cst.SimpleWhitespace(" ")), ),
                left=cst.SimpleString('"ab"'),
                whitespace_between=cst.SimpleWhitespace(" "),
                right=cst.SimpleString('"c"'),
                rpar=(cst.RightParen(
                    whitespace_before=cst.SimpleWhitespace(" ")), ),
            ),
            "code":
            '( "ab" "c" )',
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 2), (1, 10)),
        },
    ))
    def test_valid(self, **kwargs: Any) -> None:
        # We don't have sentinel nodes for atoms, so we know that 100% of atoms
        # can be parsed identically to their creation.
        self.validate_node(**kwargs)

    @data_provider(({
        "node":
        cst.FormattedStringExpression(
            cst.Name("today"),
            format_spec=(cst.FormattedStringText("%B %d, %Y"), ),
        ),
        "code":
        "{today:%B %d, %Y}",
        "parser":
        None,
        "expected_position":
        CodeRange((1, 0), (1, 17)),
    }, ))
    def test_valid_no_parse(self, **kwargs: Any) -> None:
        # Test some nodes that aren't valid source code by themselves
        self.validate_node(**kwargs)

    @data_provider((
        # Expression wrapping parenthesis rules
        {
            "get_node": (lambda: cst.Name("foo", lpar=(cst.LeftParen(), ))),
            "expected_re": "left paren without right paren",
        },
        {
            "get_node": lambda: cst.Name("foo", rpar=(cst.RightParen(), )),
            "expected_re": "right paren without left paren",
        },
        {
            "get_node": lambda: cst.Ellipsis(lpar=(cst.LeftParen(), )),
            "expected_re": "left paren without right paren",
        },
        {
            "get_node": lambda: cst.Ellipsis(rpar=(cst.RightParen(), )),
            "expected_re": "right paren without left paren",
        },
        {
            "get_node": lambda: cst.Integer("5", lpar=(cst.LeftParen(), )),
            "expected_re": "left paren without right paren",
        },
        {
            "get_node": lambda: cst.Integer("5", rpar=(cst.RightParen(), )),
            "expected_re": "right paren without left paren",
        },
        {
            "get_node": lambda: cst.Float("5.5", lpar=(cst.LeftParen(), )),
            "expected_re": "left paren without right paren",
        },
        {
            "get_node": lambda: cst.Float("5.5", rpar=(cst.RightParen(), )),
            "expected_re": "right paren without left paren",
        },
        {
            "get_node":
            (lambda: cst.Imaginary("5j", lpar=(cst.LeftParen(), ))),
            "expected_re": "left paren without right paren",
        },
        {
            "get_node":
            (lambda: cst.Imaginary("5j", rpar=(cst.RightParen(), ))),
            "expected_re": "right paren without left paren",
        },
        {
            "get_node": (lambda: cst.Integer("5", lpar=(cst.LeftParen(), ))),
            "expected_re": "left paren without right paren",
        },
        {
            "get_node": (lambda: cst.Integer("5", rpar=(cst.RightParen(), ))),
            "expected_re": "right paren without left paren",
        },
        {
            "get_node":
            (lambda: cst.SimpleString("'foo'", lpar=(cst.LeftParen(), ))),
            "expected_re":
            "left paren without right paren",
        },
        {
            "get_node":
            (lambda: cst.SimpleString("'foo'", rpar=(cst.RightParen(), ))),
            "expected_re":
            "right paren without left paren",
        },
        {
            "get_node":
            (lambda: cst.FormattedString(parts=(), lpar=(cst.LeftParen(), ))),
            "expected_re":
            "left paren without right paren",
        },
        {
            "get_node":
            (lambda: cst.FormattedString(parts=(), rpar=(cst.RightParen(), ))),
            "expected_re":
            "right paren without left paren",
        },
        {
            "get_node": (lambda: cst.ConcatenatedString(
                cst.SimpleString("'foo'"),
                cst.SimpleString("'foo'"),
                lpar=(cst.LeftParen(), ),
            )),
            "expected_re":
            "left paren without right paren",
        },
        {
            "get_node": (lambda: cst.ConcatenatedString(
                cst.SimpleString("'foo'"),
                cst.SimpleString("'foo'"),
                rpar=(cst.RightParen(), ),
            )),
            "expected_re":
            "right paren without left paren",
        },
        # Node-specific rules
        {
            "get_node": (lambda: cst.Name("")),
            "expected_re": "empty name identifier",
        },
        {
            "get_node": (lambda: cst.Name(r"\/")),
            "expected_re": "not a valid identifier",
        },
        {
            "get_node": (lambda: cst.Integer("")),
            "expected_re": "not a valid integer",
        },
        {
            "get_node": (lambda: cst.Integer("012345")),
            "expected_re": "not a valid integer",
        },
        {
            "get_node": (lambda: cst.Integer("012345")),
            "expected_re": "not a valid integer",
        },
        {
            "get_node": (lambda: cst.Integer("_12345")),
            "expected_re": "not a valid integer",
        },
        {
            "get_node": (lambda: cst.Integer("0b2")),
            "expected_re": "not a valid integer",
        },
        {
            "get_node": (lambda: cst.Integer("0o8")),
            "expected_re": "not a valid integer",
        },
        {
            "get_node": (lambda: cst.Integer("0xg")),
            "expected_re": "not a valid integer",
        },
        {
            "get_node": (lambda: cst.Integer("123.45")),
            "expected_re": "not a valid integer",
        },
        {
            "get_node": (lambda: cst.Integer("12345j")),
            "expected_re": "not a valid integer",
        },
        {
            "get_node": (lambda: cst.Float("12.3.45")),
            "expected_re": "not a valid float",
        },
        {
            "get_node": (lambda: cst.Float("12")),
            "expected_re": "not a valid float"
        },
        {
            "get_node": (lambda: cst.Float("12.3j")),
            "expected_re": "not a valid float",
        },
        {
            "get_node": (lambda: cst.Imaginary("_12345j")),
            "expected_re": "not a valid imaginary",
        },
        {
            "get_node": (lambda: cst.Imaginary("0b0j")),
            "expected_re": "not a valid imaginary",
        },
        {
            "get_node": (lambda: cst.Imaginary("0o0j")),
            "expected_re": "not a valid imaginary",
        },
        {
            "get_node": (lambda: cst.Imaginary("0x0j")),
            "expected_re": "not a valid imaginary",
        },
        {
            "get_node": (lambda: cst.SimpleString('wee""')),
            "expected_re": "Invalid string prefix",
        },
        {
            "get_node": (lambda: cst.SimpleString("'")),
            "expected_re": "must have enclosing quotes",
        },
        {
            "get_node": (lambda: cst.SimpleString('"')),
            "expected_re": "must have enclosing quotes",
        },
        {
            "get_node": (lambda: cst.SimpleString("\"'")),
            "expected_re": "must have matching enclosing quotes",
        },
        {
            "get_node": (lambda: cst.SimpleString("")),
            "expected_re": "must have enclosing quotes",
        },
        {
            "get_node": (lambda: cst.SimpleString("'bla")),
            "expected_re": "must have matching enclosing quotes",
        },
        {
            "get_node": (lambda: cst.SimpleString("f''")),
            "expected_re": "Invalid string prefix",
        },
        {
            "get_node": (lambda: cst.SimpleString("'''bla''")),
            "expected_re": "must have matching enclosing quotes",
        },
        {
            "get_node": (lambda: cst.SimpleString("'''bla\"\"\"")),
            "expected_re": "must have matching enclosing quotes",
        },
        {
            "get_node":
            (lambda: cst.FormattedString(start="'", parts=(), end="'")),
            "expected_re": "Invalid f-string prefix",
        },
        {
            "get_node":
            (lambda: cst.FormattedString(start="f'", parts=(), end='"')),
            "expected_re":
            "must have matching enclosing quotes",
        },
        {
            "get_node": (lambda: cst.ConcatenatedString(
                cst.SimpleString('"ab"',
                                 lpar=(cst.LeftParen(), ),
                                 rpar=(cst.RightParen(), )),
                cst.SimpleString('"c"'),
            )),
            "expected_re":
            "Cannot concatenate parenthesized",
        },
        {
            "get_node": (lambda: cst.ConcatenatedString(
                cst.SimpleString('"ab"'),
                cst.SimpleString('"c"',
                                 lpar=(cst.LeftParen(), ),
                                 rpar=(cst.RightParen(), )),
            )),
            "expected_re":
            "Cannot concatenate parenthesized",
        },
        {
            "get_node": (lambda: cst.ConcatenatedString(
                cst.SimpleString('"ab"'), cst.SimpleString('b"c"'))),
            "expected_re":
            "Cannot concatenate string and bytes",
        },
        # This isn't valid code: `"a" inb"abc"`
        {
            "get_node": (lambda: cst.Comparison(
                cst.SimpleString('"a"'),
                [
                    cst.ComparisonTarget(
                        cst.In(whitespace_after=cst.SimpleWhitespace("")),
                        cst.SimpleString('b"abc"'),
                    )
                ],
            )),
            "expected_re":
            "Must have at least one space around comparison operator.",
        },
        # Also not valid: `"a" in b"a"b"bc"`
        {
            "get_node": (lambda: cst.Comparison(
                cst.SimpleString('"a"'),
                [
                    cst.ComparisonTarget(
                        cst.In(whitespace_after=cst.SimpleWhitespace("")),
                        cst.ConcatenatedString(cst.SimpleString('b"a"'),
                                               cst.SimpleString('b"bc"')),
                    )
                ],
            )),
            "expected_re":
            "Must have at least one space around comparison operator.",
        },
    ))
    def test_invalid(self, **kwargs: Any) -> None:
        self.assert_invalid(**kwargs)

    @data_provider((
        {
            "code": "u'x'",
            "parser": parse_expression_as(python_version="3.3"),
            "expect_success": True,
        },
        {
            "code": "u'x'",
            "parser": parse_expression_as(python_version="3.1"),
            "expect_success": False,
        },
    ))
    def test_versions(self, **kwargs: Any) -> None:
        if is_native() and not kwargs.get("expect_success", True):
            self.skipTest("parse errors are disabled for native parser")
        self.assert_parses(**kwargs)