Example #1
0
class NamedExprTest(CSTNodeTest):
    @data_provider(
        (
            {
                "node": cst.BinaryOperation(
                    left=cst.Name("a"),
                    operator=cst.MatrixMultiply(),
                    right=cst.Name("b"),
                ),
                "code": "a @ b",
                "parser": parse_expression_as(python_version="3.8"),
            },
            {
                "node": cst.SimpleStatementLine(
                    body=(
                        cst.AugAssign(
                            target=cst.Name("a"),
                            operator=cst.MatrixMultiplyAssign(),
                            value=cst.Name("b"),
                        ),
                    ),
                ),
                "code": "a @= b\n",
                "parser": parse_statement_as(python_version="3.8"),
            },
        )
    )
    def test_valid(self, **kwargs: Any) -> None:
        self.validate_node(**kwargs)

    @data_provider(
        (
            {
                "code": "a @ b",
                "parser": parse_expression_as(python_version="3.6"),
                "expect_success": True,
            },
            {
                "code": "a @ b",
                "parser": parse_expression_as(python_version="3.3"),
                "expect_success": False,
            },
            {
                "code": "a @= b",
                "parser": parse_statement_as(python_version="3.6"),
                "expect_success": True,
            },
            {
                "code": "a @= b",
                "parser": parse_statement_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)
Example #2
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)
Example #3
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)

    @data_provider(
        (
            {
                "code": "(a, *b)",
                "parser": parse_expression_as(python_version="3.5"),
                "expect_success": True,
            },
            {
                "code": "(a, *b)",
                "parser": parse_expression_as(python_version="3.3"),
                "expect_success": False,
            },
        )
    )
    def test_versions(self, **kwargs: Any) -> None:
        self.assert_parses(**kwargs)
Example #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([
        # 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((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((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((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)

    @data_provider((
        {
            "code": "[a, *b]",
            "parser": parse_expression_as(python_version="3.5"),
            "expect_success": True,
        },
        {
            "code": "[a, *b]",
            "parser": parse_expression_as(python_version="3.3"),
            "expect_success": False,
        },
    ))
    def test_versions(self, **kwargs: Any) -> None:
        self.assert_parses(**kwargs)
Example #5
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)
Example #6
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)