Ejemplo n.º 1
0
 def leave_ComparisonTarget(self, node: cst.In, updated_node: cst.In) -> cst.In:
     if isinstance(node.operator, cst.In) and isinstance(
         node.comparator, cst.ListComp
     ):
         cmp = node.comparator
         return updated_node.with_changes(
             comparator=cst.GeneratorExp(elt=cmp.elt, for_in=cmp.for_in)
         )
     return updated_node
Ejemplo n.º 2
0
 def _gen_builtin_call(self, node: cst.Call) -> cst.Call:
     if not node.args:
         return node
     value = node.args[0].value
     if isinstance(value, cst.ListComp):
         pars: dict = {"lpar": [], "rpar": []} if len(node.args) == 1 else {}
         arg0 = node.args[0].with_changes(
             value=cst.GeneratorExp(elt=value.elt, for_in=value.for_in, **pars)
         )
         return node.with_changes(args=(arg0, *node.args[1:]))
     if isinstance(value, cst.GeneratorExp):
         if len(node.args) == 1 and value.lpar:
             arg0 = node.args[0].with_changes(
                 value=cst.GeneratorExp(
                     elt=value.elt, for_in=value.for_in, lpar=[], rpar=[]
                 )
             )
             return node.with_changes(args=(arg0, *node.args[1:]))
     return node
Ejemplo n.º 3
0
 def visit_Call(self, node: cst.Call) -> None:
     # This set excludes frozenset, max, min, sorted, sum, and tuple, which C407 would warn
     # about, because none of those functions short-circuit.
     if m.matches(
             node,
             m.Call(func=m.Name("all") | m.Name("any"),
                    args=[m.Arg(value=m.ListComp())]),
     ):
         list_comp = cst.ensure_type(node.args[0].value, cst.ListComp)
         self.report(
             node,
             UNNECESSARY_LIST_COMPREHENSION.format(
                 func=cst.ensure_type(node.func, cst.Name).value),
             replacement=node.deep_replace(
                 list_comp,
                 cst.GeneratorExp(elt=list_comp.elt,
                                  for_in=list_comp.for_in,
                                  lpar=[],
                                  rpar=[]),
             ),
         )
Ejemplo n.º 4
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.º 5
0
class SimpleCompTest(CSTNodeTest):
    @data_provider(
        [
            # simple GeneratorExp
            {
                "node": cst.GeneratorExp(
                    cst.Name("a"), cst.CompFor(target=cst.Name("b"), iter=cst.Name("c"))
                ),
                "code": "(a for b in c)",
                "parser": parse_expression,
                "expected_position": CodeRange.create((1, 1), (1, 13)),
            },
            # simple ListComp
            {
                "node": cst.ListComp(
                    cst.Name("a"), cst.CompFor(target=cst.Name("b"), iter=cst.Name("c"))
                ),
                "code": "[a for b in c]",
                "parser": parse_expression,
                "expected_position": CodeRange.create((1, 0), (1, 14)),
            },
            # simple SetComp
            {
                "node": cst.SetComp(
                    cst.Name("a"), cst.CompFor(target=cst.Name("b"), iter=cst.Name("c"))
                ),
                "code": "{a for b in c}",
                "parser": parse_expression,
            },
            # async GeneratorExp
            {
                "node": cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        asynchronous=cst.Asynchronous(),
                    ),
                ),
                "code": "(a async for b in c)",
                "parser": parse_expression,
            },
            # a generator doesn't have to own it's own parenthesis
            {
                "node": cst.Call(
                    cst.Name("func"),
                    [
                        cst.Arg(
                            cst.GeneratorExp(
                                cst.Name("a"),
                                cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
                                lpar=[],
                                rpar=[],
                            )
                        )
                    ],
                ),
                "code": "func(a for b in c)",
                "parser": parse_expression,
            },
            # add a few 'if' clauses
            {
                "node": cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        ifs=[
                            cst.CompIf(cst.Name("d")),
                            cst.CompIf(cst.Name("e")),
                            cst.CompIf(cst.Name("f")),
                        ],
                    ),
                ),
                "code": "(a for b in c if d if e if f)",
                "parser": parse_expression,
                "expected_position": CodeRange.create((1, 1), (1, 28)),
            },
            # nested/inner for-in clause
            {
                "node": cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        inner_for_in=cst.CompFor(
                            target=cst.Name("d"), iter=cst.Name("e")
                        ),
                    ),
                ),
                "code": "(a for b in c for d in e)",
                "parser": parse_expression,
            },
            # nested/inner for-in clause with an 'if' clause
            {
                "node": cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        ifs=[cst.CompIf(cst.Name("d"))],
                        inner_for_in=cst.CompFor(
                            target=cst.Name("e"), iter=cst.Name("f")
                        ),
                    ),
                ),
                "code": "(a for b in c if d for e in f)",
                "parser": parse_expression,
            },
            # custom whitespace
            {
                "node": cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        ifs=[
                            cst.CompIf(
                                cst.Name("d"),
                                whitespace_before=cst.SimpleWhitespace("\t"),
                                whitespace_before_test=cst.SimpleWhitespace("\t\t"),
                            )
                        ],
                        whitespace_before=cst.SimpleWhitespace("  "),
                        whitespace_after_for=cst.SimpleWhitespace("   "),
                        whitespace_before_in=cst.SimpleWhitespace("    "),
                        whitespace_after_in=cst.SimpleWhitespace("     "),
                    ),
                    lpar=[cst.LeftParen(whitespace_after=cst.SimpleWhitespace("\f"))],
                    rpar=[
                        cst.RightParen(whitespace_before=cst.SimpleWhitespace("\f\f"))
                    ],
                ),
                "code": "(\fa  for   b    in     c\tif\t\td\f\f)",
                "parser": parse_expression,
                "expected_position": CodeRange.create((1, 2), (1, 30)),
            },
            # custom whitespace around ListComp's brackets
            {
                "node": cst.ListComp(
                    cst.Name("a"),
                    cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
                    lbracket=cst.LeftSquareBracket(
                        whitespace_after=cst.SimpleWhitespace("\t")
                    ),
                    rbracket=cst.RightSquareBracket(
                        whitespace_before=cst.SimpleWhitespace("\t\t")
                    ),
                    lpar=[cst.LeftParen(whitespace_after=cst.SimpleWhitespace("\f"))],
                    rpar=[
                        cst.RightParen(whitespace_before=cst.SimpleWhitespace("\f\f"))
                    ],
                ),
                "code": "(\f[\ta for b in c\t\t]\f\f)",
                "parser": parse_expression,
                "expected_position": CodeRange.create((1, 2), (1, 19)),
            },
            # custom whitespace around SetComp's braces
            {
                "node": cst.SetComp(
                    cst.Name("a"),
                    cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
                    lbrace=cst.LeftCurlyBrace(
                        whitespace_after=cst.SimpleWhitespace("\t")
                    ),
                    rbrace=cst.RightCurlyBrace(
                        whitespace_before=cst.SimpleWhitespace("\t\t")
                    ),
                    lpar=[cst.LeftParen(whitespace_after=cst.SimpleWhitespace("\f"))],
                    rpar=[
                        cst.RightParen(whitespace_before=cst.SimpleWhitespace("\f\f"))
                    ],
                ),
                "code": "(\f{\ta for b in c\t\t}\f\f)",
                "parser": parse_expression,
            },
            # no whitespace between elements
            {
                "node": cst.GeneratorExp(
                    cst.Name("a", lpar=[cst.LeftParen()], rpar=[cst.RightParen()]),
                    cst.CompFor(
                        target=cst.Name(
                            "b", lpar=[cst.LeftParen()], rpar=[cst.RightParen()]
                        ),
                        iter=cst.Name(
                            "c", lpar=[cst.LeftParen()], rpar=[cst.RightParen()]
                        ),
                        ifs=[
                            cst.CompIf(
                                cst.Name(
                                    "d", lpar=[cst.LeftParen()], rpar=[cst.RightParen()]
                                ),
                                whitespace_before=cst.SimpleWhitespace(""),
                                whitespace_before_test=cst.SimpleWhitespace(""),
                            )
                        ],
                        inner_for_in=cst.CompFor(
                            target=cst.Name(
                                "e", lpar=[cst.LeftParen()], rpar=[cst.RightParen()]
                            ),
                            iter=cst.Name(
                                "f", lpar=[cst.LeftParen()], rpar=[cst.RightParen()]
                            ),
                            whitespace_before=cst.SimpleWhitespace(""),
                            whitespace_after_for=cst.SimpleWhitespace(""),
                            whitespace_before_in=cst.SimpleWhitespace(""),
                            whitespace_after_in=cst.SimpleWhitespace(""),
                        ),
                        whitespace_before=cst.SimpleWhitespace(""),
                        whitespace_after_for=cst.SimpleWhitespace(""),
                        whitespace_before_in=cst.SimpleWhitespace(""),
                        whitespace_after_in=cst.SimpleWhitespace(""),
                    ),
                    lpar=[cst.LeftParen()],
                    rpar=[cst.RightParen()],
                ),
                "code": "((a)for(b)in(c)if(d)for(e)in(f))",
                "parser": parse_expression,
                "expected_position": CodeRange.create((1, 1), (1, 31)),
            },
            # no whitespace before/after GeneratorExp is valid
            {
                "node": cst.Comparison(
                    cst.GeneratorExp(
                        cst.Name("a"),
                        cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
                    ),
                    [
                        cst.ComparisonTarget(
                            cst.Is(
                                whitespace_before=cst.SimpleWhitespace(""),
                                whitespace_after=cst.SimpleWhitespace(""),
                            ),
                            cst.GeneratorExp(
                                cst.Name("d"),
                                cst.CompFor(target=cst.Name("e"), iter=cst.Name("f")),
                            ),
                        )
                    ],
                ),
                "code": "(a for b in c)is(d for e in f)",
                "parser": parse_expression,
            },
            # no whitespace before/after ListComp is valid
            {
                "node": cst.Comparison(
                    cst.ListComp(
                        cst.Name("a"),
                        cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
                    ),
                    [
                        cst.ComparisonTarget(
                            cst.Is(
                                whitespace_before=cst.SimpleWhitespace(""),
                                whitespace_after=cst.SimpleWhitespace(""),
                            ),
                            cst.ListComp(
                                cst.Name("d"),
                                cst.CompFor(target=cst.Name("e"), iter=cst.Name("f")),
                            ),
                        )
                    ],
                ),
                "code": "[a for b in c]is[d for e in f]",
                "parser": parse_expression,
            },
            # no whitespace before/after SetComp is valid
            {
                "node": cst.Comparison(
                    cst.SetComp(
                        cst.Name("a"),
                        cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
                    ),
                    [
                        cst.ComparisonTarget(
                            cst.Is(
                                whitespace_before=cst.SimpleWhitespace(""),
                                whitespace_after=cst.SimpleWhitespace(""),
                            ),
                            cst.SetComp(
                                cst.Name("d"),
                                cst.CompFor(target=cst.Name("e"), iter=cst.Name("f")),
                            ),
                        )
                    ],
                ),
                "code": "{a for b in c}is{d for e in f}",
                "parser": parse_expression,
            },
        ]
    )
    def test_valid(self, **kwargs: Any) -> None:
        self.validate_node(**kwargs)

    @data_provider(
        (
            (
                lambda: cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
                    lpar=[cst.LeftParen(), cst.LeftParen()],
                    rpar=[cst.RightParen()],
                ),
                "unbalanced parens",
            ),
            (
                lambda: cst.ListComp(
                    cst.Name("a"),
                    cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
                    lpar=[cst.LeftParen(), cst.LeftParen()],
                    rpar=[cst.RightParen()],
                ),
                "unbalanced parens",
            ),
            (
                lambda: cst.SetComp(
                    cst.Name("a"),
                    cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
                    lpar=[cst.LeftParen(), cst.LeftParen()],
                    rpar=[cst.RightParen()],
                ),
                "unbalanced parens",
            ),
            (
                lambda: cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        whitespace_before=cst.SimpleWhitespace(""),
                    ),
                ),
                "Must have at least one space before 'for' keyword.",
            ),
            (
                lambda: cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        asynchronous=cst.Asynchronous(),
                        whitespace_before=cst.SimpleWhitespace(""),
                    ),
                ),
                "Must have at least one space before 'async' keyword.",
            ),
            (
                lambda: cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        whitespace_after_for=cst.SimpleWhitespace(""),
                    ),
                ),
                "Must have at least one space after 'for' keyword.",
            ),
            (
                lambda: cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        whitespace_before_in=cst.SimpleWhitespace(""),
                    ),
                ),
                "Must have at least one space before 'in' keyword.",
            ),
            (
                lambda: cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        whitespace_after_in=cst.SimpleWhitespace(""),
                    ),
                ),
                "Must have at least one space after 'in' keyword.",
            ),
            (
                lambda: cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        ifs=[
                            cst.CompIf(
                                cst.Name("d"),
                                whitespace_before=cst.SimpleWhitespace(""),
                            )
                        ],
                    ),
                ),
                "Must have at least one space before 'if' keyword.",
            ),
            (
                lambda: cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        ifs=[
                            cst.CompIf(
                                cst.Name("d"),
                                whitespace_before_test=cst.SimpleWhitespace(""),
                            )
                        ],
                    ),
                ),
                "Must have at least one space after 'if' keyword.",
            ),
            (
                lambda: cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        inner_for_in=cst.CompFor(
                            target=cst.Name("d"),
                            iter=cst.Name("e"),
                            whitespace_before=cst.SimpleWhitespace(""),
                        ),
                    ),
                ),
                "Must have at least one space before 'for' keyword.",
            ),
            (
                lambda: cst.GeneratorExp(
                    cst.Name("a"),
                    cst.CompFor(
                        target=cst.Name("b"),
                        iter=cst.Name("c"),
                        inner_for_in=cst.CompFor(
                            target=cst.Name("d"),
                            iter=cst.Name("e"),
                            asynchronous=cst.Asynchronous(),
                            whitespace_before=cst.SimpleWhitespace(""),
                        ),
                    ),
                ),
                "Must have at least one space before 'async' 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 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,
            },
            # 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,
            },
            # 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:
        self.assert_parses(**kwargs)
Ejemplo n.º 7
0
class DictTest(CSTNodeTest):
    @data_provider([
        # zero-element dict
        {
            "node": cst.Dict([]),
            "code": "{}",
            "parser": parse_expression,
            "expected_position": CodeRange((1, 0), (1, 2)),
        },
        # one-element dict, sentinel comma value
        {
            "node": cst.Dict([cst.DictElement(cst.Name("k"), cst.Name("v"))]),
            "code": "{k: v}",
            "parser": parse_expression,
            "expected_position": CodeRange((1, 0), (1, 6)),
        },
        {
            "node": cst.Dict([cst.StarredDictElement(cst.Name("expanded"))]),
            "code": "{**expanded}",
            "parser": parse_expression,
            "expected_position": CodeRange((1, 0), (1, 12)),
        },
        # two-element dict, sentinel comma value
        {
            "node":
            cst.Dict([
                cst.DictElement(cst.Name("k1"), cst.Name("v1")),
                cst.DictElement(cst.Name("k2"), cst.Name("v2")),
            ]),
            "code":
            "{k1: v1, k2: v2}",
            "parser":
            None,
            "expected_position":
            CodeRange((1, 0), (1, 16)),
        },
        # custom whitespace between brackets
        {
            "node":
            cst.Dict(
                [cst.DictElement(cst.Name("k"), cst.Name("v"))],
                lbrace=cst.LeftCurlyBrace(
                    whitespace_after=cst.SimpleWhitespace("\t")),
                rbrace=cst.RightCurlyBrace(
                    whitespace_before=cst.SimpleWhitespace("\t\t")),
            ),
            "code":
            "{\tk: v\t\t}",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 0), (1, 9)),
        },
        # with parenthesis
        {
            "node":
            cst.Dict(
                [cst.DictElement(cst.Name("k"), cst.Name("v"))],
                lpar=[cst.LeftParen()],
                rpar=[cst.RightParen()],
            ),
            "code":
            "({k: v})",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 1), (1, 7)),
        },
        # starred element
        {
            "node":
            cst.Dict([
                cst.StarredDictElement(cst.Name("one")),
                cst.StarredDictElement(cst.Name("two")),
            ]),
            "code":
            "{**one, **two}",
            "parser":
            None,
            "expected_position":
            CodeRange((1, 0), (1, 14)),
        },
        # custom comma on DictElement
        {
            "node":
            cst.Dict([
                cst.DictElement(cst.Name("k"),
                                cst.Name("v"),
                                comma=cst.Comma())
            ]),
            "code":
            "{k: v,}",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 0), (1, 7)),
        },
        # custom comma on StarredDictElement
        {
            "node":
            cst.Dict([
                cst.StarredDictElement(cst.Name("expanded"), comma=cst.Comma())
            ]),
            "code":
            "{**expanded,}",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 0), (1, 13)),
        },
        # custom whitespace on DictElement
        {
            "node":
            cst.Dict([
                cst.DictElement(
                    cst.Name("k"),
                    cst.Name("v"),
                    whitespace_before_colon=cst.SimpleWhitespace("\t"),
                    whitespace_after_colon=cst.SimpleWhitespace("\t\t"),
                )
            ]),
            "code":
            "{k\t:\t\tv}",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 0), (1, 8)),
        },
        # custom whitespace on StarredDictElement
        {
            "node":
            cst.Dict([
                cst.DictElement(cst.Name("k"),
                                cst.Name("v"),
                                comma=cst.Comma()),
                cst.StarredDictElement(
                    cst.Name("expanded"),
                    whitespace_before_value=cst.SimpleWhitespace("  "),
                ),
            ]),
            "code":
            "{k: v,**  expanded}",
            "parser":
            parse_expression,
            "expected_position":
            CodeRange((1, 0), (1, 19)),
        },
        # missing spaces around dict is always okay
        {
            "node":
            cst.GeneratorExp(
                cst.Name("a"),
                cst.CompFor(
                    cst.Name("b"),
                    cst.Dict([cst.DictElement(cst.Name("k"), cst.Name("v"))]),
                    ifs=[
                        cst.CompIf(
                            cst.Name("c"),
                            whitespace_before=cst.SimpleWhitespace(""),
                        )
                    ],
                    whitespace_after_in=cst.SimpleWhitespace(""),
                ),
            ),
            "parser":
            parse_expression,
            "code":
            "(a for b in{k: v}if c)",
        },
    ])
    def test_valid(self, **kwargs: Any) -> None:
        self.validate_node(**kwargs)

    @data_provider([
        # unbalanced Dict
        {
            "get_node": lambda: cst.Dict([], lpar=[cst.LeftParen()]),
            "expected_re": "left paren without right paren",
        }
    ])
    def test_invalid(self, **kwargs: Any) -> None:
        self.assert_invalid(**kwargs)

    @data_provider((
        {
            "code": "{**{}}",
            "parser": parse_expression_as(python_version="3.5"),
            "expect_success": True,
        },
        {
            "code": "{**{}}",
            "parser": parse_expression_as(python_version="3.3"),
            "expect_success": False,
        },
    ))
    def test_versions(self, **kwargs: Any) -> None:
        self.assert_parses(**kwargs)