class ReturnCreateTest(CSTNodeTest): @data_provider(( { "node": cst.SimpleStatementLine([cst.Return()]), "code": "return\n", "expected_position": CodeRange((1, 0), (1, 6)), }, { "node": cst.SimpleStatementLine([cst.Return(cst.Name("abc"))]), "code": "return abc\n", "expected_position": CodeRange((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)
def _create_issue(self, node: cst.Module) -> None: file_range = self.get_metadata(PositionProvider, node) code_range = CodeRange(start=file_range.start, end=file_range.start) issue = CodeQualityIssue( code_range, self.path, "PYRE_STRICT", "Unsafe Pyre file." ) self.issues.append(issue)
def test_position(self) -> None: # create a dummy node node = cst.Pass() # simulate codegen behavior for the dummy node # generates the code " pass " state = PositionProvidingCodegenState(" " * 4, "\n", PositionProvider()) start = CodePosition(state.line, state.column) state.add_token(" ") with state.record_syntactic_position(node): state.add_token("pass") state.add_token(" ") end = CodePosition(state.line, state.column) state.record_position(node, CodeRange(start, end)) # check syntactic position ignores whitespace self.assertEqual(state.provider._computed[node], CodeRange((1, 1), (1, 5)))
def test_coverage_percentage(self) -> None: coverage_result = to_coverage_result( CoveredAndUncoveredLines({1, 2}, {3, 4}), uncovered_ranges=[ CodeRange(CodePosition(line=3, column=3), CodePosition(line=4, column=4)) ], ) self.assertEqual(coverage_result.covered_percent, 50.0) self.assertEqual(len(coverage_result.uncovered_ranges), 1)
def test_whitespace_inclusive_position(self) -> None: # create a dummy node node = cst.Pass() # simulate codegen behavior for the dummy node # generates the code " pass " state = WhitespaceInclusivePositionProvidingCodegenState( " " * 4, "\n", WhitespaceInclusivePositionProvider() ) start = CodePosition(state.line, state.column) state.add_token(" ") with state.record_syntactic_position(node): state.add_token("pass") state.add_token(" ") end = CodePosition(state.line, state.column) state.record_position(node, CodeRange(start, end)) # check whitespace is correctly recorded self.assertEqual(state.provider._computed[node], CodeRange((1, 0), (1, 6)))
def test_return_code_range(self) -> None: collector = self._build_and_visit_annotation_collector(""" def foobar(): pass """) returns = list(collector.returns()) self.assertEqual(len(returns), 1) self.assertEqual( returns[0].code_range, CodeRange(CodePosition(2, 4), CodePosition(2, 10)), )
def visit_Call(self, node: libcst.Call) -> None: node_range = self.get_metadata(PositionProvider, node) start_node = node.func while isinstance(start_node, libcst.Attribute): start_node = start_node.attr start_node_range = self.get_metadata(PositionProvider, start_node) start_position = start_node_range.start end_position = node_range.end node_range = CodeRange(start=start_position, end=end_position) self.function_calls.append((node, node_range))
class ElseTest(CSTNodeTest): @data_provider(( { "node": cst.Else(cst.SimpleStatementSuite((cst.Pass(), ))), "code": "else: pass\n", "expected_position": CodeRange((1, 0), (1, 10)), }, { "node": cst.Else( cst.SimpleStatementSuite((cst.Pass(), )), whitespace_before_colon=cst.SimpleWhitespace(" "), ), "code": "else : pass\n", "expected_position": CodeRange((1, 0), (1, 12)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs)
def test_coverage_diagnostics(self) -> None: self.assertEqual( uncovered_range_to_diagnostic( CodeRange(CodePosition(line=1, column=1), CodePosition(line=2, column=2))), lsp.Diagnostic( range=lsp.Range( start=lsp.Position(line=0, character=1), end=lsp.Position(line=1, character=2), ), message=( "This function is not type checked. " "Consider adding parameter or return type annotations."), ), )
def test_equal_range(self) -> None: test = self expected_range = CodeRange((1, 4), (1, 6)) class EqualPositionVisitor(CSTVisitor): METADATA_DEPENDENCIES = (PositionProvider,) def visit_Equal(self, node: cst.Equal) -> None: test.assertEqual( self.get_metadata(PositionProvider, node), expected_range ) def visit_NotEqual(self, node: cst.NotEqual) -> None: test.assertEqual( self.get_metadata(PositionProvider, node), expected_range ) MetadataWrapper(parse_module("var == 1")).visit(EqualPositionVisitor()) MetadataWrapper(parse_module("var != 1")).visit(EqualPositionVisitor())
class TryTest(CSTNodeTest): @data_provider( ( # Simple try/except block { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), whitespace_after_except=cst.SimpleWhitespace(""), ), ), ), "code": "try: pass\nexcept: pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (2, 12)), }, # Try/except with a class { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), type=cst.Name("Exception"), ), ), ), "code": "try: pass\nexcept Exception: pass\n", "parser": parse_statement, }, # Try/except with a named class { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), type=cst.Name("Exception"), name=cst.AsName(cst.Name("exc")), ), ), ), "code": "try: pass\nexcept Exception as exc: pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (2, 29)), }, # Try/except with multiple clauses { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), type=cst.Name("TypeError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), type=cst.Name("KeyError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), whitespace_after_except=cst.SimpleWhitespace(""), ), ), ), "code": "try: pass\n" + "except TypeError as e: pass\n" + "except KeyError as e: pass\n" + "except: pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (4, 12)), }, # Simple try/finally block { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))), ), "code": "try: pass\nfinally: pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (2, 13)), }, # Simple try/except/finally block { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), whitespace_after_except=cst.SimpleWhitespace(""), ), ), finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))), ), "code": "try: pass\nexcept: pass\nfinally: pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (3, 13)), }, # Simple try/except/else block { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), whitespace_after_except=cst.SimpleWhitespace(""), ), ), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(),))), ), "code": "try: pass\nexcept: pass\nelse: pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (3, 10)), }, # Simple try/except/else block/finally { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), whitespace_after_except=cst.SimpleWhitespace(""), ), ), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(),))), finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))), ), "code": "try: pass\nexcept: pass\nelse: pass\nfinally: pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (4, 13)), }, # Verify whitespace in various locations { "node": cst.Try( leading_lines=(cst.EmptyLine(comment=cst.Comment("# 1")),), body=cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( leading_lines=(cst.EmptyLine(comment=cst.Comment("# 2")),), type=cst.Name("TypeError"), name=cst.AsName( cst.Name("e"), whitespace_before_as=cst.SimpleWhitespace(" "), whitespace_after_as=cst.SimpleWhitespace(" "), ), whitespace_after_except=cst.SimpleWhitespace(" "), whitespace_before_colon=cst.SimpleWhitespace(" "), body=cst.SimpleStatementSuite((cst.Pass(),)), ), ), orelse=cst.Else( leading_lines=(cst.EmptyLine(comment=cst.Comment("# 3")),), body=cst.SimpleStatementSuite((cst.Pass(),)), whitespace_before_colon=cst.SimpleWhitespace(" "), ), finalbody=cst.Finally( leading_lines=(cst.EmptyLine(comment=cst.Comment("# 4")),), body=cst.SimpleStatementSuite((cst.Pass(),)), whitespace_before_colon=cst.SimpleWhitespace(" "), ), whitespace_before_colon=cst.SimpleWhitespace(" "), ), "code": "# 1\ntry : pass\n# 2\nexcept TypeError as e : pass\n# 3\nelse : pass\n# 4\nfinally : pass\n", "parser": parse_statement, "expected_position": CodeRange((2, 0), (8, 14)), }, # Please don't write code like this { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), type=cst.Name("TypeError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), type=cst.Name("KeyError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), whitespace_after_except=cst.SimpleWhitespace(""), ), ), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(),))), finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))), ), "code": "try: pass\n" + "except TypeError as e: pass\n" + "except KeyError as e: pass\n" + "except: pass\n" + "else: pass\n" + "finally: pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (6, 13)), }, # Verify indentation { "node": DummyIndentedBlock( " ", cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), type=cst.Name("TypeError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), type=cst.Name("KeyError"), name=cst.AsName(cst.Name("e")), ), cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), whitespace_after_except=cst.SimpleWhitespace(""), ), ), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(),))), finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))), ), ), "code": " try: pass\n" + " except TypeError as e: pass\n" + " except KeyError as e: pass\n" + " except: pass\n" + " else: pass\n" + " finally: pass\n", "parser": None, }, # Verify indentation in bodies { "node": DummyIndentedBlock( " ", cst.Try( cst.IndentedBlock((cst.SimpleStatementLine((cst.Pass(),)),)), handlers=( cst.ExceptHandler( cst.IndentedBlock( (cst.SimpleStatementLine((cst.Pass(),)),) ), whitespace_after_except=cst.SimpleWhitespace(""), ), ), orelse=cst.Else( cst.IndentedBlock((cst.SimpleStatementLine((cst.Pass(),)),)) ), finalbody=cst.Finally( cst.IndentedBlock((cst.SimpleStatementLine((cst.Pass(),)),)) ), ), ), "code": " try:\n" + " pass\n" + " except:\n" + " pass\n" + " else:\n" + " pass\n" + " finally:\n" + " pass\n", "parser": None, }, # No space when using grouping parens { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), whitespace_after_except=cst.SimpleWhitespace(""), type=cst.Name( "Exception", lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),), ), ), ), ), "code": "try: pass\nexcept(Exception): pass\n", "parser": parse_statement, }, # No space when using tuple { "node": cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), handlers=( cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), whitespace_after_except=cst.SimpleWhitespace(""), type=cst.Tuple( [ cst.Element( cst.Name("IOError"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ") ), ), cst.Element(cst.Name("ImportError")), ] ), ), ), ), "code": "try: pass\nexcept(IOError, ImportError): pass\n", "parser": parse_statement, }, ) ) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider( ( { "get_node": lambda: cst.AsName(cst.Name("")), "expected_re": "empty name identifier", }, { "get_node": lambda: cst.AsName( cst.Name("bla"), whitespace_after_as=cst.SimpleWhitespace("") ), "expected_re": "between 'as'", }, { "get_node": lambda: cst.AsName( cst.Name("bla"), whitespace_before_as=cst.SimpleWhitespace("") ), "expected_re": "before 'as'", }, { "get_node": lambda: cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), name=cst.AsName(cst.Name("bla")), ), "expected_re": "name for an empty type", }, { "get_node": lambda: cst.ExceptHandler( cst.SimpleStatementSuite((cst.Pass(),)), type=cst.Name("TypeError"), whitespace_after_except=cst.SimpleWhitespace(""), ), "expected_re": "at least one space after except", }, { "get_node": lambda: cst.Try(cst.SimpleStatementSuite((cst.Pass(),))), "expected_re": "at least one ExceptHandler or Finally", }, { "get_node": lambda: cst.Try( cst.SimpleStatementSuite((cst.Pass(),)), orelse=cst.Else(cst.SimpleStatementSuite((cst.Pass(),))), finalbody=cst.Finally(cst.SimpleStatementSuite((cst.Pass(),))), ), "expected_re": "at least one ExceptHandler in order to have an Else", }, ) ) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class IfTest(CSTNodeTest): @data_provider(( # Simple if without elif or else { "node": cst.If(cst.Name("conditional"), cst.SimpleStatementSuite((cst.Pass(), ))), "code": "if conditional: pass\n", "parser": parse_statement, "expected_position": CodeRange((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((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((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((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((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((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((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((1, 0), (5, 10)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs)
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((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((1, 2), (1, 20)), ), # From expression position tests ( cst.From(cst.Integer("5"), whitespace_after_from=cst.SimpleWhitespace(" ")), "from 5", CodeRange((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 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((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((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((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 AugAssignTest(CSTNodeTest): @data_provider(( # Simple assignment constructor case. { "node": cst.AugAssign(cst.Name("foo"), cst.AddAssign(), cst.Integer("5")), "code": "foo += 5", "parser": None, "expected_position": CodeRange((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((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 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((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((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((1, 1), (1, 5)), ), ( cst.UnaryOperation( operator=cst.Minus(), expression=cst.UnaryOperation( operator=cst.Minus(), expression=cst.Integer("5") ), ), "--5", parse_expression, CodeRange((1, 0), (1, 3)), ), # multiple nested parenthesis ( cst.Integer( "5", lpar=(cst.LeftParen(), cst.LeftParen()), rpar=(cst.RightParen(), cst.RightParen()), ), "((5))", parse_expression, CodeRange((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((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 SubscriptTest(CSTNodeTest): @data_provider(( # Simple subscript expression ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement(cst.Index(cst.Integer("5"))), ), ), "foo[5]", True, ), # Test creation of subscript with slice/extslice. ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice( lower=cst.Integer("1"), upper=cst.Integer("2"), step=cst.Integer("3"), )), ), ), "foo[1:2:3]", False, ), ( cst.Subscript( cst.Name("foo"), ( cst.SubscriptElement( cst.Slice( lower=cst.Integer("1"), upper=cst.Integer("2"), step=cst.Integer("3"), )), cst.SubscriptElement(cst.Index(cst.Integer("5"))), ), ), "foo[1:2:3, 5]", False, CodeRange((1, 0), (1, 13)), ), # Test parsing of subscript with slice/extslice. ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice( lower=cst.Integer("1"), first_colon=cst.Colon(), upper=cst.Integer("2"), second_colon=cst.Colon(), step=cst.Integer("3"), )), ), ), "foo[1:2:3]", True, ), ( cst.Subscript( cst.Name("foo"), ( cst.SubscriptElement( cst.Slice( lower=cst.Integer("1"), first_colon=cst.Colon(), upper=cst.Integer("2"), second_colon=cst.Colon(), step=cst.Integer("3"), ), comma=cst.Comma(), ), cst.SubscriptElement(cst.Index(cst.Integer("5"))), ), ), "foo[1:2:3,5]", True, ), # Some more wild slice creations ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice(lower=cst.Integer("1"), upper=cst.Integer("2"))), ), ), "foo[1:2]", True, ), ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice(lower=cst.Integer("1"), upper=None)), ), ), "foo[1:]", True, ), ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice(lower=None, upper=cst.Integer("2"))), ), ), "foo[:2]", True, ), ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice( lower=cst.Integer("1"), upper=None, step=cst.Integer("3"), )), ), ), "foo[1::3]", False, ), ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice(lower=None, upper=None, step=cst.Integer("3"))), ), ), "foo[::3]", False, CodeRange((1, 0), (1, 8)), ), # Some more wild slice parsings ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice(lower=cst.Integer("1"), upper=cst.Integer("2"))), ), ), "foo[1:2]", True, ), ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice(lower=cst.Integer("1"), upper=None)), ), ), "foo[1:]", True, ), ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice(lower=None, upper=cst.Integer("2"))), ), ), "foo[:2]", True, ), ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice( lower=cst.Integer("1"), upper=None, second_colon=cst.Colon(), step=cst.Integer("3"), )), ), ), "foo[1::3]", True, ), ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice( lower=None, upper=None, second_colon=cst.Colon(), step=cst.Integer("3"), )), ), ), "foo[::3]", True, ), # Valid list clone operations rendering ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement(cst.Slice(lower=None, upper=None)), ), ), "foo[:]", True, ), ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice( lower=None, upper=None, second_colon=cst.Colon(), step=None, )), ), ), "foo[::]", True, ), # Valid list clone operations parsing ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement(cst.Slice(lower=None, upper=None)), ), ), "foo[:]", True, ), ( cst.Subscript( cst.Name("foo"), (cst.SubscriptElement( cst.Slice( lower=None, upper=None, second_colon=cst.Colon(), step=None, )), ), ), "foo[::]", True, ), # In parenthesis ( cst.Subscript( lpar=(cst.LeftParen(), ), value=cst.Name("foo"), slice=(cst.SubscriptElement(cst.Index(cst.Integer("5"))), ), rpar=(cst.RightParen(), ), ), "(foo[5])", True, ), # Verify spacing ( cst.Subscript( lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), value=cst.Name("foo"), lbracket=cst.LeftSquareBracket( whitespace_after=cst.SimpleWhitespace(" ")), slice=(cst.SubscriptElement(cst.Index(cst.Integer("5"))), ), rbracket=cst.RightSquareBracket( whitespace_before=cst.SimpleWhitespace(" ")), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), whitespace_after_value=cst.SimpleWhitespace(" "), ), "( foo [ 5 ] )", True, ), ( cst.Subscript( lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), value=cst.Name("foo"), lbracket=cst.LeftSquareBracket( whitespace_after=cst.SimpleWhitespace(" ")), slice=(cst.SubscriptElement( cst.Slice( lower=cst.Integer("1"), first_colon=cst.Colon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), upper=cst.Integer("2"), second_colon=cst.Colon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), step=cst.Integer("3"), )), ), rbracket=cst.RightSquareBracket( whitespace_before=cst.SimpleWhitespace(" ")), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), whitespace_after_value=cst.SimpleWhitespace(" "), ), "( foo [ 1 : 2 : 3 ] )", True, ), ( cst.Subscript( lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), value=cst.Name("foo"), lbracket=cst.LeftSquareBracket( whitespace_after=cst.SimpleWhitespace(" ")), slice=( cst.SubscriptElement( slice=cst.Slice( lower=cst.Integer("1"), first_colon=cst.Colon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), upper=cst.Integer("2"), second_colon=cst.Colon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), step=cst.Integer("3"), ), comma=cst.Comma( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), ), cst.SubscriptElement(slice=cst.Index(cst.Integer("5"))), ), rbracket=cst.RightSquareBracket( whitespace_before=cst.SimpleWhitespace(" ")), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), whitespace_after_value=cst.SimpleWhitespace(" "), ), "( foo [ 1 : 2 : 3 , 5 ] )", True, CodeRange((1, 2), (1, 24)), ), # Test Index, Slice, SubscriptElement (cst.Index(cst.Integer("5")), "5", False, CodeRange((1, 0), (1, 1))), ( cst.Slice(lower=None, upper=None, second_colon=cst.Colon(), step=None), "::", False, CodeRange((1, 0), (1, 2)), ), ( cst.SubscriptElement( slice=cst.Slice( lower=cst.Integer("1"), first_colon=cst.Colon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), upper=cst.Integer("2"), second_colon=cst.Colon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), step=cst.Integer("3"), ), comma=cst.Comma( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), ), "1 : 2 : 3 , ", False, CodeRange((1, 0), (1, 9)), ), )) def test_valid( self, node: cst.CSTNode, code: str, check_parsing: bool, position: Optional[CodeRange] = None, ) -> None: if check_parsing: self.validate_node(node, code, parse_expression, expected_position=position) else: self.validate_node(node, code, expected_position=position) @data_provider(( ( lambda: cst.Subscript( cst.Name("foo"), (cst.SubscriptElement(cst.Index(cst.Integer("5"))), ), lpar=(cst.LeftParen(), ), ), "left paren without right paren", ), ( lambda: cst.Subscript( cst.Name("foo"), (cst.SubscriptElement(cst.Index(cst.Integer("5"))), ), rpar=(cst.RightParen(), ), ), "right paren without left paren", ), (lambda: cst.Subscript(cst.Name("foo"), ()), "empty SubscriptElement"), )) def test_invalid(self, get_node: Callable[[], cst.CSTNode], expected_re: str) -> None: self.assert_invalid(get_node, expected_re)
class NamedExprTest(CSTNodeTest): @data_provider(( # Simple named expression { "node": cst.NamedExpr(cst.Name("x"), cst.Float("5.5")), "code": "x := 5.5", "parser": None, # Walrus operator is illegal as top-level statement "expected_position": None, }, # Parenthesized named expression { "node": cst.NamedExpr( lpar=(cst.LeftParen(), ), target=cst.Name("foo"), value=cst.Integer("5"), rpar=(cst.RightParen(), ), ), "code": "(foo := 5)", "parser": _parse_expression_force_38, "expected_position": CodeRange((1, 1), (1, 9)), }, # Make sure that spacing works { "node": cst.NamedExpr( lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), target=cst.Name("foo"), whitespace_before_walrus=cst.SimpleWhitespace(" "), whitespace_after_walrus=cst.SimpleWhitespace(" "), value=cst.Name("bar"), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), ), "code": "( foo := bar )", "parser": _parse_expression_force_38, "expected_position": CodeRange((1, 2), (1, 14)), }, # Make sure we can use these where allowed in if/while statements { "node": cst.While( test=cst.NamedExpr( target=cst.Name(value="x"), value=cst.Call(func=cst.Name(value="some_input")), ), body=cst.SimpleStatementSuite(body=[cst.Pass()]), ), "code": "while x := some_input(): pass\n", "parser": _parse_statement_force_38, "expected_position": None, }, { "node": cst.If( test=cst.NamedExpr( target=cst.Name(value="x"), value=cst.Call(func=cst.Name(value="some_input")), ), body=cst.SimpleStatementSuite(body=[cst.Pass()]), ), "code": "if x := some_input(): pass\n", "parser": _parse_statement_force_38, "expected_position": None, }, { "node": cst.If( test=cst.NamedExpr( target=cst.Name(value="x"), value=cst.Integer(value="1"), whitespace_before_walrus=cst.SimpleWhitespace(""), whitespace_after_walrus=cst.SimpleWhitespace(""), ), body=cst.SimpleStatementSuite(body=[cst.Pass()]), ), "code": "if x:=1: pass\n", "parser": _parse_statement_force_38, "expected_position": None, }, # Function args { "node": cst.Call( func=cst.Name(value="f"), args=[ cst.Arg(value=cst.NamedExpr( target=cst.Name(value="y"), value=cst.Integer(value="1"), whitespace_before_walrus=cst.SimpleWhitespace(""), whitespace_after_walrus=cst.SimpleWhitespace(""), )), ], ), "code": "f(y:=1)", "parser": _parse_expression_force_38, "expected_position": None, }, # Whitespace handling on args is fragile { "node": cst.Call( func=cst.Name(value="f"), args=[ cst.Arg( value=cst.Name(value="x"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( value=cst.NamedExpr( target=cst.Name(value="y"), value=cst.Integer(value="1"), whitespace_before_walrus=cst.SimpleWhitespace( " "), whitespace_after_walrus=cst.SimpleWhitespace( " "), ), whitespace_after_arg=cst.SimpleWhitespace(" "), ), ], ), "code": "f(x, y := 1 )", "parser": _parse_expression_force_38, "expected_position": None, }, { "node": cst.Call( func=cst.Name(value="f"), args=[ cst.Arg( value=cst.NamedExpr( target=cst.Name(value="y"), value=cst.Integer(value="1"), whitespace_before_walrus=cst.SimpleWhitespace( " "), whitespace_after_walrus=cst.SimpleWhitespace( " "), ), whitespace_after_arg=cst.SimpleWhitespace(" "), ), ], whitespace_before_args=cst.SimpleWhitespace(" "), ), "code": "f( y := 1 )", "parser": _parse_expression_force_38, "expected_position": None, }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(( { "get_node": (lambda: cst.NamedExpr( cst.Name("foo"), cst.Name("bar"), lpar=(cst.LeftParen(), ))), "expected_re": "left paren without right paren", }, { "get_node": (lambda: cst.NamedExpr( cst.Name("foo"), 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 DictCompTest(CSTNodeTest): @data_provider([ # simple DictComp { "node": cst.DictComp( cst.Name("k"), cst.Name("v"), cst.CompFor(target=cst.Name("a"), iter=cst.Name("b")), ), "code": "{k: v for a in b}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 17)), }, # custom whitespace around colon { "node": cst.DictComp( cst.Name("k"), cst.Name("v"), cst.CompFor(target=cst.Name("a"), iter=cst.Name("b")), whitespace_before_colon=cst.SimpleWhitespace("\t"), whitespace_after_colon=cst.SimpleWhitespace("\t\t"), ), "code": "{k\t:\t\tv for a in b}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 19)), }, # custom whitespace inside braces { "node": cst.DictComp( cst.Name("k"), cst.Name("v"), cst.CompFor(target=cst.Name("a"), iter=cst.Name("b")), lbrace=cst.LeftCurlyBrace( whitespace_after=cst.SimpleWhitespace("\t")), rbrace=cst.RightCurlyBrace( whitespace_before=cst.SimpleWhitespace("\t\t")), ), "code": "{\tk: v for a in b\t\t}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 20)), }, # parenthesis { "node": cst.DictComp( cst.Name("k"), cst.Name("v"), cst.CompFor(target=cst.Name("a"), iter=cst.Name("b")), lpar=[cst.LeftParen()], rpar=[cst.RightParen()], ), "code": "({k: v for a in b})", "parser": parse_expression, "expected_position": CodeRange((1, 1), (1, 18)), }, # missing spaces around DictComp is always okay { "node": cst.DictComp( cst.Name("a"), cst.Name("b"), cst.CompFor( target=cst.Name("c"), iter=cst.DictComp( cst.Name("d"), cst.Name("e"), cst.CompFor(target=cst.Name("f"), iter=cst.Name("g")), ), ifs=[ cst.CompIf( cst.Name("h"), whitespace_before=cst.SimpleWhitespace(""), ) ], whitespace_after_in=cst.SimpleWhitespace(""), ), ), "code": "{a: b for c in{d: e for f in g}if h}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 36)), }, # no whitespace before `for` clause { "node": cst.DictComp( cst.Name("k"), cst.Name("v", lpar=[cst.LeftParen()], rpar=[cst.RightParen()]), cst.CompFor( target=cst.Name("a"), iter=cst.Name("b"), whitespace_before=cst.SimpleWhitespace(""), ), ), "code": "{k: (v)for a in b}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 18)), }, ]) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider([ # unbalanced DictComp { "get_node": lambda: cst.DictComp( cst.Name("k"), cst.Name("v"), cst.CompFor(target=cst.Name("a"), iter=cst.Name("b")), lpar=[cst.LeftParen()], ), "expected_re": "left paren without right paren", }, # invalid whitespace before for/async { "get_node": lambda: cst.DictComp( cst.Name("k"), cst.Name("v"), cst.CompFor( target=cst.Name("a"), iter=cst.Name("b"), whitespace_before=cst.SimpleWhitespace(""), ), ), "expected_re": "Must have at least one space before 'for' keyword.", }, { "get_node": lambda: cst.DictComp( cst.Name("k"), cst.Name("v"), cst.CompFor( target=cst.Name("a"), iter=cst.Name("b"), asynchronous=cst.Asynchronous(), whitespace_before=cst.SimpleWhitespace(""), ), ), "expected_re": "Must have at least one space before 'async' keyword.", }, ]) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class AnnAssignTest(CSTNodeTest): @data_provider(( # Simple assignment creation case. { "node": cst.AnnAssign(cst.Name("foo"), cst.Annotation(cst.Name("str")), cst.Integer("5")), "code": "foo: str = 5", "parser": None, "expected_position": CodeRange((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((1, 0), (1, 8)), }, # Complex annotation creation { "node": cst.AnnAssign( cst.Name("foo"), cst.Annotation( cst.Subscript( cst.Name("Optional"), (cst.SubscriptElement(cst.Index(cst.Name("str"))), ), )), cst.Integer("5"), ), "code": "foo: Optional[str] = 5", "parser": None, "expected_position": CodeRange((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.SubscriptElement(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.SubscriptElement(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((1, 0), (1, 26)), }, { "node": cst.SimpleStatementLine((cst.AnnAssign( target=cst.Name("foo"), annotation=cst.Annotation( annotation=cst.Subscript( cst.Name("Optional"), (cst.SubscriptElement(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 ForTest(CSTNodeTest): @data_provider(( # Simple for block { "node": cst.For( cst.Name("target"), cst.Call(cst.Name("iter")), cst.SimpleStatementSuite((cst.Pass(), )), ), "code": "for target in iter(): pass\n", "parser": parse_statement, }, # Simple async for block { "node": cst.For( cst.Name("target"), cst.Call(cst.Name("iter")), cst.SimpleStatementSuite((cst.Pass(), )), asynchronous=cst.Asynchronous(), ), "code": "async for target in iter(): pass\n", "parser": lambda code: parse_statement( code, config=PartialParserConfig(python_version="3.7")), }, # Python 3.6 async for block { "node": cst.FunctionDef( cst.Name("foo"), cst.Parameters(), cst.IndentedBlock((cst.For( cst.Name("target"), cst.Call(cst.Name("iter")), cst.SimpleStatementSuite((cst.Pass(), )), asynchronous=cst.Asynchronous(), ), )), asynchronous=cst.Asynchronous(), ), "code": "async def foo():\n async for target in iter(): pass\n", "parser": lambda code: parse_statement( code, config=PartialParserConfig(python_version="3.6")), }, # For block with else { "node": cst.For( cst.Name("target"), cst.Call(cst.Name("iter")), cst.SimpleStatementSuite((cst.Pass(), )), cst.Else(cst.SimpleStatementSuite((cst.Pass(), ))), ), "code": "for target in iter(): pass\nelse: pass\n", "parser": parse_statement, }, # indentation { "node": DummyIndentedBlock( " ", cst.For( cst.Name("target"), cst.Call(cst.Name("iter")), cst.SimpleStatementSuite((cst.Pass(), )), ), ), "code": " for target in iter(): pass\n", "parser": None, }, # for an indented body { "node": DummyIndentedBlock( " ", cst.For( cst.Name("target"), cst.Call(cst.Name("iter")), cst.IndentedBlock((cst.SimpleStatementLine( (cst.Pass(), )), )), ), ), "code": " for target in iter():\n pass\n", "parser": None, "expected_position": CodeRange((1, 4), (2, 12)), }, # leading_lines { "node": cst.For( cst.Name("target"), cst.Call(cst.Name("iter")), cst.IndentedBlock((cst.SimpleStatementLine((cst.Pass(), )), )), cst.Else( cst.IndentedBlock((cst.SimpleStatementLine( (cst.Pass(), )), )), leading_lines=(cst.EmptyLine( comment=cst.Comment("# else comment")), ), ), leading_lines=(cst.EmptyLine( comment=cst.Comment("# leading comment")), ), ), "code": "# leading comment\nfor target in iter():\n pass\n# else comment\nelse:\n pass\n", "parser": None, "expected_position": CodeRange((2, 0), (6, 8)), }, # Weird spacing rules { "node": cst.For( cst.Name("target", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), cst.Call( cst.Name("iter"), lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), ), ), cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_for=cst.SimpleWhitespace(""), whitespace_before_in=cst.SimpleWhitespace(""), whitespace_after_in=cst.SimpleWhitespace(""), ), "code": "for(target)in(iter()): pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (1, 27)), }, # Whitespace { "node": cst.For( cst.Name("target"), cst.Call(cst.Name("iter")), cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_for=cst.SimpleWhitespace(" "), whitespace_before_in=cst.SimpleWhitespace(" "), whitespace_after_in=cst.SimpleWhitespace(" "), whitespace_before_colon=cst.SimpleWhitespace(" "), ), "code": "for target in iter() : pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (1, 31)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(( { "get_node": lambda: cst.For( cst.Name("target"), cst.Call(cst.Name("iter")), cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_for=cst.SimpleWhitespace(""), ), "expected_re": "Must have at least one space after 'for' keyword", }, { "get_node": lambda: cst.For( cst.Name("target"), cst.Call(cst.Name("iter")), cst.SimpleStatementSuite((cst.Pass(), )), whitespace_before_in=cst.SimpleWhitespace(""), ), "expected_re": "Must have at least one space before 'in' keyword", }, { "get_node": lambda: cst.For( cst.Name("target"), cst.Call(cst.Name("iter")), cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_in=cst.SimpleWhitespace(""), ), "expected_re": "Must have at least one space after 'in' keyword", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class WithTest(CSTNodeTest): @data_provider(( # Simple with block { "node": cst.With( (cst.WithItem(cst.Call(cst.Name("context_mgr"))), ), cst.SimpleStatementSuite((cst.Pass(), )), ), "code": "with context_mgr(): pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (1, 24)), }, # Simple async with block { "node": cst.With( (cst.WithItem(cst.Call(cst.Name("context_mgr"))), ), cst.SimpleStatementSuite((cst.Pass(), )), asynchronous=cst.Asynchronous(), ), "code": "async with context_mgr(): pass\n", "parser": lambda code: parse_statement( code, config=PartialParserConfig(python_version="3.7")), }, # Python 3.6 async with block { "node": cst.FunctionDef( cst.Name("foo"), cst.Parameters(), cst.IndentedBlock((cst.With( (cst.WithItem(cst.Call(cst.Name("context_mgr"))), ), cst.SimpleStatementSuite((cst.Pass(), )), asynchronous=cst.Asynchronous(), ), )), asynchronous=cst.Asynchronous(), ), "code": "async def foo():\n async with context_mgr(): pass\n", "parser": lambda code: parse_statement( code, config=PartialParserConfig(python_version="3.6")), }, # Multiple context managers { "node": cst.With( ( cst.WithItem(cst.Call(cst.Name("foo"))), cst.WithItem(cst.Call(cst.Name("bar"))), ), cst.SimpleStatementSuite((cst.Pass(), )), ), "code": "with foo(), bar(): pass\n", "parser": None, }, { "node": cst.With( ( cst.WithItem( cst.Call(cst.Name("foo")), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.WithItem(cst.Call(cst.Name("bar"))), ), cst.SimpleStatementSuite((cst.Pass(), )), ), "code": "with foo(), bar(): pass\n", "parser": parse_statement, }, # With block containing variable for context manager. { "node": cst.With( (cst.WithItem( cst.Call(cst.Name("context_mgr")), cst.AsName(cst.Name("ctx")), ), ), cst.SimpleStatementSuite((cst.Pass(), )), ), "code": "with context_mgr() as ctx: pass\n", "parser": parse_statement, }, # indentation { "node": DummyIndentedBlock( " ", cst.With( (cst.WithItem(cst.Call(cst.Name("context_mgr"))), ), cst.SimpleStatementSuite((cst.Pass(), )), ), ), "code": " with context_mgr(): pass\n", "parser": None, "expected_position": CodeRange((1, 4), (1, 28)), }, # with an indented body { "node": DummyIndentedBlock( " ", cst.With( (cst.WithItem(cst.Call(cst.Name("context_mgr"))), ), cst.IndentedBlock((cst.SimpleStatementLine( (cst.Pass(), )), )), ), ), "code": " with context_mgr():\n pass\n", "parser": None, "expected_position": CodeRange((1, 4), (2, 12)), }, # leading_lines { "node": cst.With( (cst.WithItem(cst.Call(cst.Name("context_mgr"))), ), cst.SimpleStatementSuite((cst.Pass(), )), leading_lines=(cst.EmptyLine( comment=cst.Comment("# leading comment")), ), ), "code": "# leading comment\nwith context_mgr(): pass\n", "parser": parse_statement, "expected_position": CodeRange((2, 0), (2, 24)), }, # Weird spacing rules { "node": cst.With( (cst.WithItem( cst.Call( cst.Name("context_mgr"), lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), ), )), ), cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_with=cst.SimpleWhitespace(""), ), "code": "with(context_mgr()): pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (1, 25)), }, # Whitespace { "node": cst.With( (cst.WithItem( cst.Call(cst.Name("context_mgr")), cst.AsName( cst.Name("ctx"), whitespace_before_as=cst.SimpleWhitespace(" "), whitespace_after_as=cst.SimpleWhitespace(" "), ), ), ), cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_with=cst.SimpleWhitespace(" "), whitespace_before_colon=cst.SimpleWhitespace(" "), ), "code": "with context_mgr() as ctx : pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (1, 36)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(( { "get_node": lambda: cst.With((), cst.IndentedBlock((cst.SimpleStatementLine( (cst.Pass(), )), ))), "expected_re": "A With statement must have at least one WithItem", }, { "get_node": lambda: cst.With( (cst.WithItem( cst.Call(cst.Name("foo")), comma=cst.Comma(whitespace_after=cst.SimpleWhitespace(" ") ), ), ), cst.IndentedBlock((cst.SimpleStatementLine((cst.Pass(), )), )), ), "expected_re": "The last WithItem in a With cannot have a trailing comma", }, { "get_node": lambda: cst.With( (cst.WithItem(cst.Call(cst.Name("context_mgr"))), ), cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_with=cst.SimpleWhitespace(""), ), "expected_re": "Must have at least one space after with keyword", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs) @data_provider(( { "code": "with a, b: pass", "parser": parse_statement_as(python_version="3.1"), "expect_success": True, }, { "code": "with a, b: pass", "parser": parse_statement_as(python_version="3.0"), "expect_success": False, }, )) def test_versions(self, **kwargs: Any) -> None: self.assert_parses(**kwargs)
class AwaitTest(CSTNodeTest): @data_provider(( # Some simple calls { "node": cst.Await(cst.Name("test")), "code": "await test", "parser": lambda code: parse_expression( code, config=PartialParserConfig(python_version="3.7")), "expected_position": None, }, { "node": cst.Await(cst.Call(cst.Name("test"))), "code": "await test()", "parser": lambda code: parse_expression( code, config=PartialParserConfig(python_version="3.7")), "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": lambda code: parse_expression( code, config=PartialParserConfig(python_version="3.7")), "expected_position": CodeRange((1, 2), (1, 13)), }, )) def test_valid_py37(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(( # Some simple calls { "node": cst.FunctionDef( cst.Name("foo"), cst.Parameters(), cst.IndentedBlock((cst.SimpleStatementLine( (cst.Expr(cst.Await(cst.Name("test"))), )), )), asynchronous=cst.Asynchronous(), ), "code": "async def foo():\n await test\n", "parser": lambda code: parse_statement( code, config=PartialParserConfig(python_version="3.6")), "expected_position": None, }, { "node": cst.FunctionDef( cst.Name("foo"), cst.Parameters(), cst.IndentedBlock((cst.SimpleStatementLine( (cst.Expr(cst.Await(cst.Call(cst.Name("test")))), )), )), asynchronous=cst.Asynchronous(), ), "code": "async def foo():\n await test()\n", "parser": lambda code: parse_statement( code, config=PartialParserConfig(python_version="3.6")), "expected_position": None, }, # Whitespace { "node": cst.FunctionDef( cst.Name("foo"), cst.Parameters(), cst.IndentedBlock((cst.SimpleStatementLine((cst.Expr( cst.Await( cst.Name("test"), whitespace_after_await=cst.SimpleWhitespace(" "), lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), )), )), )), asynchronous=cst.Asynchronous(), ), "code": "async def foo():\n ( await test )\n", "parser": lambda code: parse_statement( code, config=PartialParserConfig(python_version="3.6")), "expected_position": None, }, )) def test_valid_py36(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)
def cmp_position(self, actual: CodeRange, start: Tuple[int, int], end: Tuple[int, int]) -> None: self.assertEqual(actual, CodeRange(start, end))
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), "enabled_for_native": False, }, "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, enabled_for_native: bool = True) -> None: if is_native() and not enabled_for_native: self.skipTest("Disabled for native parser") self.assertEqual(parse_module(code), expected) @data_provider({ "empty": { "code": "", "expected": CodeRange((1, 0), (1, 0)) }, "empty_with_newline": { "code": "\n", "expected": CodeRange((1, 0), (2, 0)) }, "empty_program_with_comments": { "code": "# 2345", "expected": CodeRange((1, 0), (2, 0)), }, "simple_pass": { "code": "pass\n", "expected": CodeRange((1, 0), (2, 0)) }, "simple_pass_with_header_footer": { "code": "# header\npass # trailing\n# footer\n", "expected": CodeRange((1, 0), (4, 0)), }, }) def test_module_position(self, *, code: str, expected: CodeRange) -> None: wrapper = MetadataWrapper(parse_module(code)) positions = wrapper.resolve(PositionProvider) self.assertEqual(positions[wrapper.module], expected) def cmp_position(self, actual: CodeRange, start: Tuple[int, int], end: Tuple[int, int]) -> None: self.assertEqual(actual, CodeRange(start, end)) def test_function_position(self) -> None: wrapper = MetadataWrapper(parse_module("def foo():\n pass")) module = wrapper.module positions = wrapper.resolve(PositionProvider) 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(positions[stmt], (2, 4), (2, 8)) self.cmp_position(positions[pass_stmt], (2, 4), (2, 8)) def test_nested_indent_position(self) -> None: wrapper = MetadataWrapper( parse_module( "if True:\n if False:\n x = 1\nelse:\n return")) module = wrapper.module positions = wrapper.resolve(PositionProvider) 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(positions[outer_if], (1, 0), (5, 10)) self.cmp_position(positions[inner_if], (2, 4), (3, 13)) self.cmp_position(positions[assign], (3, 8), (3, 13)) self.cmp_position(positions[outer_else], (4, 0), (5, 10)) self.cmp_position(positions[return_stmt], (5, 4), (5, 10)) def test_multiline_string_position(self) -> None: wrapper = MetadataWrapper(parse_module('"abc"\\\n"def"')) module = wrapper.module positions = wrapper.resolve(PositionProvider) stmt = cast(cst.SimpleStatementLine, module.body[0]) expr = cast(cst.Expr, stmt.body[0]) string = expr.value self.cmp_position(positions[stmt], (1, 0), (2, 5)) self.cmp_position(positions[expr], (1, 0), (2, 5)) self.cmp_position(positions[string], (1, 0), (2, 5)) def test_module_config_for_parsing(self) -> None: module = parse_module("pass\r") statement = parse_statement("if True:\r pass", config=module.config_for_parsing) self.assertEqual( statement, cst.If( test=cst.Name(value="True"), body=cst.IndentedBlock( body=[cst.SimpleStatementLine(body=[cst.Pass()])], header=cst.TrailingWhitespace(newline=cst.Newline( # This would be "\r" if we didn't pass the module config forward. value=None)), ), ), )
def visit_Pass(self, node: cst.Pass) -> None: test.assertEqual( self.get_metadata(PositionProvider, node), CodeRange((1, 0), (1, 4)) )
class NonlocalConstructionTest(CSTNodeTest): @data_provider(( # Single nonlocal statement { "node": cst.Nonlocal((cst.NameItem(cst.Name("a")), )), "code": "nonlocal a", }, # Multiple entries in nonlocal statement { "node": cst.Nonlocal( (cst.NameItem(cst.Name("a")), cst.NameItem(cst.Name("b")))), "code": "nonlocal a, b", "expected_position": CodeRange((1, 0), (1, 13)), }, # Whitespace rendering test { "node": cst.Nonlocal( ( cst.NameItem( cst.Name("a"), comma=cst.Comma( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), ), cst.NameItem(cst.Name("b")), ), whitespace_after_nonlocal=cst.SimpleWhitespace(" "), ), "code": "nonlocal a , b", "expected_position": CodeRange((1, 0), (1, 17)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(( # Validate construction { "get_node": lambda: cst.Nonlocal(()), "expected_re": "A Nonlocal statement must have at least one NameItem", }, # Validate whitespace handling { "get_node": lambda: cst.Nonlocal( (cst.NameItem(cst.Name("a")), ), whitespace_after_nonlocal=cst.SimpleWhitespace(""), ), "expected_re": "Must have at least one space after 'nonlocal' keyword", }, # Validate comma handling { "get_node": lambda: cst.Nonlocal( (cst.NameItem(cst.Name("a"), comma=cst.Comma()), )), "expected_re": "The last NameItem in a Nonlocal cannot have a trailing comma", }, # Validate paren handling { "get_node": lambda: cst.Nonlocal((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)
class RaiseConstructionTest(CSTNodeTest): @data_provider(( # Simple raise { "node": cst.Raise(), "code": "raise" }, # Raise exception { "node": cst.Raise(cst.Call(cst.Name("Exception"))), "code": "raise Exception()", "expected_position": CodeRange((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((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((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((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 AssertConstructionTest(CSTNodeTest): @data_provider(( # Simple assert { "node": cst.Assert(cst.Name("True")), "code": "assert True", "parser": None, "expected_position": None, }, # Assert with message { "node": cst.Assert(cst.Name("True"), cst.SimpleString('"Value should be true"')), "code": 'assert True, "Value should be true"', "parser": None, "expected_position": None, }, # Whitespace oddities test { "node": cst.Assert( cst.Name("True", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), whitespace_after_assert=cst.SimpleWhitespace(""), ), "code": "assert(True)", "parser": None, "expected_position": CodeRange((1, 0), (1, 12)), }, # Whitespace rendering test { "node": cst.Assert( whitespace_after_assert=cst.SimpleWhitespace(" "), test=cst.Name("True"), comma=cst.Comma( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), msg=cst.SimpleString('"Value should be true"'), ), "code": 'assert True , "Value should be true"', "parser": None, "expected_position": CodeRange((1, 0), (1, 39)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(( # Validate whitespace handling { "get_node": (lambda: cst.Assert( cst.Name("True"), whitespace_after_assert=cst.SimpleWhitespace(""), )), "expected_re": "Must have at least one space after 'assert'", }, # Validate comma handling { "get_node": (lambda: cst.Assert(test=cst.Name("True"), comma=cst.Comma())), "expected_re": "Cannot have trailing comma after 'test'", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs)
class AssignTest(CSTNodeTest): @data_provider(( # Simple assignment creation case. { "node": cst.Assign((cst.AssignTarget(cst.Name("foo")), ), cst.Integer("5")), "code": "foo = 5", "parser": None, "expected_position": CodeRange((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((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((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)