Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/libcst/_nodes/statement.py: 45%
1520 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 07:09 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 07:09 +0000
1# Copyright (c) Meta Platforms, Inc. and affiliates.
2#
3# This source code is licensed under the MIT license found in the
4# LICENSE file in the root directory of this source tree.
6import inspect
7import re
8from abc import ABC, abstractmethod
9from dataclasses import dataclass, field
10from typing import Optional, Pattern, Sequence, Union
12from libcst._add_slots import add_slots
13from libcst._maybe_sentinel import MaybeSentinel
14from libcst._nodes.base import CSTNode, CSTValidationError
15from libcst._nodes.expression import (
16 _BaseParenthesizedNode,
17 Annotation,
18 Arg,
19 Asynchronous,
20 Attribute,
21 BaseAssignTargetExpression,
22 BaseDelTargetExpression,
23 BaseExpression,
24 ConcatenatedString,
25 ExpressionPosition,
26 From,
27 LeftCurlyBrace,
28 LeftParen,
29 LeftSquareBracket,
30 List,
31 Name,
32 Parameters,
33 RightCurlyBrace,
34 RightParen,
35 RightSquareBracket,
36 SimpleString,
37 Tuple,
38)
39from libcst._nodes.internal import (
40 CodegenState,
41 visit_body_sequence,
42 visit_optional,
43 visit_required,
44 visit_sentinel,
45 visit_sequence,
46)
47from libcst._nodes.op import (
48 AssignEqual,
49 BaseAugOp,
50 BitOr,
51 Comma,
52 Dot,
53 ImportStar,
54 Semicolon,
55)
56from libcst._nodes.whitespace import (
57 BaseParenthesizableWhitespace,
58 EmptyLine,
59 ParenthesizedWhitespace,
60 SimpleWhitespace,
61 TrailingWhitespace,
62)
63from libcst._visitors import CSTVisitorT
65_INDENT_WHITESPACE_RE: Pattern[str] = re.compile(r"[ \f\t]+", re.UNICODE)
68class BaseSuite(CSTNode, ABC):
69 """
70 A dummy base-class for both :class:`SimpleStatementSuite` and :class:`IndentedBlock`.
71 This exists to simplify type definitions and isinstance checks.
73 A suite is a group of statements controlled by a clause. A suite can be one or
74 more semicolon-separated simple statements on the same line as the header,
75 following the header’s colon, or it can be one or more indented statements on
76 subsequent lines.
78 -- https://docs.python.org/3/reference/compound_stmts.html
79 """
81 __slots__ = ()
83 body: Union[Sequence["BaseStatement"], Sequence["BaseSmallStatement"]]
86class BaseStatement(CSTNode, ABC):
87 """
88 A class that exists to allow for typing to specify that any statement is allowed
89 in a particular location.
90 """
92 __slots__ = ()
95class BaseSmallStatement(CSTNode, ABC):
96 """
97 Encapsulates a small statement, like ``del`` or ``pass``, and optionally adds a
98 trailing semicolon. A small statement is always contained inside a
99 :class:`SimpleStatementLine` or :class:`SimpleStatementSuite`. This exists to
100 simplify type definitions and isinstance checks.
101 """
103 __slots__ = ()
105 #: An optional semicolon that appears after a small statement. This is optional
106 #: for the last small statement in a :class:`SimpleStatementLine` or
107 #: :class:`SimpleStatementSuite`, but all other small statements inside a simple
108 #: statement must contain a semicolon to disambiguate multiple small statements
109 #: on the same line.
110 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
112 @abstractmethod
113 def _codegen_impl(
114 self, state: CodegenState, default_semicolon: bool = False
115 ) -> None:
116 ...
119@add_slots
120@dataclass(frozen=True)
121class Del(BaseSmallStatement):
122 """
123 Represents a ``del`` statement. ``del`` is always followed by a target.
124 """
126 #: The target expression will be deleted. This can be a name, a tuple,
127 #: an item of a list, an item of a dictionary, or an attribute.
128 target: BaseDelTargetExpression
130 #: The whitespace after the ``del`` keyword.
131 whitespace_after_del: SimpleWhitespace = SimpleWhitespace.field(" ")
133 #: Optional semicolon when this is used in a statement line. This semicolon
134 #: owns the whitespace on both sides of it when it is used.
135 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
137 def _validate(self) -> None:
138 if (
139 self.whitespace_after_del.empty
140 and not self.target._safe_to_use_with_word_operator(
141 ExpressionPosition.RIGHT
142 )
143 ):
144 raise CSTValidationError("Must have at least one space after 'del'.")
146 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Del":
147 return Del(
148 target=visit_required(self, "target", self.target, visitor),
149 whitespace_after_del=visit_required(
150 self, "whitespace_after_del", self.whitespace_after_del, visitor
151 ),
152 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
153 )
155 def _codegen_impl(
156 self, state: CodegenState, default_semicolon: bool = False
157 ) -> None:
158 with state.record_syntactic_position(self):
159 state.add_token("del")
160 self.whitespace_after_del._codegen(state)
161 self.target._codegen(state)
163 semicolon = self.semicolon
164 if isinstance(semicolon, MaybeSentinel):
165 if default_semicolon:
166 state.add_token("; ")
167 elif isinstance(semicolon, Semicolon):
168 semicolon._codegen(state)
171@add_slots
172@dataclass(frozen=True)
173class Pass(BaseSmallStatement):
174 """
175 Represents a ``pass`` statement.
176 """
178 #: Optional semicolon when this is used in a statement line. This semicolon
179 #: owns the whitespace on both sides of it when it is used.
180 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
182 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Pass":
183 return Pass(
184 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor)
185 )
187 def _codegen_impl(
188 self, state: CodegenState, default_semicolon: bool = False
189 ) -> None:
190 with state.record_syntactic_position(self):
191 state.add_token("pass")
193 semicolon = self.semicolon
194 if isinstance(semicolon, MaybeSentinel):
195 if default_semicolon:
196 state.add_token("; ")
197 elif isinstance(semicolon, Semicolon):
198 semicolon._codegen(state)
201@add_slots
202@dataclass(frozen=True)
203class Break(BaseSmallStatement):
204 """
205 Represents a ``break`` statement, which is used to break out of a :class:`For`
206 or :class:`While` loop early.
207 """
209 #: Optional semicolon when this is used in a statement line. This semicolon
210 #: owns the whitespace on both sides of it when it is used.
211 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
213 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Break":
214 return Break(
215 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor)
216 )
218 def _codegen_impl(
219 self, state: CodegenState, default_semicolon: bool = False
220 ) -> None:
221 with state.record_syntactic_position(self):
222 state.add_token("break")
224 semicolon = self.semicolon
225 if isinstance(semicolon, MaybeSentinel):
226 if default_semicolon:
227 state.add_token("; ")
228 elif isinstance(semicolon, Semicolon):
229 semicolon._codegen(state)
232@add_slots
233@dataclass(frozen=True)
234class Continue(BaseSmallStatement):
235 """
236 Represents a ``continue`` statement, which is used to skip to the next iteration
237 in a :class:`For` or :class:`While` loop.
238 """
240 #: Optional semicolon when this is used in a statement line. This semicolon
241 #: owns the whitespace on both sides of it when it is used.
242 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
244 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Continue":
245 return Continue(
246 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor)
247 )
249 def _codegen_impl(
250 self, state: CodegenState, default_semicolon: bool = False
251 ) -> None:
252 with state.record_syntactic_position(self):
253 state.add_token("continue")
255 semicolon = self.semicolon
256 if isinstance(semicolon, MaybeSentinel):
257 if default_semicolon:
258 state.add_token("; ")
259 elif isinstance(semicolon, Semicolon):
260 semicolon._codegen(state)
263@add_slots
264@dataclass(frozen=True)
265class Return(BaseSmallStatement):
266 """
267 Represents a ``return`` or a ``return x`` statement.
268 """
270 #: The optional expression that will be evaluated and returned.
271 value: Optional[BaseExpression] = None
273 #: Optional whitespace after the ``return`` keyword before the optional
274 #: value expression.
275 whitespace_after_return: Union[
276 SimpleWhitespace, MaybeSentinel
277 ] = MaybeSentinel.DEFAULT
279 #: Optional semicolon when this is used in a statement line. This semicolon
280 #: owns the whitespace on both sides of it when it is used.
281 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
283 def _validate(self) -> None:
284 value = self.value
285 if value is not None:
286 whitespace_after_return = self.whitespace_after_return
287 has_no_gap = (
288 not isinstance(whitespace_after_return, MaybeSentinel)
289 and whitespace_after_return.empty
290 )
291 if has_no_gap and not value._safe_to_use_with_word_operator(
292 ExpressionPosition.RIGHT
293 ):
294 raise CSTValidationError("Must have at least one space after 'return'.")
296 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Return":
297 return Return(
298 whitespace_after_return=visit_sentinel(
299 self, "whitespace_after_return", self.whitespace_after_return, visitor
300 ),
301 value=visit_optional(self, "value", self.value, visitor),
302 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
303 )
305 def _codegen_impl(
306 self, state: CodegenState, default_semicolon: bool = False
307 ) -> None:
308 with state.record_syntactic_position(self):
309 state.add_token("return")
310 whitespace_after_return = self.whitespace_after_return
311 value = self.value
312 if isinstance(whitespace_after_return, MaybeSentinel):
313 if value is not None:
314 state.add_token(" ")
315 else:
316 whitespace_after_return._codegen(state)
317 if value is not None:
318 value._codegen(state)
320 semicolon = self.semicolon
321 if isinstance(semicolon, MaybeSentinel):
322 if default_semicolon:
323 state.add_token("; ")
324 elif isinstance(semicolon, Semicolon):
325 semicolon._codegen(state)
328@add_slots
329@dataclass(frozen=True)
330class Expr(BaseSmallStatement):
331 """
332 An expression used as a statement, where the result is unused and unassigned.
333 The most common place you will find this is in function calls where the return
334 value is unneeded.
335 """
337 #: The expression itself. Python will evaluate the expression but not assign
338 #: the result anywhere.
339 value: BaseExpression
341 #: Optional semicolon when this is used in a statement line. This semicolon
342 #: owns the whitespace on both sides of it when it is used.
343 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
345 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Expr":
346 return Expr(
347 value=visit_required(self, "value", self.value, visitor),
348 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
349 )
351 def _codegen_impl(
352 self, state: CodegenState, default_semicolon: bool = False
353 ) -> None:
354 with state.record_syntactic_position(self):
355 self.value._codegen(state)
357 semicolon = self.semicolon
358 if isinstance(semicolon, MaybeSentinel):
359 if default_semicolon:
360 state.add_token("; ")
361 elif isinstance(semicolon, Semicolon):
362 semicolon._codegen(state)
365class _BaseSimpleStatement(CSTNode, ABC):
366 """
367 A simple statement is a series of small statements joined together by semicolons.
369 simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
371 Whitespace between each small statement is owned by the small statements themselves.
372 It can be found on the required semicolon that will be attached to each non-terminal
373 small statement.
374 """
376 __slots__ = ()
378 #: Sequence of small statements. All but the last statement are required to have
379 #: a semicolon.
380 body: Sequence[BaseSmallStatement]
382 #: Any trailing comment and the final ``NEWLINE``, which is part of small statement's
383 #: grammar.
384 trailing_whitespace: TrailingWhitespace
386 def _validate(self) -> None:
387 body = self.body
388 for small_stmt in body[:-1]:
389 if small_stmt.semicolon is None:
390 raise CSTValidationError(
391 "All but the last SmallStatement in a SimpleStatementLine or "
392 + "SimpleStatementSuite must have a trailing semicolon. Otherwise, "
393 + "there's no way to syntatically disambiguate each SmallStatement "
394 + "on the same line."
395 )
397 def _codegen_impl(self, state: CodegenState) -> None:
398 body = self.body
399 if body:
400 laststmt = len(body) - 1
401 with state.record_syntactic_position(self, end_node=body[laststmt]):
402 for idx, stmt in enumerate(body):
403 stmt._codegen(state, default_semicolon=(idx != laststmt))
404 else:
405 # Empty simple statement blocks are not syntactically valid in Python
406 # unless they contain a 'pass' statement, so add one here.
407 with state.record_syntactic_position(self):
408 state.add_token("pass")
410 self.trailing_whitespace._codegen(state)
413@add_slots
414@dataclass(frozen=True)
415class SimpleStatementLine(_BaseSimpleStatement, BaseStatement):
416 """
417 A simple statement that's part of an IndentedBlock or Module. A simple statement is
418 a series of small statements joined together by semicolons.
420 This isn't differentiated from a :class:`SimpleStatementSuite` in the grammar, but
421 because a :class:`SimpleStatementLine` can own additional whitespace that a
422 :class:`SimpleStatementSuite` doesn't have, we're differentiating it in the CST.
423 """
425 #: Sequence of small statements. All but the last statement are required to have
426 #: a semicolon.
427 body: Sequence[BaseSmallStatement]
429 #: Sequence of empty lines appearing before this simple statement line.
430 leading_lines: Sequence[EmptyLine] = ()
432 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
433 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field()
435 def _visit_and_replace_children(
436 self, visitor: CSTVisitorT
437 ) -> "SimpleStatementLine":
438 return SimpleStatementLine(
439 leading_lines=visit_sequence(
440 self, "leading_lines", self.leading_lines, visitor
441 ),
442 body=visit_sequence(self, "body", self.body, visitor),
443 trailing_whitespace=visit_required(
444 self, "trailing_whitespace", self.trailing_whitespace, visitor
445 ),
446 )
448 def _is_removable(self) -> bool:
449 # If we have an empty body, we are removable since we don't represent
450 # anything concrete.
451 return not self.body
453 def _codegen_impl(self, state: CodegenState) -> None:
454 for ll in self.leading_lines:
455 ll._codegen(state)
456 state.add_indent_tokens()
457 _BaseSimpleStatement._codegen_impl(self, state)
460@add_slots
461@dataclass(frozen=True)
462class SimpleStatementSuite(_BaseSimpleStatement, BaseSuite):
463 """
464 A simple statement that's used as a suite. A simple statement is a series of small
465 statements joined together by semicolons. A suite is the thing that follows the
466 colon in a compound statement.
468 .. code-block::
470 if test:<leading_whitespace><body><trailing_whitespace>
472 This isn't differentiated from a :class:`SimpleStatementLine` in the grammar, but
473 because the two classes need to track different whitespace, we're differentiating
474 it in the CST.
475 """
477 #: Sequence of small statements. All but the last statement are required to have
478 #: a semicolon.
479 body: Sequence[BaseSmallStatement]
481 #: The whitespace between the colon in the parent statement and the body.
482 leading_whitespace: SimpleWhitespace = SimpleWhitespace.field(" ")
484 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
485 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field()
487 def _visit_and_replace_children(
488 self, visitor: CSTVisitorT
489 ) -> "SimpleStatementSuite":
490 return SimpleStatementSuite(
491 leading_whitespace=visit_required(
492 self, "leading_whitespace", self.leading_whitespace, visitor
493 ),
494 body=visit_sequence(self, "body", self.body, visitor),
495 trailing_whitespace=visit_required(
496 self, "trailing_whitespace", self.trailing_whitespace, visitor
497 ),
498 )
500 def _codegen_impl(self, state: CodegenState) -> None:
501 self.leading_whitespace._codegen(state)
502 _BaseSimpleStatement._codegen_impl(self, state)
505@add_slots
506@dataclass(frozen=True)
507class Else(CSTNode):
508 """
509 An ``else`` clause that appears optionally after an :class:`If`, :class:`While`,
510 :class:`Try`, or :class:`For` statement.
512 This node does not match ``elif`` clauses in :class:`If` statements. It also
513 does not match the required ``else`` clause in an :class:`IfExp` expression
514 (``a = if b else c``).
515 """
517 #: The body of else clause.
518 body: BaseSuite
520 #: Sequence of empty lines appearing before this compound statement line.
521 leading_lines: Sequence[EmptyLine] = ()
523 #: The whitespace appearing after the ``else`` keyword but before the colon.
524 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
526 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Else":
527 return Else(
528 leading_lines=visit_sequence(
529 self, "leading_lines", self.leading_lines, visitor
530 ),
531 whitespace_before_colon=visit_required(
532 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
533 ),
534 body=visit_required(self, "body", self.body, visitor),
535 )
537 def _codegen_impl(self, state: CodegenState) -> None:
538 for ll in self.leading_lines:
539 ll._codegen(state)
540 state.add_indent_tokens()
542 with state.record_syntactic_position(self, end_node=self.body):
543 state.add_token("else")
544 self.whitespace_before_colon._codegen(state)
545 state.add_token(":")
546 self.body._codegen(state)
549class BaseCompoundStatement(BaseStatement, ABC):
550 """
551 Encapsulates a compound statement, like ``if True: pass`` or ``while True: pass``.
552 This exists to simplify type definitions and isinstance checks.
554 Compound statements contain (groups of) other statements; they affect or control
555 the execution of those other statements in some way. In general, compound
556 statements span multiple lines, although in simple incarnations a whole compound
557 statement may be contained in one line.
559 -- https://docs.python.org/3/reference/compound_stmts.html
560 """
562 __slots__ = ()
564 #: The body of this compound statement.
565 body: BaseSuite
567 #: Any empty lines or comments appearing before this statement.
568 leading_lines: Sequence[EmptyLine]
571@add_slots
572@dataclass(frozen=True)
573class If(BaseCompoundStatement):
574 """
575 An ``if`` statement. ``test`` holds a single test expression.
577 ``elif`` clauses don’t have a special representation in the AST, but rather appear as
578 extra :class:`If` nodes within the ``orelse`` section of the previous one.
579 """
581 #: The expression that, when evaluated, should give us a truthy/falsey value.
582 test: BaseExpression # TODO: should be a test_nocond
584 #: The body of this compound statement.
585 body: BaseSuite
587 #: An optional ``elif`` or ``else`` clause. :class:`If` signifies an ``elif`` block.
588 #: :class:`Else` signifies an ``else`` block. ``None`` signifies no ``else`` or
589 #:``elif`` block.
590 orelse: Union["If", Else, None] = None
592 #: Sequence of empty lines appearing before this compound statement line.
593 leading_lines: Sequence[EmptyLine] = ()
595 #: The whitespace appearing after the ``if`` keyword but before the test expression.
596 whitespace_before_test: SimpleWhitespace = SimpleWhitespace.field(" ")
598 #: The whitespace appearing after the test expression but before the colon.
599 whitespace_after_test: SimpleWhitespace = SimpleWhitespace.field("")
601 # TODO: _validate
603 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "If":
604 return If(
605 leading_lines=visit_sequence(
606 self, "leading_lines", self.leading_lines, visitor
607 ),
608 whitespace_before_test=visit_required(
609 self, "whitespace_before_test", self.whitespace_before_test, visitor
610 ),
611 test=visit_required(self, "test", self.test, visitor),
612 whitespace_after_test=visit_required(
613 self, "whitespace_after_test", self.whitespace_after_test, visitor
614 ),
615 body=visit_required(self, "body", self.body, visitor),
616 orelse=visit_optional(self, "orelse", self.orelse, visitor),
617 )
619 def _codegen_impl(self, state: CodegenState, is_elif: bool = False) -> None:
620 for ll in self.leading_lines:
621 ll._codegen(state)
622 state.add_indent_tokens()
624 end_node = self.body if self.orelse is None else self.orelse
625 with state.record_syntactic_position(self, end_node=end_node):
626 state.add_token("elif" if is_elif else "if")
627 self.whitespace_before_test._codegen(state)
628 self.test._codegen(state)
629 self.whitespace_after_test._codegen(state)
630 state.add_token(":")
631 self.body._codegen(state)
632 orelse = self.orelse
633 if orelse is not None:
634 if isinstance(orelse, If): # special-case elif
635 orelse._codegen(state, is_elif=True)
636 else: # is an Else clause
637 orelse._codegen(state)
640@add_slots
641@dataclass(frozen=True)
642class IndentedBlock(BaseSuite):
643 """
644 Represents a block of statements beginning with an ``INDENT`` token and ending in a
645 ``DEDENT`` token. Used as the body of compound statements, such as an if statement's
646 body.
648 A common alternative to an :class:`IndentedBlock` is a :class:`SimpleStatementSuite`,
649 which can also be used as a :class:`BaseSuite`, meaning that it can be used as the
650 body of many compound statements.
652 An :class:`IndentedBlock` always occurs after a colon in a
653 :class:`BaseCompoundStatement`, so it owns the trailing whitespace for the compound
654 statement's clause.
656 .. code-block::
658 if test: # IndentedBlock's header
659 body
660 """
662 #: Sequence of statements belonging to this indented block.
663 body: Sequence[BaseStatement]
665 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
666 header: TrailingWhitespace = TrailingWhitespace.field()
668 #: A string represents a specific indentation. A ``None`` value uses the modules's
669 #: default indentation. This is included because indentation is allowed to be
670 #: inconsistent across a file, just not ambiguously.
671 indent: Optional[str] = None
673 #: Any trailing comments or lines after the dedent that are owned by this indented
674 #: block. Statements own preceeding and same-line trailing comments, but not
675 #: trailing lines, so it falls on :class:`IndentedBlock` to own it. In the case
676 #: that a statement follows an :class:`IndentedBlock`, that statement will own the
677 #: comments and lines that are at the same indent as the statement, and this
678 #: :class:`IndentedBlock` will own the comments and lines that are indented further.
679 footer: Sequence[EmptyLine] = ()
681 def _validate(self) -> None:
682 indent = self.indent
683 if indent is not None:
684 if len(indent) == 0:
685 raise CSTValidationError(
686 "An indented block must have a non-zero width indent."
687 )
688 if _INDENT_WHITESPACE_RE.fullmatch(indent) is None:
689 raise CSTValidationError(
690 "An indent must be composed of only whitespace characters."
691 )
693 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "IndentedBlock":
694 return IndentedBlock(
695 header=visit_required(self, "header", self.header, visitor),
696 indent=self.indent,
697 body=visit_body_sequence(self, "body", self.body, visitor),
698 footer=visit_sequence(self, "footer", self.footer, visitor),
699 )
701 def _codegen_impl(self, state: CodegenState) -> None:
702 self.header._codegen(state)
704 indent = self.indent
705 state.increase_indent(state.default_indent if indent is None else indent)
707 if self.body:
708 with state.record_syntactic_position(
709 self, start_node=self.body[0], end_node=self.body[-1]
710 ):
711 for stmt in self.body:
712 # IndentedBlock is responsible for adjusting the current indentation level,
713 # but its children are responsible for actually adding that indentation to
714 # the token list.
715 stmt._codegen(state)
716 else:
717 # Empty indented blocks are not syntactically valid in Python unless
718 # they contain a 'pass' statement, so add one here.
719 state.add_indent_tokens()
720 with state.record_syntactic_position(self):
721 state.add_token("pass")
722 state.add_token(state.default_newline)
724 for f in self.footer:
725 f._codegen(state)
727 state.decrease_indent()
730@add_slots
731@dataclass(frozen=True)
732class AsName(CSTNode):
733 """
734 An ``as name`` clause inside an :class:`ExceptHandler`, :class:`ImportAlias` or
735 :class:`WithItem` node.
736 """
738 #: Identifier that the parent node will be aliased to.
739 name: Union[Name, Tuple, List]
741 #: Whitespace between the parent node and the ``as`` keyword.
742 whitespace_before_as: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
744 #: Whitespace between the ``as`` keyword and the name.
745 whitespace_after_as: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
747 def _validate(self) -> None:
748 if self.whitespace_after_as.empty:
749 raise CSTValidationError(
750 "There must be at least one space between 'as' and name."
751 )
753 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AsName":
754 return AsName(
755 whitespace_before_as=visit_required(
756 self, "whitespace_before_as", self.whitespace_before_as, visitor
757 ),
758 name=visit_required(self, "name", self.name, visitor),
759 whitespace_after_as=visit_required(
760 self, "whitespace_after_as", self.whitespace_after_as, visitor
761 ),
762 )
764 def _codegen_impl(self, state: CodegenState) -> None:
765 self.whitespace_before_as._codegen(state)
766 state.add_token("as")
767 self.whitespace_after_as._codegen(state)
768 self.name._codegen(state)
771@add_slots
772@dataclass(frozen=True)
773class ExceptHandler(CSTNode):
774 """
775 An ``except`` clause that appears optionally after a :class:`Try` statement.
776 """
778 #: The body of the except.
779 body: BaseSuite
781 #: The type of exception this catches. Can be a tuple in some cases,
782 #: or ``None`` if the code is catching all exceptions.
783 type: Optional[BaseExpression] = None
785 #: The optional name that a caught exception is assigned to.
786 name: Optional[AsName] = None
788 #: Sequence of empty lines appearing before this compound statement line.
789 leading_lines: Sequence[EmptyLine] = ()
791 #: The whitespace between the ``except`` keyword and the type attribute.
792 whitespace_after_except: SimpleWhitespace = SimpleWhitespace.field(" ")
794 #: The whitespace after any type or name node (whichever comes last) and
795 #: the colon.
796 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
798 def _validate(self) -> None:
799 name = self.name
800 if self.type is None and name is not None:
801 raise CSTValidationError("Cannot have a name for an empty type.")
802 if name is not None and not isinstance(name.name, Name):
803 raise CSTValidationError(
804 "Must use a Name node for AsName name inside ExceptHandler."
805 )
806 type_ = self.type
807 if type_ is not None and self.whitespace_after_except.empty:
808 # Space is only required when the first char in `type` could start
809 # an identifier. In the most common cases, we want to allow
810 # grouping or tuple parens.
811 if isinstance(type_, Name) and not type_.lpar:
812 raise CSTValidationError(
813 "Must have at least one space after except when ExceptHandler has a type."
814 )
815 name = self.name
816 if (
817 type_ is not None
818 and name is not None
819 and name.whitespace_before_as.empty
820 and not type_._safe_to_use_with_word_operator(ExpressionPosition.LEFT)
821 ):
822 raise CSTValidationError(
823 "Must have at least one space before as keyword in an except handler."
824 )
826 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ExceptHandler":
827 return ExceptHandler(
828 leading_lines=visit_sequence(
829 self, "leading_lines", self.leading_lines, visitor
830 ),
831 whitespace_after_except=visit_required(
832 self, "whitespace_after_except", self.whitespace_after_except, visitor
833 ),
834 type=visit_optional(self, "type", self.type, visitor),
835 name=visit_optional(self, "name", self.name, visitor),
836 whitespace_before_colon=visit_required(
837 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
838 ),
839 body=visit_required(self, "body", self.body, visitor),
840 )
842 def _codegen_impl(self, state: CodegenState) -> None:
843 for ll in self.leading_lines:
844 ll._codegen(state)
845 state.add_indent_tokens()
847 with state.record_syntactic_position(self, end_node=self.body):
848 state.add_token("except")
849 self.whitespace_after_except._codegen(state)
850 typenode = self.type
851 if typenode is not None:
852 typenode._codegen(state)
853 namenode = self.name
854 if namenode is not None:
855 namenode._codegen(state)
856 self.whitespace_before_colon._codegen(state)
857 state.add_token(":")
858 self.body._codegen(state)
861@add_slots
862@dataclass(frozen=True)
863class ExceptStarHandler(CSTNode):
864 """
865 An ``except*`` clause that appears after a :class:`TryStar` statement.
866 """
868 #: The body of the except.
869 body: BaseSuite
871 #: The type of exception this catches. Can be a tuple in some cases.
872 type: BaseExpression
874 #: The optional name that a caught exception is assigned to.
875 name: Optional[AsName] = None
877 #: Sequence of empty lines appearing before this compound statement line.
878 leading_lines: Sequence[EmptyLine] = ()
880 #: The whitespace between the ``except`` keyword and the star.
881 whitespace_after_except: SimpleWhitespace = SimpleWhitespace.field("")
883 #: The whitespace between the star and the type.
884 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field(" ")
886 #: The whitespace after any type or name node (whichever comes last) and
887 #: the colon.
888 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
890 def _validate(self) -> None:
891 name = self.name
892 if name is not None and not isinstance(name.name, Name):
893 raise CSTValidationError(
894 "Must use a Name node for AsName name inside ExceptHandler."
895 )
897 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ExceptStarHandler":
898 return ExceptStarHandler(
899 leading_lines=visit_sequence(
900 self, "leading_lines", self.leading_lines, visitor
901 ),
902 whitespace_after_except=visit_required(
903 self, "whitespace_after_except", self.whitespace_after_except, visitor
904 ),
905 whitespace_after_star=visit_required(
906 self, "whitespace_after_star", self.whitespace_after_star, visitor
907 ),
908 type=visit_required(self, "type", self.type, visitor),
909 name=visit_optional(self, "name", self.name, visitor),
910 whitespace_before_colon=visit_required(
911 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
912 ),
913 body=visit_required(self, "body", self.body, visitor),
914 )
916 def _codegen_impl(self, state: CodegenState) -> None:
917 for ll in self.leading_lines:
918 ll._codegen(state)
919 state.add_indent_tokens()
921 with state.record_syntactic_position(self, end_node=self.body):
922 state.add_token("except")
923 self.whitespace_after_except._codegen(state)
924 state.add_token("*")
925 self.whitespace_after_star._codegen(state)
926 typenode = self.type
927 if typenode is not None:
928 typenode._codegen(state)
929 namenode = self.name
930 if namenode is not None:
931 namenode._codegen(state)
932 self.whitespace_before_colon._codegen(state)
933 state.add_token(":")
934 self.body._codegen(state)
937@add_slots
938@dataclass(frozen=True)
939class Finally(CSTNode):
940 """
941 A ``finally`` clause that appears optionally after a :class:`Try` statement.
942 """
944 #: The body of the except.
945 body: BaseSuite
947 #: Sequence of empty lines appearing before this compound statement line.
948 leading_lines: Sequence[EmptyLine] = ()
950 #: The whitespace that appears after the ``finally`` keyword but before
951 #: the colon.
952 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
954 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Finally":
955 return Finally(
956 leading_lines=visit_sequence(
957 self, "leading_lines", self.leading_lines, visitor
958 ),
959 whitespace_before_colon=visit_required(
960 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
961 ),
962 body=visit_required(self, "body", self.body, visitor),
963 )
965 def _codegen_impl(self, state: CodegenState) -> None:
966 for ll in self.leading_lines:
967 ll._codegen(state)
968 state.add_indent_tokens()
970 with state.record_syntactic_position(self, end_node=self.body):
971 state.add_token("finally")
972 self.whitespace_before_colon._codegen(state)
973 state.add_token(":")
974 self.body._codegen(state)
977@add_slots
978@dataclass(frozen=True)
979class Try(BaseCompoundStatement):
980 """
981 A regular ``try`` statement that cannot contain :class:`ExceptStar` blocks. For
982 ``try`` statements that can contain :class:`ExceptStar` blocks, see
983 :class:`TryStar`.
984 """
986 #: The suite that is wrapped with a try statement.
987 body: BaseSuite
989 #: A list of zero or more exception handlers.
990 handlers: Sequence[ExceptHandler] = ()
992 #: An optional else case.
993 orelse: Optional[Else] = None
995 #: An optional finally case.
996 finalbody: Optional[Finally] = None
998 #: Sequence of empty lines appearing before this compound statement line.
999 leading_lines: Sequence[EmptyLine] = ()
1001 #: The whitespace that appears after the ``try`` keyword but before
1002 #: the colon.
1003 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1005 def _validate(self) -> None:
1006 if len(self.handlers) == 0 and self.finalbody is None:
1007 raise CSTValidationError(
1008 "A Try statement must have at least one ExceptHandler or Finally"
1009 )
1010 if len(self.handlers) == 0 and self.orelse is not None:
1011 raise CSTValidationError(
1012 "A Try statement must have at least one ExceptHandler in order "
1013 + "to have an Else."
1014 )
1015 # Check bare excepts are always at the last position
1016 if any(handler.type is None for handler in self.handlers[:-1]):
1017 raise CSTValidationError("The bare except: handler must be the last one.")
1019 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Try":
1020 return Try(
1021 leading_lines=visit_sequence(
1022 self, "leading_lines", self.leading_lines, visitor
1023 ),
1024 whitespace_before_colon=visit_required(
1025 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1026 ),
1027 body=visit_required(self, "body", self.body, visitor),
1028 handlers=visit_sequence(self, "handlers", self.handlers, visitor),
1029 orelse=visit_optional(self, "orelse", self.orelse, visitor),
1030 finalbody=visit_optional(self, "finalbody", self.finalbody, visitor),
1031 )
1033 def _codegen_impl(self, state: CodegenState) -> None:
1034 for ll in self.leading_lines:
1035 ll._codegen(state)
1036 state.add_indent_tokens()
1038 end_node = self.body
1039 if len(self.handlers) > 0:
1040 end_node = self.handlers[-1]
1041 orelse = self.orelse
1042 end_node = end_node if orelse is None else orelse
1043 finalbody = self.finalbody
1044 end_node = end_node if finalbody is None else finalbody
1045 with state.record_syntactic_position(self, end_node=end_node):
1046 state.add_token("try")
1047 self.whitespace_before_colon._codegen(state)
1048 state.add_token(":")
1049 self.body._codegen(state)
1050 for handler in self.handlers:
1051 handler._codegen(state)
1052 if orelse is not None:
1053 orelse._codegen(state)
1054 if finalbody is not None:
1055 finalbody._codegen(state)
1058@add_slots
1059@dataclass(frozen=True)
1060class TryStar(BaseCompoundStatement):
1061 """
1062 A ``try`` statement with ``except*`` blocks.
1063 """
1065 #: The suite that is wrapped with a try statement.
1066 body: BaseSuite
1068 #: A list of one or more exception handlers.
1069 handlers: Sequence[ExceptStarHandler]
1071 #: An optional else case.
1072 orelse: Optional[Else] = None
1074 #: An optional finally case.
1075 finalbody: Optional[Finally] = None
1077 #: Sequence of empty lines appearing before this compound statement line.
1078 leading_lines: Sequence[EmptyLine] = ()
1080 #: The whitespace that appears after the ``try`` keyword but before
1081 #: the colon.
1082 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1084 def _validate(self) -> None:
1085 if len(self.handlers) == 0:
1086 raise CSTValidationError(
1087 "A TryStar statement must have at least one ExceptHandler"
1088 )
1090 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TryStar":
1091 return TryStar(
1092 leading_lines=visit_sequence(
1093 self, "leading_lines", self.leading_lines, visitor
1094 ),
1095 whitespace_before_colon=visit_required(
1096 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1097 ),
1098 body=visit_required(self, "body", self.body, visitor),
1099 handlers=visit_sequence(self, "handlers", self.handlers, visitor),
1100 orelse=visit_optional(self, "orelse", self.orelse, visitor),
1101 finalbody=visit_optional(self, "finalbody", self.finalbody, visitor),
1102 )
1104 def _codegen_impl(self, state: CodegenState) -> None:
1105 for ll in self.leading_lines:
1106 ll._codegen(state)
1107 state.add_indent_tokens()
1109 end_node = self.handlers[-1]
1110 orelse = self.orelse
1111 end_node = end_node if orelse is None else orelse
1112 finalbody = self.finalbody
1113 end_node = end_node if finalbody is None else finalbody
1114 with state.record_syntactic_position(self, end_node=end_node):
1115 state.add_token("try")
1116 self.whitespace_before_colon._codegen(state)
1117 state.add_token(":")
1118 self.body._codegen(state)
1119 for handler in self.handlers:
1120 handler._codegen(state)
1121 if orelse is not None:
1122 orelse._codegen(state)
1123 if finalbody is not None:
1124 finalbody._codegen(state)
1127@add_slots
1128@dataclass(frozen=True)
1129class ImportAlias(CSTNode):
1130 """
1131 An import, with an optional :class:`AsName`. Used in both :class:`Import` and
1132 :class:`ImportFrom` to specify a single import out of another module.
1133 """
1135 #: Name or Attribute node representing the object we are importing.
1136 name: Union[Attribute, Name]
1138 #: Local alias we will import the above object as.
1139 asname: Optional[AsName] = None
1141 #: Any trailing comma that appears after this import. This is optional for the
1142 #: last :class:`ImportAlias` in a :class:`Import` or :class:`ImportFrom`, but all
1143 #: other import aliases inside an import must contain a comma to disambiguate
1144 #: multiple imports.
1145 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
1147 def _validate(self) -> None:
1148 asname = self.asname
1149 if asname is not None:
1150 if not isinstance(asname.name, Name):
1151 raise CSTValidationError(
1152 "Must use a Name node for AsName name inside ImportAlias."
1153 )
1154 if asname.whitespace_before_as.empty:
1155 raise CSTValidationError(
1156 "Must have at least one space before as keyword in an ImportAlias."
1157 )
1158 try:
1159 self.evaluated_name
1160 except Exception as e:
1161 if str(e) == "Logic error!":
1162 raise CSTValidationError(
1163 "The imported name must be a valid qualified name."
1164 )
1165 raise e
1167 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ImportAlias":
1168 return ImportAlias(
1169 name=visit_required(self, "name", self.name, visitor),
1170 asname=visit_optional(self, "asname", self.asname, visitor),
1171 comma=visit_sentinel(self, "comma", self.comma, visitor),
1172 )
1174 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
1175 with state.record_syntactic_position(self):
1176 self.name._codegen(state)
1177 asname = self.asname
1178 if asname is not None:
1179 asname._codegen(state)
1181 comma = self.comma
1182 if comma is MaybeSentinel.DEFAULT and default_comma:
1183 state.add_token(", ")
1184 elif isinstance(comma, Comma):
1185 comma._codegen(state)
1187 def _name(self, node: CSTNode) -> str:
1188 # Unrolled version of get_full_name_for_node to avoid circular imports.
1189 if isinstance(node, Name):
1190 return node.value
1191 elif isinstance(node, Attribute):
1192 return f"{self._name(node.value)}.{node.attr.value}"
1193 else:
1194 raise Exception("Logic error!")
1196 @property
1197 def evaluated_name(self) -> str:
1198 """
1199 Returns the string name this :class:`ImportAlias` represents.
1200 """
1201 return self._name(self.name)
1203 @property
1204 def evaluated_alias(self) -> Optional[str]:
1205 """
1206 Returns the string name for any alias that this :class:`ImportAlias`
1207 has. If there is no ``asname`` attribute, this returns ``None``.
1208 """
1209 asname = self.asname
1210 if asname is not None:
1211 return self._name(asname.name)
1212 return None
1215@add_slots
1216@dataclass(frozen=True)
1217class Import(BaseSmallStatement):
1218 """
1219 An ``import`` statement.
1220 """
1222 #: One or more names that are being imported, with optional local aliases.
1223 names: Sequence[ImportAlias]
1225 #: Optional semicolon when this is used in a statement line. This semicolon
1226 #: owns the whitespace on both sides of it when it is used.
1227 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1229 #: The whitespace that appears after the ``import`` keyword but before
1230 #: the first import alias.
1231 whitespace_after_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1233 def _validate(self) -> None:
1234 if len(self.names) == 0:
1235 raise CSTValidationError(
1236 "An ImportStatement must have at least one ImportAlias"
1237 )
1238 if isinstance(self.names[-1].comma, Comma):
1239 raise CSTValidationError(
1240 "An ImportStatement does not allow a trailing comma"
1241 )
1242 if self.whitespace_after_import.empty:
1243 raise CSTValidationError("Must have at least one space after import.")
1245 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Import":
1246 return Import(
1247 whitespace_after_import=visit_required(
1248 self, "whitespace_after_import", self.whitespace_after_import, visitor
1249 ),
1250 names=visit_sequence(self, "names", self.names, visitor),
1251 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1252 )
1254 def _codegen_impl(
1255 self, state: CodegenState, default_semicolon: bool = False
1256 ) -> None:
1257 with state.record_syntactic_position(self):
1258 state.add_token("import")
1259 self.whitespace_after_import._codegen(state)
1260 lastname = len(self.names) - 1
1261 for i, name in enumerate(self.names):
1262 name._codegen(state, default_comma=(i != lastname))
1264 semicolon = self.semicolon
1265 if isinstance(semicolon, MaybeSentinel):
1266 if default_semicolon:
1267 state.add_token("; ")
1268 elif isinstance(semicolon, Semicolon):
1269 semicolon._codegen(state)
1272@add_slots
1273@dataclass(frozen=True)
1274class ImportFrom(BaseSmallStatement):
1275 """
1276 A ``from x import y`` statement.
1277 """
1279 #: Name or Attribute node representing the module we're importing from.
1280 #: This is optional as :class:`ImportFrom` allows purely relative imports.
1281 module: Optional[Union[Attribute, Name]]
1283 #: One or more names that are being imported from the specified module,
1284 #: with optional local aliases.
1285 names: Union[Sequence[ImportAlias], ImportStar]
1287 #: Sequence of :class:`Dot` nodes indicating relative import level.
1288 relative: Sequence[Dot] = ()
1290 #: Optional open parenthesis for multi-line import continuation.
1291 lpar: Optional[LeftParen] = None
1293 #: Optional close parenthesis for multi-line import continuation.
1294 rpar: Optional[RightParen] = None
1296 #: Optional semicolon when this is used in a statement line. This semicolon
1297 #: owns the whitespace on both sides of it when it is used.
1298 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1300 #: The whitespace that appears after the ``from`` keyword but before
1301 #: the module and any relative import dots.
1302 whitespace_after_from: SimpleWhitespace = SimpleWhitespace.field(" ")
1304 #: The whitespace that appears after the module but before the
1305 #: ``import`` keyword.
1306 whitespace_before_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1308 #: The whitespace that appears after the ``import`` keyword but
1309 #: before the first import name or optional left paren.
1310 whitespace_after_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1312 def _validate_module(self) -> None:
1313 if self.module is None and len(self.relative) == 0:
1314 raise CSTValidationError(
1315 "Must have a module specified if there is no relative import."
1316 )
1318 def _validate_names(self) -> None:
1319 names = self.names
1320 if isinstance(names, Sequence):
1321 if len(names) == 0:
1322 raise CSTValidationError(
1323 "An ImportFrom must have at least one ImportAlias"
1324 )
1325 for name in names[:-1]:
1326 if name.comma is None:
1327 raise CSTValidationError("Non-final ImportAliases require a comma")
1328 if self.lpar is not None and self.rpar is None:
1329 raise CSTValidationError("Cannot have left paren without right paren.")
1330 if self.lpar is None and self.rpar is not None:
1331 raise CSTValidationError("Cannot have right paren without left paren.")
1332 if isinstance(names, ImportStar):
1333 if self.lpar is not None or self.rpar is not None:
1334 raise CSTValidationError(
1335 "An ImportFrom using ImportStar cannot have parens"
1336 )
1338 def _validate_whitespace(self) -> None:
1339 if self.whitespace_after_from.empty and not self.relative:
1340 raise CSTValidationError("Must have at least one space after from.")
1341 if self.whitespace_before_import.empty and not (
1342 self.relative and self.module is None
1343 ):
1344 raise CSTValidationError("Must have at least one space before import.")
1345 if (
1346 self.whitespace_after_import.empty
1347 and self.lpar is None
1348 and not isinstance(self.names, ImportStar)
1349 ):
1350 raise CSTValidationError("Must have at least one space after import.")
1352 def _validate(self) -> None:
1353 self._validate_module()
1354 self._validate_names()
1355 self._validate_whitespace()
1357 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ImportFrom":
1358 names = self.names
1359 return ImportFrom(
1360 whitespace_after_from=visit_required(
1361 self, "whitespace_after_from", self.whitespace_after_from, visitor
1362 ),
1363 relative=visit_sequence(self, "relative", self.relative, visitor),
1364 module=visit_optional(self, "module", self.module, visitor),
1365 whitespace_before_import=visit_required(
1366 self, "whitespace_before_import", self.whitespace_before_import, visitor
1367 ),
1368 whitespace_after_import=visit_required(
1369 self, "whitespace_after_import", self.whitespace_after_import, visitor
1370 ),
1371 lpar=visit_optional(self, "lpar", self.lpar, visitor),
1372 names=(
1373 visit_required(self, "names", names, visitor)
1374 if isinstance(names, ImportStar)
1375 else visit_sequence(self, "names", names, visitor)
1376 ),
1377 rpar=visit_optional(self, "rpar", self.rpar, visitor),
1378 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1379 )
1381 def _codegen_impl(
1382 self, state: CodegenState, default_semicolon: bool = False
1383 ) -> None:
1384 names = self.names
1385 end_node = names[-1] if isinstance(names, Sequence) else names
1386 end_node = end_node if self.rpar is None else self.rpar
1387 with state.record_syntactic_position(self, end_node=end_node):
1388 state.add_token("from")
1389 self.whitespace_after_from._codegen(state)
1390 for dot in self.relative:
1391 dot._codegen(state)
1392 module = self.module
1393 if module is not None:
1394 module._codegen(state)
1395 self.whitespace_before_import._codegen(state)
1396 state.add_token("import")
1397 self.whitespace_after_import._codegen(state)
1398 lpar = self.lpar
1399 if lpar is not None:
1400 lpar._codegen(state)
1401 if isinstance(names, Sequence):
1402 lastname = len(names) - 1
1403 for i, name in enumerate(names):
1404 name._codegen(state, default_comma=(i != lastname))
1405 if isinstance(names, ImportStar):
1406 names._codegen(state)
1407 rpar = self.rpar
1408 if rpar is not None:
1409 rpar._codegen(state)
1411 semicolon = self.semicolon
1412 if isinstance(semicolon, MaybeSentinel):
1413 if default_semicolon:
1414 state.add_token("; ")
1415 elif isinstance(semicolon, Semicolon):
1416 semicolon._codegen(state)
1419@add_slots
1420@dataclass(frozen=True)
1421class AssignTarget(CSTNode):
1422 """
1423 A target for an :class:`Assign`. Owns the equals sign and the whitespace around it.
1424 """
1426 #: The target expression being assigned to.
1427 target: BaseAssignTargetExpression
1429 #: The whitespace appearing before the equals sign.
1430 whitespace_before_equal: SimpleWhitespace = SimpleWhitespace.field(" ")
1432 #: The whitespace appearing after the equals sign.
1433 whitespace_after_equal: SimpleWhitespace = SimpleWhitespace.field(" ")
1435 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AssignTarget":
1436 return AssignTarget(
1437 target=visit_required(self, "target", self.target, visitor),
1438 whitespace_before_equal=visit_required(
1439 self, "whitespace_before_equal", self.whitespace_before_equal, visitor
1440 ),
1441 whitespace_after_equal=visit_required(
1442 self, "whitespace_after_equal", self.whitespace_after_equal, visitor
1443 ),
1444 )
1446 def _codegen_impl(self, state: CodegenState) -> None:
1447 with state.record_syntactic_position(self):
1448 self.target._codegen(state)
1450 self.whitespace_before_equal._codegen(state)
1451 state.add_token("=")
1452 self.whitespace_after_equal._codegen(state)
1455@add_slots
1456@dataclass(frozen=True)
1457class Assign(BaseSmallStatement):
1458 """
1459 An assignment statement such as ``x = y`` or ``x = y = z``. Unlike
1460 :class:`AnnAssign`, this does not allow type annotations but does
1461 allow for multiple targets.
1462 """
1464 #: One or more targets that are being assigned to.
1465 targets: Sequence[AssignTarget]
1467 #: The expression being assigned to the targets.
1468 value: BaseExpression
1470 #: Optional semicolon when this is used in a statement line. This semicolon
1471 #: owns the whitespace on both sides of it when it is used.
1472 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1474 def _validate(self) -> None:
1475 if len(self.targets) == 0:
1476 raise CSTValidationError(
1477 "An Assign statement must have at least one AssignTarget"
1478 )
1480 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Assign":
1481 return Assign(
1482 targets=visit_sequence(self, "targets", self.targets, visitor),
1483 value=visit_required(self, "value", self.value, visitor),
1484 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1485 )
1487 def _codegen_impl(
1488 self, state: CodegenState, default_semicolon: bool = False
1489 ) -> None:
1490 with state.record_syntactic_position(self):
1491 for target in self.targets:
1492 target._codegen(state)
1493 self.value._codegen(state)
1495 semicolon = self.semicolon
1496 if isinstance(semicolon, MaybeSentinel):
1497 if default_semicolon:
1498 state.add_token("; ")
1499 elif isinstance(semicolon, Semicolon):
1500 semicolon._codegen(state)
1503@add_slots
1504@dataclass(frozen=True)
1505class AnnAssign(BaseSmallStatement):
1506 """
1507 An assignment statement such as ``x: int = 5`` or ``x: int``. This only
1508 allows for one assignment target unlike :class:`Assign` but it includes
1509 a variable annotation. Also unlike :class:`Assign`, the assignment target
1510 is optional, as it is possible to annotate an expression without assigning
1511 to it.
1512 """
1514 #: The target that is being annotated and possibly assigned to.
1515 target: BaseAssignTargetExpression
1517 #: The annotation for the target.
1518 annotation: Annotation
1520 #: The optional expression being assigned to the target.
1521 value: Optional[BaseExpression] = None
1523 #: The equals sign used to denote assignment if there is a value.
1524 equal: Union[AssignEqual, MaybeSentinel] = MaybeSentinel.DEFAULT
1526 #: Optional semicolon when this is used in a statement line. This semicolon
1527 #: owns the whitespace on both sides of it when it is used.
1528 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1530 def _validate(self) -> None:
1531 if self.value is None and isinstance(self.equal, AssignEqual):
1532 raise CSTValidationError(
1533 "Must have a value when specifying an AssignEqual."
1534 )
1536 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AnnAssign":
1537 return AnnAssign(
1538 target=visit_required(self, "target", self.target, visitor),
1539 annotation=visit_required(self, "annotation", self.annotation, visitor),
1540 equal=visit_sentinel(self, "equal", self.equal, visitor),
1541 value=visit_optional(self, "value", self.value, visitor),
1542 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1543 )
1545 def _codegen_impl(
1546 self, state: CodegenState, default_semicolon: bool = False
1547 ) -> None:
1548 with state.record_syntactic_position(self):
1549 self.target._codegen(state)
1550 self.annotation._codegen(state, default_indicator=":")
1551 equal = self.equal
1552 if equal is MaybeSentinel.DEFAULT and self.value is not None:
1553 state.add_token(" = ")
1554 elif isinstance(equal, AssignEqual):
1555 equal._codegen(state)
1556 value = self.value
1557 if value is not None:
1558 value._codegen(state)
1560 semicolon = self.semicolon
1561 if isinstance(semicolon, MaybeSentinel):
1562 if default_semicolon:
1563 state.add_token("; ")
1564 elif isinstance(semicolon, Semicolon):
1565 semicolon._codegen(state)
1568@add_slots
1569@dataclass(frozen=True)
1570class AugAssign(BaseSmallStatement):
1571 """
1572 An augmented assignment statement, such as ``x += 5``.
1573 """
1575 #: Target that is being operated on and assigned to.
1576 target: BaseAssignTargetExpression
1578 #: The augmented assignment operation being performed.
1579 operator: BaseAugOp
1581 #: The value used with the above operator to calculate the new assignment.
1582 value: BaseExpression
1584 #: Optional semicolon when this is used in a statement line. This semicolon
1585 #: owns the whitespace on both sides of it when it is used.
1586 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1588 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AugAssign":
1589 return AugAssign(
1590 target=visit_required(self, "target", self.target, visitor),
1591 operator=visit_required(self, "operator", self.operator, visitor),
1592 value=visit_required(self, "value", self.value, visitor),
1593 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1594 )
1596 def _codegen_impl(
1597 self, state: CodegenState, default_semicolon: bool = False
1598 ) -> None:
1599 with state.record_syntactic_position(self):
1600 self.target._codegen(state)
1601 self.operator._codegen(state)
1602 self.value._codegen(state)
1604 semicolon = self.semicolon
1605 if isinstance(semicolon, MaybeSentinel):
1606 if default_semicolon:
1607 state.add_token("; ")
1608 elif isinstance(semicolon, Semicolon):
1609 semicolon._codegen(state)
1612@add_slots
1613@dataclass(frozen=True)
1614class Decorator(CSTNode):
1615 """
1616 A single decorator that decorates a :class:`FunctionDef` or a :class:`ClassDef`.
1617 """
1619 #: The decorator that will return a new function wrapping the parent
1620 #: of this decorator.
1621 decorator: BaseExpression
1623 #: Line comments and empty lines before this decorator. The parent
1624 #: :class:`FunctionDef` or :class:`ClassDef` node owns leading lines before
1625 #: the first decorator so that if the first decorator is removed, spacing is preserved.
1626 leading_lines: Sequence[EmptyLine] = ()
1628 #: Whitespace after the ``@`` and before the decorator expression itself.
1629 whitespace_after_at: SimpleWhitespace = SimpleWhitespace.field("")
1631 #: Optional trailing comment and newline following the decorator before the next line.
1632 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field()
1634 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Decorator":
1635 return Decorator(
1636 leading_lines=visit_sequence(
1637 self, "leading_lines", self.leading_lines, visitor
1638 ),
1639 whitespace_after_at=visit_required(
1640 self, "whitespace_after_at", self.whitespace_after_at, visitor
1641 ),
1642 decorator=visit_required(self, "decorator", self.decorator, visitor),
1643 trailing_whitespace=visit_required(
1644 self, "trailing_whitespace", self.trailing_whitespace, visitor
1645 ),
1646 )
1648 def _codegen_impl(self, state: CodegenState) -> None:
1649 for ll in self.leading_lines:
1650 ll._codegen(state)
1651 state.add_indent_tokens()
1653 with state.record_syntactic_position(self):
1654 state.add_token("@")
1655 self.whitespace_after_at._codegen(state)
1656 self.decorator._codegen(state)
1658 self.trailing_whitespace._codegen(state)
1661def get_docstring_impl(
1662 body: Union[BaseSuite, Sequence[Union[SimpleStatementLine, BaseCompoundStatement]]],
1663 clean: bool,
1664) -> Optional[str]:
1665 """
1666 Implementation Reference:
1667 - :func:`ast.get_docstring` https://docs.python.org/3/library/ast.html#ast.get_docstring
1668 and https://github.com/python/cpython/blob/89aa4694fc8c6d190325ef8ed6ce6a6b8efb3e50/Lib/ast.py#L254
1669 - PEP 257 https://www.python.org/dev/peps/pep-0257/
1670 """
1671 if isinstance(body, Sequence):
1672 if body:
1673 expr = body[0]
1674 else:
1675 return None
1676 else:
1677 expr = body
1678 while isinstance(expr, (BaseSuite, SimpleStatementLine)):
1679 if len(expr.body) == 0:
1680 return None
1681 expr = expr.body[0]
1682 if not isinstance(expr, Expr):
1683 return None
1684 val = expr.value
1685 if isinstance(val, (SimpleString, ConcatenatedString)):
1686 evaluated_value = val.evaluated_value
1687 else:
1688 return None
1689 if isinstance(evaluated_value, bytes):
1690 return None
1692 if evaluated_value is not None and clean:
1693 return inspect.cleandoc(evaluated_value)
1694 return evaluated_value
1697@add_slots
1698@dataclass(frozen=True)
1699class FunctionDef(BaseCompoundStatement):
1700 """
1701 A function definition.
1702 """
1704 #: The function name.
1705 name: Name
1707 #: The function parameters. Present even if there are no params.
1708 params: Parameters
1710 #: The function body.
1711 body: BaseSuite
1713 #: Sequence of decorators applied to this function. Decorators are listed in
1714 #: order that they appear in source (top to bottom) as apposed to the order
1715 #: that they are applied to the function at runtime.
1716 decorators: Sequence[Decorator] = ()
1718 #: An optional return annotation, if the function is annotated.
1719 returns: Optional[Annotation] = None
1721 #: Optional async modifier, if this is an async function.
1722 asynchronous: Optional[Asynchronous] = None
1724 #: Leading empty lines and comments before the first decorator. We
1725 #: assume any comments before the first decorator are owned by the
1726 #: function definition itself. If there are no decorators, this will
1727 #: still contain all of the empty lines and comments before the
1728 #: function definition.
1729 leading_lines: Sequence[EmptyLine] = ()
1731 #: Empty lines and comments between the final decorator and the
1732 #: :class:`FunctionDef` node. In the case of no decorators, this will be empty.
1733 lines_after_decorators: Sequence[EmptyLine] = ()
1735 #: Whitespace after the ``def`` keyword and before the function name.
1736 whitespace_after_def: SimpleWhitespace = SimpleWhitespace.field(" ")
1738 #: Whitespace after the function name and before the opening parenthesis for
1739 #: the parameters.
1740 whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("")
1742 #: Whitespace after the opening parenthesis for the parameters but before
1743 #: the first param itself.
1744 whitespace_before_params: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
1746 #: Whitespace after the closing parenthesis or return annotation and before
1747 #: the colon.
1748 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1750 def _validate(self) -> None:
1751 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
1752 raise CSTValidationError("Cannot have parens around Name in a FunctionDef.")
1753 if self.whitespace_after_def.empty:
1754 raise CSTValidationError(
1755 "There must be at least one space between 'def' and name."
1756 )
1758 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "FunctionDef":
1759 return FunctionDef(
1760 leading_lines=visit_sequence(
1761 self, "leading_lines", self.leading_lines, visitor
1762 ),
1763 decorators=visit_sequence(self, "decorators", self.decorators, visitor),
1764 lines_after_decorators=visit_sequence(
1765 self, "lines_after_decorators", self.lines_after_decorators, visitor
1766 ),
1767 asynchronous=visit_optional(
1768 self, "asynchronous", self.asynchronous, visitor
1769 ),
1770 whitespace_after_def=visit_required(
1771 self, "whitespace_after_def", self.whitespace_after_def, visitor
1772 ),
1773 name=visit_required(self, "name", self.name, visitor),
1774 whitespace_after_name=visit_required(
1775 self, "whitespace_after_name", self.whitespace_after_name, visitor
1776 ),
1777 whitespace_before_params=visit_required(
1778 self, "whitespace_before_params", self.whitespace_before_params, visitor
1779 ),
1780 params=visit_required(self, "params", self.params, visitor),
1781 returns=visit_optional(self, "returns", self.returns, visitor),
1782 whitespace_before_colon=visit_required(
1783 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1784 ),
1785 body=visit_required(self, "body", self.body, visitor),
1786 )
1788 def _codegen_impl(self, state: CodegenState) -> None:
1789 for ll in self.leading_lines:
1790 ll._codegen(state)
1791 for decorator in self.decorators:
1792 decorator._codegen(state)
1793 for lad in self.lines_after_decorators:
1794 lad._codegen(state)
1795 state.add_indent_tokens()
1797 with state.record_syntactic_position(self, end_node=self.body):
1798 asynchronous = self.asynchronous
1799 if asynchronous is not None:
1800 asynchronous._codegen(state)
1801 state.add_token("def")
1802 self.whitespace_after_def._codegen(state)
1803 self.name._codegen(state)
1804 self.whitespace_after_name._codegen(state)
1805 state.add_token("(")
1806 self.whitespace_before_params._codegen(state)
1807 self.params._codegen(state)
1808 state.add_token(")")
1809 returns = self.returns
1810 if returns is not None:
1811 returns._codegen(state, default_indicator="->")
1812 self.whitespace_before_colon._codegen(state)
1813 state.add_token(":")
1814 self.body._codegen(state)
1816 def get_docstring(self, clean: bool = True) -> Optional[str]:
1817 """
1818 When docstring is available, returns a :func:`inspect.cleandoc` cleaned docstring.
1819 Otherwise, returns ``None``.
1820 """
1821 return get_docstring_impl(self.body, clean)
1824@add_slots
1825@dataclass(frozen=True)
1826class ClassDef(BaseCompoundStatement):
1827 """
1828 A class definition.
1829 """
1831 #: The class name.
1832 name: Name
1834 #: The class body.
1835 body: BaseSuite
1837 #: Sequence of base classes this class inherits from.
1838 bases: Sequence[Arg] = ()
1840 #: Sequence of keywords, such as "metaclass".
1841 keywords: Sequence[Arg] = ()
1843 #: Sequence of decorators applied to this class.
1844 decorators: Sequence[Decorator] = ()
1846 #: Optional open parenthesis used when there are bases or keywords.
1847 lpar: Union[LeftParen, MaybeSentinel] = MaybeSentinel.DEFAULT
1849 #: Optional close parenthesis used when there are bases or keywords.
1850 rpar: Union[RightParen, MaybeSentinel] = MaybeSentinel.DEFAULT
1852 #: Leading empty lines and comments before the first decorator. We
1853 #: assume any comments before the first decorator are owned by the
1854 #: class definition itself. If there are no decorators, this will
1855 #: still contain all of the empty lines and comments before the
1856 #: class definition.
1857 leading_lines: Sequence[EmptyLine] = ()
1859 #: Empty lines and comments between the final decorator and the
1860 #: :class:`ClassDef` node. In the case of no decorators, this will be empty.
1861 lines_after_decorators: Sequence[EmptyLine] = ()
1863 #: Whitespace after the ``class`` keyword and before the class name.
1864 whitespace_after_class: SimpleWhitespace = SimpleWhitespace.field(" ")
1866 #: Whitespace after the class name and before the opening parenthesis for
1867 #: the bases and keywords.
1868 whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("")
1870 #: Whitespace after the closing parenthesis or class name and before
1871 #: the colon.
1872 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1874 def _validate_whitespace(self) -> None:
1875 if self.whitespace_after_class.empty:
1876 raise CSTValidationError(
1877 "There must be at least one space between 'class' and name."
1878 )
1880 def _validate_parens(self) -> None:
1881 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
1882 raise CSTValidationError("Cannot have parens around Name in a ClassDef.")
1883 if isinstance(self.lpar, MaybeSentinel) and isinstance(self.rpar, RightParen):
1884 raise CSTValidationError(
1885 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
1886 )
1887 if isinstance(self.lpar, LeftParen) and isinstance(self.rpar, MaybeSentinel):
1888 raise CSTValidationError(
1889 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
1890 )
1892 def _validate_args(self) -> None:
1893 if any((arg.keyword is not None) for arg in self.bases):
1894 raise CSTValidationError("Bases must be arguments without keywords.")
1895 if any((arg.keyword is None and arg.star != "**") for arg in self.keywords):
1896 raise CSTValidationError(
1897 "Keywords must be arguments with keywords or dictionary expansions."
1898 )
1900 def _validate(self) -> None:
1901 self._validate_whitespace()
1902 self._validate_parens()
1903 self._validate_args()
1905 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ClassDef":
1906 return ClassDef(
1907 leading_lines=visit_sequence(
1908 self, "leading_lines", self.leading_lines, visitor
1909 ),
1910 decorators=visit_sequence(self, "decorators", self.decorators, visitor),
1911 lines_after_decorators=visit_sequence(
1912 self, "lines_after_decorators", self.lines_after_decorators, visitor
1913 ),
1914 whitespace_after_class=visit_required(
1915 self, "whitespace_after_class", self.whitespace_after_class, visitor
1916 ),
1917 name=visit_required(self, "name", self.name, visitor),
1918 whitespace_after_name=visit_required(
1919 self, "whitespace_after_name", self.whitespace_after_name, visitor
1920 ),
1921 lpar=visit_sentinel(self, "lpar", self.lpar, visitor),
1922 bases=visit_sequence(self, "bases", self.bases, visitor),
1923 keywords=visit_sequence(self, "keywords", self.keywords, visitor),
1924 rpar=visit_sentinel(self, "rpar", self.rpar, visitor),
1925 whitespace_before_colon=visit_required(
1926 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1927 ),
1928 body=visit_required(self, "body", self.body, visitor),
1929 )
1931 def _codegen_impl(self, state: CodegenState) -> None: # noqa: C901
1932 for ll in self.leading_lines:
1933 ll._codegen(state)
1934 for decorator in self.decorators:
1935 decorator._codegen(state)
1936 for lad in self.lines_after_decorators:
1937 lad._codegen(state)
1938 state.add_indent_tokens()
1940 with state.record_syntactic_position(self, end_node=self.body):
1941 state.add_token("class")
1942 self.whitespace_after_class._codegen(state)
1943 self.name._codegen(state)
1944 self.whitespace_after_name._codegen(state)
1945 lpar = self.lpar
1946 if isinstance(lpar, MaybeSentinel):
1947 if self.bases or self.keywords:
1948 state.add_token("(")
1949 elif isinstance(lpar, LeftParen):
1950 lpar._codegen(state)
1951 args = [*self.bases, *self.keywords]
1952 last_arg = len(args) - 1
1953 for i, arg in enumerate(args):
1954 arg._codegen(state, default_comma=(i != last_arg))
1955 rpar = self.rpar
1956 if isinstance(rpar, MaybeSentinel):
1957 if self.bases or self.keywords:
1958 state.add_token(")")
1959 elif isinstance(rpar, RightParen):
1960 rpar._codegen(state)
1961 self.whitespace_before_colon._codegen(state)
1962 state.add_token(":")
1963 self.body._codegen(state)
1965 def get_docstring(self, clean: bool = True) -> Optional[str]:
1966 """
1967 Returns a :func:`inspect.cleandoc` cleaned docstring if the docstring is available, ``None`` otherwise.
1968 """
1969 return get_docstring_impl(self.body, clean)
1972@add_slots
1973@dataclass(frozen=True)
1974class WithItem(CSTNode):
1975 """
1976 A single context manager in a :class:`With` block, with an optional variable name.
1977 """
1979 #: Expression that evaluates to a context manager.
1980 item: BaseExpression
1982 #: Variable to assign the context manager to, if it is needed in the
1983 #: :class:`With` body.
1984 asname: Optional[AsName] = None
1986 #: This is forbidden for the last :class:`WithItem` in a :class:`With`, but all
1987 #: other items inside a with block must contain a comma to separate them.
1988 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
1990 def _validate(self) -> None:
1991 asname = self.asname
1992 if (
1993 asname is not None
1994 and asname.whitespace_before_as.empty
1995 and not self.item._safe_to_use_with_word_operator(ExpressionPosition.LEFT)
1996 ):
1997 raise CSTValidationError("Must have at least one space before as keyword.")
1999 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "WithItem":
2000 return WithItem(
2001 item=visit_required(self, "item", self.item, visitor),
2002 asname=visit_optional(self, "asname", self.asname, visitor),
2003 comma=visit_sentinel(self, "comma", self.comma, visitor),
2004 )
2006 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
2007 with state.record_syntactic_position(self):
2008 self.item._codegen(state)
2009 asname = self.asname
2010 if asname is not None:
2011 asname._codegen(state)
2013 comma = self.comma
2014 if comma is MaybeSentinel.DEFAULT and default_comma:
2015 state.add_token(", ")
2016 elif isinstance(comma, Comma):
2017 comma._codegen(state)
2020@add_slots
2021@dataclass(frozen=True)
2022class With(BaseCompoundStatement):
2023 """
2024 A ``with`` statement.
2025 """
2027 #: A sequence of one or more items that evaluate to context managers.
2028 items: Sequence[WithItem]
2030 #: The suite that is wrapped with this statement.
2031 body: BaseSuite
2033 #: Optional async modifier if this is an ``async with`` statement.
2034 asynchronous: Optional[Asynchronous] = None
2036 #: Sequence of empty lines appearing before this with statement.
2037 leading_lines: Sequence[EmptyLine] = ()
2039 #: Optional open parenthesis for multi-line with bindings
2040 lpar: Union[LeftParen, MaybeSentinel] = MaybeSentinel.DEFAULT
2042 #: Optional close parenthesis for multi-line with bindings
2043 rpar: Union[RightParen, MaybeSentinel] = MaybeSentinel.DEFAULT
2045 #: Whitespace after the ``with`` keyword and before the first item.
2046 whitespace_after_with: SimpleWhitespace = SimpleWhitespace.field(" ")
2048 #: Whitespace after the last item and before the colon.
2049 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2051 def _validate_parens(self) -> None:
2052 if isinstance(self.lpar, MaybeSentinel) and isinstance(self.rpar, RightParen):
2053 raise CSTValidationError(
2054 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
2055 )
2056 if isinstance(self.lpar, LeftParen) and isinstance(self.rpar, MaybeSentinel):
2057 raise CSTValidationError(
2058 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
2059 )
2061 def _validate(self) -> None:
2062 self._validate_parens()
2063 if len(self.items) == 0:
2064 raise CSTValidationError(
2065 "A With statement must have at least one WithItem."
2066 )
2067 if (
2068 isinstance(self.rpar, MaybeSentinel)
2069 and self.items[-1].comma != MaybeSentinel.DEFAULT
2070 ):
2071 raise CSTValidationError(
2072 "The last WithItem in an unparenthesized With cannot have a trailing comma."
2073 )
2074 if self.whitespace_after_with.empty and not (
2075 isinstance(self.lpar, LeftParen)
2076 or self.items[0].item._safe_to_use_with_word_operator(
2077 ExpressionPosition.RIGHT
2078 )
2079 ):
2080 raise CSTValidationError("Must have at least one space after with keyword.")
2082 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "With":
2083 return With(
2084 leading_lines=visit_sequence(
2085 self, "leading_lines", self.leading_lines, visitor
2086 ),
2087 asynchronous=visit_optional(
2088 self, "asynchronous", self.asynchronous, visitor
2089 ),
2090 whitespace_after_with=visit_required(
2091 self, "whitespace_after_with", self.whitespace_after_with, visitor
2092 ),
2093 lpar=visit_sentinel(self, "lpar", self.lpar, visitor),
2094 items=visit_sequence(self, "items", self.items, visitor),
2095 rpar=visit_sentinel(self, "rpar", self.rpar, visitor),
2096 whitespace_before_colon=visit_required(
2097 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2098 ),
2099 body=visit_required(self, "body", self.body, visitor),
2100 )
2102 def _codegen_impl(self, state: CodegenState) -> None:
2103 for ll in self.leading_lines:
2104 ll._codegen(state)
2105 state.add_indent_tokens()
2107 needs_paren = False
2108 for item in self.items:
2109 comma = item.comma
2110 if isinstance(comma, Comma):
2111 if isinstance(
2112 comma.whitespace_after,
2113 (EmptyLine, TrailingWhitespace, ParenthesizedWhitespace),
2114 ):
2115 needs_paren = True
2116 break
2118 with state.record_syntactic_position(self, end_node=self.body):
2119 asynchronous = self.asynchronous
2120 if asynchronous is not None:
2121 asynchronous._codegen(state)
2122 state.add_token("with")
2123 self.whitespace_after_with._codegen(state)
2124 lpar = self.lpar
2125 if isinstance(lpar, LeftParen):
2126 lpar._codegen(state)
2127 elif needs_paren:
2128 state.add_token("(")
2129 last_item = len(self.items) - 1
2130 for i, item in enumerate(self.items):
2131 item._codegen(state, default_comma=(i != last_item))
2132 rpar = self.rpar
2133 if isinstance(rpar, RightParen):
2134 rpar._codegen(state)
2135 elif needs_paren:
2136 state.add_token(")")
2137 self.whitespace_before_colon._codegen(state)
2138 state.add_token(":")
2139 self.body._codegen(state)
2142@add_slots
2143@dataclass(frozen=True)
2144class For(BaseCompoundStatement):
2145 """
2146 A ``for target in iter`` statement.
2147 """
2149 #: The target of the iterator in the for statement.
2150 target: BaseAssignTargetExpression
2152 #: The iterable expression we will loop over.
2153 iter: BaseExpression
2155 #: The suite that is wrapped with this statement.
2156 body: BaseSuite
2158 #: An optional else case which will be executed if there is no
2159 #: :class:`Break` statement encountered while looping.
2160 orelse: Optional[Else] = None
2162 #: Optional async modifier, if this is an `async for` statement.
2163 asynchronous: Optional[Asynchronous] = None
2165 #: Sequence of empty lines appearing before this for statement.
2166 leading_lines: Sequence[EmptyLine] = ()
2168 #: Whitespace after the ``for`` keyword and before the target.
2169 whitespace_after_for: SimpleWhitespace = SimpleWhitespace.field(" ")
2171 #: Whitespace after the target and before the ``in`` keyword.
2172 whitespace_before_in: SimpleWhitespace = SimpleWhitespace.field(" ")
2174 #: Whitespace after the ``in`` keyword and before the iter.
2175 whitespace_after_in: SimpleWhitespace = SimpleWhitespace.field(" ")
2177 #: Whitespace after the iter and before the colon.
2178 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2180 def _validate(self) -> None:
2181 if (
2182 self.whitespace_after_for.empty
2183 and not self.target._safe_to_use_with_word_operator(
2184 ExpressionPosition.RIGHT
2185 )
2186 ):
2187 raise CSTValidationError(
2188 "Must have at least one space after 'for' keyword."
2189 )
2191 if (
2192 self.whitespace_before_in.empty
2193 and not self.target._safe_to_use_with_word_operator(ExpressionPosition.LEFT)
2194 ):
2195 raise CSTValidationError(
2196 "Must have at least one space before 'in' keyword."
2197 )
2199 if (
2200 self.whitespace_after_in.empty
2201 and not self.iter._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2202 ):
2203 raise CSTValidationError("Must have at least one space after 'in' keyword.")
2205 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "For":
2206 return For(
2207 leading_lines=visit_sequence(
2208 self, "leading_lines", self.leading_lines, visitor
2209 ),
2210 asynchronous=visit_optional(
2211 self, "asynchronous", self.asynchronous, visitor
2212 ),
2213 whitespace_after_for=visit_required(
2214 self, "whitespace_after_for", self.whitespace_after_for, visitor
2215 ),
2216 target=visit_required(self, "target", self.target, visitor),
2217 whitespace_before_in=visit_required(
2218 self, "whitespace_before_in", self.whitespace_before_in, visitor
2219 ),
2220 whitespace_after_in=visit_required(
2221 self, "whitespace_after_in", self.whitespace_after_in, visitor
2222 ),
2223 iter=visit_required(self, "iter", self.iter, visitor),
2224 whitespace_before_colon=visit_required(
2225 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2226 ),
2227 body=visit_required(self, "body", self.body, visitor),
2228 orelse=visit_optional(self, "orelse", self.orelse, visitor),
2229 )
2231 def _codegen_impl(self, state: CodegenState) -> None:
2232 for ll in self.leading_lines:
2233 ll._codegen(state)
2234 state.add_indent_tokens()
2236 end_node = self.body if self.orelse is None else self.orelse
2237 with state.record_syntactic_position(self, end_node=end_node):
2238 asynchronous = self.asynchronous
2239 if asynchronous is not None:
2240 asynchronous._codegen(state)
2241 state.add_token("for")
2242 self.whitespace_after_for._codegen(state)
2243 self.target._codegen(state)
2244 self.whitespace_before_in._codegen(state)
2245 state.add_token("in")
2246 self.whitespace_after_in._codegen(state)
2247 self.iter._codegen(state)
2248 self.whitespace_before_colon._codegen(state)
2249 state.add_token(":")
2250 self.body._codegen(state)
2251 orelse = self.orelse
2252 if orelse is not None:
2253 orelse._codegen(state)
2256@add_slots
2257@dataclass(frozen=True)
2258class While(BaseCompoundStatement):
2259 """
2260 A ``while`` statement.
2261 """
2263 #: The test we will loop against.
2264 test: BaseExpression
2266 #: The suite that is wrapped with this statement.
2267 body: BaseSuite
2269 #: An optional else case which will be executed if there is no
2270 #: :class:`Break` statement encountered while looping.
2271 orelse: Optional[Else] = None
2273 #: Sequence of empty lines appearing before this while statement.
2274 leading_lines: Sequence[EmptyLine] = ()
2276 #: Whitespace after the ``while`` keyword and before the test.
2277 whitespace_after_while: SimpleWhitespace = SimpleWhitespace.field(" ")
2279 #: Whitespace after the test and before the colon.
2280 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2282 def _validate(self) -> None:
2283 if (
2284 self.whitespace_after_while.empty
2285 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2286 ):
2287 raise CSTValidationError(
2288 "Must have at least one space after 'while' keyword."
2289 )
2291 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "While":
2292 return While(
2293 leading_lines=visit_sequence(
2294 self, "leading_lines", self.leading_lines, visitor
2295 ),
2296 whitespace_after_while=visit_required(
2297 self, "whitespace_after_while", self.whitespace_after_while, visitor
2298 ),
2299 test=visit_required(self, "test", self.test, visitor),
2300 whitespace_before_colon=visit_required(
2301 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2302 ),
2303 body=visit_required(self, "body", self.body, visitor),
2304 orelse=visit_optional(self, "orelse", self.orelse, visitor),
2305 )
2307 def _codegen_impl(self, state: CodegenState) -> None:
2308 for ll in self.leading_lines:
2309 ll._codegen(state)
2310 state.add_indent_tokens()
2312 end_node = self.body if self.orelse is None else self.orelse
2313 with state.record_syntactic_position(self, end_node=end_node):
2314 state.add_token("while")
2315 self.whitespace_after_while._codegen(state)
2316 self.test._codegen(state)
2317 self.whitespace_before_colon._codegen(state)
2318 state.add_token(":")
2319 self.body._codegen(state)
2320 orelse = self.orelse
2321 if orelse is not None:
2322 orelse._codegen(state)
2325@add_slots
2326@dataclass(frozen=True)
2327class Raise(BaseSmallStatement):
2328 """
2329 A ``raise exc`` or ``raise exc from cause`` statement.
2330 """
2332 #: The exception that we should raise.
2333 exc: Optional[BaseExpression] = None
2335 #: Optionally, a ``from cause`` clause to allow us to raise an exception
2336 #: out of another exception's context.
2337 cause: Optional[From] = None
2339 #: Any whitespace appearing between the ``raise`` keyword and the exception.
2340 whitespace_after_raise: Union[
2341 SimpleWhitespace, MaybeSentinel
2342 ] = MaybeSentinel.DEFAULT
2344 #: Optional semicolon when this is used in a statement line. This semicolon
2345 #: owns the whitespace on both sides of it when it is used.
2346 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2348 def _validate(self) -> None:
2349 # Validate correct construction
2350 if self.exc is None and self.cause is not None:
2351 raise CSTValidationError(
2352 "Must have an 'exc' when specifying 'clause'. on Raise."
2353 )
2355 # Validate spacing between "raise" and "exc"
2356 exc = self.exc
2357 if exc is not None:
2358 whitespace_after_raise = self.whitespace_after_raise
2359 has_no_gap = (
2360 not isinstance(whitespace_after_raise, MaybeSentinel)
2361 and whitespace_after_raise.empty
2362 )
2363 if has_no_gap and not exc._safe_to_use_with_word_operator(
2364 ExpressionPosition.RIGHT
2365 ):
2366 raise CSTValidationError("Must have at least one space after 'raise'.")
2368 # Validate spacing between "exc" and "from"
2369 cause = self.cause
2370 if exc is not None and cause is not None:
2371 whitespace_before_from = cause.whitespace_before_from
2372 has_no_gap = (
2373 not isinstance(whitespace_before_from, MaybeSentinel)
2374 and whitespace_before_from.empty
2375 )
2376 if has_no_gap and not exc._safe_to_use_with_word_operator(
2377 ExpressionPosition.LEFT
2378 ):
2379 raise CSTValidationError("Must have at least one space before 'from'.")
2381 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Raise":
2382 return Raise(
2383 whitespace_after_raise=visit_sentinel(
2384 self, "whitespace_after_raise", self.whitespace_after_raise, visitor
2385 ),
2386 exc=visit_optional(self, "exc", self.exc, visitor),
2387 cause=visit_optional(self, "cause", self.cause, visitor),
2388 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2389 )
2391 def _codegen_impl(
2392 self, state: CodegenState, default_semicolon: bool = False
2393 ) -> None:
2394 with state.record_syntactic_position(self):
2395 exc = self.exc
2396 cause = self.cause
2397 state.add_token("raise")
2398 whitespace_after_raise = self.whitespace_after_raise
2399 if isinstance(whitespace_after_raise, MaybeSentinel):
2400 if exc is not None:
2401 state.add_token(" ")
2402 else:
2403 whitespace_after_raise._codegen(state)
2404 if exc is not None:
2405 exc._codegen(state)
2406 if cause is not None:
2407 cause._codegen(state, default_space=" ")
2409 semicolon = self.semicolon
2410 if isinstance(semicolon, MaybeSentinel):
2411 if default_semicolon:
2412 state.add_token("; ")
2413 elif isinstance(semicolon, Semicolon):
2414 semicolon._codegen(state)
2417@add_slots
2418@dataclass(frozen=True)
2419class Assert(BaseSmallStatement):
2420 """
2421 An assert statement such as ``assert x > 5`` or ``assert x > 5, 'Uh oh!'``
2422 """
2424 #: The test we are going to assert on.
2425 test: BaseExpression
2427 #: The optional message to display if the test evaluates to a falsey value.
2428 msg: Optional[BaseExpression] = None
2430 #: A comma separating test and message, if there is a message.
2431 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2433 #: Whitespace appearing after the ``assert`` keyword and before the test.
2434 whitespace_after_assert: SimpleWhitespace = SimpleWhitespace.field(" ")
2436 #: Optional semicolon when this is used in a statement line. This semicolon
2437 #: owns the whitespace on both sides of it when it is used.
2438 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2440 def _validate(self) -> None:
2441 # Validate whitespace
2442 if (
2443 self.whitespace_after_assert.empty
2444 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2445 ):
2446 raise CSTValidationError("Must have at least one space after 'assert'.")
2448 # Validate comma rules
2449 if self.msg is None and isinstance(self.comma, Comma):
2450 raise CSTValidationError("Cannot have trailing comma after 'test'.")
2452 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Assert":
2453 return Assert(
2454 whitespace_after_assert=visit_required(
2455 self, "whitespace_after_assert", self.whitespace_after_assert, visitor
2456 ),
2457 test=visit_required(self, "test", self.test, visitor),
2458 comma=visit_sentinel(self, "comma", self.comma, visitor),
2459 msg=visit_optional(self, "msg", self.msg, visitor),
2460 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2461 )
2463 def _codegen_impl(
2464 self, state: CodegenState, default_semicolon: bool = False
2465 ) -> None:
2466 with state.record_syntactic_position(self):
2467 state.add_token("assert")
2468 self.whitespace_after_assert._codegen(state)
2469 self.test._codegen(state)
2471 comma = self.comma
2472 msg = self.msg
2473 if isinstance(comma, MaybeSentinel):
2474 if msg is not None:
2475 state.add_token(", ")
2476 else:
2477 comma._codegen(state)
2478 if msg is not None:
2479 msg._codegen(state)
2481 semicolon = self.semicolon
2482 if isinstance(semicolon, MaybeSentinel):
2483 if default_semicolon:
2484 state.add_token("; ")
2485 elif isinstance(semicolon, Semicolon):
2486 semicolon._codegen(state)
2489@add_slots
2490@dataclass(frozen=True)
2491class NameItem(CSTNode):
2492 """
2493 A single identifier name inside a :class:`Global` or :class:`Nonlocal` statement.
2495 This exists because a list of names in a ``global`` or ``nonlocal`` statement need
2496 to be separated by a comma, which ends up owned by the :class:`NameItem` node.
2497 """
2499 #: Identifier name.
2500 name: Name
2502 #: This is forbidden for the last :class:`NameItem` in a
2503 #: :class:`Global`/:class:`Nonlocal`, but all other tems inside a ``global`` or
2504 #: ``nonlocal`` statement must contain a comma to separate them.
2505 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2507 def _validate(self) -> None:
2508 # No parens around names here
2509 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
2510 raise CSTValidationError("Cannot have parens around names in NameItem.")
2512 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "NameItem":
2513 return NameItem(
2514 name=visit_required(self, "name", self.name, visitor),
2515 comma=visit_sentinel(self, "comma", self.comma, visitor),
2516 )
2518 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
2519 with state.record_syntactic_position(self):
2520 self.name._codegen(state)
2522 comma = self.comma
2523 if comma is MaybeSentinel.DEFAULT and default_comma:
2524 state.add_token(", ")
2525 elif isinstance(comma, Comma):
2526 comma._codegen(state)
2529@add_slots
2530@dataclass(frozen=True)
2531class Global(BaseSmallStatement):
2532 """
2533 A ``global`` statement.
2534 """
2536 #: A list of one or more names.
2537 names: Sequence[NameItem]
2539 #: Whitespace appearing after the ``global`` keyword and before the first name.
2540 whitespace_after_global: SimpleWhitespace = SimpleWhitespace.field(" ")
2542 #: Optional semicolon when this is used in a statement line. This semicolon
2543 #: owns the whitespace on both sides of it when it is used.
2544 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2546 def _validate(self) -> None:
2547 if len(self.names) == 0:
2548 raise CSTValidationError(
2549 "A Global statement must have at least one NameItem."
2550 )
2551 if self.names[-1].comma != MaybeSentinel.DEFAULT:
2552 raise CSTValidationError(
2553 "The last NameItem in a Global cannot have a trailing comma."
2554 )
2555 if self.whitespace_after_global.empty:
2556 raise CSTValidationError(
2557 "Must have at least one space after 'global' keyword."
2558 )
2560 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Global":
2561 return Global(
2562 whitespace_after_global=visit_required(
2563 self, "whitespace_after_global", self.whitespace_after_global, visitor
2564 ),
2565 names=visit_sequence(self, "names", self.names, visitor),
2566 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2567 )
2569 def _codegen_impl(
2570 self, state: CodegenState, default_semicolon: bool = False
2571 ) -> None:
2572 with state.record_syntactic_position(self):
2573 state.add_token("global")
2574 self.whitespace_after_global._codegen(state)
2575 last_name = len(self.names) - 1
2576 for i, name in enumerate(self.names):
2577 name._codegen(state, default_comma=(i != last_name))
2579 semicolon = self.semicolon
2580 if isinstance(semicolon, MaybeSentinel):
2581 if default_semicolon:
2582 state.add_token("; ")
2583 elif isinstance(semicolon, Semicolon):
2584 semicolon._codegen(state)
2587@add_slots
2588@dataclass(frozen=True)
2589class Nonlocal(BaseSmallStatement):
2590 """
2591 A ``nonlocal`` statement.
2592 """
2594 #: A list of one or more names.
2595 names: Sequence[NameItem]
2597 #: Whitespace appearing after the ``global`` keyword and before the first name.
2598 whitespace_after_nonlocal: SimpleWhitespace = SimpleWhitespace.field(" ")
2600 #: Optional semicolon when this is used in a statement line. This semicolon
2601 #: owns the whitespace on both sides of it when it is used.
2602 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2604 def _validate(self) -> None:
2605 if len(self.names) == 0:
2606 raise CSTValidationError(
2607 "A Nonlocal statement must have at least one NameItem."
2608 )
2609 if self.names[-1].comma != MaybeSentinel.DEFAULT:
2610 raise CSTValidationError(
2611 "The last NameItem in a Nonlocal cannot have a trailing comma."
2612 )
2613 if self.whitespace_after_nonlocal.empty:
2614 raise CSTValidationError(
2615 "Must have at least one space after 'nonlocal' keyword."
2616 )
2618 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Nonlocal":
2619 return Nonlocal(
2620 whitespace_after_nonlocal=visit_required(
2621 self,
2622 "whitespace_after_nonlocal",
2623 self.whitespace_after_nonlocal,
2624 visitor,
2625 ),
2626 names=visit_sequence(self, "names", self.names, visitor),
2627 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2628 )
2630 def _codegen_impl(
2631 self, state: CodegenState, default_semicolon: bool = False
2632 ) -> None:
2633 with state.record_syntactic_position(self):
2634 state.add_token("nonlocal")
2635 self.whitespace_after_nonlocal._codegen(state)
2636 last_name = len(self.names) - 1
2637 for i, name in enumerate(self.names):
2638 name._codegen(state, default_comma=(i != last_name))
2640 semicolon = self.semicolon
2641 if isinstance(semicolon, MaybeSentinel):
2642 if default_semicolon:
2643 state.add_token("; ")
2644 elif isinstance(semicolon, Semicolon):
2645 semicolon._codegen(state)
2648class MatchPattern(_BaseParenthesizedNode, ABC):
2649 """
2650 A base class for anything that can appear as a pattern in a :class:`Match`
2651 statement.
2652 """
2654 __slots__ = ()
2657@add_slots
2658@dataclass(frozen=True)
2659# pyre-fixme[13]: Attribute `body` is never initialized.
2660class Match(BaseCompoundStatement):
2661 """
2662 A ``match`` statement.
2663 """
2665 #: The subject of the match.
2666 subject: BaseExpression
2668 #: A non-empty list of match cases.
2669 cases: Sequence["MatchCase"]
2671 #: Sequence of empty lines appearing before this compound statement line.
2672 leading_lines: Sequence[EmptyLine] = ()
2674 #: Whitespace between the ``match`` keyword and the subject.
2675 whitespace_after_match: SimpleWhitespace = SimpleWhitespace.field(" ")
2677 #: Whitespace after the subject but before the colon.
2678 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2680 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
2681 whitespace_after_colon: TrailingWhitespace = TrailingWhitespace.field()
2683 #: A string represents a specific indentation. A ``None`` value uses the modules's
2684 #: default indentation. This is included because indentation is allowed to be
2685 #: inconsistent across a file, just not ambiguously.
2686 indent: Optional[str] = None
2688 #: Any trailing comments or lines after the dedent that are owned by this match
2689 #: block. Statements own preceeding and same-line trailing comments, but not
2690 #: trailing lines, so it falls on :class:`Match` to own it. In the case
2691 #: that a statement follows a :class:`Match` block, that statement will own the
2692 #: comments and lines that are at the same indent as the statement, and this
2693 #: :class:`Match` will own the comments and lines that are indented further.
2694 footer: Sequence[EmptyLine] = ()
2696 def _validate(self) -> None:
2697 if len(self.cases) == 0:
2698 raise CSTValidationError("A match statement must have at least one case.")
2700 indent = self.indent
2701 if indent is not None:
2702 if len(indent) == 0:
2703 raise CSTValidationError(
2704 "A match statement must have a non-zero width indent."
2705 )
2706 if _INDENT_WHITESPACE_RE.fullmatch(indent) is None:
2707 raise CSTValidationError(
2708 "An indent must be composed of only whitespace characters."
2709 )
2711 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Match":
2712 return Match(
2713 leading_lines=visit_sequence(
2714 self, "leading_lines", self.leading_lines, visitor
2715 ),
2716 whitespace_after_match=visit_required(
2717 self, "whitespace_after_match", self.whitespace_after_match, visitor
2718 ),
2719 subject=visit_required(self, "subject", self.subject, visitor),
2720 whitespace_before_colon=visit_required(
2721 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2722 ),
2723 whitespace_after_colon=visit_required(
2724 self, "whitespace_after_colon", self.whitespace_after_colon, visitor
2725 ),
2726 indent=self.indent,
2727 cases=visit_sequence(self, "cases", self.cases, visitor),
2728 footer=visit_sequence(self, "footer", self.footer, visitor),
2729 )
2731 def _codegen_impl(self, state: CodegenState) -> None:
2732 for ll in self.leading_lines:
2733 ll._codegen(state)
2734 state.add_indent_tokens()
2736 with state.record_syntactic_position(self, end_node=self.cases[-1]):
2737 state.add_token("match")
2738 self.whitespace_after_match._codegen(state)
2739 self.subject._codegen(state)
2740 self.whitespace_before_colon._codegen(state)
2741 state.add_token(":")
2742 self.whitespace_after_colon._codegen(state)
2744 indent = self.indent
2745 state.increase_indent(state.default_indent if indent is None else indent)
2746 for c in self.cases:
2747 c._codegen(state)
2749 for f in self.footer:
2750 f._codegen(state)
2752 state.decrease_indent()
2755@add_slots
2756@dataclass(frozen=True)
2757class MatchCase(CSTNode):
2758 """
2759 A single ``case`` block of a :class:`Match` statement.
2760 """
2762 #: The pattern that ``subject`` will be matched against.
2763 pattern: MatchPattern
2765 #: The body of this case block, to be evaluated if ``pattern`` matches ``subject``
2766 #: and ``guard`` evaluates to a truthy value.
2767 body: BaseSuite
2769 #: Optional expression that will be evaluated if ``pattern`` matches ``subject``.
2770 guard: Optional[BaseExpression] = None
2772 #: Sequence of empty lines appearing before this case block.
2773 leading_lines: Sequence[EmptyLine] = ()
2775 #: Whitespace directly after the ``case`` keyword.
2776 whitespace_after_case: SimpleWhitespace = SimpleWhitespace.field(" ")
2778 #: Whitespace before the ``if`` keyword in case there's a guard expression.
2779 whitespace_before_if: SimpleWhitespace = SimpleWhitespace.field("")
2781 #: Whitespace after the ``if`` keyword in case there's a guard expression.
2782 whitespace_after_if: SimpleWhitespace = SimpleWhitespace.field("")
2784 #: Whitespace before the colon.
2785 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2787 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
2788 return MatchCase(
2789 leading_lines=visit_sequence(
2790 self, "leading_lines", self.leading_lines, visitor
2791 ),
2792 whitespace_after_case=visit_required(
2793 self, "whitespace_after_case", self.whitespace_after_case, visitor
2794 ),
2795 pattern=visit_required(self, "pattern", self.pattern, visitor),
2796 # pyre-fixme[6]: Expected `SimpleWhitespace` for 4th param but got
2797 # `Optional[SimpleWhitespace]`.
2798 whitespace_before_if=visit_optional(
2799 self, "whitespace_before_if", self.whitespace_before_if, visitor
2800 ),
2801 # pyre-fixme[6]: Expected `SimpleWhitespace` for 5th param but got
2802 # `Optional[SimpleWhitespace]`.
2803 whitespace_after_if=visit_optional(
2804 self, "whitespace_after_if", self.whitespace_after_if, visitor
2805 ),
2806 guard=visit_optional(self, "guard", self.guard, visitor),
2807 body=visit_required(self, "body", self.body, visitor),
2808 )
2810 def _codegen_impl(self, state: CodegenState) -> None:
2811 for ll in self.leading_lines:
2812 ll._codegen(state)
2813 state.add_indent_tokens()
2814 with state.record_syntactic_position(self, end_node=self.body):
2815 state.add_token("case")
2816 self.whitespace_after_case._codegen(state)
2817 self.pattern._codegen(state)
2819 guard = self.guard
2820 if guard is not None:
2821 self.whitespace_before_if._codegen(state)
2822 state.add_token("if")
2823 self.whitespace_after_if._codegen(state)
2824 guard._codegen(state)
2826 self.whitespace_before_colon._codegen(state)
2827 state.add_token(":")
2828 self.body._codegen(state)
2831@add_slots
2832@dataclass(frozen=True)
2833class MatchValue(MatchPattern):
2834 """
2835 A match literal or value pattern that compares by equality.
2836 """
2838 #: an expression to compare to
2839 value: BaseExpression
2841 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
2842 return MatchValue(value=visit_required(self, "value", self.value, visitor))
2844 def _codegen_impl(self, state: CodegenState) -> None:
2845 with state.record_syntactic_position(self):
2846 self.value._codegen(state)
2848 @property
2849 def lpar(self) -> Sequence[LeftParen]:
2850 return self.value.lpar
2852 @lpar.setter
2853 def lpar(self, value: Sequence[LeftParen]) -> None:
2854 self.value.lpar = value
2856 @property
2857 def rpar(self) -> Sequence[RightParen]:
2858 return self.value.rpar
2860 @rpar.setter
2861 def rpar(self, value: Sequence[RightParen]) -> None:
2862 self.value.rpar = value
2865@add_slots
2866@dataclass(frozen=True)
2867class MatchSingleton(MatchPattern):
2868 """
2869 A match literal pattern that compares by identity.
2870 """
2872 #: a literal to compare to
2873 value: Name
2875 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
2876 return MatchSingleton(value=visit_required(self, "value", self.value, visitor))
2878 def _validate(self) -> None:
2879 if self.value.value not in {"True", "False", "None"}:
2880 raise CSTValidationError(
2881 "A match singleton can only be True, False, or None"
2882 )
2884 def _codegen_impl(self, state: CodegenState) -> None:
2885 with state.record_syntactic_position(self):
2886 self.value._codegen(state)
2888 @property
2889 def lpar(self) -> Sequence[LeftParen]:
2890 return self.value.lpar
2892 @lpar.setter
2893 def lpar(self, value: Sequence[LeftParen]) -> None:
2894 # pyre-fixme[41]: Cannot reassign final attribute `lpar`.
2895 self.value.lpar = value
2897 @property
2898 def rpar(self) -> Sequence[RightParen]:
2899 return self.value.rpar
2901 @rpar.setter
2902 def rpar(self, value: Sequence[RightParen]) -> None:
2903 # pyre-fixme[41]: Cannot reassign final attribute `rpar`.
2904 self.value.rpar = value
2907@add_slots
2908@dataclass(frozen=True)
2909class MatchSequenceElement(CSTNode):
2910 """
2911 An element in a sequence match pattern.
2912 """
2914 value: MatchPattern
2916 #: An optional trailing comma.
2917 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2919 def _visit_and_replace_children(
2920 self, visitor: CSTVisitorT
2921 ) -> "MatchSequenceElement":
2922 return MatchSequenceElement(
2923 value=visit_required(self, "value", self.value, visitor),
2924 comma=visit_sentinel(self, "comma", self.comma, visitor),
2925 )
2927 def _codegen_impl(
2928 self,
2929 state: CodegenState,
2930 default_comma: bool = False,
2931 default_comma_whitespace: bool = True,
2932 ) -> None:
2933 with state.record_syntactic_position(self):
2934 self.value._codegen(state)
2935 comma = self.comma
2936 if comma is MaybeSentinel.DEFAULT and default_comma:
2937 state.add_token(", " if default_comma_whitespace else ",")
2938 elif isinstance(comma, Comma):
2939 comma._codegen(state)
2942@add_slots
2943@dataclass(frozen=True)
2944class MatchStar(CSTNode):
2945 """
2946 A starred element in a sequence match pattern. Matches the rest of the sequence.
2947 """
2949 #: The name of the pattern binding. A ``None`` value represents ``*_``.
2950 name: Optional[Name] = None
2952 #: An optional trailing comma.
2953 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2955 #: Optional whitespace between the star and the name.
2956 whitespace_before_name: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
2958 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchStar":
2959 return MatchStar(
2960 whitespace_before_name=visit_required(
2961 self, "whitespace_before_name", self.whitespace_before_name, visitor
2962 ),
2963 name=visit_optional(self, "name", self.name, visitor),
2964 comma=visit_sentinel(self, "comma", self.comma, visitor),
2965 )
2967 def _codegen_impl(
2968 self,
2969 state: CodegenState,
2970 default_comma: bool = False,
2971 default_comma_whitespace: bool = True,
2972 ) -> None:
2973 with state.record_syntactic_position(self):
2974 state.add_token("*")
2975 self.whitespace_before_name._codegen(state)
2976 name = self.name
2977 if name is None:
2978 state.add_token("_")
2979 else:
2980 name._codegen(state)
2981 comma = self.comma
2982 if comma is MaybeSentinel.DEFAULT and default_comma:
2983 state.add_token(", " if default_comma_whitespace else ",")
2984 elif isinstance(comma, Comma):
2985 comma._codegen(state)
2988class MatchSequence(MatchPattern, ABC):
2989 """
2990 A match sequence pattern. It's either a :class:`MatchList` or a :class:`MatchTuple`.
2991 Matches a variable length sequence if one of the patterns is a :class:`MatchStar`,
2992 otherwise matches a fixed length sequence.
2993 """
2995 __slots__ = ()
2997 #: Patterns to be matched against the subject elements if it is a sequence.
2998 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3001@add_slots
3002@dataclass(frozen=True)
3003class MatchList(MatchSequence):
3004 """
3005 A list match pattern. It's either an "open sequence pattern" (without brackets) or a
3006 regular list literal (with brackets).
3007 """
3009 #: Patterns to be matched against the subject elements if it is a sequence.
3010 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3012 #: An optional left bracket. If missing, this is an open sequence pattern.
3013 lbracket: Optional[LeftSquareBracket] = LeftSquareBracket.field()
3015 #: An optional left bracket. If missing, this is an open sequence pattern.
3016 rbracket: Optional[RightSquareBracket] = RightSquareBracket.field()
3018 #: Parenthesis at the beginning of the node
3019 lpar: Sequence[LeftParen] = ()
3020 #: Parentheses after the pattern, but before a comma (if there is one).
3021 rpar: Sequence[RightParen] = ()
3023 def _validate(self) -> None:
3024 if self.lbracket and not self.rbracket:
3025 raise CSTValidationError("Cannot have left bracket without right bracket")
3026 if self.rbracket and not self.lbracket:
3027 raise CSTValidationError("Cannot have right bracket without left bracket")
3029 if not self.patterns and not self.lbracket:
3030 raise CSTValidationError(
3031 "Must have brackets if matching against empty list"
3032 )
3034 super(MatchList, self)._validate()
3036 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchList":
3037 return MatchList(
3038 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3039 lbracket=visit_optional(self, "lbracket", self.lbracket, visitor),
3040 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3041 rbracket=visit_optional(self, "rbracket", self.rbracket, visitor),
3042 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3043 )
3045 def _codegen_impl(self, state: CodegenState) -> None:
3046 with self._parenthesize(state):
3047 lbracket = self.lbracket
3048 if lbracket is not None:
3049 lbracket._codegen(state)
3050 pats = self.patterns
3051 for idx, pat in enumerate(pats):
3052 pat._codegen(state, default_comma=(idx < len(pats) - 1))
3053 rbracket = self.rbracket
3054 if rbracket is not None:
3055 rbracket._codegen(state)
3058@add_slots
3059@dataclass(frozen=True)
3060class MatchTuple(MatchSequence):
3061 """
3062 A tuple match pattern.
3063 """
3065 #: Patterns to be matched against the subject elements if it is a sequence.
3066 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3068 #: Parenthesis at the beginning of the node
3069 lpar: Sequence[LeftParen] = field(default_factory=lambda: (LeftParen(),))
3070 #: Parentheses after the pattern, but before a comma (if there is one).
3071 rpar: Sequence[RightParen] = field(default_factory=lambda: (RightParen(),))
3073 def _validate(self) -> None:
3074 if len(self.lpar) < 1:
3075 raise CSTValidationError(
3076 "Tuple patterns must have at least pair of parenthesis"
3077 )
3079 super(MatchTuple, self)._validate()
3081 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchTuple":
3082 return MatchTuple(
3083 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3084 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3085 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3086 )
3088 def _codegen_impl(self, state: CodegenState) -> None:
3089 with self._parenthesize(state):
3090 pats = self.patterns
3091 patlen = len(pats)
3092 for idx, pat in enumerate(pats):
3093 pat._codegen(
3094 state,
3095 default_comma=patlen == 1 or (idx < patlen - 1),
3096 default_comma_whitespace=patlen != 1,
3097 )
3100@add_slots
3101@dataclass(frozen=True)
3102class MatchMappingElement(CSTNode):
3103 """
3104 A ``key: value`` pair in a match mapping pattern.
3105 """
3107 key: BaseExpression
3109 #: The pattern to be matched corresponding to ``key``.
3110 pattern: MatchPattern
3112 #: An optional trailing comma.
3113 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3115 #: Whitespace between ``key`` and the colon.
3116 whitespace_before_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3118 #: Whitespace between the colon and ``pattern``.
3119 whitespace_after_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
3121 def _visit_and_replace_children(
3122 self, visitor: CSTVisitorT
3123 ) -> "MatchMappingElement":
3124 return MatchMappingElement(
3125 key=visit_required(self, "key", self.key, visitor),
3126 whitespace_before_colon=visit_required(
3127 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
3128 ),
3129 whitespace_after_colon=visit_required(
3130 self, "whitespace_after_colon", self.whitespace_after_colon, visitor
3131 ),
3132 pattern=visit_required(self, "pattern", self.pattern, visitor),
3133 comma=visit_sentinel(self, "comma", self.comma, visitor),
3134 )
3136 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
3137 with state.record_syntactic_position(self):
3138 self.key._codegen(state)
3139 self.whitespace_before_colon._codegen(state)
3140 state.add_token(":")
3141 self.whitespace_after_colon._codegen(state)
3142 self.pattern._codegen(state)
3143 comma = self.comma
3144 if comma is MaybeSentinel.DEFAULT and default_comma:
3145 state.add_token(", ")
3146 elif isinstance(comma, Comma):
3147 comma._codegen(state)
3150@add_slots
3151@dataclass(frozen=True)
3152class MatchMapping(MatchPattern):
3153 """
3154 A match mapping pattern.
3155 """
3157 #: A sequence of mapping elements.
3158 elements: Sequence[MatchMappingElement] = ()
3160 #: Left curly brace at the beginning of the pattern.
3161 lbrace: LeftCurlyBrace = LeftCurlyBrace.field()
3163 #: Right curly brace at the end of the pattern.
3164 rbrace: RightCurlyBrace = RightCurlyBrace.field()
3166 #: An optional name to capture the remaining elements of the mapping.
3167 rest: Optional[Name] = None
3169 #: Optional whitespace between stars and ``rest``.
3170 whitespace_before_rest: SimpleWhitespace = SimpleWhitespace.field("")
3172 #: An optional trailing comma attached to ``rest``.
3173 trailing_comma: Optional[Comma] = None
3175 #: Parenthesis at the beginning of the node
3176 lpar: Sequence[LeftParen] = ()
3177 #: Parentheses after the pattern
3178 rpar: Sequence[RightParen] = ()
3180 def _validate(self) -> None:
3181 if isinstance(self.trailing_comma, Comma) and self.rest is not None:
3182 raise CSTValidationError("Cannot have a trailing comma without **rest")
3183 super(MatchMapping, self)._validate()
3185 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchMapping":
3186 return MatchMapping(
3187 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3188 lbrace=visit_required(self, "lbrace", self.lbrace, visitor),
3189 elements=visit_sequence(self, "elements", self.elements, visitor),
3190 whitespace_before_rest=visit_required(
3191 self, "whitespace_before_rest", self.whitespace_before_rest, visitor
3192 ),
3193 rest=visit_optional(self, "rest", self.rest, visitor),
3194 trailing_comma=visit_optional(
3195 self, "trailing_comma", self.trailing_comma, visitor
3196 ),
3197 rbrace=visit_required(self, "rbrace", self.rbrace, visitor),
3198 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3199 )
3201 def _codegen_impl(self, state: CodegenState) -> None:
3202 with self._parenthesize(state):
3203 self.lbrace._codegen(state)
3204 elems = self.elements
3205 rest = self.rest
3206 for idx, el in enumerate(elems):
3207 el._codegen(
3208 state, default_comma=rest is not None or idx < len(elems) - 1
3209 )
3211 if rest is not None:
3212 state.add_token("**")
3213 self.whitespace_before_rest._codegen(state)
3214 rest._codegen(state)
3215 comma = self.trailing_comma
3216 if comma is not None:
3217 comma._codegen(state)
3219 self.rbrace._codegen(state)
3222@add_slots
3223@dataclass(frozen=True)
3224class MatchKeywordElement(CSTNode):
3225 """
3226 A key=value pair in a :class:`MatchClass`.
3227 """
3229 key: Name
3231 #: The pattern to be matched against the attribute named ``key``.
3232 pattern: MatchPattern
3234 #: An optional trailing comma.
3235 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3237 #: Whitespace between ``key`` and the equals sign.
3238 whitespace_before_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3240 #: Whitespace between the equals sign and ``pattern``.
3241 whitespace_after_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3243 def _visit_and_replace_children(
3244 self, visitor: CSTVisitorT
3245 ) -> "MatchKeywordElement":
3246 return MatchKeywordElement(
3247 key=visit_required(self, "key", self.key, visitor),
3248 whitespace_before_equal=visit_required(
3249 self, "whitespace_before_equal", self.whitespace_before_equal, visitor
3250 ),
3251 whitespace_after_equal=visit_required(
3252 self, "whitespace_after_equal", self.whitespace_after_equal, visitor
3253 ),
3254 pattern=visit_required(self, "pattern", self.pattern, visitor),
3255 comma=visit_sentinel(self, "comma", self.comma, visitor),
3256 )
3258 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
3259 with state.record_syntactic_position(self):
3260 self.key._codegen(state)
3261 self.whitespace_before_equal._codegen(state)
3262 state.add_token("=")
3263 self.whitespace_after_equal._codegen(state)
3264 self.pattern._codegen(state)
3265 comma = self.comma
3266 if comma is MaybeSentinel.DEFAULT and default_comma:
3267 state.add_token(", ")
3268 elif isinstance(comma, Comma):
3269 comma._codegen(state)
3272@add_slots
3273@dataclass(frozen=True)
3274class MatchClass(MatchPattern):
3275 """
3276 A match class pattern.
3277 """
3279 #: An expression giving the nominal class to be matched.
3280 cls: BaseExpression
3282 #: A sequence of patterns to be matched against the class defined sequence of
3283 #: pattern matching attributes.
3284 patterns: Sequence[MatchSequenceElement] = ()
3286 #: A sequence of additional attribute names and corresponding patterns to be
3287 #: matched.
3288 kwds: Sequence[MatchKeywordElement] = ()
3290 #: Whitespace between the class name and the left parenthesis.
3291 whitespace_after_cls: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3293 #: Whitespace between the left parenthesis and the first pattern.
3294 whitespace_before_patterns: BaseParenthesizableWhitespace = SimpleWhitespace.field(
3295 ""
3296 )
3298 #: Whitespace between the last pattern and the right parenthesis.
3299 whitespace_after_kwds: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3301 #: Parenthesis at the beginning of the node
3302 lpar: Sequence[LeftParen] = ()
3303 #: Parentheses after the pattern
3304 rpar: Sequence[RightParen] = ()
3306 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchClass":
3307 return MatchClass(
3308 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3309 cls=visit_required(self, "cls", self.cls, visitor),
3310 whitespace_after_cls=visit_required(
3311 self, "whitespace_after_cls", self.whitespace_after_cls, visitor
3312 ),
3313 whitespace_before_patterns=visit_required(
3314 self,
3315 "whitespace_before_patterns",
3316 self.whitespace_before_patterns,
3317 visitor,
3318 ),
3319 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3320 kwds=visit_sequence(self, "kwds", self.kwds, visitor),
3321 whitespace_after_kwds=visit_required(
3322 self, "whitespace_after_kwds", self.whitespace_after_kwds, visitor
3323 ),
3324 )
3326 def _codegen_impl(self, state: CodegenState) -> None:
3327 with self._parenthesize(state):
3328 self.cls._codegen(state)
3329 self.whitespace_after_cls._codegen(state)
3330 state.add_token("(")
3331 self.whitespace_before_patterns._codegen(state)
3332 pats = self.patterns
3333 kwds = self.kwds
3334 for idx, pat in enumerate(pats):
3335 pat._codegen(state, default_comma=idx + 1 < len(pats) + len(kwds))
3336 for idx, kwd in enumerate(kwds):
3337 kwd._codegen(state, default_comma=idx + 1 < len(kwds))
3338 self.whitespace_after_kwds._codegen(state)
3339 state.add_token(")")
3342@add_slots
3343@dataclass(frozen=True)
3344class MatchAs(MatchPattern):
3345 """
3346 A match "as-pattern", capture pattern, or wildcard pattern.
3347 """
3349 #: The match pattern that the subject will be matched against. If this is ``None``,
3350 #: the node represents a capture pattern (i.e. a bare name) and will always succeed.
3351 pattern: Optional[MatchPattern] = None
3353 #: The name that will be bound if the pattern is successful. If this is ``None``,
3354 #: ``pattern`` must also be ``None`` and the node represents the wildcard pattern
3355 #: (i.e. ``_``).
3356 name: Optional[Name] = None
3358 #: Whitespace between ``pattern`` and the ``as`` keyword (if ``pattern`` is not
3359 #: ``None``)
3360 whitespace_before_as: Union[
3361 BaseParenthesizableWhitespace, MaybeSentinel
3362 ] = MaybeSentinel.DEFAULT
3364 #: Whitespace between the ``as`` keyword and ``name`` (if ``pattern`` is not
3365 #: ``None``)
3366 whitespace_after_as: Union[
3367 BaseParenthesizableWhitespace, MaybeSentinel
3368 ] = MaybeSentinel.DEFAULT
3370 #: Parenthesis at the beginning of the node
3371 lpar: Sequence[LeftParen] = ()
3372 #: Parentheses after the pattern
3373 rpar: Sequence[RightParen] = ()
3375 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchAs":
3376 return MatchAs(
3377 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3378 pattern=visit_optional(self, "pattern", self.pattern, visitor),
3379 whitespace_before_as=visit_sentinel(
3380 self, "whitespace_before_as", self.whitespace_before_as, visitor
3381 ),
3382 whitespace_after_as=visit_sentinel(
3383 self, "whitespace_after_as", self.whitespace_after_as, visitor
3384 ),
3385 name=visit_optional(self, "name", self.name, visitor),
3386 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3387 )
3389 def _validate(self) -> None:
3390 if self.name is None and self.pattern is not None:
3391 raise CSTValidationError("Pattern must be None if name is None")
3392 super(MatchAs, self)._validate()
3394 def _codegen_impl(self, state: CodegenState) -> None:
3395 with self._parenthesize(state):
3396 pat = self.pattern
3397 name = self.name
3398 if pat is not None:
3399 pat._codegen(state)
3400 ws_before = self.whitespace_before_as
3401 if ws_before is MaybeSentinel.DEFAULT:
3402 state.add_token(" ")
3403 elif isinstance(ws_before, BaseParenthesizableWhitespace):
3404 ws_before._codegen(state)
3405 state.add_token("as")
3406 ws_after = self.whitespace_after_as
3407 if ws_after is MaybeSentinel.DEFAULT:
3408 state.add_token(" ")
3409 elif isinstance(ws_after, BaseParenthesizableWhitespace):
3410 ws_after._codegen(state)
3411 if name is None:
3412 state.add_token("_")
3413 else:
3414 name._codegen(state)
3417@add_slots
3418@dataclass(frozen=True)
3419class MatchOrElement(CSTNode):
3420 """
3421 An element in a :class:`MatchOr` node.
3422 """
3424 pattern: MatchPattern
3426 #: An optional ``|`` separator.
3427 separator: Union[BitOr, MaybeSentinel] = MaybeSentinel.DEFAULT
3429 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOrElement":
3430 return MatchOrElement(
3431 pattern=visit_required(self, "pattern", self.pattern, visitor),
3432 separator=visit_sentinel(self, "separator", self.separator, visitor),
3433 )
3435 def _codegen_impl(
3436 self, state: CodegenState, default_separator: bool = False
3437 ) -> None:
3438 with state.record_syntactic_position(self):
3439 self.pattern._codegen(state)
3440 sep = self.separator
3441 if sep is MaybeSentinel.DEFAULT and default_separator:
3442 state.add_token(" | ")
3443 elif isinstance(sep, BitOr):
3444 sep._codegen(state)
3447@add_slots
3448@dataclass(frozen=True)
3449class MatchOr(MatchPattern):
3450 """
3451 A match "or-pattern". It matches each of its subpatterns in turn to the subject,
3452 until one succeeds. The or-pattern is then deemed to succeed. If none of the
3453 subpatterns succeed the or-pattern fails.
3454 """
3456 #: The subpatterns to be tried in turn.
3457 patterns: Sequence[MatchOrElement]
3459 #: Parenthesis at the beginning of the node
3460 lpar: Sequence[LeftParen] = ()
3461 #: Parentheses after the pattern
3462 rpar: Sequence[RightParen] = ()
3464 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOr":
3465 return MatchOr(
3466 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3467 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3468 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3469 )
3471 def _codegen_impl(self, state: CodegenState) -> None:
3472 with self._parenthesize(state):
3473 pats = self.patterns
3474 for idx, pat in enumerate(pats):
3475 pat._codegen(state, default_separator=idx + 1 < len(pats))