mirror of
https://github.com/Instagram/LibCST.git
synced 2025-12-23 10:35:53 +00:00
Add node class and parser implementation for List
This is based heavily on the implementation of Tuple, and was pretty straightforward as a result.
This commit is contained in:
parent
edd8496f19
commit
a293787f8c
5 changed files with 184 additions and 15 deletions
|
|
@ -46,6 +46,7 @@ from libcst.nodes._expression import (
|
|||
Lambda,
|
||||
LeftParen,
|
||||
LeftSquareBracket,
|
||||
List,
|
||||
Name,
|
||||
Number,
|
||||
Param,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from tokenize import (
|
|||
Imagnumber as IMAGNUMBER_RE,
|
||||
Intnumber as INTNUMBER_RE,
|
||||
)
|
||||
from typing import Callable, Generator, List, Optional, Sequence, Union
|
||||
from typing import Callable, Generator, Optional, Sequence, Union
|
||||
|
||||
from typing_extensions import Literal
|
||||
|
||||
|
|
@ -1505,7 +1505,7 @@ class Lambda(BaseExpression):
|
|||
# Validate parents
|
||||
super(Lambda, self)._validate()
|
||||
# Sum up all parameters
|
||||
all_params: List[Param] = [
|
||||
all_params = [
|
||||
*self.params.params,
|
||||
*self.params.default_params,
|
||||
*self.params.kwonly_params,
|
||||
|
|
@ -2214,3 +2214,45 @@ class Tuple(BaseAtom, BaseAssignTargetExpression, BaseDelTargetExpression):
|
|||
default_comma=(idx < len(elements) - 1),
|
||||
default_comma_whitespace=True,
|
||||
)
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class List(BaseAtom, BaseAssignTargetExpression, BaseDelTargetExpression):
|
||||
elements: Sequence[Union[Element, StarredElement]]
|
||||
|
||||
# Open bracket surrounding the list
|
||||
lbracket: LeftSquareBracket = LeftSquareBracket()
|
||||
|
||||
# Close bracket surrounding the list
|
||||
rbracket: RightSquareBracket = RightSquareBracket()
|
||||
|
||||
# Sequence of open parenthesis for precedence dictation.
|
||||
lpar: Sequence[LeftParen] = ()
|
||||
|
||||
# Sequence of close parenthesis for precedence dictation.
|
||||
rpar: Sequence[RightParen] = ()
|
||||
|
||||
def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool:
|
||||
return True
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitor) -> "List":
|
||||
return List(
|
||||
lpar=visit_sequence("lpar", self.lpar, visitor),
|
||||
lbracket=visit_required("lbracket", self.lbracket, visitor),
|
||||
elements=visit_sequence("elements", self.elements, visitor),
|
||||
rbracket=visit_required("rbracket", self.rbracket, visitor),
|
||||
rpar=visit_sequence("rpar", self.rpar, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(self, state: CodegenState) -> None:
|
||||
with self._parenthesize(state):
|
||||
self.lbracket._codegen(state)
|
||||
elements = self.elements
|
||||
for idx, el in enumerate(elements):
|
||||
el._codegen(
|
||||
state,
|
||||
default_comma=(idx < len(elements) - 1),
|
||||
default_comma_whitespace=True,
|
||||
)
|
||||
self.rbracket._codegen(state)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ from libcst.nodes._expression import (
|
|||
Name,
|
||||
Parameters,
|
||||
RightParen,
|
||||
Tuple,
|
||||
)
|
||||
from libcst.nodes._internal import (
|
||||
CodegenState,
|
||||
|
|
@ -1603,9 +1602,7 @@ class For(BaseCompoundStatement):
|
|||
"""
|
||||
|
||||
# The target of the iterator in the for statement.
|
||||
target: Union[
|
||||
Name, Tuple
|
||||
] # TODO: Should be a Union[Name, Tuple, List] once we support this.
|
||||
target: BaseAssignTargetExpression
|
||||
|
||||
# The iterable expression we will loop over.
|
||||
iter: BaseExpression
|
||||
|
|
|
|||
109
libcst/nodes/tests/test_list.py
Normal file
109
libcst/nodes/tests/test_list.py
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
# pyre-strict
|
||||
from typing import Any, Callable
|
||||
|
||||
import libcst.nodes as cst
|
||||
from libcst.nodes.tests.base import CSTNodeTest
|
||||
from libcst.parser import parse_expression, parse_statement
|
||||
from libcst.testing.utils import data_provider
|
||||
|
||||
|
||||
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,
|
||||
},
|
||||
# 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,
|
||||
},
|
||||
# starred element
|
||||
{
|
||||
"node": cst.List(
|
||||
[
|
||||
cst.StarredElement(cst.Name("one")),
|
||||
cst.StarredElement(cst.Name("two")),
|
||||
]
|
||||
),
|
||||
"code": "[*one, *two]",
|
||||
"parser": None,
|
||||
},
|
||||
# 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)
|
||||
|
|
@ -9,7 +9,7 @@ from tokenize import (
|
|||
Imagnumber as IMAGNUMBER_RE,
|
||||
Intnumber as INTNUMBER_RE,
|
||||
)
|
||||
from typing import Any, Dict, List, Sequence, Type
|
||||
from typing import Any, Dict, List, Sequence, Type, Union
|
||||
|
||||
import libcst.nodes as cst
|
||||
from libcst._maybe_sentinel import MaybeSentinel
|
||||
|
|
@ -736,7 +736,28 @@ def convert_atom_basic(config: ParserConfig, children: Sequence[Any]) -> Any:
|
|||
|
||||
@with_production("atom_squarebrackets", "'[' [testlist_comp_list] ']'")
|
||||
def convert_atom_squarebrackets(config: ParserConfig, children: Sequence[Any]) -> Any:
|
||||
return make_dummy_node(config, children)
|
||||
lbracket_tok, *body, rbracket_tok = children
|
||||
lbracket = cst.LeftSquareBracket(
|
||||
whitespace_after=parse_parenthesizable_whitespace(
|
||||
config, lbracket_tok.whitespace_after
|
||||
)
|
||||
)
|
||||
|
||||
rbracket = cst.RightSquareBracket(
|
||||
whitespace_before=parse_parenthesizable_whitespace(
|
||||
config, rbracket_tok.whitespace_before
|
||||
)
|
||||
)
|
||||
|
||||
if len(body) == 0:
|
||||
list_node = cst.List((), lbracket=lbracket, rbracket=rbracket)
|
||||
else: # len(body) == 1
|
||||
if isinstance(body[0].value, cst.List): # TODO: Remove this conditional
|
||||
list_node = body[0].value.with_changes(lbracket=lbracket, rbracket=rbracket)
|
||||
else: # TODO: Remove this branch; this handles for DummyNodes
|
||||
list_node = cst.DummyNode([lbracket, body[0].value, rbracket])
|
||||
|
||||
return WithLeadingWhitespace(list_node, lbracket_tok.whitespace_before)
|
||||
|
||||
|
||||
@with_production("atom_curlybrackets", "'{' [dictorsetmaker] '}'")
|
||||
|
|
@ -921,10 +942,7 @@ def convert_fstring_format_spec(config: ParserConfig, children: Sequence[Any]) -
|
|||
)
|
||||
def convert_testlist_comp_tuple(config: ParserConfig, children: Sequence[Any]) -> Any:
|
||||
return _convert_testlist_comp(
|
||||
config,
|
||||
children,
|
||||
single_child_is_sequence=False, # should be true for testlist_comp_list
|
||||
sequence_type=cst.Tuple,
|
||||
config, children, single_child_is_sequence=False, sequence_type=cst.Tuple
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -933,14 +951,16 @@ def convert_testlist_comp_tuple(config: ParserConfig, children: Sequence[Any]) -
|
|||
"(test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )",
|
||||
)
|
||||
def convert_testlist_comp_list(config: ParserConfig, children: Sequence[Any]) -> Any:
|
||||
return make_dummy_node(config, children)
|
||||
return _convert_testlist_comp(
|
||||
config, children, single_child_is_sequence=True, sequence_type=cst.List
|
||||
)
|
||||
|
||||
|
||||
def _convert_testlist_comp(
|
||||
config: ParserConfig,
|
||||
children: Sequence[Any],
|
||||
single_child_is_sequence: bool,
|
||||
sequence_type: Type[cst.Tuple],
|
||||
sequence_type: Union[Type[cst.Tuple], Type[cst.List]],
|
||||
) -> Any:
|
||||
# This is either a single-element list, or the second token is a comma, so we're not
|
||||
# in a generator.
|
||||
|
|
@ -968,7 +988,7 @@ def _convert_sequencelike(
|
|||
config: ParserConfig,
|
||||
children: Sequence[Any],
|
||||
single_child_is_sequence: bool,
|
||||
sequence_type: Type[cst.Tuple], # TODO: Type[Union[Tuple, List, Set]]
|
||||
sequence_type: Union[Type[cst.Tuple], Type[cst.List]], # TODO: support cst.Set
|
||||
) -> Any:
|
||||
if not single_child_is_sequence and len(children) == 1:
|
||||
return children[0]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue