def refactor_import_star(self, updated_node: cst.ImportFrom) -> cst.ImportFrom: """Add used import aliases to import star. :param updated_node: `cst.ImportFrom` node to refactor. :returns: refactored node. """ is_multiline = len(self._used_names) > 3 used_aliases: List[cst.ImportAlias] = [] for name in self._used_names: # Skip any dotted name in order # to avoid names collision. if "." in name: continue # Initialy create a single line alias. cst_alias = cst.ImportAlias( name=cst.Name(name), comma=cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), ) # Convert the single line alias to multiline # if there're more than 3 used names. if is_multiline: cst_alias = self._multiline_alias(cst_alias) used_aliases.append(cst_alias) return self._stylize(updated_node, used_aliases, is_multiline)
class NonlocalParsingTest(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"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.NameItem(cst.Name("b")), )), "code": "nonlocal a, b", }, # 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", }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node( parser=lambda code: ensure_type(parse_statement(code), cst. SimpleStatementLine).body[0], **kwargs, )
def _multiline_alias(self, alias: cst.ImportAlias) -> cst.ImportAlias: # Convert the given `alias` to multiline `alias`. return cst.ImportAlias( name=alias.name, asname=alias.asname, comma=cst.Comma( whitespace_after=ImportTransformer. _multiline_parenthesized_whitespace(self._indentation + SPACE4)), )
def test_adding_parens(self) -> None: node = cst.With( ( cst.WithItem( cst.Call(cst.Name("foo")), comma=cst.Comma( whitespace_after=cst.ParenthesizedWhitespace(), ), ), cst.WithItem(cst.Call(cst.Name("bar")), comma=cst.Comma()), ), cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(whitespace_after=cst.SimpleWhitespace(" ")), rpar=cst.RightParen(whitespace_before=cst.SimpleWhitespace(" ")), ) module = cst.Module([]) self.assertEqual( module.code_for_node(node), ("with ( foo(),\n" "bar(), ): pass\n") # noqa )
def leave_Call( self, original_node: cst.Call, updated_node: cst.Call, ) -> cst.Call: if len(updated_node.args) < self.argument_count: return updated_node else: last_arg = updated_node.args[-1] return updated_node.with_changes(args=( *updated_node.args[:-1], last_arg.with_changes(comma=cst.Comma()), ), )
def leave_Parameters( self, original_node: cst.Parameters, updated_node: cst.Parameters, ) -> cst.Parameters: skip = ( # self.parameter_count is None or len(updated_node.params) < self.parameter_count or (len(updated_node.params) == 1 and updated_node.params[0].name.value in {"self", "cls"})) if skip: return updated_node else: last_param = updated_node.params[-1] return updated_node.with_changes(params=( *updated_node.params[:-1], last_param.with_changes(comma=cst.Comma()), ), )
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 ClassDefParserTest(CSTNodeTest): @data_provider(( # Simple classdef # pyre-fixme[6]: Incompatible parameter type { "node": cst.ClassDef(cst.Name("Foo"), cst.SimpleStatementSuite((cst.Pass(), ))), "code": "class Foo: pass\n", }, { "node": cst.ClassDef( cst.Name("Foo"), cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(), rpar=cst.RightParen(), ), "code": "class Foo(): pass\n", }, # Positional arguments render test { "node": cst.ClassDef( cst.Name("Foo"), cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(), bases=(cst.Arg(cst.Name("obj")), ), rpar=cst.RightParen(), ), "code": "class Foo(obj): pass\n", }, { "node": cst.ClassDef( cst.Name("Foo"), cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(), bases=( cst.Arg( cst.Name("Bar"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg( cst.Name("Baz"), comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Arg(cst.Name("object")), ), rpar=cst.RightParen(), ), "code": "class Foo(Bar, Baz, object): pass\n", }, # Keyword arguments render test { "node": cst.ClassDef( cst.Name("Foo"), cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(), keywords=(cst.Arg( keyword=cst.Name("metaclass"), equal=cst.AssignEqual(), value=cst.Name("Bar"), ), ), rpar=cst.RightParen(), ), "code": "class Foo(metaclass = Bar): pass\n", }, # Iterator expansion render test { "node": cst.ClassDef( cst.Name("Foo"), cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(), bases=(cst.Arg(star="*", value=cst.Name("one")), ), rpar=cst.RightParen(), ), "code": "class Foo(*one): pass\n", }, { "node": cst.ClassDef( cst.Name("Foo"), cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(), bases=( 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")), ), rpar=cst.RightParen(), ), "code": "class Foo(*one, *two, *three): pass\n", }, # Dictionary expansion render test { "node": cst.ClassDef( cst.Name("Foo"), cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(), keywords=(cst.Arg(star="**", value=cst.Name("one")), ), rpar=cst.RightParen(), ), "code": "class Foo(**one): pass\n", }, { "node": cst.ClassDef( cst.Name("Foo"), cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(), keywords=( 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")), ), rpar=cst.RightParen(), ), "code": "class Foo(**one, **two, **three): pass\n", }, # Decorator render tests { "node": cst.ClassDef( cst.Name("Foo"), cst.SimpleStatementSuite((cst.Pass(), )), decorators=(cst.Decorator(cst.Name("foo")), ), lpar=cst.LeftParen(), rpar=cst.RightParen(), ), "code": "@foo\nclass Foo(): pass\n", "expected_position": CodeRange((2, 0), (2, 17)), }, { "node": cst.ClassDef( leading_lines=( cst.EmptyLine(), cst.EmptyLine(comment=cst.Comment("# leading comment 1")), ), decorators=( cst.Decorator(cst.Name("foo"), leading_lines=()), cst.Decorator( cst.Name("bar"), leading_lines=(cst.EmptyLine( comment=cst.Comment("# leading comment 2")), ), ), cst.Decorator( cst.Name("baz"), leading_lines=(cst.EmptyLine( comment=cst.Comment("# leading comment 3")), ), ), ), lines_after_decorators=(cst.EmptyLine( comment=cst.Comment("# class comment")), ), name=cst.Name("Foo"), body=cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(), rpar=cst.RightParen(), ), "code": "\n# leading comment 1\n@foo\n# leading comment 2\n@bar\n# leading comment 3\n@baz\n# class comment\nclass Foo(): pass\n", "expected_position": CodeRange((9, 0), (9, 17)), }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs, parser=parse_statement)
class AssertParsingTest(CSTNodeTest): @data_provider(( # Simple assert { "node": cst.Assert(cst.Name("True")), "code": "assert True", "parser": _assert_parser, "expected_position": None, }, # Assert with message { "node": cst.Assert( cst.Name("True"), cst.SimpleString('"Value should be true"'), comma=cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), ), "code": 'assert True, "Value should be true"', "parser": _assert_parser, "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": _assert_parser, "expected_position": None, }, # 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": _assert_parser, "expected_position": None, }, )) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs)
class LambdaParserTest(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"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param(cst.Name("baz"), star=""), )), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), "lambda bar, baz: 5", ), # Test basic positional default params ( cst.Lambda( cst.Parameters(default_params=( cst.Param( cst.Name("bar"), default=cst.SimpleString('"one"'), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("baz"), default=cst.Integer("5"), equal=cst.AssignEqual(), star="", ), )), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), 'lambda bar = "one", baz = 5: 5', ), # Mixed positional and default params. ( cst.Lambda( cst.Parameters( params=(cst.Param( cst.Name("bar"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), ), default_params=(cst.Param( cst.Name("baz"), default=cst.Integer("5"), equal=cst.AssignEqual(), star="", ), ), ), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), "lambda bar, baz = 5: 5", ), # Test kwonly params ( cst.Lambda( cst.Parameters( star_arg=cst.ParamStar(), kwonly_params=( cst.Param( cst.Name("bar"), default=cst.SimpleString('"one"'), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param(cst.Name("baz"), star=""), ), ), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), 'lambda *, bar = "one", baz: 5', ), # Mixed params and kwonly_params ( cst.Lambda( cst.Parameters( params=( cst.Param( cst.Name("first"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("second"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), ), star_arg=cst.ParamStar(), kwonly_params=( cst.Param( cst.Name("bar"), default=cst.SimpleString('"one"'), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("baz"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("biz"), default=cst.SimpleString('"two"'), equal=cst.AssignEqual(), star="", ), ), ), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), '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"), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("second"), default=cst.Float("1.5"), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), ), star_arg=cst.ParamStar(), kwonly_params=( cst.Param( cst.Name("bar"), default=cst.SimpleString('"one"'), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("baz"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("biz"), default=cst.SimpleString('"two"'), equal=cst.AssignEqual(), star="", ), ), ), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), '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"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("second"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), ), default_params=( cst.Param( cst.Name("third"), default=cst.Float("1.0"), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("fourth"), default=cst.Float("1.5"), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), ), star_arg=cst.ParamStar(), kwonly_params=( cst.Param( cst.Name("bar"), default=cst.SimpleString('"one"'), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("baz"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("biz"), default=cst.SimpleString('"two"'), equal=cst.AssignEqual(), star="", ), ), ), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), 'lambda first, second, third = 1.0, fourth = 1.5, *, bar = "one", baz, biz = "two": 5', ), # Test star_arg ( cst.Lambda( cst.Parameters( star_arg=cst.Param(cst.Name("params"), star="*")), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), "lambda *params: 5", ), # Typed star_arg, include kwonly_params ( cst.Lambda( cst.Parameters( star_arg=cst.Param( cst.Name("params"), star="*", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), kwonly_params=( cst.Param( cst.Name("bar"), default=cst.SimpleString('"one"'), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("baz"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("biz"), default=cst.SimpleString('"two"'), equal=cst.AssignEqual(), star="", ), ), ), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), '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"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("second"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), ), default_params=( cst.Param( cst.Name("third"), default=cst.Float("1.0"), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("fourth"), default=cst.Float("1.5"), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), ), star_arg=cst.Param( cst.Name("params"), star="*", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), kwonly_params=( cst.Param( cst.Name("bar"), default=cst.SimpleString('"one"'), equal=cst.AssignEqual(), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("baz"), star="", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Param( cst.Name("biz"), default=cst.SimpleString('"two"'), equal=cst.AssignEqual(), star="", ), ), ), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), '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"), star="**")), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), "lambda **kwparams: 5", ), # Test star_arg and kwarg ( cst.Lambda( cst.Parameters( star_arg=cst.Param( cst.Name("params"), star="*", comma=cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), star_kwarg=cst.Param(cst.Name("kwparams"), star="**"), ), cst.Integer("5"), whitespace_after_lambda=cst.SimpleWhitespace(" "), ), "lambda *params, **kwparams: 5", ), # Inner whitespace ( cst.Lambda( lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), params=cst.Parameters(), colon=cst.Colon( whitespace_before=cst.SimpleWhitespace(" "), whitespace_after=cst.SimpleWhitespace(" "), ), body=cst.Integer("5"), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), ), "( lambda : 5 )", ), )) def test_valid(self, node: cst.CSTNode, code: str, position: Optional[CodeRange] = None) -> None: self.validate_node(node, code, parse_expression, position)
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 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 ImportCreateTest(CSTNodeTest): @data_provider( ( # Simple import statement { "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((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((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 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 DictTest(CSTNodeTest): @data_provider([ # zero-element dict { "node": cst.Dict([]), "code": "{}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 2)), }, # one-element dict, sentinel comma value { "node": cst.Dict([cst.DictElement(cst.Name("k"), cst.Name("v"))]), "code": "{k: v}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 6)), }, { "node": cst.Dict([cst.StarredDictElement(cst.Name("expanded"))]), "code": "{**expanded}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 12)), }, # two-element dict, sentinel comma value { "node": cst.Dict([ cst.DictElement(cst.Name("k1"), cst.Name("v1")), cst.DictElement(cst.Name("k2"), cst.Name("v2")), ]), "code": "{k1: v1, k2: v2}", "parser": None, "expected_position": CodeRange((1, 0), (1, 16)), }, # custom whitespace between brackets { "node": cst.Dict( [cst.DictElement(cst.Name("k"), cst.Name("v"))], lbrace=cst.LeftCurlyBrace( whitespace_after=cst.SimpleWhitespace("\t")), rbrace=cst.RightCurlyBrace( whitespace_before=cst.SimpleWhitespace("\t\t")), ), "code": "{\tk: v\t\t}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 9)), }, # with parenthesis { "node": cst.Dict( [cst.DictElement(cst.Name("k"), cst.Name("v"))], lpar=[cst.LeftParen()], rpar=[cst.RightParen()], ), "code": "({k: v})", "parser": parse_expression, "expected_position": CodeRange((1, 1), (1, 7)), }, # starred element { "node": cst.Dict([ cst.StarredDictElement(cst.Name("one")), cst.StarredDictElement(cst.Name("two")), ]), "code": "{**one, **two}", "parser": None, "expected_position": CodeRange((1, 0), (1, 14)), }, # custom comma on DictElement { "node": cst.Dict([ cst.DictElement(cst.Name("k"), cst.Name("v"), comma=cst.Comma()) ]), "code": "{k: v,}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 7)), }, # custom comma on StarredDictElement { "node": cst.Dict([ cst.StarredDictElement(cst.Name("expanded"), comma=cst.Comma()) ]), "code": "{**expanded,}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 13)), }, # custom whitespace on DictElement { "node": cst.Dict([ cst.DictElement( cst.Name("k"), cst.Name("v"), whitespace_before_colon=cst.SimpleWhitespace("\t"), whitespace_after_colon=cst.SimpleWhitespace("\t\t"), ) ]), "code": "{k\t:\t\tv}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 8)), }, # custom whitespace on StarredDictElement { "node": cst.Dict([ cst.DictElement(cst.Name("k"), cst.Name("v"), comma=cst.Comma()), cst.StarredDictElement( cst.Name("expanded"), whitespace_before_value=cst.SimpleWhitespace(" "), ), ]), "code": "{k: v,** expanded}", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 19)), }, # missing spaces around dict is always okay { "node": cst.GeneratorExp( cst.Name("a"), cst.CompFor( cst.Name("b"), cst.Dict([cst.DictElement(cst.Name("k"), cst.Name("v"))]), ifs=[ cst.CompIf( cst.Name("c"), whitespace_before=cst.SimpleWhitespace(""), ) ], whitespace_after_in=cst.SimpleWhitespace(""), ), ), "parser": parse_expression, "code": "(a for b in{k: v}if c)", }, ]) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider([ # unbalanced Dict { "get_node": lambda: cst.Dict([], lpar=[cst.LeftParen()]), "expected_re": "left paren without right paren", } ]) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs) @data_provider(( { "code": "{**{}}", "parser": parse_expression_as(python_version="3.5"), "expect_success": True, }, { "code": "{**{}}", "parser": parse_expression_as(python_version="3.3"), "expect_success": False, }, )) def test_versions(self, **kwargs: Any) -> None: self.assert_parses(**kwargs)
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 CallTest(CSTNodeTest): @data_provider(( # Simple call { "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((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((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 ImportFromParseTest(CSTNodeTest): @data_provider( ( # Simple from import statement { "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"), comma=cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), ), 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(whitespace_after=cst.SimpleWhitespace(" ")), ), cst.ImportAlias(cst.Name("baz"), comma=cst.Comma()), ), ), "code": "from foo import bar, baz,", }, # Star import statement { "node": cst.ImportFrom(module=cst.Name("foo"), names=cst.ImportStar()), "code": "from foo import *", }, # 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)", }, # 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 )", }, ) ) def test_valid(self, **kwargs: Any) -> None: self.validate_node( parser=lambda code: ensure_type( parse_statement(code), cst.SimpleStatementLine ).body[0], **kwargs, )
class ImportFromCreateTest(CSTNodeTest): @data_provider( ( # Simple from import statement { "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((1, 0), (1, 23)), }, # Star import statement { "node": cst.ImportFrom(module=cst.Name("foo"), names=cst.ImportStar()), "code": "from foo import *", "expected_position": CodeRange((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((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((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 ImportParseTest(CSTNodeTest): @data_provider( ( # Simple import statement { "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")), comma=cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), ), cst.ImportAlias( cst.Attribute(cst.Name("foo"), cst.Name("baz")) ), ) ), "code": "import foo.bar, foo.baz", }, # 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")), comma=cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), ), 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")), comma=cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), ), cst.ImportAlias( cst.Attribute(cst.Name("insta"), cst.Name("gram")), comma=cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), ), cst.ImportAlias( cst.Attribute(cst.Name("foo"), cst.Name("baz")), comma=cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), ), 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", }, ) ) def test_valid(self, **kwargs: Any) -> None: self.validate_node( parser=lambda code: ensure_type( parse_statement(code), cst.SimpleStatementLine ).body[0], **kwargs, )
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 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 ListTest(CSTNodeTest): # A lot of Element/StarredElement tests are provided by the tests for Tuple, so we # we don't need to duplicate them here. @data_provider([ # one-element list, sentinel comma value { "node": cst.Set([cst.Element(cst.Name("single_element"))]), "code": "{single_element}", "parser": parse_expression, }, # custom whitespace between brackets { "node": cst.Set( [cst.Element(cst.Name("single_element"))], lbrace=cst.LeftCurlyBrace( whitespace_after=cst.SimpleWhitespace("\t")), rbrace=cst.RightCurlyBrace( whitespace_before=cst.SimpleWhitespace(" ")), ), "code": "{\tsingle_element }", "parser": parse_expression, }, # two-element list, sentinel comma value { "node": cst.Set( [cst.Element(cst.Name("one")), cst.Element(cst.Name("two"))]), "code": "{one, two}", "parser": None, }, # with parenthesis { "node": cst.Set( [cst.Element(cst.Name("one"))], lpar=[cst.LeftParen()], rpar=[cst.RightParen()], ), "code": "({one})", "parser": None, }, # starred element { "node": cst.Set([ cst.StarredElement(cst.Name("one")), cst.StarredElement(cst.Name("two")), ]), "code": "{*one, *two}", "parser": None, }, # missing spaces around set, always okay { "node": cst.GeneratorExp( cst.Name("elt"), cst.CompFor( target=cst.Name("elt"), iter=cst.Set([ cst.Element( cst.Name("one"), cst.Comma( whitespace_after=cst.SimpleWhitespace(" ")), ), cst.Element(cst.Name("two")), ]), ifs=[ cst.CompIf( cst.Name("test"), whitespace_before=cst.SimpleWhitespace(""), ) ], whitespace_after_in=cst.SimpleWhitespace(""), ), ), "code": "(elt for elt in{one, two}if test)", "parser": parse_expression, }, ]) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider(( ( lambda: cst.Set( [cst.Element(cst.Name("mismatched"))], lpar=[cst.LeftParen(), cst.LeftParen()], rpar=[cst.RightParen()], ), "unbalanced parens", ), (lambda: cst.Set([]), "at least one element"), )) def test_invalid(self, get_node: Callable[[], cst.CSTNode], expected_re: str) -> None: self.assert_invalid(get_node, expected_re) @data_provider(( { "code": "{*x, 2}", "parser": parse_expression_as(python_version="3.5"), "expect_success": True, }, { "code": "{*x, 2}", "parser": parse_expression_as(python_version="3.3"), "expect_success": False, }, )) def test_versions(self, **kwargs: Any) -> None: if is_native() and not kwargs.get("expect_success", True): self.skipTest("parse errors are disabled for native parser") self.assert_parses(**kwargs)
class AtomTest(CSTNodeTest): @data_provider(( # Simple identifier { "node": cst.Name("test"), "code": "test", "parser": parse_expression, "expected_position": None, }, # Parenthesized identifier { "node": cst.Name("test", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), "code": "(test)", "parser": parse_expression, "expected_position": CodeRange((1, 1), (1, 5)), }, # Decimal integers { "node": cst.Integer("12345"), "code": "12345", "parser": parse_expression, "expected_position": None, }, { "node": cst.Integer("0000"), "code": "0000", "parser": parse_expression, "expected_position": None, }, { "node": cst.Integer("1_234_567"), "code": "1_234_567", "parser": parse_expression, "expected_position": None, }, { "node": cst.Integer("0_000"), "code": "0_000", "parser": parse_expression, "expected_position": None, }, # Binary integers { "node": cst.Integer("0b0000"), "code": "0b0000", "parser": parse_expression, "expected_position": None, }, { "node": cst.Integer("0B1011_0100"), "code": "0B1011_0100", "parser": parse_expression, "expected_position": None, }, # Octal integers { "node": cst.Integer("0o12345"), "code": "0o12345", "parser": parse_expression, "expected_position": None, }, { "node": cst.Integer("0O12_345"), "code": "0O12_345", "parser": parse_expression, "expected_position": None, }, # Hex numbers { "node": cst.Integer("0x123abc"), "code": "0x123abc", "parser": parse_expression, "expected_position": None, }, { "node": cst.Integer("0X12_3ABC"), "code": "0X12_3ABC", "parser": parse_expression, "expected_position": None, }, # Parenthesized integers { "node": cst.Integer("123", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), "code": "(123)", "parser": parse_expression, "expected_position": CodeRange((1, 1), (1, 4)), }, # Non-exponent floats { "node": cst.Float("12345."), "code": "12345.", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float("00.00"), "code": "00.00", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float("12.21"), "code": "12.21", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float(".321"), "code": ".321", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float("1_234_567."), "code": "1_234_567.", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float("0.000_000"), "code": "0.000_000", "parser": parse_expression, "expected_position": None, }, # Exponent floats { "node": cst.Float("12345.e10"), "code": "12345.e10", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float("00.00e10"), "code": "00.00e10", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float("12.21e10"), "code": "12.21e10", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float(".321e10"), "code": ".321e10", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float("1_234_567.e10"), "code": "1_234_567.e10", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float("0.000_000e10"), "code": "0.000_000e10", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float("1e+10"), "code": "1e+10", "parser": parse_expression, "expected_position": None, }, { "node": cst.Float("1e-10"), "code": "1e-10", "parser": parse_expression, "expected_position": None, }, # Parenthesized floats { "node": cst.Float("123.4", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), "code": "(123.4)", "parser": parse_expression, "expected_position": CodeRange((1, 1), (1, 6)), }, # Imaginary numbers { "node": cst.Imaginary("12345j"), "code": "12345j", "parser": parse_expression, "expected_position": None, }, { "node": cst.Imaginary("1_234_567J"), "code": "1_234_567J", "parser": parse_expression, "expected_position": None, }, { "node": cst.Imaginary("12345.e10j"), "code": "12345.e10j", "parser": parse_expression, "expected_position": None, }, { "node": cst.Imaginary(".321J"), "code": ".321J", "parser": parse_expression, "expected_position": None, }, # Parenthesized imaginary { "node": cst.Imaginary("123.4j", lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), "code": "(123.4j)", "parser": parse_expression, "expected_position": CodeRange((1, 1), (1, 7)), }, # Simple elipses { "node": cst.Ellipsis(), "code": "...", "parser": parse_expression, "expected_position": None, }, # Parenthesized elipses { "node": cst.Ellipsis(lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), "code": "(...)", "parser": parse_expression, "expected_position": CodeRange((1, 1), (1, 4)), }, # Simple strings { "node": cst.SimpleString('""'), "code": '""', "parser": parse_expression, "expected_position": None, }, { "node": cst.SimpleString("''"), "code": "''", "parser": parse_expression, "expected_position": None, }, { "node": cst.SimpleString('"test"'), "code": '"test"', "parser": parse_expression, "expected_position": None, }, { "node": cst.SimpleString('b"test"'), "code": 'b"test"', "parser": parse_expression, "expected_position": None, }, { "node": cst.SimpleString('r"test"'), "code": 'r"test"', "parser": parse_expression, "expected_position": None, }, { "node": cst.SimpleString('"""test"""'), "code": '"""test"""', "parser": parse_expression, "expected_position": None, }, # Validate parens { "node": cst.SimpleString('"test"', lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), "code": '("test")', "parser": parse_expression, "expected_position": None, }, { "node": cst.SimpleString('rb"test"', lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), "code": '(rb"test")', "parser": parse_expression, "expected_position": CodeRange((1, 1), (1, 9)), }, # Test that _safe_to_use_with_word_operator allows no space around quotes { "node": cst.Comparison( cst.SimpleString('"a"'), [ cst.ComparisonTarget( cst.In( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), cst.SimpleString('"abc"'), ) ], ), "code": '"a"in"abc"', "parser": parse_expression, }, { "node": cst.Comparison( cst.SimpleString('"a"'), [ cst.ComparisonTarget( cst.In( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), cst.ConcatenatedString(cst.SimpleString('"a"'), cst.SimpleString('"bc"')), ) ], ), "code": '"a"in"a""bc"', "parser": parse_expression, }, # Parenthesis make no spaces around a prefix okay { "node": cst.Comparison( cst.SimpleString('b"a"'), [ cst.ComparisonTarget( cst.In( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), cst.SimpleString( 'b"abc"', lpar=[cst.LeftParen()], rpar=[cst.RightParen()], ), ) ], ), "code": 'b"a"in(b"abc")', "parser": parse_expression, }, { "node": cst.Comparison( cst.SimpleString('b"a"'), [ cst.ComparisonTarget( cst.In( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), cst.ConcatenatedString( cst.SimpleString('b"a"'), cst.SimpleString('b"bc"'), lpar=[cst.LeftParen()], rpar=[cst.RightParen()], ), ) ], ), "code": 'b"a"in(b"a"b"bc")', "parser": parse_expression, }, # Empty formatted strings { "node": cst.FormattedString(start='f"', parts=(), end='"'), "code": 'f""', "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString(start="f'", parts=(), end="'"), "code": "f''", "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString(start='f"""', parts=(), end='"""'), "code": 'f""""""', "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString(start="f'''", parts=(), end="'''"), "code": "f''''''", "parser": parse_expression, "expected_position": None, }, # Non-empty formatted strings { "node": cst.FormattedString(parts=(cst.FormattedStringText("foo"), )), "code": 'f"foo"', "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString( parts=(cst.FormattedStringExpression(cst.Name("foo")), )), "code": 'f"{foo}"', "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString(parts=( cst.FormattedStringText("foo "), cst.FormattedStringExpression(cst.Name("bar")), cst.FormattedStringText(" baz"), )), "code": 'f"foo {bar} baz"', "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString(parts=( cst.FormattedStringText("foo "), cst.FormattedStringExpression(cst.Call(cst.Name("bar"))), cst.FormattedStringText(" baz"), )), "code": 'f"foo {bar()} baz"', "parser": parse_expression, "expected_position": None, }, # Formatted strings with conversions and format specifiers { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Name("foo"), conversion="s"), )), "code": 'f"{foo!s}"', "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Name("foo"), format_spec=()), )), "code": 'f"{foo:}"', "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Name("today"), format_spec=(cst.FormattedStringText("%B %d, %Y"), ), ), )), "code": 'f"{today:%B %d, %Y}"', "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Name("foo"), format_spec=(cst.FormattedStringExpression(cst.Name("bar")), ), ), )), "code": 'f"{foo:{bar}}"', "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Name("foo"), format_spec=( cst.FormattedStringExpression(cst.Name("bar")), cst.FormattedStringText("."), cst.FormattedStringExpression(cst.Name("baz")), ), ), )), "code": 'f"{foo:{bar}.{baz}}"', "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Name("foo"), conversion="s", format_spec=(cst.FormattedStringExpression(cst.Name("bar")), ), ), )), "code": 'f"{foo!s:{bar}}"', "parser": parse_expression, "expected_position": None, }, # Test equality expression added in 3.8. { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Name("foo"), equal=cst.AssignEqual( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), ), ), ), "code": 'f"{foo=}"', "parser": _parse_expression_force_38, "expected_position": None, }, { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Name("foo"), equal=cst.AssignEqual( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), conversion="s", ), ), ), "code": 'f"{foo=!s}"', "parser": _parse_expression_force_38, "expected_position": None, }, { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Name("foo"), equal=cst.AssignEqual( whitespace_before=cst.SimpleWhitespace(""), whitespace_after=cst.SimpleWhitespace(""), ), conversion="s", format_spec=(cst.FormattedStringExpression(cst.Name("bar")), ), ), ), ), "code": 'f"{foo=!s:{bar}}"', "parser": _parse_expression_force_38, "expected_position": None, }, # Test that equality support doesn't break existing support { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Comparison( left=cst.Name(value="a", ), comparisons=[ cst.ComparisonTarget( operator=cst.Equal(), comparator=cst.Name(value="b", ), ), ], ), ), ), ), "code": 'f"{a == b}"', "parser": _parse_expression_force_38, "expected_position": None, }, { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Comparison( left=cst.Name(value="a", ), comparisons=[ cst.ComparisonTarget( operator=cst.NotEqual(), comparator=cst.Name(value="b", ), ), ], ), ), ), ), "code": 'f"{a != b}"', "parser": _parse_expression_force_38, "expected_position": None, }, { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.NamedExpr( target=cst.Name(value="a", ), value=cst.Integer(value="5", ), lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), ), ), ), ), ), "code": 'f"{(a := 5)}"', "parser": _parse_expression_force_38, "expected_position": None, }, { "node": cst.FormattedString(parts=(cst.FormattedStringExpression( cst.Yield( value=cst.Integer("1"), whitespace_after_yield=cst.SimpleWhitespace(" "), ), ), ), ), "code": 'f"{yield 1}"', "parser": _parse_expression_force_38, "expected_position": None, }, { "node": cst.FormattedString(parts=( cst.FormattedStringText("\\N{X Y}"), cst.FormattedStringExpression(cst.Name(value="Z"), ), ), ), "code": 'f"\\N{X Y}{Z}"', "parser": parse_expression, "expected_position": None, }, { "node": cst.FormattedString( parts=( cst.FormattedStringText("\\"), cst.FormattedStringExpression(cst.Name(value="a"), ), ), start='fr"', ), "code": 'fr"\\{a}"', "parser": parse_expression, "expected_position": None, }, # Validate parens { "node": cst.FormattedString( start='f"', parts=(), end='"', lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), ), ), "code": '(f"")', "parser": parse_expression, "expected_position": CodeRange((1, 1), (1, 4)), }, # Generator expression (doesn't make sense, but legal syntax) { "node": cst.FormattedString( start='f"', parts=[ cst.FormattedStringExpression(expression=cst.GeneratorExp( elt=cst.Name(value="x", ), for_in=cst.CompFor( target=cst.Name(value="x", ), iter=cst.Name(value="y", ), ), lpar=[], rpar=[], ), ), ], end='"', ), "code": 'f"{x for x in y}"', "parser": parse_expression, "expected_position": None, }, # Unpacked tuple { "node": cst.FormattedString( parts=[ cst.FormattedStringExpression(expression=cst.Tuple( elements=[ cst.Element( value=cst.Name(value="a", ), comma=cst.Comma( whitespace_before=cst.SimpleWhitespace( value="", ), whitespace_after=cst.SimpleWhitespace( value=" ", ), ), ), cst.Element(value=cst.Name(value="b", ), ), ], lpar=[], rpar=[], ), ), ], start="f'", end="'", ), "code": "f'{a, b}'", "parser": parse_expression, "expected_position": None, }, # Conditional expression { "node": cst.FormattedString( parts=[ cst.FormattedStringExpression(expression=cst.IfExp( test=cst.Name(value="b", ), body=cst.Name(value="a", ), orelse=cst.Name(value="c", ), ), ), ], start="f'", end="'", ), "code": "f'{a if b else c}'", "parser": parse_expression, "expected_position": None, }, # Concatenated strings { "node": cst.ConcatenatedString(cst.SimpleString('"ab"'), cst.SimpleString('"c"')), "code": '"ab""c"', "parser": parse_expression, "expected_position": None, }, { "node": cst.ConcatenatedString( cst.SimpleString('"ab"'), cst.ConcatenatedString(cst.SimpleString('"c"'), cst.SimpleString('"d"')), ), "code": '"ab""c""d"', "parser": parse_expression, "expected_position": None, }, # mixed SimpleString and FormattedString { "node": cst.ConcatenatedString( cst.FormattedString([cst.FormattedStringText("ab")]), cst.SimpleString('"c"'), ), "code": 'f"ab""c"', "parser": parse_expression, "expected_position": None, }, { "node": cst.ConcatenatedString( cst.SimpleString('"ab"'), cst.FormattedString([cst.FormattedStringText("c")]), ), "code": '"ab"f"c"', "parser": parse_expression, "expected_position": None, }, # Concatenated parenthesized strings { "node": cst.ConcatenatedString( lpar=(cst.LeftParen(), ), left=cst.SimpleString('"ab"'), right=cst.SimpleString('"c"'), rpar=(cst.RightParen(), ), ), "code": '("ab""c")', "parser": parse_expression, "expected_position": None, }, # Validate spacing { "node": cst.ConcatenatedString( lpar=(cst.LeftParen( whitespace_after=cst.SimpleWhitespace(" ")), ), left=cst.SimpleString('"ab"'), whitespace_between=cst.SimpleWhitespace(" "), right=cst.SimpleString('"c"'), rpar=(cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), ), "code": '( "ab" "c" )', "parser": parse_expression, "expected_position": CodeRange((1, 2), (1, 10)), }, )) def test_valid(self, **kwargs: Any) -> None: # We don't have sentinel nodes for atoms, so we know that 100% of atoms # can be parsed identically to their creation. self.validate_node(**kwargs) @data_provider(({ "node": cst.FormattedStringExpression( cst.Name("today"), format_spec=(cst.FormattedStringText("%B %d, %Y"), ), ), "code": "{today:%B %d, %Y}", "parser": None, "expected_position": CodeRange((1, 0), (1, 17)), }, )) def test_valid_no_parse(self, **kwargs: Any) -> None: # Test some nodes that aren't valid source code by themselves self.validate_node(**kwargs) @data_provider(( # Expression wrapping parenthesis rules { "get_node": (lambda: cst.Name("foo", lpar=(cst.LeftParen(), ))), "expected_re": "left paren without right paren", }, { "get_node": lambda: cst.Name("foo", rpar=(cst.RightParen(), )), "expected_re": "right paren without left paren", }, { "get_node": lambda: cst.Ellipsis(lpar=(cst.LeftParen(), )), "expected_re": "left paren without right paren", }, { "get_node": lambda: cst.Ellipsis(rpar=(cst.RightParen(), )), "expected_re": "right paren without left paren", }, { "get_node": lambda: cst.Integer("5", lpar=(cst.LeftParen(), )), "expected_re": "left paren without right paren", }, { "get_node": lambda: cst.Integer("5", rpar=(cst.RightParen(), )), "expected_re": "right paren without left paren", }, { "get_node": lambda: cst.Float("5.5", lpar=(cst.LeftParen(), )), "expected_re": "left paren without right paren", }, { "get_node": lambda: cst.Float("5.5", rpar=(cst.RightParen(), )), "expected_re": "right paren without left paren", }, { "get_node": (lambda: cst.Imaginary("5j", lpar=(cst.LeftParen(), ))), "expected_re": "left paren without right paren", }, { "get_node": (lambda: cst.Imaginary("5j", rpar=(cst.RightParen(), ))), "expected_re": "right paren without left paren", }, { "get_node": (lambda: cst.Integer("5", lpar=(cst.LeftParen(), ))), "expected_re": "left paren without right paren", }, { "get_node": (lambda: cst.Integer("5", rpar=(cst.RightParen(), ))), "expected_re": "right paren without left paren", }, { "get_node": (lambda: cst.SimpleString("'foo'", lpar=(cst.LeftParen(), ))), "expected_re": "left paren without right paren", }, { "get_node": (lambda: cst.SimpleString("'foo'", rpar=(cst.RightParen(), ))), "expected_re": "right paren without left paren", }, { "get_node": (lambda: cst.FormattedString(parts=(), lpar=(cst.LeftParen(), ))), "expected_re": "left paren without right paren", }, { "get_node": (lambda: cst.FormattedString(parts=(), rpar=(cst.RightParen(), ))), "expected_re": "right paren without left paren", }, { "get_node": (lambda: cst.ConcatenatedString( cst.SimpleString("'foo'"), cst.SimpleString("'foo'"), lpar=(cst.LeftParen(), ), )), "expected_re": "left paren without right paren", }, { "get_node": (lambda: cst.ConcatenatedString( cst.SimpleString("'foo'"), cst.SimpleString("'foo'"), rpar=(cst.RightParen(), ), )), "expected_re": "right paren without left paren", }, # Node-specific rules { "get_node": (lambda: cst.Name("")), "expected_re": "empty name identifier", }, { "get_node": (lambda: cst.Name(r"\/")), "expected_re": "not a valid identifier", }, { "get_node": (lambda: cst.Integer("")), "expected_re": "not a valid integer", }, { "get_node": (lambda: cst.Integer("012345")), "expected_re": "not a valid integer", }, { "get_node": (lambda: cst.Integer("012345")), "expected_re": "not a valid integer", }, { "get_node": (lambda: cst.Integer("_12345")), "expected_re": "not a valid integer", }, { "get_node": (lambda: cst.Integer("0b2")), "expected_re": "not a valid integer", }, { "get_node": (lambda: cst.Integer("0o8")), "expected_re": "not a valid integer", }, { "get_node": (lambda: cst.Integer("0xg")), "expected_re": "not a valid integer", }, { "get_node": (lambda: cst.Integer("123.45")), "expected_re": "not a valid integer", }, { "get_node": (lambda: cst.Integer("12345j")), "expected_re": "not a valid integer", }, { "get_node": (lambda: cst.Float("12.3.45")), "expected_re": "not a valid float", }, { "get_node": (lambda: cst.Float("12")), "expected_re": "not a valid float" }, { "get_node": (lambda: cst.Float("12.3j")), "expected_re": "not a valid float", }, { "get_node": (lambda: cst.Imaginary("_12345j")), "expected_re": "not a valid imaginary", }, { "get_node": (lambda: cst.Imaginary("0b0j")), "expected_re": "not a valid imaginary", }, { "get_node": (lambda: cst.Imaginary("0o0j")), "expected_re": "not a valid imaginary", }, { "get_node": (lambda: cst.Imaginary("0x0j")), "expected_re": "not a valid imaginary", }, { "get_node": (lambda: cst.SimpleString('wee""')), "expected_re": "Invalid string prefix", }, { "get_node": (lambda: cst.SimpleString("'")), "expected_re": "must have enclosing quotes", }, { "get_node": (lambda: cst.SimpleString('"')), "expected_re": "must have enclosing quotes", }, { "get_node": (lambda: cst.SimpleString("\"'")), "expected_re": "must have matching enclosing quotes", }, { "get_node": (lambda: cst.SimpleString("")), "expected_re": "must have enclosing quotes", }, { "get_node": (lambda: cst.SimpleString("'bla")), "expected_re": "must have matching enclosing quotes", }, { "get_node": (lambda: cst.SimpleString("f''")), "expected_re": "Invalid string prefix", }, { "get_node": (lambda: cst.SimpleString("'''bla''")), "expected_re": "must have matching enclosing quotes", }, { "get_node": (lambda: cst.SimpleString("'''bla\"\"\"")), "expected_re": "must have matching enclosing quotes", }, { "get_node": (lambda: cst.FormattedString(start="'", parts=(), end="'")), "expected_re": "Invalid f-string prefix", }, { "get_node": (lambda: cst.FormattedString(start="f'", parts=(), end='"')), "expected_re": "must have matching enclosing quotes", }, { "get_node": (lambda: cst.ConcatenatedString( cst.SimpleString('"ab"', lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), cst.SimpleString('"c"'), )), "expected_re": "Cannot concatenate parenthesized", }, { "get_node": (lambda: cst.ConcatenatedString( cst.SimpleString('"ab"'), cst.SimpleString('"c"', lpar=(cst.LeftParen(), ), rpar=(cst.RightParen(), )), )), "expected_re": "Cannot concatenate parenthesized", }, { "get_node": (lambda: cst.ConcatenatedString( cst.SimpleString('"ab"'), cst.SimpleString('b"c"'))), "expected_re": "Cannot concatenate string and bytes", }, # This isn't valid code: `"a" inb"abc"` { "get_node": (lambda: cst.Comparison( cst.SimpleString('"a"'), [ cst.ComparisonTarget( cst.In(whitespace_after=cst.SimpleWhitespace("")), cst.SimpleString('b"abc"'), ) ], )), "expected_re": "Must have at least one space around comparison operator.", }, # Also not valid: `"a" in b"a"b"bc"` { "get_node": (lambda: cst.Comparison( cst.SimpleString('"a"'), [ cst.ComparisonTarget( cst.In(whitespace_after=cst.SimpleWhitespace("")), cst.ConcatenatedString(cst.SimpleString('b"a"'), cst.SimpleString('b"bc"')), ) ], )), "expected_re": "Must have at least one space around comparison operator.", }, )) def test_invalid(self, **kwargs: Any) -> None: self.assert_invalid(**kwargs) @data_provider(( { "code": "u'x'", "parser": parse_expression_as(python_version="3.3"), "expect_success": True, }, { "code": "u'x'", "parser": parse_expression_as(python_version="3.1"), "expect_success": False, }, )) def test_versions(self, **kwargs: Any) -> None: if is_native() and not kwargs.get("expect_success", True): self.skipTest("parse errors are disabled for native parser") self.assert_parses(**kwargs)
class TupleTest(CSTNodeTest): @data_provider( [ # zero-element tuple {"node": cst.Tuple([]), "code": "()", "parser": parse_expression}, # one-element tuple, sentinel comma value { "node": cst.Tuple([cst.Element(cst.Name("single_element"))]), "code": "(single_element,)", "parser": None, }, { "node": cst.Tuple([cst.StarredElement(cst.Name("single_element"))]), "code": "(*single_element,)", "parser": None, }, # two-element tuple, sentinel comma value { "node": cst.Tuple( [cst.Element(cst.Name("one")), cst.Element(cst.Name("two"))] ), "code": "(one, two)", "parser": None, }, # remove parenthesis { "node": cst.Tuple( [cst.Element(cst.Name("one")), cst.Element(cst.Name("two"))], lpar=[], rpar=[], ), "code": "one, two", "parser": None, }, # add extra parenthesis { "node": cst.Tuple( [cst.Element(cst.Name("one")), cst.Element(cst.Name("two"))], lpar=[cst.LeftParen(), cst.LeftParen()], rpar=[cst.RightParen(), cst.RightParen()], ), "code": "((one, two))", "parser": None, }, # starred element { "node": cst.Tuple( [ cst.StarredElement(cst.Name("one")), cst.StarredElement(cst.Name("two")), ] ), "code": "(*one, *two)", "parser": None, }, # custom comma on Element { "node": cst.Tuple( [ cst.Element(cst.Name("one"), comma=cst.Comma()), cst.Element(cst.Name("two"), comma=cst.Comma()), ] ), "code": "(one,two,)", "parser": parse_expression, }, # custom comma on StarredElement { "node": cst.Tuple( [ cst.StarredElement(cst.Name("one"), comma=cst.Comma()), cst.StarredElement(cst.Name("two"), comma=cst.Comma()), ] ), "code": "(*one,*two,)", "parser": parse_expression, "expected_position": CodeRange((1, 1), (1, 11)), }, # custom parenthesis on StarredElement { "node": cst.Tuple( [ cst.StarredElement( cst.Name("abc"), lpar=[cst.LeftParen()], rpar=[cst.RightParen()], comma=cst.Comma(), ) ] ), "code": "((*abc),)", "parser": parse_expression, "expected_position": CodeRange((1, 1), (1, 8)), }, # custom whitespace on StarredElement { "node": cst.Tuple( [ cst.Element(cst.Name("one"), comma=cst.Comma()), cst.StarredElement( cst.Name("two"), whitespace_before_value=cst.SimpleWhitespace(" "), lpar=[cst.LeftParen()], rpar=[cst.RightParen()], ), ], lpar=[], rpar=[], # rpar can't own the trailing whitespace if it's not there ), "code": "one,(* two)", "parser": parse_expression, "expected_position": CodeRange((1, 0), (1, 12)), }, # missing spaces around tuple, okay with parenthesis { "node": cst.For( target=cst.Tuple( [ cst.Element(cst.Name("k"), comma=cst.Comma()), cst.Element(cst.Name("v")), ] ), iter=cst.Name("abc"), body=cst.SimpleStatementSuite([cst.Pass()]), whitespace_after_for=cst.SimpleWhitespace(""), whitespace_before_in=cst.SimpleWhitespace(""), ), "code": "for(k,v)in abc: pass\n", "parser": parse_statement, }, # no spaces around tuple, but using values that are parenthesized { "node": cst.For( target=cst.Tuple( [ cst.Element( cst.Name( "k", lpar=[cst.LeftParen()], rpar=[cst.RightParen()] ), comma=cst.Comma(), ), cst.Element( cst.Name( "v", lpar=[cst.LeftParen()], rpar=[cst.RightParen()] ) ), ], lpar=[], rpar=[], ), iter=cst.Name("abc"), body=cst.SimpleStatementSuite([cst.Pass()]), whitespace_after_for=cst.SimpleWhitespace(""), whitespace_before_in=cst.SimpleWhitespace(""), ), "code": "for(k),(v)in abc: pass\n", "parser": parse_statement, }, # starred elements are safe to use without a space before them { "node": cst.For( target=cst.Tuple( [cst.StarredElement(cst.Name("foo"), comma=cst.Comma())], lpar=[], rpar=[], ), iter=cst.Name("bar"), body=cst.SimpleStatementSuite([cst.Pass()]), whitespace_after_for=cst.SimpleWhitespace(""), ), "code": "for*foo, in bar: pass\n", "parser": parse_statement, }, # a trailing comma doesn't mess up TrailingWhitespace { "node": cst.SimpleStatementLine( [ cst.Expr( cst.Tuple( [ cst.Element(cst.Name("one"), comma=cst.Comma()), cst.Element(cst.Name("two"), comma=cst.Comma()), ], lpar=[], rpar=[], ) ) ], trailing_whitespace=cst.TrailingWhitespace( whitespace=cst.SimpleWhitespace(" "), comment=cst.Comment("# comment"), ), ), "code": "one,two, # comment\n", "parser": parse_statement, }, ] ) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) @data_provider( ( ( lambda: cst.Tuple([], lpar=[], rpar=[]), "A zero-length tuple must be wrapped in parentheses.", ), ( lambda: cst.Tuple( [cst.Element(cst.Name("mismatched"))], lpar=[cst.LeftParen(), cst.LeftParen()], rpar=[cst.RightParen()], ), "unbalanced parens", ), ( lambda: cst.For( target=cst.Tuple([cst.Element(cst.Name("el"))], lpar=[], rpar=[]), iter=cst.Name("it"), body=cst.SimpleStatementSuite([cst.Pass()]), whitespace_after_for=cst.SimpleWhitespace(""), ), "Must have at least one space after 'for' keyword.", ), ( lambda: cst.For( target=cst.Tuple([cst.Element(cst.Name("el"))], lpar=[], rpar=[]), iter=cst.Name("it"), body=cst.SimpleStatementSuite([cst.Pass()]), whitespace_before_in=cst.SimpleWhitespace(""), ), "Must have at least one space before 'in' keyword.", ), # an additional check for StarredElement, since it's a separate codepath ( lambda: cst.For( target=cst.Tuple( [cst.StarredElement(cst.Name("el"))], lpar=[], rpar=[] ), iter=cst.Name("it"), body=cst.SimpleStatementSuite([cst.Pass()]), whitespace_before_in=cst.SimpleWhitespace(""), ), "Must have at least one space before 'in' keyword.", ), ) ) def test_invalid( self, get_node: Callable[[], cst.CSTNode], expected_re: str ) -> None: self.assert_invalid(get_node, expected_re)
class WithTest(CSTNodeTest): maxDiff: int = 2000 @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)), }, # 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)), }, # Weird spacing rules, that parse differently depending on whether # we are using a grammar that included parenthesized with statements. { "node": cst.With( (cst.WithItem( cst.Call( cst.Name("context_mgr"), lpar=() if is_native() else (cst.LeftParen(), ), rpar=() if is_native() else (cst.RightParen(), ), )), ), cst.SimpleStatementSuite((cst.Pass(), )), lpar=(cst.LeftParen() if is_native() else MaybeSentinel.DEFAULT), rpar=(cst.RightParen() if is_native() else MaybeSentinel.DEFAULT), whitespace_after_with=cst.SimpleWhitespace(""), ), "code": "with(context_mgr()): pass\n", "parser": parse_statement, "expected_position": CodeRange((1, 0), (1, 25)), }, # Multi-line parenthesized with. { "node": cst.With( ( cst.WithItem( cst.Call(cst.Name("foo")), comma=cst. Comma(whitespace_after=cst.ParenthesizedWhitespace( first_line=cst.TrailingWhitespace( whitespace=cst.SimpleWhitespace(value="", ), comment=None, newline=cst.Newline(value=None, ), ), empty_lines=[], indent=True, last_line=cst.SimpleWhitespace(value=" ", ), )), ), cst.WithItem(cst.Call(cst.Name("bar")), comma=cst.Comma()), ), cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(whitespace_after=cst.SimpleWhitespace(" ")), rpar=cst.RightParen( whitespace_before=cst.SimpleWhitespace(" ")), ), "code": ("with ( foo(),\n" " bar(), ): pass\n"), # noqa "parser": parse_statement if is_native() else None, "expected_position": CodeRange((1, 0), (2, 21)), }, )) 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 an unparenthesized 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", }, { "get_node": lambda: cst.With( (cst.WithItem(cst.Call(cst.Name("context_mgr"))), ), cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_with=cst.SimpleWhitespace(""), lpar=cst.LeftParen(), ), "expected_re": "Do not mix concrete LeftParen/RightParen with " + "MaybeSentinel", }, { "get_node": lambda: cst.With( (cst.WithItem(cst.Call(cst.Name("context_mgr"))), ), cst.SimpleStatementSuite((cst.Pass(), )), whitespace_after_with=cst.SimpleWhitespace(""), rpar=cst.RightParen(), ), "expected_re": "Do not mix concrete LeftParen/RightParen with " + "MaybeSentinel", }, )) 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: if is_native() and not kwargs.get("expect_success", True): self.skipTest("parse errors are disabled for native parser") self.assert_parses(**kwargs) def test_adding_parens(self) -> None: node = cst.With( ( cst.WithItem( cst.Call(cst.Name("foo")), comma=cst.Comma( whitespace_after=cst.ParenthesizedWhitespace(), ), ), cst.WithItem(cst.Call(cst.Name("bar")), comma=cst.Comma()), ), cst.SimpleStatementSuite((cst.Pass(), )), lpar=cst.LeftParen(whitespace_after=cst.SimpleWhitespace(" ")), rpar=cst.RightParen(whitespace_before=cst.SimpleWhitespace(" ")), ) module = cst.Module([]) self.assertEqual( module.code_for_node(node), ("with ( foo(),\n" "bar(), ): pass\n") # noqa )
def import_to_node_multi(imp: SortableImport, module: cst.Module) -> cst.BaseStatement: body: List[cst.BaseSmallStatement] = [] names: List[cst.ImportAlias] = [] prev: Optional[cst.ImportAlias] = None following: List[str] = [] lpar_lines: List[cst.EmptyLine] = [] lpar_inline: cst.TrailingWhitespace = cst.TrailingWhitespace() item_count = len(imp.items) for idx, item in enumerate(imp.items): name = name_to_node(item.name) asname = cst.AsName( name=cst.Name(item.asname)) if item.asname else None # Leading comments actually have to be trailing comments on the previous node. # That means putting them on the lpar node for the first item if item.comments.before: lines = [ cst.EmptyLine( indent=True, comment=cst.Comment(c), whitespace=cst.SimpleWhitespace(module.default_indent), ) for c in item.comments.before ] if prev is None: lpar_lines.extend(lines) else: prev.comma.whitespace_after.empty_lines.extend( lines) # type: ignore # all items except the last needs whitespace to indent the *next* line/item indent = idx != (len(imp.items) - 1) first_line = cst.TrailingWhitespace() inline = COMMENT_INDENT.join(item.comments.inline) if inline: first_line = cst.TrailingWhitespace( whitespace=cst.SimpleWhitespace(COMMENT_INDENT), comment=cst.Comment(inline), ) if idx == item_count - 1: following = item.comments.following + imp.comments.final else: following = item.comments.following after = cst.ParenthesizedWhitespace( indent=True, first_line=first_line, empty_lines=[ cst.EmptyLine( indent=True, comment=cst.Comment(c), whitespace=cst.SimpleWhitespace(module.default_indent), ) for c in following ], last_line=cst.SimpleWhitespace( module.default_indent if indent else ""), ) node = cst.ImportAlias( name=name, asname=asname, comma=cst.Comma(whitespace_after=after), ) names.append(node) prev = node # from foo import ( # bar # ) if imp.stem: stem, ndots = split_relative(imp.stem) if not stem: module_name = None else: module_name = name_to_node(stem) relative = (cst.Dot(), ) * ndots # inline comment following lparen if imp.comments.first_inline: inline = COMMENT_INDENT.join(imp.comments.first_inline) lpar_inline = cst.TrailingWhitespace( whitespace=cst.SimpleWhitespace(COMMENT_INDENT), comment=cst.Comment(inline), ) body = [ cst.ImportFrom( module=module_name, names=names, relative=relative, lpar=cst.LeftParen( whitespace_after=cst.ParenthesizedWhitespace( indent=True, first_line=lpar_inline, empty_lines=lpar_lines, last_line=cst.SimpleWhitespace(module.default_indent), ), ), rpar=cst.RightParen(), ) ] # import foo else: raise ValueError("can't render basic imports on multiple lines") # comment lines above import leading_lines = [ cst.EmptyLine(indent=True, comment=cst.Comment(line)) if line.startswith("#") else cst.EmptyLine(indent=False) for line in imp.comments.before ] # inline comments following import/rparen if imp.comments.last_inline: inline = COMMENT_INDENT.join(imp.comments.last_inline) trailing = cst.TrailingWhitespace( whitespace=cst.SimpleWhitespace(COMMENT_INDENT), comment=cst.Comment(inline)) else: trailing = cst.TrailingWhitespace() return cst.SimpleStatementLine( body=body, leading_lines=leading_lines, trailing_whitespace=trailing, )
class MatchTest(CSTNodeTest): @data_provider( ( # Values and singletons { "node": cst.Match( subject=cst.Name("x"), cases=[ cst.MatchCase( pattern=cst.MatchSingleton(cst.Name("None")), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( pattern=cst.MatchValue(cst.SimpleString('"foo"')), body=cst.SimpleStatementSuite((cst.Pass(),)), ), ], ), "code": "match x:\n" + " case None: pass\n" + ' case "foo": pass\n', "parser": parser, }, # Parenthesized value { "node": cst.Match( subject=cst.Name( value="x", ), cases=[ cst.MatchCase( pattern=cst.MatchAs( pattern=cst.MatchValue( value=cst.Integer( value="1", lpar=[ cst.LeftParen(), ], rpar=[ cst.RightParen(), ], ), ), name=cst.Name( value="z", ), whitespace_before_as=cst.SimpleWhitespace(" "), whitespace_after_as=cst.SimpleWhitespace(" "), ), body=cst.SimpleStatementSuite([cst.Pass()]), ), ], ), "code": "match x:\n case (1) as z: pass\n", "parser": parser, }, # List patterns { "node": cst.Match( subject=cst.Name("x"), cases=[ cst.MatchCase( # empty list pattern=cst.MatchList( [], lbracket=cst.LeftSquareBracket(), rbracket=cst.RightSquareBracket(), ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # single element list pattern=cst.MatchList( [ cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")) ) ], lbracket=cst.LeftSquareBracket(), rbracket=cst.RightSquareBracket(), ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # single element list with trailing comma pattern=cst.MatchList( [ cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")), cst.Comma(), ) ], lbracket=cst.LeftSquareBracket(), rbracket=cst.RightSquareBracket(), ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), ], ), "code": ( "match x:\n" + " case []: pass\n" + " case [None]: pass\n" + " case [None,]: pass\n" ), "parser": parser, }, # Tuple patterns { "node": cst.Match( subject=cst.Name("x"), cases=[ cst.MatchCase( # empty tuple pattern=cst.MatchTuple( [], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # two element tuple pattern=cst.MatchTuple( [ cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")), cst.Comma(), ), cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")), ), ], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # single element tuple with trailing comma pattern=cst.MatchTuple( [ cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")), cst.Comma(), ) ], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # two element tuple pattern=cst.MatchTuple( [ cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")), cst.Comma(), ), cst.MatchStar( comma=cst.Comma(), ), cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")), ), ], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), ], ), "code": ( "match x:\n" + " case (): pass\n" + " case (None,None): pass\n" + " case (None,): pass\n" + " case (None,*_,None): pass\n" ), "parser": parser, }, # Mapping patterns { "node": cst.Match( subject=cst.Name("x"), cases=[ cst.MatchCase( # empty mapping pattern=cst.MatchMapping( [], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # two element mapping pattern=cst.MatchMapping( [ cst.MatchMappingElement( key=cst.SimpleString('"a"'), pattern=cst.MatchSingleton(cst.Name("None")), comma=cst.Comma(), ), cst.MatchMappingElement( key=cst.SimpleString('"b"'), pattern=cst.MatchSingleton(cst.Name("None")), ), ], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # single element mapping with trailing comma pattern=cst.MatchMapping( [ cst.MatchMappingElement( key=cst.SimpleString('"a"'), pattern=cst.MatchSingleton(cst.Name("None")), comma=cst.Comma(), ) ], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # rest pattern=cst.MatchMapping( rest=cst.Name("rest"), ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), ], ), "code": ( "match x:\n" + " case {}: pass\n" + ' case {"a": None,"b": None}: pass\n' + ' case {"a": None,}: pass\n' + " case {**rest}: pass\n" ), "parser": parser, }, # Class patterns { "node": cst.Match( subject=cst.Name("x"), cases=[ cst.MatchCase( # empty class pattern=cst.MatchClass( cls=cst.Attribute(cst.Name("a"), cst.Name("b")), ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # single pattern class pattern=cst.MatchClass( cls=cst.Attribute(cst.Name("a"), cst.Name("b")), patterns=[ cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")) ) ], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # single pattern class with trailing comma pattern=cst.MatchClass( cls=cst.Attribute(cst.Name("a"), cst.Name("b")), patterns=[ cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")), comma=cst.Comma(), ) ], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # single keyword pattern class pattern=cst.MatchClass( cls=cst.Attribute(cst.Name("a"), cst.Name("b")), kwds=[ cst.MatchKeywordElement( key=cst.Name("foo"), pattern=cst.MatchSingleton(cst.Name("None")), ) ], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # single keyword pattern class with trailing comma pattern=cst.MatchClass( cls=cst.Attribute(cst.Name("a"), cst.Name("b")), kwds=[ cst.MatchKeywordElement( key=cst.Name("foo"), pattern=cst.MatchSingleton(cst.Name("None")), comma=cst.Comma(), ) ], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( # now all at once pattern=cst.MatchClass( cls=cst.Attribute(cst.Name("a"), cst.Name("b")), patterns=[ cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")), cst.Comma(), ), cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")), cst.Comma(), ), ], kwds=[ cst.MatchKeywordElement( key=cst.Name("foo"), pattern=cst.MatchSingleton(cst.Name("None")), comma=cst.Comma(), ), cst.MatchKeywordElement( key=cst.Name("bar"), pattern=cst.MatchSingleton(cst.Name("None")), comma=cst.Comma(), ), ], ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), ], ), "code": ( "match x:\n" + " case a.b(): pass\n" + " case a.b(None): pass\n" + " case a.b(None,): pass\n" + " case a.b(foo=None): pass\n" + " case a.b(foo=None,): pass\n" + " case a.b(None,None,foo=None,bar=None,): pass\n" ), "parser": parser, }, # as pattern { "node": cst.Match( subject=cst.Name("x"), cases=[ cst.MatchCase( pattern=cst.MatchAs(), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( pattern=cst.MatchAs(name=cst.Name("foo")), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( pattern=cst.MatchAs( pattern=cst.MatchSingleton(cst.Name("None")), name=cst.Name("bar"), whitespace_before_as=cst.SimpleWhitespace(" "), whitespace_after_as=cst.SimpleWhitespace(" "), ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), ], ), "code": "match x:\n" + " case _: pass\n" + " case foo: pass\n" + " case None as bar: pass\n", "parser": parser, }, # or pattern { "node": cst.Match( subject=cst.Name("x"), cases=[ cst.MatchCase( pattern=cst.MatchOr( [ cst.MatchOrElement( cst.MatchSingleton(cst.Name("None")), cst.BitOr(), ), cst.MatchOrElement( cst.MatchSingleton(cst.Name("False")), cst.BitOr(), ), cst.MatchOrElement( cst.MatchSingleton(cst.Name("True")) ), ] ), body=cst.SimpleStatementSuite((cst.Pass(),)), ) ], ), "code": "match x:\n case None | False | True: pass\n", "parser": parser, }, { # exercise sentinels "node": cst.Match( subject=cst.Name("x"), cases=[ cst.MatchCase( pattern=cst.MatchList( [cst.MatchStar(), cst.MatchStar()], lbracket=None, rbracket=None, ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( pattern=cst.MatchTuple( [ cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")) ) ] ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( pattern=cst.MatchAs( pattern=cst.MatchTuple( [ cst.MatchSequenceElement( cst.MatchSingleton(cst.Name("None")) ) ] ), name=cst.Name("bar"), ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), cst.MatchCase( pattern=cst.MatchOr( [ cst.MatchOrElement( cst.MatchSingleton(cst.Name("None")), ), cst.MatchOrElement( cst.MatchSingleton(cst.Name("False")), ), cst.MatchOrElement( cst.MatchSingleton(cst.Name("True")) ), ] ), body=cst.SimpleStatementSuite((cst.Pass(),)), ), ], ), "code": "match x:\n" + " case *_, *_: pass\n" + " case (None,): pass\n" + " case (None,) as bar: pass\n" + " case None | False | True: pass\n", "parser": None, }, # Match without whitespace between keyword and the expr { "node": cst.Match( subject=cst.Name( "x", lpar=[cst.LeftParen()], rpar=[cst.RightParen()] ), cases=[ cst.MatchCase( pattern=cst.MatchSingleton( cst.Name( "None", lpar=[cst.LeftParen()], rpar=[cst.RightParen()], ) ), body=cst.SimpleStatementSuite((cst.Pass(),)), whitespace_after_case=cst.SimpleWhitespace( value="", ), ), ], whitespace_after_match=cst.SimpleWhitespace( value="", ), ), "code": "match(x):\n case(None): pass\n", "parser": parser, }, ) ) def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs)