class ReturnCreateTest(CSTNodeTest): @data_provider(( { "node": cst.SimpleStatementLine([cst.Return()]), "code": "return\n", "expected_position": CodeRange.create((1, 0), (1, 6)), }, { "node": cst.SimpleStatementLine([cst.Return(cst.Name("abc"))]), "code": "return abc\n", "expected_position": CodeRange.create((1, 0), (1, 10)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(({ "get_node": lambda: cst.Return(cst.Name("abc"), whitespace_after_return=cst.SimpleWhitespace("")), "expected_re": "Must have at least one space after 'return'.", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class ElseTest(CSTNodeTest): @data_provider( ( { "node": cst.Else(cst.SimpleStatementSuite((cst.Pass(),))), "code": "else: pass\n", "expected_position": CodeRange.create((1, 0), (1, 10)), }, { "node": cst.Else( cst.SimpleStatementSuite((cst.Pass(),)), whitespace_before_colon=cst.SimpleWhitespace(" "), ), "code": "else : pass\n", "expected_position": CodeRange.create((1, 0), (1, 12)), }, ) ) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs)
class SmallStatementTest(CSTNodeTest): @data_provider( ( # pyre-fixme[6]: Incompatible parameter type {"node": cst.Pass(), "code": "pass"}, {"node": cst.Pass(semicolon=cst.Semicolon()), "code": "pass;"}, { "node": cst.Pass( semicolon=cst.Semicolon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ) ), "code": "pass ; ", "expected_position": CodeRange.create((1, 0), (1, 4)), }, {"node": cst.Continue(), "code": "continue"}, {"node": cst.Continue(semicolon=cst.Semicolon()), "code": "continue;"}, { "node": cst.Continue( semicolon=cst.Semicolon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ) ), "code": "continue ; ", "expected_position": CodeRange.create((1, 0), (1, 8)), }, {"node": cst.Break(), "code": "break"}, {"node": cst.Break(semicolon=cst.Semicolon()), "code": "break;"}, { "node": cst.Break( semicolon=cst.Semicolon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ) ), "code": "break ; ", "expected_position": CodeRange.create((1, 0), (1, 5)), }, { "node": cst.Expr( cst.BinaryOperation(cst.Name("x"), cst.Add(), cst.Name("y")) ), "code": "x + y", }, { "node": cst.Expr( cst.BinaryOperation(cst.Name("x"), cst.Add(), cst.Name("y")), semicolon=cst.Semicolon(), ), "code": "x + y;", }, { "node": cst.Expr( cst.BinaryOperation(cst.Name("x"), cst.Add(), cst.Name("y")), semicolon=cst.Semicolon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), ), "code": "x + y ; ", "expected_position": CodeRange.create((1, 0), (1, 5)), }, ) ) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs)
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.create((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.create((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.create((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)
class ModuleTest(CSTNodeTest): @data_provider(( # simplest possible program (cst.Module((cst.SimpleStatementLine((cst.Pass(), )), )), "pass\n"), # test default_newline ( cst.Module((cst.SimpleStatementLine((cst.Pass(), )), ), default_newline="\r"), "pass\r", ), # test header/footer ( cst.Module( (cst.SimpleStatementLine((cst.Pass(), )), ), header=(cst.EmptyLine(comment=cst.Comment("# header")), ), footer=(cst.EmptyLine(comment=cst.Comment("# footer")), ), ), "# header\npass\n# footer\n", ), # test has_trailing_newline ( cst.Module( (cst.SimpleStatementLine((cst.Pass(), )), ), has_trailing_newline=False, ), "pass", ), # an empty file (cst.Module((), has_trailing_newline=False), ""), # a file with only comments ( cst.Module( (), header=(cst.EmptyLine( comment=cst.Comment("# nothing to see here")), ), ), "# nothing to see here\n", ), # TODO: test default_indent )) def test_code_and_bytes_properties(self, module: cst.Module, expected: str) -> None: self.assertEqual(module.code, expected) self.assertEqual(module.bytes, expected.encode("utf-8")) @data_provider(( (cst.Module(()), cst.Newline(), "\n"), (cst.Module((), default_newline="\r\n"), cst.Newline(), "\r\n"), # has_trailing_newline has no effect on code_for_node (cst.Module((), has_trailing_newline=False), cst.Newline(), "\n"), # TODO: test default_indent )) def test_code_for_node(self, module: cst.Module, node: cst.CSTNode, expected: str) -> None: self.assertEqual(module.code_for_node(node), expected) @data_provider({ "empty_program": { "code": "", "expected": cst.Module([], has_trailing_newline=False), }, "empty_program_with_newline": { "code": "\n", "expected": cst.Module([], has_trailing_newline=True), }, "empty_program_with_comments": { "code": "# some comment\n", "expected": cst.Module( [], header=[cst.EmptyLine(comment=cst.Comment("# some comment"))]), }, "simple_pass": { "code": "pass\n", "expected": cst.Module([cst.SimpleStatementLine([cst.Pass()])]), }, "simple_pass_with_header_footer": { "code": "# header\npass # trailing\n# footer\n", "expected": cst.Module( [ cst.SimpleStatementLine( [cst.Pass()], trailing_whitespace=cst.TrailingWhitespace( whitespace=cst.SimpleWhitespace(" "), comment=cst.Comment("# trailing"), ), ) ], header=[cst.EmptyLine(comment=cst.Comment("# header"))], footer=[cst.EmptyLine(comment=cst.Comment("# footer"))], ), }, }) def test_parser(self, *, code: str, expected: cst.Module) -> None: self.assertEqual(parse_module(code), expected) @data_provider({ "empty": { "code": "", "expected": CodeRange.create((1, 0), (1, 0)) }, "empty_with_newline": { "code": "\n", "expected": CodeRange.create((1, 0), (2, 0)), }, "empty_program_with_comments": { "code": "# 2345", "expected": CodeRange.create((1, 0), (2, 0)), }, "simple_pass": { "code": "pass\n", "expected": CodeRange.create((1, 0), (2, 0)), }, "simple_pass_with_header_footer": { "code": "# header\npass # trailing\n# footer\n", "expected": CodeRange.create((1, 0), (4, 0)), }, }) def test_module_position(self, *, code: str, expected: CodeRange) -> None: module = parse_module(code) provider = SyntacticPositionProvider() module.code_for_node(module, provider) self.assertEqual(provider._computed[module], expected) # TODO: remove this self.assertEqual(module._metadata[SyntacticPositionProvider], expected) def cmp_position(self, actual: CodeRange, start: Tuple[int, int], end: Tuple[int, int]) -> None: self.assertEqual(actual, CodeRange.create(start, end)) def test_function_position(self) -> None: module = parse_module("def foo():\n pass") provider = SyntacticPositionProvider() module.code_for_node(module, provider) fn = cast(cst.FunctionDef, module.body[0]) stmt = cast(cst.SimpleStatementLine, fn.body.body[0]) pass_stmt = cast(cst.Pass, stmt.body[0]) self.cmp_position(provider._computed[stmt], (2, 4), (2, 8)) self.cmp_position(provider._computed[pass_stmt], (2, 4), (2, 8)) def test_nested_indent_position(self) -> None: module = parse_module( "if True:\n if False:\n x = 1\nelse:\n return") provider = SyntacticPositionProvider() module.code_for_node(module, provider) outer_if = cast(cst.If, module.body[0]) inner_if = cast(cst.If, outer_if.body.body[0]) assign = cast(cst.SimpleStatementLine, inner_if.body.body[0]).body[0] outer_else = cast(cst.Else, outer_if.orelse) return_stmt = cast(cst.SimpleStatementLine, outer_else.body.body[0]).body[0] self.cmp_position(provider._computed[outer_if], (1, 0), (5, 10)) self.cmp_position(provider._computed[inner_if], (2, 4), (3, 13)) self.cmp_position(provider._computed[assign], (3, 8), (3, 13)) self.cmp_position(provider._computed[outer_else], (4, 0), (5, 10)) self.cmp_position(provider._computed[return_stmt], (5, 4), (5, 10)) def test_multiline_string_position(self) -> None: module = parse_module('"abc"\\\n"def"') provider = SyntacticPositionProvider() module.code_for_node(module, provider) stmt = cast(cst.SimpleStatementLine, module.body[0]) expr = cast(cst.Expr, stmt.body[0]) string = expr.value self.cmp_position(provider._computed[stmt], (1, 0), (2, 5)) self.cmp_position(provider._computed[expr], (1, 0), (2, 5)) self.cmp_position(provider._computed[string], (1, 0), (2, 5))
class YieldConstructionTest(CSTNodeTest): @data_provider( ( # Simple yield (cst.Yield(), "yield"), # yield expression (cst.Yield(cst.Name("a")), "yield a"), # yield from expression (cst.Yield(cst.From(cst.Call(cst.Name("a")))), "yield from a()"), # Parenthesizing tests ( cst.Yield( lpar=(cst.LeftParen(),), value=cst.Integer("5"), rpar=(cst.RightParen(),), ), "(yield 5)", ), # Whitespace oddities tests ( cst.Yield( cst.Name("a", lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),)), whitespace_after_yield=cst.SimpleWhitespace(""), ), "yield(a)", CodeRange.create((1, 0), (1, 8)), ), ( cst.Yield( cst.From( cst.Call( cst.Name("a"), lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),), ), whitespace_after_from=cst.SimpleWhitespace(""), ) ), "yield from(a())", ), # Whitespace rendering/parsing tests ( cst.Yield( lpar=(cst.LeftParen(whitespace_after=cst.SimpleWhitespace(" ")),), value=cst.Integer("5"), whitespace_after_yield=cst.SimpleWhitespace(" "), rpar=(cst.RightParen(whitespace_before=cst.SimpleWhitespace(" ")),), ), "( yield 5 )", ), ( cst.Yield( lpar=(cst.LeftParen(whitespace_after=cst.SimpleWhitespace(" ")),), value=cst.From( cst.Call(cst.Name("bla")), whitespace_after_from=cst.SimpleWhitespace(" "), ), whitespace_after_yield=cst.SimpleWhitespace(" "), rpar=(cst.RightParen(whitespace_before=cst.SimpleWhitespace(" ")),), ), "( yield from bla() )", CodeRange.create((1, 2), (1, 20)), ), # From expression position tests ( cst.From( cst.Integer("5"), whitespace_after_from=cst.SimpleWhitespace(" ") ), "from 5", CodeRange.create((1, 0), (1, 6)), ), ) ) def test_valid( self, node: cst.CSTNode, code: str, position: Optional[CodeRange] = None ) -> None: self.validate_node(node, code, expected_position=position) @data_provider( ( # Paren validation ( lambda: cst.Yield(lpar=(cst.LeftParen(),)), "left paren without right paren", ), ( lambda: cst.Yield(rpar=(cst.RightParen(),)), "right paren without left paren", ), # Make sure we have adequate space after yield ( lambda: cst.Yield( cst.Name("a"), whitespace_after_yield=cst.SimpleWhitespace("") ), "Must have at least one space after 'yield' keyword", ), ( lambda: cst.Yield( cst.From(cst.Call(cst.Name("a"))), whitespace_after_yield=cst.SimpleWhitespace(""), ), "Must have at least one space after 'yield' keyword", ), # MAke sure we have adequate space after from ( lambda: cst.Yield( cst.From( cst.Call(cst.Name("a")), whitespace_after_from=cst.SimpleWhitespace(""), ) ), "Must have at least one space after 'from' keyword", ), ) ) def test_invalid( self, get_node: Callable[[], cst.CSTNode], expected_re: str ) -> None: self.assert_invalid(get_node, expected_re)
class ListTest(CSTNodeTest): # A lot of Element/StarredElement tests are provided by the tests for Tuple, so we # we don't need to duplicate them here. @data_provider([ # zero-element list { "node": cst.List([]), "code": "[]", "parser": parse_expression }, # one-element list, sentinel comma value { "node": cst.List([cst.Element(cst.Name("single_element"))]), "code": "[single_element]", "parser": parse_expression, }, # custom whitespace between brackets { "node": cst.List( [cst.Element(cst.Name("single_element"))], lbracket=cst.LeftSquareBracket( whitespace_after=cst.SimpleWhitespace("\t")), rbracket=cst.RightSquareBracket( whitespace_before=cst.SimpleWhitespace(" ")), ), "code": "[\tsingle_element ]", "parser": parse_expression, "expected_position": CodeRange.create((1, 0), (1, 21)), }, # two-element list, sentinel comma value { "node": cst.List( [cst.Element(cst.Name("one")), cst.Element(cst.Name("two"))]), "code": "[one, two]", "parser": None, }, # with parenthesis { "node": cst.List( [cst.Element(cst.Name("one"))], lpar=[cst.LeftParen()], rpar=[cst.RightParen()], ), "code": "([one])", "parser": None, "expected_position": CodeRange.create((1, 1), (1, 6)), }, # starred element { "node": cst.List([ cst.StarredElement(cst.Name("one")), cst.StarredElement(cst.Name("two")), ]), "code": "[*one, *two]", "parser": None, "expected_position": CodeRange.create((1, 0), (1, 12)), }, # missing spaces around list, always okay { "node": cst.For( target=cst.List([ cst.Element(cst.Name("k"), comma=cst.Comma()), cst.Element(cst.Name("v")), ]), iter=cst.Name("abc"), body=cst.SimpleStatementSuite([cst.Pass()]), whitespace_after_for=cst.SimpleWhitespace(""), whitespace_before_in=cst.SimpleWhitespace(""), ), "code": "for[k,v]in abc: pass\n", "parser": parse_statement, }, ]) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider((( lambda: cst.List( [cst.Element(cst.Name("mismatched"))], lpar=[cst.LeftParen(), cst.LeftParen()], rpar=[cst.RightParen()], ), "unbalanced parens", ), )) def test_invalid(self, get_node: Callable[[], cst.CSTNode], expected_re: str) -> None: self.assert_invalid(get_node, expected_re)
class RaiseConstructionTest(CSTNodeTest): @data_provider(( # Simple raise # pyre-fixme[6]: Incompatible parameter type { "node": cst.Raise(), "code": "raise" }, # Raise exception { "node": cst.Raise(cst.Call(cst.Name("Exception"))), "code": "raise Exception()", "expected_position": CodeRange.create((1, 0), (1, 17)), }, # Raise exception from cause { "node": cst.Raise(cst.Call(cst.Name("Exception")), cst.From(cst.Name("cause"))), "code": "raise Exception() from cause", }, # Whitespace oddities test { "node": cst.Raise( cst.Call( cst.Name("Exception"), lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), ), ), cst.From( cst.Name("cause", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), whitespace_before_from=cst.SimpleWhitespace(""), whitespace_after_from=cst.SimpleWhitespace(""), ), whitespace_after_raise=cst.SimpleWhitespace(""), ), "code": "raise(Exception())from(cause)", "expected_position": CodeRange.create((1, 0), (1, 29)), }, { "node": cst.Raise( cst.Call(cst.Name("Exception")), cst.From( cst.Name("cause"), whitespace_before_from=cst.SimpleWhitespace(""), ), ), "code": "raise Exception()from cause", "expected_position": CodeRange.create((1, 0), (1, 27)), }, # Whitespace rendering test { "node": cst.Raise( exc=cst.Call(cst.Name("Exception")), cause=cst.From( cst.Name("cause"), whitespace_before_from=cst.SimpleWhitespace(" "), whitespace_after_from=cst.SimpleWhitespace(" "), ), whitespace_after_raise=cst.SimpleWhitespace(" "), ), "code": "raise Exception() from cause", "expected_position": CodeRange.create((1, 0), (1, 31)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(( # Validate construction { "get_node": lambda: cst.Raise(cause=cst.From(cst.Name("cause"))), "expected_re": "Must have an 'exc' when specifying 'clause'. on Raise", }, # Validate whitespace handling { "get_node": lambda: cst.Raise( cst.Call(cst.Name("Exception")), whitespace_after_raise=cst.SimpleWhitespace(""), ), "expected_re": "Must have at least one space after 'raise'", }, { "get_node": lambda: cst.Raise( cst.Name("exc"), cst.From( cst.Name("cause"), whitespace_before_from=cst.SimpleWhitespace(""), ), ), "expected_re": "Must have at least one space before 'from'", }, { "get_node": lambda: cst.Raise( cst.Name("exc"), cst.From( cst.Name("cause"), whitespace_after_from=cst.SimpleWhitespace(""), ), ), "expected_re": "Must have at least one space after 'from'", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class TryTest(CSTNodeTest): @data_provider(( # Simple try/except block # pyre-fixme[6]: Incompatible parameter type { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(), )), handlers=(cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_except=cst.SimpleWhitespace(""), ), ), ), "code": "try: pass\nexcept: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (2, 12)), }, # Try/except with a class { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(), )), handlers=(cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), type=cst.Name("Exception"), ), ), ), "code": "try: pass\nexcept Exception: pass\n", "parser": parse_statement, }, # Try/except with a named class { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(), )), handlers=(cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), type=cst.Name("Exception"), name=cst.AsName(cst.Name("exc")), ), ), ), "code": "try: pass\nexcept Exception as exc: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (2, 29)), }, # Try/except with multiple clauses { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(), )), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), type=cst.Name("TypeError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), type=cst.Name("KeyError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_except=cst.SimpleWhitespace(""), ), ), ), "code": "try: pass\n" + "except TypeError as e: pass\n" + "except KeyError as e: pass\n" + "except: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (4, 12)), }, # Simple try/finally block { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(), )), finalbody=cst.Finally(cst.SimpleStatementSuite( (cst.Pass(), ))), ), "code": "try: pass\nfinally: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (2, 13)), }, # Simple try/except/finally block { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(), )), handlers=(cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_except=cst.SimpleWhitespace(""), ), ), finalbody=cst.Finally(cst.SimpleStatementSuite( (cst.Pass(), ))), ), "code": "try: pass\nexcept: pass\nfinally: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (3, 13)), }, # Simple try/except/else block { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(), )), handlers=(cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_except=cst.SimpleWhitespace(""), ), ), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(), ))), ), "code": "try: pass\nexcept: pass\nelse: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (3, 10)), }, # Simple try/except/else block/finally { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(), )), handlers=(cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_except=cst.SimpleWhitespace(""), ), ), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(), ))), finalbody=cst.Finally(cst.SimpleStatementSuite( (cst.Pass(), ))), ), "code": "try: pass\nexcept: pass\nelse: pass\nfinally: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (4, 13)), }, # Verify whitespace in various locations { "node": cst.Try( leading_lines=(cst.EmptyLine(comment=cst.Comment("# 1")), ), body=cst.SimpleStatementSuite((cst.Pass(), )), handlers=(cst.ExceptHandler( leading_lines=(cst.EmptyLine( comment=cst.Comment("# 2")), ), type=cst.Name("TypeError"), name=cst.AsName( cst.Name("e"), whitespace_before_as=cst.SimpleWhitespace(" "), whitespace_after_as=cst.SimpleWhitespace(" "), ), whitespace_after_except=cst.SimpleWhitespace(" "), whitespace_before_colon=cst.SimpleWhitespace(" "), body=cst.SimpleStatementSuite((cst.Pass(), )), ), ), orelse=cst.Else( leading_lines=(cst.EmptyLine( comment=cst.Comment("# 3")), ), body=cst.SimpleStatementSuite((cst.Pass(), )), whitespace_before_colon=cst.SimpleWhitespace(" "), ), finalbody=cst.Finally( leading_lines=(cst.EmptyLine( comment=cst.Comment("# 4")), ), body=cst.SimpleStatementSuite((cst.Pass(), )), whitespace_before_colon=cst.SimpleWhitespace(" "), ), whitespace_before_colon=cst.SimpleWhitespace(" "), ), "code": "# 1\ntry : pass\n# 2\nexcept TypeError as e : pass\n# 3\nelse : pass\n# 4\nfinally : pass\n", "parser": parse_statement, "expected_position": CodeRange.create((2, 0), (8, 14)), }, # Please don't write code like this { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(), )), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), type=cst.Name("TypeError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), type=cst.Name("KeyError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_except=cst.SimpleWhitespace(""), ), ), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(), ))), finalbody=cst.Finally(cst.SimpleStatementSuite( (cst.Pass(), ))), ), "code": "try: pass\n" + "except TypeError as e: pass\n" + "except KeyError as e: pass\n" + "except: pass\n" + "else: pass\n" + "finally: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (6, 13)), }, # Verify indentation { "node": DummyIndentedBlock( " ", cst.Try( cst.SimpleStatementSuite((cst.Pass(), )), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), type=cst.Name("TypeError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), type=cst.Name("KeyError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_except=cst.SimpleWhitespace(""), ), ), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(), ))), finalbody=cst.Finally( cst.SimpleStatementSuite((cst.Pass(), ))), ), ), "code": " try: pass\n" + " except TypeError as e: pass\n" + " except KeyError as e: pass\n" + " except: pass\n" + " else: pass\n" + " finally: pass\n", "parser": None, }, # Verify indentation in bodies { "node": DummyIndentedBlock( " ", cst.Try( cst.IndentedBlock((cst.SimpleStatementLine( (cst.Pass(), )), )), handlers=(cst.ExceptHandler( cst.IndentedBlock((cst.SimpleStatementLine( (cst.Pass(), )), )), whitespace_after_except=cst.SimpleWhitespace(""), ), ), orelse=cst.Else( cst.IndentedBlock((cst.SimpleStatementLine( (cst.Pass(), )), ))), finalbody=cst.Finally( cst.IndentedBlock((cst.SimpleStatementLine( (cst.Pass(), )), ))), ), ), "code": " try:\n" + " pass\n" + " except:\n" + " pass\n" + " else:\n" + " pass\n" + " finally:\n" + " pass\n", "parser": None, }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(( # pyre-fixme[6]: Incompatible parameter type { "get_node": lambda: cst.AsName(cst.Name("")), "expected_re": "empty name identifier", }, { "get_node": lambda: cst.AsName(cst.Name("bla"), whitespace_after_as=cst.SimpleWhitespace("")), "expected_re": "between 'as'", }, { "get_node": lambda: cst.AsName(cst.Name("bla"), whitespace_before_as=cst.SimpleWhitespace("")), "expected_re": "before 'as'", }, { "get_node": lambda: cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), name=cst.AsName(cst.Name("bla")), ), "expected_re": "name for an empty type", }, { "get_node": lambda: cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(), )), type=cst.Name("TypeError"), whitespace_after_except=cst.SimpleWhitespace(""), ), "expected_re": "at least one space after except", }, { "get_node": lambda: cst.Try(cst.SimpleStatementSuite((cst.Pass(), ))), "expected_re": "at least one ExceptHandler or Finally", }, { "get_node": lambda: cst.Try( cst.SimpleStatementSuite((cst.Pass(), )), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(), ))), finalbody=cst.Finally(cst.SimpleStatementSuite( (cst.Pass(), ))), ), "expected_re": "at least one ExceptHandler in order to have an Else", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class AwaitTest(CSTNodeTest): @data_provider(( # Some simple calls # pyre-fixme[6]: Incompatible parameter type { "node": cst.Await(cst.Name("test")), "code": "await test", "parser": parse_expression, "expected_position": None, }, { "node": cst.Await(cst.Call(cst.Name("test"))), "code": "await test()", "parser": parse_expression, "expected_position": None, }, # Whitespace { "node": cst.Await( cst.Name("test"), whitespace_after_await=cst.SimpleWhitespace(" "), lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), ), "code": "( await test )", "parser": parse_expression, "expected_position": CodeRange.create((1, 2), (1, 13)), }, )) 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(( # Expression wrapping parenthesis rules { "get_node": (lambda: cst.Await(cst.Name("foo"), lpar=(cst.LeftParen(), ))), "expected_re": "left paren without right paren", }, { "get_node": (lambda: cst.Await(cst.Name("foo"), rpar=(cst.RightParen(), ))), "expected_re": "right paren without left paren", }, { "get_node": (lambda: cst.Await(cst.Name("foo"), whitespace_after_await=cst.SimpleWhitespace("")) ), "expected_re": "at least one space after await", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class AugAssignTest(CSTNodeTest): @data_provider(( # Simple assignment constructor case. # pyre-fixme[6]: Incompatible parameter type { "node": cst.AugAssign(cst.Name("foo"), cst.AddAssign(), cst.Integer("5")), "code": "foo += 5", "parser": None, "expected_position": CodeRange.create((1, 0), (1, 8)), }, { "node": cst.AugAssign(cst.Name("bar"), cst.MultiplyAssign(), cst.Name("foo")), "code": "bar *= foo", "parser": None, "expected_position": None, }, # Whitespace constructor test { "node": cst.AugAssign( target=cst.Name("foo"), operator=cst.LeftShiftAssign( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), value=cst.Integer("5"), ), "code": "foo <<= 5", "parser": None, "expected_position": CodeRange.create((1, 0), (1, 11)), }, # Simple assignment parser case. { "node": cst.SimpleStatementLine((cst.AugAssign(cst.Name("foo"), cst.AddAssign(), cst.Integer("5")), )), "code": "foo += 5\n", "parser": parse_statement, "expected_position": None, }, { "node": cst.SimpleStatementLine((cst.AugAssign(cst.Name("bar"), cst.MultiplyAssign(), cst.Name("foo")), )), "code": "bar *= foo\n", "parser": parse_statement, "expected_position": None, }, # Whitespace parser test { "node": cst.SimpleStatementLine((cst.AugAssign( target=cst.Name("foo"), operator=cst.LeftShiftAssign( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), value=cst.Integer("5"), ), )), "code": "foo <<= 5\n", "parser": parse_statement, "expected_position": None, }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs)
class AssignTest(CSTNodeTest): @data_provider(( # Simple assignment creation case. # pyre-fixme[6]: Incompatible parameter type { "node": cst.Assign((cst.AssignTarget(cst.Name("foo")), ), cst.Integer("5")), "code": "foo = 5", "parser": None, "expected_position": CodeRange.create((1, 0), (1, 7)), }, # Multiple targets creation { "node": cst.Assign( ( cst.AssignTarget(cst.Name("foo")), cst.AssignTarget(cst.Name("bar")), ), cst.Integer("5"), ), "code": "foo = bar = 5", "parser": None, "expected_position": CodeRange.create((1, 0), (1, 13)), }, # Whitespace test for creating nodes { "node": cst.Assign( (cst.AssignTarget( cst.Name("foo"), whitespace_before_equal=cst.SimpleWhitespace(""), whitespace_after_equal=cst.SimpleWhitespace(""), ), ), cst.Integer("5"), ), "code": "foo=5", "parser": None, "expected_position": CodeRange.create((1, 0), (1, 5)), }, # Simple assignment parser case. { "node": cst.SimpleStatementLine((cst.Assign( (cst.AssignTarget(cst.Name("foo")), ), cst.Integer("5")), )), "code": "foo = 5\n", "parser": parse_statement, "expected_position": None, }, # Multiple targets parser { "node": cst.SimpleStatementLine((cst.Assign( ( cst.AssignTarget(cst.Name("foo")), cst.AssignTarget(cst.Name("bar")), ), cst.Integer("5"), ), )), "code": "foo = bar = 5\n", "parser": parse_statement, "expected_position": None, }, # Whitespace test parser { "node": cst.SimpleStatementLine((cst.Assign( (cst.AssignTarget( cst.Name("foo"), whitespace_before_equal=cst.SimpleWhitespace(""), whitespace_after_equal=cst.SimpleWhitespace(""), ), ), cst.Integer("5"), ), )), "code": "foo=5\n", "parser": parse_statement, "expected_position": None, }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(({ "get_node": (lambda: cst.Assign(targets=(), value=cst.Integer("5"))), "expected_re": "at least one AssignTarget", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class AnnAssignTest(CSTNodeTest): @data_provider(( # Simple assignment creation case. # pyre-fixme[6]: Incompatible parameter type { "node": cst.AnnAssign(cst.Name("foo"), cst.Annotation(cst.Name("str")), cst.Integer("5")), "code": "foo: str = 5", "parser": None, "expected_position": CodeRange.create((1, 0), (1, 12)), }, # Annotation creation without assignment { "node": cst.AnnAssign(cst.Name("foo"), cst.Annotation(cst.Name("str"))), "code": "foo: str", "parser": None, "expected_position": CodeRange.create((1, 0), (1, 8)), }, # Complex annotation creation { "node": cst.AnnAssign( cst.Name("foo"), cst.Annotation( cst.Subscript(cst.Name("Optional"), cst.Index(cst.Name("str")))), cst.Integer("5"), ), "code": "foo: Optional[str] = 5", "parser": None, "expected_position": CodeRange.create((1, 0), (1, 22)), }, # Simple assignment parser case. { "node": cst.SimpleStatementLine((cst.AnnAssign( target=cst.Name("foo"), annotation=cst.Annotation( annotation=cst.Name("str"), whitespace_before_indicator=cst.SimpleWhitespace(""), ), equal=cst.AssignEqual(), value=cst.Integer("5"), ), )), "code": "foo: str = 5\n", "parser": parse_statement, "expected_position": None, }, # Annotation without assignment { "node": cst.SimpleStatementLine((cst.AnnAssign( target=cst.Name("foo"), annotation=cst.Annotation( annotation=cst.Name("str"), whitespace_before_indicator=cst.SimpleWhitespace(""), ), value=None, ), )), "code": "foo: str\n", "parser": parse_statement, "expected_position": None, }, # Complex annotation { "node": cst.SimpleStatementLine((cst.AnnAssign( target=cst.Name("foo"), annotation=cst.Annotation( annotation=cst.Subscript(cst.Name("Optional"), cst.Index(cst.Name("str"))), whitespace_before_indicator=cst.SimpleWhitespace(""), ), equal=cst.AssignEqual(), value=cst.Integer("5"), ), )), "code": "foo: Optional[str] = 5\n", "parser": parse_statement, "expected_position": None, }, # Whitespace test { "node": cst.AnnAssign( target=cst.Name("foo"), annotation=cst.Annotation( annotation=cst.Subscript(cst.Name("Optional"), cst.Index(cst.Name("str"))), whitespace_before_indicator=cst.SimpleWhitespace(" "), whitespace_after_indicator=cst.SimpleWhitespace(" "), ), equal=cst.AssignEqual( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), value=cst.Integer("5"), ), "code": "foo : Optional[str] = 5", "parser": None, "expected_position": CodeRange.create((1, 0), (1, 26)), }, { "node": cst.SimpleStatementLine((cst.AnnAssign( target=cst.Name("foo"), annotation=cst.Annotation( annotation=cst.Subscript(cst.Name("Optional"), cst.Index(cst.Name("str"))), whitespace_before_indicator=cst.SimpleWhitespace(" "), whitespace_after_indicator=cst.SimpleWhitespace(" "), ), equal=cst.AssignEqual( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), value=cst.Integer("5"), ), )), "code": "foo : Optional[str] = 5\n", "parser": parse_statement, "expected_position": None, }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(({ "get_node": (lambda: cst.AnnAssign( target=cst.Name("foo"), annotation=cst.Annotation(cst.Name("str")), equal=cst.AssignEqual(), value=None, )), "expected_re": "Must have a value when specifying an AssignEqual.", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class UnaryOperationTest(CSTNodeTest): @data_provider(( # Simple unary operations (cst.UnaryOperation(cst.Plus(), cst.Name("foo")), "+foo"), (cst.UnaryOperation(cst.Minus(), cst.Name("foo")), "-foo"), (cst.UnaryOperation(cst.BitInvert(), cst.Name("foo")), "~foo"), (cst.UnaryOperation(cst.Not(), cst.Name("foo")), "not foo"), # Parenthesized unary operation ( cst.UnaryOperation( lpar=(cst.LeftParen(), ), operator=cst.Not(), expression=cst.Name("foo"), rpar=(cst.RightParen(), ), ), "(not foo)", CodeRange.create((1, 1), (1, 8)), ), ( cst.UnaryOperation( operator=cst.Not(whitespace_after=cst.SimpleWhitespace("")), expression=cst.Name("foo", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), ), "not(foo)", CodeRange.create((1, 0), (1, 8)), ), # Make sure that spacing works ( cst.UnaryOperation( lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), operator=cst.Not(whitespace_after=cst.SimpleWhitespace(" ")), expression=cst.Name("foo"), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), ), "( not foo )", CodeRange.create((1, 2), (1, 10)), ), )) def test_valid(self, node: cst.CSTNode, code: str, position: Optional[CodeRange] = None) -> None: self.validate_node(node, code, parse_expression, expected_position=position) @data_provider(( ( lambda: cst.UnaryOperation( cst.Plus(), cst.Name("foo"), lpar=(cst.LeftParen(), )), "left paren without right paren", ), ( lambda: cst.UnaryOperation( cst.Plus(), cst.Name("foo"), rpar=(cst.RightParen(), )), "right paren without left paren", ), ( lambda: cst.UnaryOperation( operator=cst.Not(whitespace_after=cst.SimpleWhitespace("")), expression=cst.Name("foo"), ), "at least one space after not operator", ), )) def test_invalid(self, get_node: Callable[[], cst.CSTNode], expected_re: str) -> None: self.assert_invalid(get_node, expected_re)
class DelTest(CSTNodeTest): @data_provider(( { "node": cst.SimpleStatementLine([cst.Del(cst.Name("abc"))]), "code": "del abc\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (1, 7)), }, { "node": cst.SimpleStatementLine([ cst.Del( cst.Name("abc"), whitespace_after_del=cst.SimpleWhitespace(" "), ) ]), "code": "del abc\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (1, 9)), }, { "node": cst.SimpleStatementLine([ cst.Del( cst.Name("abc", lpar=[cst.LeftParen()], rpar=[cst.RightParen()]), whitespace_after_del=cst.SimpleWhitespace(""), ) ]), "code": "del(abc)\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (1, 8)), }, { "node": cst.SimpleStatementLine( [cst.Del(cst.Name("abc"), semicolon=cst.Semicolon())]), "code": "del abc;\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (1, 7)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(({ "get_node": lambda: cst.Del(cst.Name("abc"), whitespace_after_del=cst.SimpleWhitespace("")), "expected_re": "Must have at least one space after 'del'.", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class ImportCreateTest(CSTNodeTest): @data_provider(( # Simple import statement # pyre-fixme[6]: Incompatible parameter type { "node": cst.Import(names=(cst.ImportAlias(cst.Name("foo")), )), "code": "import foo", }, { "node": cst.Import(names=(cst.ImportAlias( cst.Attribute(cst.Name("foo"), cst.Name("bar"))), )), "code": "import foo.bar", }, { "node": cst.Import(names=(cst.ImportAlias( cst.Attribute(cst.Name("foo"), cst.Name("bar"))), )), "code": "import foo.bar", }, # Comma-separated list of imports { "node": cst.Import(names=( cst.ImportAlias(cst.Attribute(cst.Name("foo"), cst.Name( "bar"))), cst.ImportAlias(cst.Attribute(cst.Name("foo"), cst.Name( "baz"))), )), "code": "import foo.bar, foo.baz", "expected_position": CodeRange.create((1, 0), (1, 23)), }, # Import with an alias { "node": cst.Import(names=(cst.ImportAlias( cst.Attribute(cst.Name("foo"), cst.Name("bar")), asname=cst.AsName(cst.Name("baz")), ), )), "code": "import foo.bar as baz", }, # Import with an alias, comma separated { "node": cst.Import(names=( cst.ImportAlias( cst.Attribute(cst.Name("foo"), cst.Name("bar")), asname=cst.AsName(cst.Name("baz")), ), cst.ImportAlias( cst.Attribute(cst.Name("foo"), cst.Name("baz")), asname=cst.AsName(cst.Name("bar")), ), )), "code": "import foo.bar as baz, foo.baz as bar", }, # Combine for fun and profit { "node": cst.Import(names=( cst.ImportAlias( cst.Attribute(cst.Name("foo"), cst.Name("bar")), asname=cst.AsName(cst.Name("baz")), ), cst.ImportAlias( cst.Attribute(cst.Name("insta"), cst.Name("gram"))), cst.ImportAlias(cst.Attribute(cst.Name("foo"), cst.Name( "baz"))), cst.ImportAlias(cst.Name("unittest"), asname=cst.AsName(cst.Name("ut"))), )), "code": "import foo.bar as baz, insta.gram, foo.baz, unittest as ut", }, # Verify whitespace works everywhere. { "node": cst.Import( names=( cst.ImportAlias( cst.Attribute( cst.Name("foo"), cst.Name("bar"), dot=cst.Dot( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), ), asname=cst.AsName( cst.Name("baz"), whitespace_before_as=cst.SimpleWhitespace(" "), whitespace_after_as=cst.SimpleWhitespace(" "), ), comma=cst.Comma( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), ), cst.ImportAlias( cst.Name("unittest"), asname=cst.AsName( cst.Name("ut"), whitespace_before_as=cst.SimpleWhitespace(" "), whitespace_after_as=cst.SimpleWhitespace(" "), ), ), ), whitespace_after_import=cst.SimpleWhitespace(" "), ), "code": "import foo . bar as baz , unittest as ut", "expected_position": CodeRange.create((1, 0), (1, 46)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(( { "get_node": lambda: cst.Import(names=()), "expected_re": "at least one ImportAlias", }, { "get_node": lambda: cst.Import(names=(cst.ImportAlias(cst.Name("")), )), "expected_re": "empty name identifier", }, { "get_node": lambda: cst.Import(names=(cst.ImportAlias( cst.Attribute(cst.Name(""), cst.Name("bla"))), )), "expected_re": "empty name identifier", }, { "get_node": lambda: cst.Import(names=(cst.ImportAlias( cst.Attribute(cst.Name("bla"), cst.Name(""))), )), "expected_re": "empty name identifier", }, { "get_node": lambda: cst.Import(names=(cst.ImportAlias( cst.Attribute(cst.Name("foo"), cst.Name("bar")), comma=cst.Comma(), ), )), "expected_re": "trailing comma", }, { "get_node": lambda: cst.Import( names=(cst.ImportAlias( cst.Attribute(cst.Name("foo"), cst.Name("bar"))), ), whitespace_after_import=cst.SimpleWhitespace(""), ), "expected_re": "at least one space", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class ImportFromCreateTest(CSTNodeTest): @data_provider(( # Simple from import statement # pyre-fixme[6]: Incompatible parameter type { "node": cst.ImportFrom(module=cst.Name("foo"), names=(cst.ImportAlias(cst.Name("bar")), )), "code": "from foo import bar", }, # From import statement with alias { "node": cst.ImportFrom( module=cst.Name("foo"), names=(cst.ImportAlias(cst.Name("bar"), asname=cst.AsName(cst.Name("baz"))), ), ), "code": "from foo import bar as baz", }, # Multiple imports { "node": cst.ImportFrom( module=cst.Name("foo"), names=( cst.ImportAlias(cst.Name("bar")), cst.ImportAlias(cst.Name("baz")), ), ), "code": "from foo import bar, baz", }, # Trailing comma { "node": cst.ImportFrom( module=cst.Name("foo"), names=( cst.ImportAlias(cst.Name("bar"), comma=cst.Comma()), cst.ImportAlias(cst.Name("baz"), comma=cst.Comma()), ), ), "code": "from foo import bar,baz,", "expected_position": CodeRange.create((1, 0), (1, 23)), }, # Star import statement { "node": cst.ImportFrom(module=cst.Name("foo"), names=cst.ImportStar()), "code": "from foo import *", "expected_position": CodeRange.create((1, 0), (1, 17)), }, # Simple relative import statement { "node": cst.ImportFrom( relative=(cst.Dot(), ), module=cst.Name("foo"), names=(cst.ImportAlias(cst.Name("bar")), ), ), "code": "from .foo import bar", }, { "node": cst.ImportFrom( relative=(cst.Dot(), cst.Dot()), module=cst.Name("foo"), names=(cst.ImportAlias(cst.Name("bar")), ), ), "code": "from ..foo import bar", }, # Relative only import { "node": cst.ImportFrom( relative=(cst.Dot(), cst.Dot()), module=None, names=(cst.ImportAlias(cst.Name("bar")), ), ), "code": "from .. import bar", }, # Parenthesis { "node": cst.ImportFrom( module=cst.Name("foo"), lpar=cst.LeftParen(), names=(cst.ImportAlias(cst.Name("bar"), asname=cst.AsName(cst.Name("baz"))), ), rpar=cst.RightParen(), ), "code": "from foo import (bar as baz)", "expected_position": CodeRange.create((1, 0), (1, 28)), }, # Verify whitespace works everywhere. { "node": cst.ImportFrom( relative=( cst.Dot( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), cst.Dot( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), ), module=cst.Name("foo"), lpar=cst.LeftParen(whitespace_after=cst.SimpleWhitespace(" ")), names=( cst.ImportAlias( cst.Name("bar"), asname=cst.AsName( cst.Name("baz"), whitespace_before_as=cst.SimpleWhitespace(" "), whitespace_after_as=cst.SimpleWhitespace(" "), ), comma=cst.Comma( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), ), cst.ImportAlias( cst.Name("unittest"), asname=cst.AsName( cst.Name("ut"), whitespace_before_as=cst.SimpleWhitespace(" "), whitespace_after_as=cst.SimpleWhitespace(" "), ), ), ), rpar=cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), whitespace_after_from=cst.SimpleWhitespace(" "), whitespace_before_import=cst.SimpleWhitespace(" "), whitespace_after_import=cst.SimpleWhitespace(" "), ), "code": "from . . foo import ( bar as baz , unittest as ut )", "expected_position": CodeRange.create((1, 0), (1, 61)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(( { "get_node": lambda: cst.ImportFrom(module=None, names=(cst.ImportAlias(cst.Name("bar")), )), "expected_re": "Must have a module specified", }, { "get_node": lambda: cst.ImportFrom(module=cst.Name("foo"), names=()), "expected_re": "at least one ImportAlias", }, { "get_node": lambda: cst.ImportFrom( module=cst.Name("foo"), names=(cst.ImportAlias(cst.Name("bar")), ), lpar=cst.LeftParen(), ), "expected_re": "left paren without right paren", }, { "get_node": lambda: cst.ImportFrom( module=cst.Name("foo"), names=(cst.ImportAlias(cst.Name("bar")), ), rpar=cst.RightParen(), ), "expected_re": "right paren without left paren", }, { "get_node": lambda: cst.ImportFrom(module=cst.Name("foo"), names=cst.ImportStar(), lpar=cst.LeftParen()), "expected_re": "cannot have parens", }, { "get_node": lambda: cst.ImportFrom( module=cst.Name("foo"), names=cst.ImportStar(), rpar=cst.RightParen(), ), "expected_re": "cannot have parens", }, { "get_node": lambda: cst.ImportFrom( module=cst.Name("foo"), names=(cst.ImportAlias(cst.Name("bar")), ), whitespace_after_from=cst.SimpleWhitespace(""), ), "expected_re": "one space after from", }, { "get_node": lambda: cst.ImportFrom( module=cst.Name("foo"), names=(cst.ImportAlias(cst.Name("bar")), ), whitespace_before_import=cst.SimpleWhitespace(""), ), "expected_re": "one space before import", }, { "get_node": lambda: cst.ImportFrom( module=cst.Name("foo"), names=(cst.ImportAlias(cst.Name("bar")), ), whitespace_after_import=cst.SimpleWhitespace(""), ), "expected_re": "one space after import", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class DictTest(CSTNodeTest): @data_provider([ # zero-element dict { "node": cst.Dict([]), "code": "{}", "parser": parse_expression, "expected_position": CodeRange.create((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.create((1, 0), (1, 6)), }, { "node": cst.Dict([cst.StarredDictElement(cst.Name("expanded"))]), "code": "{**expanded}", "parser": parse_expression, "expected_position": CodeRange.create((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.create((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.create((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.create((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.create((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.create((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.create((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.create((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.create((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)
class ComparisonTest(CSTNodeTest): @data_provider(( # Simple comparison statements ( cst.Comparison( cst.Name("foo"), (cst.ComparisonTarget(cst.LessThan(), cst.Integer("5")), ), ), "foo < 5", ), ( cst.Comparison( cst.Name("foo"), (cst.ComparisonTarget(cst.NotEqual(), cst.Integer("5")), ), ), "foo != 5", ), ( cst.Comparison( cst.Name("foo"), (cst.ComparisonTarget(cst.Is(), cst.Name("True")), )), "foo is True", ), ( cst.Comparison( cst.Name("foo"), (cst.ComparisonTarget(cst.IsNot(), cst.Name("False")), ), ), "foo is not False", ), ( cst.Comparison( cst.Name("foo"), (cst.ComparisonTarget(cst.In(), cst.Name("bar")), )), "foo in bar", ), ( cst.Comparison( cst.Name("foo"), (cst.ComparisonTarget(cst.NotIn(), cst.Name("bar")), ), ), "foo not in bar", ), # Comparison with parens ( cst.Comparison( lpar=(cst.LeftParen(), ), left=cst.Name("foo"), comparisons=(cst.ComparisonTarget( operator=cst.NotIn(), comparator=cst.Name("bar")), ), rpar=(cst.RightParen(), ), ), "(foo not in bar)", ), ( cst.Comparison( left=cst.Name("a", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), comparisons=( cst.ComparisonTarget( operator=cst.Is( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), comparator=cst.Name("b", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), ), cst.ComparisonTarget( operator=cst.Is( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), comparator=cst.Name("c", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), ), ), ), "(a)is(b)is(c)", ), # Valid expressions that look like they shouldn't parse ( cst.Comparison( left=cst.Integer("5"), comparisons=(cst.ComparisonTarget( operator=cst.NotIn( whitespace_before=cst.SimpleWhitespace("")), comparator=cst.Name("bar"), ), ), ), "5not in bar", ), # Validate that spacing works properly ( cst.Comparison( lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), left=cst.Name("foo"), comparisons=(cst.ComparisonTarget( operator=cst.NotIn( whitespace_before=cst.SimpleWhitespace(" "), whitespace_between=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), comparator=cst.Name("bar"), ), ), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), ), "( foo not in bar )", ), # Do some complex nodes ( cst.Comparison( left=cst.Name("baz"), comparisons=(cst.ComparisonTarget( operator=cst.Equal(), comparator=cst.Comparison( lpar=(cst.LeftParen(), ), left=cst.Name("foo"), comparisons=(cst.ComparisonTarget( operator=cst.NotIn(), comparator=cst.Name("bar")), ), rpar=(cst.RightParen(), ), ), ), ), ), "baz == (foo not in bar)", CodeRange.create((1, 0), (1, 23)), ), ( cst.Comparison( left=cst.Name("a"), comparisons=( cst.ComparisonTarget(operator=cst.GreaterThan(), comparator=cst.Name("b")), cst.ComparisonTarget(operator=cst.GreaterThan(), comparator=cst.Name("c")), ), ), "a > b > c", CodeRange.create((1, 0), (1, 9)), ), # Is safe to use with word operators if it's leading/trailing children are ( cst.IfExp( body=cst.Comparison( left=cst.Name("a"), comparisons=(cst.ComparisonTarget( operator=cst.GreaterThan(), comparator=cst.Name( "b", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), ), ), ), ), ), test=cst.Comparison( left=cst.Name("c", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), comparisons=(cst.ComparisonTarget( operator=cst.GreaterThan(), comparator=cst.Name("d")), ), ), orelse=cst.Name("e"), whitespace_before_if=cst.SimpleWhitespace(""), whitespace_after_if=cst.SimpleWhitespace(""), ), "a > (b)if(c) > d else e", ), # is safe to use with word operators if entirely surrounded in parenthesis ( cst.IfExp( body=cst.Name("a"), test=cst.Comparison( left=cst.Name("b"), comparisons=(cst.ComparisonTarget( operator=cst.GreaterThan(), comparator=cst.Name("c")), ), lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), ), ), orelse=cst.Name("d"), whitespace_after_if=cst.SimpleWhitespace(""), whitespace_before_else=cst.SimpleWhitespace(""), ), "a if(b > c)else d", ), )) def test_valid(self, node: cst.CSTNode, code: str, position: Optional[CodeRange] = None) -> None: self.validate_node(node, code, parse_expression, expected_position=position) @data_provider(( ( lambda: cst.Comparison( cst.Name("foo"), (cst.ComparisonTarget(cst.LessThan(), cst.Integer("5")), ), lpar=(cst.LeftParen(), ), ), "left paren without right paren", ), ( lambda: cst.Comparison( cst.Name("foo"), (cst.ComparisonTarget(cst.LessThan(), cst.Integer("5")), ), rpar=(cst.RightParen(), ), ), "right paren without left paren", ), ( lambda: cst.Comparison(cst.Name("foo"), ()), "at least one ComparisonTarget", ), ( lambda: cst.Comparison( left=cst.Name("foo"), comparisons=(cst.ComparisonTarget( operator=cst.NotIn(whitespace_before=cst.SimpleWhitespace( "")), comparator=cst.Name("bar"), ), ), ), "at least one space around comparison operator", ), ( lambda: cst.Comparison( left=cst.Name("foo"), comparisons=(cst.ComparisonTarget( operator=cst.NotIn(whitespace_after=cst.SimpleWhitespace( "")), comparator=cst.Name("bar"), ), ), ), "at least one space around comparison operator", ), # multi-target comparisons ( lambda: cst.Comparison( left=cst.Name("a"), comparisons=( cst.ComparisonTarget(operator=cst.Is(), comparator=cst.Name("b")), cst.ComparisonTarget( operator=cst.Is(whitespace_before=cst.SimpleWhitespace( "")), comparator=cst.Name("c"), ), ), ), "at least one space around comparison operator", ), ( lambda: cst.Comparison( left=cst.Name("a"), comparisons=( cst.ComparisonTarget(operator=cst.Is(), comparator=cst.Name("b")), cst.ComparisonTarget( operator=cst.Is(whitespace_after=cst.SimpleWhitespace( "")), comparator=cst.Name("c"), ), ), ), "at least one space around comparison operator", ), # whitespace around the comparision itself # a ifb > c else d ( lambda: cst.IfExp( body=cst.Name("a"), test=cst.Comparison( left=cst.Name("b"), comparisons=(cst. ComparisonTarget(operator=cst.GreaterThan(), comparator=cst.Name("c")), ), ), orelse=cst.Name("d"), whitespace_after_if=cst.SimpleWhitespace(""), ), "Must have at least one space after 'if' keyword.", ), # a if b > celse d ( lambda: cst.IfExp( body=cst.Name("a"), test=cst.Comparison( left=cst.Name("b"), comparisons=(cst. ComparisonTarget(operator=cst.GreaterThan(), comparator=cst.Name("c")), ), ), orelse=cst.Name("d"), whitespace_before_else=cst.SimpleWhitespace(""), ), "Must have at least one space before 'else' keyword.", ), )) def test_invalid(self, get_node: Callable[[], cst.CSTNode], expected_re: str) -> None: self.assert_invalid(get_node, expected_re)
class GlobalConstructionTest(CSTNodeTest): @data_provider( ( # Single global statement # pyre-fixme[6]: Incompatible parameter type {"node": cst.Global((cst.NameItem(cst.Name("a")),)), "code": "global a"}, # Multiple entries in global statement { "node": cst.Global( (cst.NameItem(cst.Name("a")), cst.NameItem(cst.Name("b"))) ), "code": "global a, b", }, # Whitespace rendering test { "node": cst.Global( ( cst.NameItem( cst.Name("a"), comma=cst.Comma( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), ), cst.NameItem(cst.Name("b")), ), whitespace_after_global=cst.SimpleWhitespace(" "), ), "code": "global a , b", "expected_position": CodeRange.create((1, 0), (1, 15)), }, ) ) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider( ( # Validate construction { "get_node": lambda: cst.Global(()), "expected_re": "A Global statement must have at least one NameItem", }, # Validate whitespace handling { "get_node": lambda: cst.Global( (cst.NameItem(cst.Name("a")),), whitespace_after_global=cst.SimpleWhitespace(""), ), "expected_re": "Must have at least one space after 'global' keyword", }, # Validate comma handling { "get_node": lambda: cst.Global( (cst.NameItem(cst.Name("a"), comma=cst.Comma()),) ), "expected_re": "The last NameItem in a Global cannot have a trailing comma", }, # Validate paren handling { "get_node": lambda: cst.Global( ( cst.NameItem( cst.Name( "a", lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),) ) ), ) ), "expected_re": "Cannot have parens around names in NameItem", }, ) ) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
def visit_Pass(self, node: cst.Pass) -> None: range = self.get_metadata(SyntacticPositionProvider, node) test.assertEqual(range, CodeRange.create((1, 0), (1, 4)))
class BinaryOperationTest(CSTNodeTest): @data_provider( ( # Simple binary operations # pyre-fixme[6]: Incompatible parameter type { "node": cst.BinaryOperation( cst.Name("foo"), cst.Add(), cst.Float("5.5") ), "code": "foo + 5.5", "parser": parse_expression, "expected_position": None, }, { "node": cst.BinaryOperation( cst.Name("foo"), cst.Subtract(), cst.Float("5.5") ), "code": "foo - 5.5", "parser": parse_expression, "expected_position": None, }, { "node": cst.BinaryOperation( cst.Name("foo"), cst.LeftShift(), cst.Integer("5") ), "code": "foo << 5", "parser": parse_expression, "expected_position": None, }, { "node": cst.BinaryOperation( cst.Name("foo"), cst.RightShift(), cst.Integer("5") ), "code": "foo >> 5", "parser": parse_expression, "expected_position": None, }, { "node": cst.BinaryOperation( cst.Name("foo"), cst.BitAnd(), cst.Name("bar") ), "code": "foo & bar", "parser": parse_expression, "expected_position": None, }, { "node": cst.BinaryOperation( cst.Name("foo"), cst.BitXor(), cst.Name("bar") ), "code": "foo ^ bar", "parser": parse_expression, "expected_position": None, }, { "node": cst.BinaryOperation( cst.Name("foo"), cst.BitOr(), cst.Name("bar") ), "code": "foo | bar", "parser": parse_expression, "expected_position": None, }, { "node": cst.BinaryOperation( cst.Name("foo"), cst.Multiply(), cst.Float("5.5") ), "code": "foo * 5.5", "parser": parse_expression, "expected_position": None, }, { "node": cst.BinaryOperation( cst.Name("foo"), cst.MatrixMultiply(), cst.Float("5.5") ), "code": "foo @ 5.5", "parser": parse_expression, "expected_position": None, }, { "node": cst.BinaryOperation( cst.Name("foo"), cst.Divide(), cst.Float("5.5") ), "code": "foo / 5.5", "parser": parse_expression, "expected_position": None, }, { "node": cst.BinaryOperation( cst.Name("foo"), cst.Modulo(), cst.Float("5.5") ), "code": "foo % 5.5", "parser": parse_expression, "expected_position": None, }, { "node": cst.BinaryOperation( cst.Name("foo"), cst.FloorDivide(), cst.Float("5.5") ), "code": "foo // 5.5", "parser": parse_expression, "expected_position": None, }, # Parenthesized binary operation { "node": cst.BinaryOperation( lpar=(cst.LeftParen(),), left=cst.Name("foo"), operator=cst.LeftShift(), right=cst.Integer("5"), rpar=(cst.RightParen(),), ), "code": "(foo << 5)", "parser": parse_expression, "expected_position": None, }, # Make sure that spacing works { "node": cst.BinaryOperation( lpar=(cst.LeftParen(whitespace_after=cst.SimpleWhitespace(" ")),), left=cst.Name("foo"), operator=cst.Multiply( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), right=cst.Name("bar"), rpar=(cst.RightParen(whitespace_before=cst.SimpleWhitespace(" ")),), ), "code": "( foo * bar )", "parser": parse_expression, "expected_position": CodeRange.create((1, 2), (1, 13)), }, ) ) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider( ( { "get_node": ( lambda: cst.BinaryOperation( cst.Name("foo"), cst.Add(), cst.Name("bar"), lpar=(cst.LeftParen(),), ) ), "expected_re": "left paren without right paren", }, { "get_node": ( lambda: cst.BinaryOperation( cst.Name("foo"), cst.Add(), cst.Name("bar"), rpar=(cst.RightParen(),), ) ), "expected_re": "right paren without left paren", }, ) ) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class SimpleStatementTest(CSTNodeTest): @data_provider(( # a single-element SimpleStatementLine # pyre-fixme[6]: Incompatible parameter type { "node": cst.SimpleStatementLine((cst.Pass(), )), "code": "pass\n", "parser": parse_statement, }, # a multi-element SimpleStatementLine { "node": cst.SimpleStatementLine( (cst.Pass(semicolon=cst.Semicolon()), cst.Continue())), "code": "pass;continue\n", "parser": parse_statement, }, # a multi-element SimpleStatementLine with whitespace { "node": cst.SimpleStatementLine(( cst.Pass(semicolon=cst.Semicolon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), )), cst.Continue(), )), "code": "pass ; continue\n", "parser": parse_statement, }, # A more complicated SimpleStatementLine { "node": cst.SimpleStatementLine(( cst.Pass(semicolon=cst.Semicolon()), cst.Continue(semicolon=cst.Semicolon()), cst.Break(), )), "code": "pass;continue;break\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (1, 19)), }, # a multi-element SimpleStatementLine, inferred semicolons { "node": cst.SimpleStatementLine((cst.Pass(), cst.Continue(), cst.Break())), "code": "pass; continue; break\n", "parser": None, # No test for parsing, since we are using sentinels. }, # some expression statements { "node": cst.SimpleStatementLine((cst.Expr(cst.Name("None")), )), "code": "None\n", "parser": parse_statement, }, { "node": cst.SimpleStatementLine((cst.Expr(cst.Name("True")), )), "code": "True\n", "parser": parse_statement, }, { "node": cst.SimpleStatementLine((cst.Expr(cst.Name("False")), )), "code": "False\n", "parser": parse_statement, }, { "node": cst.SimpleStatementLine((cst.Expr(cst.Ellipsis()), )), "code": "...\n", "parser": parse_statement, }, # Test some numbers { "node": cst.SimpleStatementLine((cst.Expr(cst.Integer("5")), )), "code": "5\n", "parser": parse_statement, }, { "node": cst.SimpleStatementLine((cst.Expr(cst.Float("5.5")), )), "code": "5.5\n", "parser": parse_statement, }, { "node": cst.SimpleStatementLine((cst.Expr(cst.Imaginary("5j")), )), "code": "5j\n", "parser": parse_statement, }, # Test some numbers with parens { "node": cst.SimpleStatementLine((cst.Expr( cst.Integer("5", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), ))), )), "code": "(5)\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (1, 3)), }, { "node": cst.SimpleStatementLine((cst.Expr( cst.Float("5.5", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), ))), )), "code": "(5.5)\n", "parser": parse_statement, }, { "node": cst.SimpleStatementLine((cst.Expr( cst.Imaginary("5j", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), ))), )), "code": "(5j)\n", "parser": parse_statement, }, # Test some strings { "node": cst.SimpleStatementLine((cst.Expr(cst.SimpleString('"abc"')), )), "code": '"abc"\n', "parser": parse_statement, }, { "node": cst.SimpleStatementLine((cst.Expr( cst.ConcatenatedString(cst.SimpleString('"abc"'), cst.SimpleString('"def"'))), )), "code": '"abc""def"\n', "parser": parse_statement, }, { "node": cst.SimpleStatementLine((cst.Expr( cst.ConcatenatedString( left=cst.SimpleString('"abc"'), whitespace_between=cst.SimpleWhitespace(" "), right=cst.ConcatenatedString( left=cst.SimpleString('"def"'), whitespace_between=cst.SimpleWhitespace(" "), right=cst.SimpleString('"ghi"'), ), )), )), "code": '"abc" "def" "ghi"\n', "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (1, 17)), }, # Test parenthesis rules { "node": cst.SimpleStatementLine((cst.Expr( cst.Ellipsis(lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), ))), )), "code": "(...)\n", "parser": parse_statement, }, # Test parenthesis with whitespace ownership { "node": cst.SimpleStatementLine((cst.Expr( cst.Ellipsis( lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), )), )), "code": "( ... )\n", "parser": parse_statement, }, { "node": cst.SimpleStatementLine((cst.Expr( cst.Ellipsis( lpar=( cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), rpar=( cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), )), )), "code": "( ( ( ... ) ) )\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (1, 21)), }, # Test parenthesis rules with expressions { "node": cst.SimpleStatementLine((cst.Expr( cst.Ellipsis( lpar=(cst.LeftParen( whitespace_after=cst.ParenthesizedWhitespace( first_line=cst.TrailingWhitespace(), empty_lines=(cst.EmptyLine( comment=cst.Comment("# Wow, a comment!")), ), indent=True, last_line=cst.SimpleWhitespace(" "), )), ), rpar=(cst.RightParen( whitespace_before=cst.ParenthesizedWhitespace( first_line=cst.TrailingWhitespace(), empty_lines=(), indent=True, last_line=cst.SimpleWhitespace(""), )), ), )), )), "code": "(\n# Wow, a comment!\n ...\n)\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (4, 1)), }, # test trailing whitespace { "node": cst.SimpleStatementLine( (cst.Pass(), ), trailing_whitespace=cst.TrailingWhitespace( whitespace=cst.SimpleWhitespace(" "), comment=cst.Comment("# trailing comment"), ), ), "code": "pass # trailing comment\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (1, 4)), }, # test leading comment { "node": cst.SimpleStatementLine( (cst.Pass(), ), leading_lines=(cst.EmptyLine( comment=cst.Comment("# comment")), ), ), "code": "# comment\npass\n", "parser": parse_statement, "expected_position": CodeRange.create((2, 0), (2, 4)), }, # test indentation { "node": DummyIndentedBlock( " ", cst.SimpleStatementLine( (cst.Pass(), ), leading_lines=(cst.EmptyLine( comment=cst.Comment("# comment")), ), ), ), "code": " # comment\n pass\n", "expected_position": CodeRange.create((2, 4), (2, 8)), }, # test suite variant { "node": cst.SimpleStatementSuite((cst.Pass(), )), "code": " pass\n", "expected_position": CodeRange.create((1, 1), (1, 5)), }, { "node": cst.SimpleStatementSuite( (cst.Pass(), ), leading_whitespace=cst.SimpleWhitespace("")), "code": "pass\n", "expected_position": CodeRange.create((1, 0), (1, 4)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs)
class LambdaCreationTest(CSTNodeTest): @data_provider(( # Simple lambda (cst.Lambda(cst.Parameters(), cst.Integer("5")), "lambda: 5"), # Test basic positional params ( cst.Lambda( cst.Parameters(params=(cst.Param(cst.Name("bar")), cst.Param(cst.Name("baz")))), cst.Integer("5"), ), "lambda bar, baz: 5", ), # Test basic positional default params ( cst.Lambda( cst.Parameters(default_params=( cst.Param(cst.Name("bar"), default=cst.SimpleString('"one"')), cst.Param(cst.Name("baz"), default=cst.Integer("5")), )), cst.Integer("5"), ), 'lambda bar = "one", baz = 5: 5', ), # Mixed positional and default params. ( cst.Lambda( cst.Parameters( params=(cst.Param(cst.Name("bar")), ), default_params=(cst.Param(cst.Name("baz"), default=cst.Integer("5")), ), ), cst.Integer("5"), ), "lambda bar, baz = 5: 5", ), # Test kwonly params ( cst.Lambda( cst.Parameters(kwonly_params=( cst.Param(cst.Name("bar"), default=cst.SimpleString('"one"')), cst.Param(cst.Name("baz")), )), cst.Integer("5"), ), 'lambda *, bar = "one", baz: 5', ), # Mixed params and kwonly_params ( cst.Lambda( cst.Parameters( params=( cst.Param(cst.Name("first")), cst.Param(cst.Name("second")), ), kwonly_params=( cst.Param(cst.Name("bar"), default=cst.SimpleString('"one"')), cst.Param(cst.Name("baz")), cst.Param(cst.Name("biz"), default=cst.SimpleString('"two"')), ), ), cst.Integer("5"), ), 'lambda first, second, *, bar = "one", baz, biz = "two": 5', ), # Mixed default_params and kwonly_params ( cst.Lambda( cst.Parameters( default_params=( cst.Param(cst.Name("first"), default=cst.Float("1.0")), cst.Param(cst.Name("second"), default=cst.Float("1.5")), ), kwonly_params=( cst.Param(cst.Name("bar"), default=cst.SimpleString('"one"')), cst.Param(cst.Name("baz")), cst.Param(cst.Name("biz"), default=cst.SimpleString('"two"')), ), ), cst.Integer("5"), ), 'lambda first = 1.0, second = 1.5, *, bar = "one", baz, biz = "two": 5', ), # Mixed params, default_params, and kwonly_params ( cst.Lambda( cst.Parameters( params=( cst.Param(cst.Name("first")), cst.Param(cst.Name("second")), ), default_params=( cst.Param(cst.Name("third"), default=cst.Float("1.0")), cst.Param(cst.Name("fourth"), default=cst.Float("1.5")), ), kwonly_params=( cst.Param(cst.Name("bar"), default=cst.SimpleString('"one"')), cst.Param(cst.Name("baz")), cst.Param(cst.Name("biz"), default=cst.SimpleString('"two"')), ), ), cst.Integer("5"), ), 'lambda first, second, third = 1.0, fourth = 1.5, *, bar = "one", baz, biz = "two": 5', CodeRange.create((1, 0), (1, 84)), ), # Test star_arg ( cst.Lambda( cst.Parameters(star_arg=cst.Param(cst.Name("params"))), cst.Integer("5"), ), "lambda *params: 5", ), # Typed star_arg, include kwonly_params ( cst.Lambda( cst.Parameters( star_arg=cst.Param(cst.Name("params")), kwonly_params=( cst.Param(cst.Name("bar"), default=cst.SimpleString('"one"')), cst.Param(cst.Name("baz")), cst.Param(cst.Name("biz"), default=cst.SimpleString('"two"')), ), ), cst.Integer("5"), ), 'lambda *params, bar = "one", baz, biz = "two": 5', ), # Mixed params default_params, star_arg and kwonly_params ( cst.Lambda( cst.Parameters( params=( cst.Param(cst.Name("first")), cst.Param(cst.Name("second")), ), default_params=( cst.Param(cst.Name("third"), default=cst.Float("1.0")), cst.Param(cst.Name("fourth"), default=cst.Float("1.5")), ), star_arg=cst.Param(cst.Name("params")), kwonly_params=( cst.Param(cst.Name("bar"), default=cst.SimpleString('"one"')), cst.Param(cst.Name("baz")), cst.Param(cst.Name("biz"), default=cst.SimpleString('"two"')), ), ), cst.Integer("5"), ), 'lambda first, second, third = 1.0, fourth = 1.5, *params, bar = "one", baz, biz = "two": 5', ), # Test star_arg and star_kwarg ( cst.Lambda( cst.Parameters(star_kwarg=cst.Param(cst.Name("kwparams"))), cst.Integer("5"), ), "lambda **kwparams: 5", ), # Test star_arg and kwarg ( cst.Lambda( cst.Parameters( star_arg=cst.Param(cst.Name("params")), star_kwarg=cst.Param(cst.Name("kwparams")), ), cst.Integer("5"), ), "lambda *params, **kwparams: 5", ), # Inner whitespace ( cst.Lambda( lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), whitespace_after_lambda=cst.SimpleWhitespace(" "), params=cst.Parameters(), colon=cst.Colon(whitespace_after=cst.SimpleWhitespace(" ")), body=cst.Integer("5"), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), ), "( lambda : 5 )", CodeRange.create((1, 2), (1, 13)), ), )) def test_valid(self, node: cst.CSTNode, code: str, position: Optional[CodeRange] = None) -> None: self.validate_node(node, code, expected_position=position) @data_provider(( ( lambda: cst.Lambda( cst.Parameters(params=(cst.Param(cst.Name("arg")), )), cst.Integer("5"), lpar=(cst.LeftParen(), ), ), "left paren without right paren", ), ( lambda: cst.Lambda( cst.Parameters(params=(cst.Param(cst.Name("arg")), )), cst.Integer("5"), rpar=(cst.RightParen(), ), ), "right paren without left paren", ), ( lambda: cst.Lambda( cst.Parameters(params=(cst.Param(cst.Name("arg")), )), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(""), ), "at least one space after lambda", ), ( lambda: cst.Lambda( cst.Parameters(default_params=(cst.Param( cst.Name("arg"), default=cst.Integer("5")), )), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(""), ), "at least one space after lambda", ), ( lambda: cst.Lambda( cst.Parameters(star_arg=cst.Param(cst.Name("arg"))), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(""), ), "at least one space after lambda", ), ( lambda: cst.Lambda( cst.Parameters(kwonly_params=(cst.Param(cst.Name("arg")), )), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(""), ), "at least one space after lambda", ), ( lambda: cst.Lambda( cst.Parameters(star_kwarg=cst.Param(cst.Name("arg"))), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(""), ), "at least one space after lambda", ), ( lambda: cst.Lambda( cst.Parameters(star_kwarg=cst.Param(cst.Name("bar"), equal=cst.AssignEqual())), cst.Integer("5"), ), "Must have a default when specifying an AssignEqual.", ), ( lambda: cst.Lambda( cst.Parameters(star_kwarg=cst.Param(cst.Name("bar"), star="***")), cst.Integer("5"), ), r"Must specify either '', '\*' or '\*\*' for star.", ), ( lambda: cst.Lambda( cst.Parameters(params=(cst.Param( cst.Name("bar"), default=cst.SimpleString('"one"')), )), cst.Integer("5"), ), "Cannot have defaults for params", ), ( lambda: cst.Lambda( cst.Parameters(default_params=(cst.Param(cst.Name("bar")), )), cst.Integer("5"), ), "Must have defaults for default_params", ), ( lambda: cst.Lambda(cst.Parameters(star_arg=cst.ParamStar()), cst.Integer("5")), "Must have at least one kwonly param if ParamStar is used.", ), ( lambda: cst.Lambda( cst.Parameters(params=(cst.Param(cst.Name("bar"), star="*"), ) ), cst.Integer("5"), ), "Expecting a star prefix of ''", ), ( lambda: cst.Lambda( cst.Parameters(default_params=(cst.Param( cst.Name("bar"), default=cst.SimpleString('"one"'), star="*", ), )), cst.Integer("5"), ), "Expecting a star prefix of ''", ), ( lambda: cst.Lambda( cst.Parameters(kwonly_params=(cst.Param(cst.Name("bar"), star="*"), )), cst.Integer("5"), ), "Expecting a star prefix of ''", ), ( lambda: cst.Lambda( cst.Parameters(star_arg=cst.Param(cst.Name("bar"), star="**")), cst.Integer("5"), ), r"Expecting a star prefix of '\*'", ), ( lambda: cst.Lambda( cst.Parameters(star_kwarg=cst.Param(cst.Name("bar"), star="*") ), cst.Integer("5"), ), r"Expecting a star prefix of '\*\*'", ), ( lambda: cst.Lambda( cst.Parameters(params=(cst.Param( cst.Name("arg"), annotation=cst.Annotation(cst.Name("str")), ), )), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(""), ), "Lambda params cannot have type annotations", ), ( lambda: cst.Lambda( cst.Parameters(default_params=(cst.Param( cst.Name("arg"), default=cst.Integer("5"), annotation=cst.Annotation(cst.Name("str")), ), )), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(""), ), "Lambda params cannot have type annotations", ), ( lambda: cst.Lambda( cst.Parameters(star_arg=cst.Param(cst.Name("arg"), annotation=cst.Annotation( cst.Name("str")))), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(""), ), "Lambda params cannot have type annotations", ), ( lambda: cst.Lambda( cst.Parameters(kwonly_params=(cst.Param( cst.Name("arg"), annotation=cst.Annotation(cst.Name("str")), ), )), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(""), ), "Lambda params cannot have type annotations", ), ( lambda: cst.Lambda( cst.Parameters(star_kwarg=cst.Param(cst.Name("arg"), annotation=cst.Annotation( cst.Name("str")))), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(""), ), "Lambda params cannot have type annotations", ), )) def test_invalid(self, get_node: Callable[[], cst.CSTNode], expected_re: str) -> None: self.assert_invalid(get_node, expected_re)
def cmp_position(self, actual: CodeRange, start: Tuple[int, int], end: Tuple[int, int]) -> None: self.assertEqual(actual, CodeRange.create(start, end))
class NumberTest(CSTNodeTest): @data_provider(( # Simple number (cst.Integer("5"), "5", parse_expression), # Negted number ( cst.UnaryOperation(operator=cst.Minus(), expression=cst.Integer("5")), "-5", parse_expression, CodeRange.create((1, 0), (1, 2)), ), # In parenthesis ( cst.UnaryOperation( lpar=(cst.LeftParen(), ), operator=cst.Minus(), expression=cst.Integer("5"), rpar=(cst.RightParen(), ), ), "(-5)", parse_expression, CodeRange.create((1, 1), (1, 3)), ), ( cst.UnaryOperation( lpar=(cst.LeftParen(), ), operator=cst.Minus(), expression=cst.Integer("5", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), rpar=(cst.RightParen(), ), ), "(-(5))", parse_expression, CodeRange.create((1, 1), (1, 5)), ), ( cst.UnaryOperation( operator=cst.Minus(), expression=cst.UnaryOperation(operator=cst.Minus(), expression=cst.Integer("5")), ), "--5", parse_expression, CodeRange.create((1, 0), (1, 3)), ), # multiple nested parenthesis ( cst.Integer( "5", lpar=(cst.LeftParen(), cst.LeftParen()), rpar=(cst.RightParen(), cst.RightParen()), ), "((5))", parse_expression, CodeRange.create((1, 2), (1, 3)), ), ( cst.UnaryOperation( lpar=(cst.LeftParen(), ), operator=cst.Plus(), expression=cst.Integer( "5", lpar=(cst.LeftParen(), cst.LeftParen()), rpar=(cst.RightParen(), cst.RightParen()), ), rpar=(cst.RightParen(), ), ), "(+((5)))", parse_expression, CodeRange.create((1, 1), (1, 7)), ), )) def test_valid( self, node: cst.CSTNode, code: str, parser: Optional[Callable[[str], cst.CSTNode]], position: Optional[CodeRange] = None, ) -> None: self.validate_node(node, code, parser, expected_position=position) @data_provider(( ( lambda: cst.Integer("5", lpar=(cst.LeftParen(), )), "left paren without right paren", ), ( lambda: cst.Integer("5", rpar=(cst.RightParen(), )), "right paren without left paren", ), ( lambda: cst.Float("5.5", lpar=(cst.LeftParen(), )), "left paren without right paren", ), ( lambda: cst.Float("5.5", rpar=(cst.RightParen(), )), "right paren without left paren", ), ( lambda: cst.Imaginary("5i", lpar=(cst.LeftParen(), )), "left paren without right paren", ), ( lambda: cst.Imaginary("5i", rpar=(cst.RightParen(), )), "right paren without left paren", ), )) def test_invalid(self, get_node: Callable[[], cst.CSTNode], expected_re: str) -> None: self.assert_invalid(get_node, expected_re)
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)
class CallTest(CSTNodeTest): @data_provider(( # Simple call # pyre-fixme[6]: Incompatible parameter type { "node": cst.Call(cst.Name("foo")), "code": "foo()", "parser": parse_expression, "expected_position": None, }, { "node": cst.Call(cst.Name("foo"), whitespace_before_args=cst.SimpleWhitespace(" ")), "code": "foo( )", "parser": parse_expression, "expected_position": None, }, # Call with attribute dereference { "node": cst.Call(cst.Attribute(cst.Name("foo"), cst.Name("bar"))), "code": "foo.bar()", "parser": parse_expression, "expected_position": None, }, # Positional arguments render test { "node": cst.Call(cst.Name("foo"), (cst.Arg(cst.Integer("1")), )), "code": "foo(1)", "parser": None, "expected_position": None, }, { "node": cst.Call( cst.Name("foo"), ( cst.Arg(cst.Integer("1")), cst.Arg(cst.Integer("2")), cst.Arg(cst.Integer("3")), ), ), "code": "foo(1, 2, 3)", "parser": None, "expected_position": None, }, # Positional arguments parse test { "node": cst.Call(cst.Name("foo"), (cst.Arg(value=cst.Integer("1")), )), "code": "foo(1)", "parser": parse_expression, "expected_position": None, }, { "node": cst.Call( cst.Name("foo"), (cst.Arg( value=cst.Integer("1"), whitespace_after_arg=cst.SimpleWhitespace(" "), ), ), whitespace_after_func=cst.SimpleWhitespace(" "), whitespace_before_args=cst.SimpleWhitespace(" "), ), "code": "foo ( 1 )", "parser": parse_expression, "expected_position": None, }, { "node": cst.Call( cst.Name("foo"), (cst.Arg( value=cst.Integer("1"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), ), whitespace_after_func=cst.SimpleWhitespace(" "), whitespace_before_args=cst.SimpleWhitespace(" "), ), "code": "foo ( 1, )", "parser": parse_expression, "expected_position": None, }, { "node": cst.Call( cst.Name("foo"), ( cst.Arg( value=cst.Integer("1"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( value=cst.Integer("2"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg(value=cst.Integer("3")), ), ), "code": "foo(1, 2, 3)", "parser": parse_expression, "expected_position": None, }, # Keyword arguments render test { "node": cst.Call( cst.Name("foo"), (cst.Arg(keyword=cst.Name("one"), value=cst.Integer("1")), ), ), "code": "foo(one = 1)", "parser": None, "expected_position": None, }, { "node": cst.Call( cst.Name("foo"), ( cst.Arg(keyword=cst.Name("one"), value=cst.Integer("1")), cst.Arg(keyword=cst.Name("two"), value=cst.Integer("2")), cst.Arg(keyword=cst.Name("three"), value=cst.Integer("3")), ), ), "code": "foo(one = 1, two = 2, three = 3)", "parser": None, "expected_position": None, }, # Keyword arguments parser test { "node": cst.Call( cst.Name("foo"), (cst.Arg( keyword=cst.Name("one"), equal=cst.AssignEqual(), value=cst.Integer("1"), ), ), ), "code": "foo(one = 1)", "parser": parse_expression, "expected_position": None, }, { "node": cst.Call( cst.Name("foo"), ( cst.Arg( keyword=cst.Name("one"), equal=cst.AssignEqual(), value=cst.Integer("1"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( keyword=cst.Name("two"), equal=cst.AssignEqual(), value=cst.Integer("2"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( keyword=cst.Name("three"), equal=cst.AssignEqual(), value=cst.Integer("3"), ), ), ), "code": "foo(one = 1, two = 2, three = 3)", "parser": parse_expression, "expected_position": None, }, # Iterator expansion render test { "node": cst.Call(cst.Name("foo"), (cst.Arg(star="*", value=cst.Name("one")), )), "code": "foo(*one)", "parser": None, "expected_position": None, }, { "node": cst.Call( cst.Name("foo"), ( cst.Arg(star="*", value=cst.Name("one")), cst.Arg(star="*", value=cst.Name("two")), cst.Arg(star="*", value=cst.Name("three")), ), ), "code": "foo(*one, *two, *three)", "parser": None, "expected_position": None, }, # Iterator expansion parser test { "node": cst.Call(cst.Name("foo"), (cst.Arg(star="*", value=cst.Name("one")), )), "code": "foo(*one)", "parser": parse_expression, "expected_position": None, }, { "node": cst.Call( cst.Name("foo"), ( cst.Arg( star="*", value=cst.Name("one"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( star="*", value=cst.Name("two"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg(star="*", value=cst.Name("three")), ), ), "code": "foo(*one, *two, *three)", "parser": parse_expression, "expected_position": None, }, # Dictionary expansion render test { "node": cst.Call(cst.Name("foo"), (cst.Arg(star="**", value=cst.Name("one")), )), "code": "foo(**one)", "parser": None, "expected_position": None, }, { "node": cst.Call( cst.Name("foo"), ( cst.Arg(star="**", value=cst.Name("one")), cst.Arg(star="**", value=cst.Name("two")), cst.Arg(star="**", value=cst.Name("three")), ), ), "code": "foo(**one, **two, **three)", "parser": None, "expected_position": None, }, # Dictionary expansion parser test { "node": cst.Call(cst.Name("foo"), (cst.Arg(star="**", value=cst.Name("one")), )), "code": "foo(**one)", "parser": parse_expression, "expected_position": None, }, { "node": cst.Call( cst.Name("foo"), ( cst.Arg( star="**", value=cst.Name("one"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( star="**", value=cst.Name("two"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg(star="**", value=cst.Name("three")), ), ), "code": "foo(**one, **two, **three)", "parser": parse_expression, "expected_position": None, }, # Complicated mingling rules render test { "node": cst.Call( cst.Name("foo"), ( cst.Arg(value=cst.Name("pos1")), cst.Arg(star="*", value=cst.Name("list1")), cst.Arg(value=cst.Name("pos2")), cst.Arg(value=cst.Name("pos3")), cst.Arg(star="*", value=cst.Name("list2")), cst.Arg(value=cst.Name("pos4")), cst.Arg(star="*", value=cst.Name("list3")), cst.Arg(keyword=cst.Name("kw1"), value=cst.Integer("1")), cst.Arg(star="*", value=cst.Name("list4")), cst.Arg(keyword=cst.Name("kw2"), value=cst.Integer("2")), cst.Arg(star="*", value=cst.Name("list5")), cst.Arg(keyword=cst.Name("kw3"), value=cst.Integer("3")), cst.Arg(star="**", value=cst.Name("dict1")), cst.Arg(keyword=cst.Name("kw4"), value=cst.Integer("4")), cst.Arg(star="**", value=cst.Name("dict2")), ), ), "code": "foo(pos1, *list1, pos2, pos3, *list2, pos4, *list3, kw1 = 1, *list4, kw2 = 2, *list5, kw3 = 3, **dict1, kw4 = 4, **dict2)", "parser": None, "expected_position": None, }, # Complicated mingling rules parser test { "node": cst.Call( cst.Name("foo"), ( cst.Arg( value=cst.Name("pos1"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( star="*", value=cst.Name("list1"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( value=cst.Name("pos2"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( value=cst.Name("pos3"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( star="*", value=cst.Name("list2"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( value=cst.Name("pos4"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( star="*", value=cst.Name("list3"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( keyword=cst.Name("kw1"), equal=cst.AssignEqual(), value=cst.Integer("1"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( star="*", value=cst.Name("list4"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( keyword=cst.Name("kw2"), equal=cst.AssignEqual(), value=cst.Integer("2"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( star="*", value=cst.Name("list5"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( keyword=cst.Name("kw3"), equal=cst.AssignEqual(), value=cst.Integer("3"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( star="**", value=cst.Name("dict1"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( keyword=cst.Name("kw4"), equal=cst.AssignEqual(), value=cst.Integer("4"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg(star="**", value=cst.Name("dict2")), ), ), "code": "foo(pos1, *list1, pos2, pos3, *list2, pos4, *list3, kw1 = 1, *list4, kw2 = 2, *list5, kw3 = 3, **dict1, kw4 = 4, **dict2)", "parser": parse_expression, "expected_position": None, }, # Test whitespace { "node": cst.Call( lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), func=cst.Name("foo"), whitespace_after_func=cst.SimpleWhitespace(" "), whitespace_before_args=cst.SimpleWhitespace(" "), args=( cst.Arg( keyword=None, value=cst.Name("pos1"), comma=cst.Comma( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), ), cst.Arg( star="*", whitespace_after_star=cst.SimpleWhitespace(" "), keyword=None, value=cst.Name("list1"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( keyword=cst.Name("kw1"), equal=cst.AssignEqual( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), value=cst.Integer("1"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( star="**", keyword=None, whitespace_after_star=cst.SimpleWhitespace(" "), value=cst.Name("dict1"), whitespace_after_arg=cst.SimpleWhitespace(" "), ), ), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), ), "code": "( foo ( pos1 , * list1, kw1=1, ** dict1 ) )", "parser": parse_expression, "expected_position": CodeRange.create((1, 2), (1, 43)), }, # Test args { "node": cst.Arg( star="*", whitespace_after_star=cst.SimpleWhitespace(" "), keyword=None, value=cst.Name("list1"), comma=cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), ), "code": "* list1, ", "parser": None, "expected_position": CodeRange.create((1, 0), (1, 8)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(( # Basic expression parenthesizing tests. { "get_node": lambda: cst.Call(func=cst.Name("foo"), lpar=(cst.LeftParen(), )), "expected_re": "left paren without right paren", }, { "get_node": lambda: cst.Call(func=cst.Name("foo"), rpar=(cst.RightParen(), )), "expected_re": "right paren without left paren", }, # Test that we handle keyword stuff correctly. { "get_node": lambda: cst.Call( func=cst.Name("foo"), args=(cst.Arg(equal=cst.AssignEqual(), value=cst.SimpleString("'baz'")), ), ), "expected_re": "Must have a keyword when specifying an AssignEqual", }, # Test that we separate *, ** and keyword args correctly { "get_node": lambda: cst.Call( func=cst.Name("foo"), args=(cst.Arg( star="*", keyword=cst.Name("bar"), value=cst.SimpleString("'baz'"), ), ), ), "expected_re": "Cannot specify a star and a keyword together", }, # Test for expected star inputs only { "get_node": lambda: cst.Call( func=cst.Name("foo"), # pyre-ignore: Ignore type on 'star' since we're testing behavior # when somebody isn't using a type checker. args=(cst.Arg(star="***", value=cst.SimpleString("'baz'")), ), ), "expected_re": r"Must specify either '', '\*' or '\*\*' for star", }, # Test ordering exceptions { "get_node": lambda: cst.Call( func=cst.Name("foo"), args=( cst.Arg(star="**", value=cst.Name("bar")), cst.Arg(star="*", value=cst.Name("baz")), ), ), "expected_re": "Cannot have iterable argument unpacking after keyword argument unpacking", }, { "get_node": lambda: cst.Call( func=cst.Name("foo"), args=( cst.Arg(star="**", value=cst.Name("bar")), cst.Arg(value=cst.Name("baz")), ), ), "expected_re": "Cannot have positional argument after keyword argument unpacking", }, { "get_node": lambda: cst.Call( func=cst.Name("foo"), args=( cst.Arg(keyword=cst.Name("arg"), value=cst.SimpleString("'baz'")), cst.Arg(value=cst.SimpleString("'bar'")), ), ), "expected_re": "Cannot have positional argument after keyword argument", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class BooleanOperationTest(CSTNodeTest): @data_provider( ( # Simple boolean operations # pyre-fixme[6]: Incompatible parameter type { "node": cst.BooleanOperation( cst.Name("foo"), cst.And(), cst.Name("bar") ), "code": "foo and bar", "parser": parse_expression, "expected_position": None, }, { "node": cst.BooleanOperation( cst.Name("foo"), cst.Or(), cst.Name("bar") ), "code": "foo or bar", "parser": parse_expression, "expected_position": None, }, # Parenthesized boolean operation { "node": cst.BooleanOperation( lpar=(cst.LeftParen(),), left=cst.Name("foo"), operator=cst.Or(), right=cst.Name("bar"), rpar=(cst.RightParen(),), ), "code": "(foo or bar)", "parser": parse_expression, "expected_position": None, }, { "node": cst.BooleanOperation( left=cst.Name( "foo", lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),) ), operator=cst.Or( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), right=cst.Name( "bar", lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),) ), ), "code": "(foo)or(bar)", "parser": parse_expression, "expected_position": CodeRange.create((1, 0), (1, 12)), }, # Make sure that spacing works { "node": cst.BooleanOperation( lpar=(cst.LeftParen(whitespace_after=cst.SimpleWhitespace(" ")),), left=cst.Name("foo"), operator=cst.And( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), right=cst.Name("bar"), rpar=(cst.RightParen(whitespace_before=cst.SimpleWhitespace(" ")),), ), "code": "( foo and bar )", "parser": parse_expression, "expected_position": None, }, ) ) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider( ( { "get_node": lambda: cst.BooleanOperation( cst.Name("foo"), cst.And(), cst.Name("bar"), lpar=(cst.LeftParen(),) ), "expected_re": "left paren without right paren", }, { "get_node": lambda: cst.BooleanOperation( cst.Name("foo"), cst.And(), cst.Name("bar"), rpar=(cst.RightParen(),), ), "expected_re": "right paren without left paren", }, { "get_node": lambda: cst.BooleanOperation( left=cst.Name("foo"), operator=cst.Or( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), right=cst.Name("bar"), ), "expected_re": "at least one space around boolean operator", }, ) ) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class IfTest(CSTNodeTest): @data_provider(( # Simple if without elif or else # pyre-fixme[6]: Incompatible parameter type { "node": cst.If(cst.Name("conditional"), cst.SimpleStatementSuite((cst.Pass(), ))), "code": "if conditional: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (1, 20)), }, # else clause { "node": cst.If( cst.Name("conditional"), cst.SimpleStatementSuite((cst.Pass(), )), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(), ))), ), "code": "if conditional: pass\nelse: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (2, 10)), }, # elif clause { "node": cst.If( cst.Name("conditional"), cst.SimpleStatementSuite((cst.Pass(), )), orelse=cst.If( cst.Name("other_conditional"), cst.SimpleStatementSuite((cst.Pass(), )), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(), ))), ), ), "code": "if conditional: pass\nelif other_conditional: pass\nelse: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (3, 10)), }, # indentation { "node": DummyIndentedBlock( " ", cst.If( cst.Name("conditional"), cst.SimpleStatementSuite((cst.Pass(), )), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(), ))), ), ), "code": " if conditional: pass\n else: pass\n", "parser": None, "expected_position": CodeRange.create((1, 4), (2, 14)), }, # with an indented body { "node": DummyIndentedBlock( " ", cst.If( cst.Name("conditional"), cst.IndentedBlock((cst.SimpleStatementLine( (cst.Pass(), )), )), ), ), "code": " if conditional:\n pass\n", "parser": None, "expected_position": CodeRange.create((1, 4), (2, 12)), }, # leading_lines { "node": cst.If( cst.Name("conditional"), cst.SimpleStatementSuite((cst.Pass(), )), leading_lines=(cst.EmptyLine( comment=cst.Comment("# leading comment")), ), ), "code": "# leading comment\nif conditional: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((2, 0), (2, 20)), }, # whitespace before/after test and else { "node": cst.If( cst.Name("conditional"), cst.SimpleStatementSuite((cst.Pass(), )), whitespace_before_test=cst.SimpleWhitespace(" "), whitespace_after_test=cst.SimpleWhitespace(" "), orelse=cst.Else( cst.SimpleStatementSuite((cst.Pass(), )), whitespace_before_colon=cst.SimpleWhitespace(" "), ), ), "code": "if conditional : pass\nelse : pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (2, 11)), }, # empty lines between if/elif/else clauses, not captured by the suite. { "node": cst.If( cst.Name("test_a"), cst.SimpleStatementSuite((cst.Pass(), )), orelse=cst.If( cst.Name("test_b"), cst.SimpleStatementSuite((cst.Pass(), )), leading_lines=(cst.EmptyLine(), ), orelse=cst.Else( cst.SimpleStatementSuite((cst.Pass(), )), leading_lines=(cst.EmptyLine(), ), ), ), ), "code": "if test_a: pass\n\nelif test_b: pass\n\nelse: pass\n", "parser": parse_statement, "expected_position": CodeRange.create((1, 0), (5, 10)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs)