Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/libcst/_nodes/statement.py: 45%
1647 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:43 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:43 +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 Colon,
52 Comma,
53 Dot,
54 ImportStar,
55 Semicolon,
56)
57from libcst._nodes.whitespace import (
58 BaseParenthesizableWhitespace,
59 EmptyLine,
60 ParenthesizedWhitespace,
61 SimpleWhitespace,
62 TrailingWhitespace,
63)
64from libcst._visitors import CSTVisitorT
66_INDENT_WHITESPACE_RE: Pattern[str] = re.compile(r"[ \f\t]+", re.UNICODE)
69class BaseSuite(CSTNode, ABC):
70 """
71 A dummy base-class for both :class:`SimpleStatementSuite` and :class:`IndentedBlock`.
72 This exists to simplify type definitions and isinstance checks.
74 A suite is a group of statements controlled by a clause. A suite can be one or
75 more semicolon-separated simple statements on the same line as the header,
76 following the header’s colon, or it can be one or more indented statements on
77 subsequent lines.
79 -- https://docs.python.org/3/reference/compound_stmts.html
80 """
82 __slots__ = ()
84 body: Union[Sequence["BaseStatement"], Sequence["BaseSmallStatement"]]
87class BaseStatement(CSTNode, ABC):
88 """
89 A class that exists to allow for typing to specify that any statement is allowed
90 in a particular location.
91 """
93 __slots__ = ()
96class BaseSmallStatement(CSTNode, ABC):
97 """
98 Encapsulates a small statement, like ``del`` or ``pass``, and optionally adds a
99 trailing semicolon. A small statement is always contained inside a
100 :class:`SimpleStatementLine` or :class:`SimpleStatementSuite`. This exists to
101 simplify type definitions and isinstance checks.
102 """
104 __slots__ = ()
106 #: An optional semicolon that appears after a small statement. This is optional
107 #: for the last small statement in a :class:`SimpleStatementLine` or
108 #: :class:`SimpleStatementSuite`, but all other small statements inside a simple
109 #: statement must contain a semicolon to disambiguate multiple small statements
110 #: on the same line.
111 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
113 @abstractmethod
114 def _codegen_impl(
115 self, state: CodegenState, default_semicolon: bool = False
116 ) -> None:
117 ...
120@add_slots
121@dataclass(frozen=True)
122class Del(BaseSmallStatement):
123 """
124 Represents a ``del`` statement. ``del`` is always followed by a target.
125 """
127 #: The target expression will be deleted. This can be a name, a tuple,
128 #: an item of a list, an item of a dictionary, or an attribute.
129 target: BaseDelTargetExpression
131 #: The whitespace after the ``del`` keyword.
132 whitespace_after_del: SimpleWhitespace = SimpleWhitespace.field(" ")
134 #: Optional semicolon when this is used in a statement line. This semicolon
135 #: owns the whitespace on both sides of it when it is used.
136 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
138 def _validate(self) -> None:
139 if (
140 self.whitespace_after_del.empty
141 and not self.target._safe_to_use_with_word_operator(
142 ExpressionPosition.RIGHT
143 )
144 ):
145 raise CSTValidationError("Must have at least one space after 'del'.")
147 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Del":
148 return Del(
149 target=visit_required(self, "target", self.target, visitor),
150 whitespace_after_del=visit_required(
151 self, "whitespace_after_del", self.whitespace_after_del, visitor
152 ),
153 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
154 )
156 def _codegen_impl(
157 self, state: CodegenState, default_semicolon: bool = False
158 ) -> None:
159 with state.record_syntactic_position(self):
160 state.add_token("del")
161 self.whitespace_after_del._codegen(state)
162 self.target._codegen(state)
164 semicolon = self.semicolon
165 if isinstance(semicolon, MaybeSentinel):
166 if default_semicolon:
167 state.add_token("; ")
168 elif isinstance(semicolon, Semicolon):
169 semicolon._codegen(state)
172@add_slots
173@dataclass(frozen=True)
174class Pass(BaseSmallStatement):
175 """
176 Represents a ``pass`` statement.
177 """
179 #: Optional semicolon when this is used in a statement line. This semicolon
180 #: owns the whitespace on both sides of it when it is used.
181 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
183 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Pass":
184 return Pass(
185 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor)
186 )
188 def _codegen_impl(
189 self, state: CodegenState, default_semicolon: bool = False
190 ) -> None:
191 with state.record_syntactic_position(self):
192 state.add_token("pass")
194 semicolon = self.semicolon
195 if isinstance(semicolon, MaybeSentinel):
196 if default_semicolon:
197 state.add_token("; ")
198 elif isinstance(semicolon, Semicolon):
199 semicolon._codegen(state)
202@add_slots
203@dataclass(frozen=True)
204class Break(BaseSmallStatement):
205 """
206 Represents a ``break`` statement, which is used to break out of a :class:`For`
207 or :class:`While` loop early.
208 """
210 #: Optional semicolon when this is used in a statement line. This semicolon
211 #: owns the whitespace on both sides of it when it is used.
212 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
214 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Break":
215 return Break(
216 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor)
217 )
219 def _codegen_impl(
220 self, state: CodegenState, default_semicolon: bool = False
221 ) -> None:
222 with state.record_syntactic_position(self):
223 state.add_token("break")
225 semicolon = self.semicolon
226 if isinstance(semicolon, MaybeSentinel):
227 if default_semicolon:
228 state.add_token("; ")
229 elif isinstance(semicolon, Semicolon):
230 semicolon._codegen(state)
233@add_slots
234@dataclass(frozen=True)
235class Continue(BaseSmallStatement):
236 """
237 Represents a ``continue`` statement, which is used to skip to the next iteration
238 in a :class:`For` or :class:`While` loop.
239 """
241 #: Optional semicolon when this is used in a statement line. This semicolon
242 #: owns the whitespace on both sides of it when it is used.
243 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
245 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Continue":
246 return Continue(
247 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor)
248 )
250 def _codegen_impl(
251 self, state: CodegenState, default_semicolon: bool = False
252 ) -> None:
253 with state.record_syntactic_position(self):
254 state.add_token("continue")
256 semicolon = self.semicolon
257 if isinstance(semicolon, MaybeSentinel):
258 if default_semicolon:
259 state.add_token("; ")
260 elif isinstance(semicolon, Semicolon):
261 semicolon._codegen(state)
264@add_slots
265@dataclass(frozen=True)
266class Return(BaseSmallStatement):
267 """
268 Represents a ``return`` or a ``return x`` statement.
269 """
271 #: The optional expression that will be evaluated and returned.
272 value: Optional[BaseExpression] = None
274 #: Optional whitespace after the ``return`` keyword before the optional
275 #: value expression.
276 whitespace_after_return: Union[
277 SimpleWhitespace, MaybeSentinel
278 ] = MaybeSentinel.DEFAULT
280 #: Optional semicolon when this is used in a statement line. This semicolon
281 #: owns the whitespace on both sides of it when it is used.
282 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
284 def _validate(self) -> None:
285 value = self.value
286 if value is not None:
287 whitespace_after_return = self.whitespace_after_return
288 has_no_gap = (
289 not isinstance(whitespace_after_return, MaybeSentinel)
290 and whitespace_after_return.empty
291 )
292 if has_no_gap and not value._safe_to_use_with_word_operator(
293 ExpressionPosition.RIGHT
294 ):
295 raise CSTValidationError("Must have at least one space after 'return'.")
297 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Return":
298 return Return(
299 whitespace_after_return=visit_sentinel(
300 self, "whitespace_after_return", self.whitespace_after_return, visitor
301 ),
302 value=visit_optional(self, "value", self.value, visitor),
303 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
304 )
306 def _codegen_impl(
307 self, state: CodegenState, default_semicolon: bool = False
308 ) -> None:
309 with state.record_syntactic_position(self):
310 state.add_token("return")
311 whitespace_after_return = self.whitespace_after_return
312 value = self.value
313 if isinstance(whitespace_after_return, MaybeSentinel):
314 if value is not None:
315 state.add_token(" ")
316 else:
317 whitespace_after_return._codegen(state)
318 if value is not None:
319 value._codegen(state)
321 semicolon = self.semicolon
322 if isinstance(semicolon, MaybeSentinel):
323 if default_semicolon:
324 state.add_token("; ")
325 elif isinstance(semicolon, Semicolon):
326 semicolon._codegen(state)
329@add_slots
330@dataclass(frozen=True)
331class Expr(BaseSmallStatement):
332 """
333 An expression used as a statement, where the result is unused and unassigned.
334 The most common place you will find this is in function calls where the return
335 value is unneeded.
336 """
338 #: The expression itself. Python will evaluate the expression but not assign
339 #: the result anywhere.
340 value: BaseExpression
342 #: Optional semicolon when this is used in a statement line. This semicolon
343 #: owns the whitespace on both sides of it when it is used.
344 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
346 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Expr":
347 return Expr(
348 value=visit_required(self, "value", self.value, visitor),
349 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
350 )
352 def _codegen_impl(
353 self, state: CodegenState, default_semicolon: bool = False
354 ) -> None:
355 with state.record_syntactic_position(self):
356 self.value._codegen(state)
358 semicolon = self.semicolon
359 if isinstance(semicolon, MaybeSentinel):
360 if default_semicolon:
361 state.add_token("; ")
362 elif isinstance(semicolon, Semicolon):
363 semicolon._codegen(state)
366class _BaseSimpleStatement(CSTNode, ABC):
367 """
368 A simple statement is a series of small statements joined together by semicolons.
370 simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
372 Whitespace between each small statement is owned by the small statements themselves.
373 It can be found on the required semicolon that will be attached to each non-terminal
374 small statement.
375 """
377 __slots__ = ()
379 #: Sequence of small statements. All but the last statement are required to have
380 #: a semicolon.
381 body: Sequence[BaseSmallStatement]
383 #: Any trailing comment and the final ``NEWLINE``, which is part of small statement's
384 #: grammar.
385 trailing_whitespace: TrailingWhitespace
387 def _validate(self) -> None:
388 body = self.body
389 for small_stmt in body[:-1]:
390 if small_stmt.semicolon is None:
391 raise CSTValidationError(
392 "All but the last SmallStatement in a SimpleStatementLine or "
393 + "SimpleStatementSuite must have a trailing semicolon. Otherwise, "
394 + "there's no way to syntatically disambiguate each SmallStatement "
395 + "on the same line."
396 )
398 def _codegen_impl(self, state: CodegenState) -> None:
399 body = self.body
400 if body:
401 laststmt = len(body) - 1
402 with state.record_syntactic_position(self, end_node=body[laststmt]):
403 for idx, stmt in enumerate(body):
404 stmt._codegen(state, default_semicolon=(idx != laststmt))
405 else:
406 # Empty simple statement blocks are not syntactically valid in Python
407 # unless they contain a 'pass' statement, so add one here.
408 with state.record_syntactic_position(self):
409 state.add_token("pass")
411 self.trailing_whitespace._codegen(state)
414@add_slots
415@dataclass(frozen=True)
416class SimpleStatementLine(_BaseSimpleStatement, BaseStatement):
417 """
418 A simple statement that's part of an IndentedBlock or Module. A simple statement is
419 a series of small statements joined together by semicolons.
421 This isn't differentiated from a :class:`SimpleStatementSuite` in the grammar, but
422 because a :class:`SimpleStatementLine` can own additional whitespace that a
423 :class:`SimpleStatementSuite` doesn't have, we're differentiating it in the CST.
424 """
426 #: Sequence of small statements. All but the last statement are required to have
427 #: a semicolon.
428 body: Sequence[BaseSmallStatement]
430 #: Sequence of empty lines appearing before this simple statement line.
431 leading_lines: Sequence[EmptyLine] = ()
433 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
434 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field()
436 def _visit_and_replace_children(
437 self, visitor: CSTVisitorT
438 ) -> "SimpleStatementLine":
439 return SimpleStatementLine(
440 leading_lines=visit_sequence(
441 self, "leading_lines", self.leading_lines, visitor
442 ),
443 body=visit_sequence(self, "body", self.body, visitor),
444 trailing_whitespace=visit_required(
445 self, "trailing_whitespace", self.trailing_whitespace, visitor
446 ),
447 )
449 def _is_removable(self) -> bool:
450 # If we have an empty body, we are removable since we don't represent
451 # anything concrete.
452 return not self.body
454 def _codegen_impl(self, state: CodegenState) -> None:
455 for ll in self.leading_lines:
456 ll._codegen(state)
457 state.add_indent_tokens()
458 _BaseSimpleStatement._codegen_impl(self, state)
461@add_slots
462@dataclass(frozen=True)
463class SimpleStatementSuite(_BaseSimpleStatement, BaseSuite):
464 """
465 A simple statement that's used as a suite. A simple statement is a series of small
466 statements joined together by semicolons. A suite is the thing that follows the
467 colon in a compound statement.
469 .. code-block::
471 if test:<leading_whitespace><body><trailing_whitespace>
473 This isn't differentiated from a :class:`SimpleStatementLine` in the grammar, but
474 because the two classes need to track different whitespace, we're differentiating
475 it in the CST.
476 """
478 #: Sequence of small statements. All but the last statement are required to have
479 #: a semicolon.
480 body: Sequence[BaseSmallStatement]
482 #: The whitespace between the colon in the parent statement and the body.
483 leading_whitespace: SimpleWhitespace = SimpleWhitespace.field(" ")
485 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
486 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field()
488 def _visit_and_replace_children(
489 self, visitor: CSTVisitorT
490 ) -> "SimpleStatementSuite":
491 return SimpleStatementSuite(
492 leading_whitespace=visit_required(
493 self, "leading_whitespace", self.leading_whitespace, visitor
494 ),
495 body=visit_sequence(self, "body", self.body, visitor),
496 trailing_whitespace=visit_required(
497 self, "trailing_whitespace", self.trailing_whitespace, visitor
498 ),
499 )
501 def _codegen_impl(self, state: CodegenState) -> None:
502 self.leading_whitespace._codegen(state)
503 _BaseSimpleStatement._codegen_impl(self, state)
506@add_slots
507@dataclass(frozen=True)
508class Else(CSTNode):
509 """
510 An ``else`` clause that appears optionally after an :class:`If`, :class:`While`,
511 :class:`Try`, or :class:`For` statement.
513 This node does not match ``elif`` clauses in :class:`If` statements. It also
514 does not match the required ``else`` clause in an :class:`IfExp` expression
515 (``a = if b else c``).
516 """
518 #: The body of else clause.
519 body: BaseSuite
521 #: Sequence of empty lines appearing before this compound statement line.
522 leading_lines: Sequence[EmptyLine] = ()
524 #: The whitespace appearing after the ``else`` keyword but before the colon.
525 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
527 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Else":
528 return Else(
529 leading_lines=visit_sequence(
530 self, "leading_lines", self.leading_lines, visitor
531 ),
532 whitespace_before_colon=visit_required(
533 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
534 ),
535 body=visit_required(self, "body", self.body, visitor),
536 )
538 def _codegen_impl(self, state: CodegenState) -> None:
539 for ll in self.leading_lines:
540 ll._codegen(state)
541 state.add_indent_tokens()
543 with state.record_syntactic_position(self, end_node=self.body):
544 state.add_token("else")
545 self.whitespace_before_colon._codegen(state)
546 state.add_token(":")
547 self.body._codegen(state)
550class BaseCompoundStatement(BaseStatement, ABC):
551 """
552 Encapsulates a compound statement, like ``if True: pass`` or ``while True: pass``.
553 This exists to simplify type definitions and isinstance checks.
555 Compound statements contain (groups of) other statements; they affect or control
556 the execution of those other statements in some way. In general, compound
557 statements span multiple lines, although in simple incarnations a whole compound
558 statement may be contained in one line.
560 -- https://docs.python.org/3/reference/compound_stmts.html
561 """
563 __slots__ = ()
565 #: The body of this compound statement.
566 body: BaseSuite
568 #: Any empty lines or comments appearing before this statement.
569 leading_lines: Sequence[EmptyLine]
572@add_slots
573@dataclass(frozen=True)
574class If(BaseCompoundStatement):
575 """
576 An ``if`` statement. ``test`` holds a single test expression.
578 ``elif`` clauses don’t have a special representation in the AST, but rather appear as
579 extra :class:`If` nodes within the ``orelse`` section of the previous one.
580 """
582 #: The expression that, when evaluated, should give us a truthy/falsey value.
583 test: BaseExpression # TODO: should be a test_nocond
585 #: The body of this compound statement.
586 body: BaseSuite
588 #: An optional ``elif`` or ``else`` clause. :class:`If` signifies an ``elif`` block.
589 #: :class:`Else` signifies an ``else`` block. ``None`` signifies no ``else`` or
590 #:``elif`` block.
591 orelse: Union["If", Else, None] = None
593 #: Sequence of empty lines appearing before this compound statement line.
594 leading_lines: Sequence[EmptyLine] = ()
596 #: The whitespace appearing after the ``if`` keyword but before the test expression.
597 whitespace_before_test: SimpleWhitespace = SimpleWhitespace.field(" ")
599 #: The whitespace appearing after the test expression but before the colon.
600 whitespace_after_test: SimpleWhitespace = SimpleWhitespace.field("")
602 # TODO: _validate
604 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "If":
605 return If(
606 leading_lines=visit_sequence(
607 self, "leading_lines", self.leading_lines, visitor
608 ),
609 whitespace_before_test=visit_required(
610 self, "whitespace_before_test", self.whitespace_before_test, visitor
611 ),
612 test=visit_required(self, "test", self.test, visitor),
613 whitespace_after_test=visit_required(
614 self, "whitespace_after_test", self.whitespace_after_test, visitor
615 ),
616 body=visit_required(self, "body", self.body, visitor),
617 orelse=visit_optional(self, "orelse", self.orelse, visitor),
618 )
620 def _codegen_impl(self, state: CodegenState, is_elif: bool = False) -> None:
621 for ll in self.leading_lines:
622 ll._codegen(state)
623 state.add_indent_tokens()
625 end_node = self.body if self.orelse is None else self.orelse
626 with state.record_syntactic_position(self, end_node=end_node):
627 state.add_token("elif" if is_elif else "if")
628 self.whitespace_before_test._codegen(state)
629 self.test._codegen(state)
630 self.whitespace_after_test._codegen(state)
631 state.add_token(":")
632 self.body._codegen(state)
633 orelse = self.orelse
634 if orelse is not None:
635 if isinstance(orelse, If): # special-case elif
636 orelse._codegen(state, is_elif=True)
637 else: # is an Else clause
638 orelse._codegen(state)
641@add_slots
642@dataclass(frozen=True)
643class IndentedBlock(BaseSuite):
644 """
645 Represents a block of statements beginning with an ``INDENT`` token and ending in a
646 ``DEDENT`` token. Used as the body of compound statements, such as an if statement's
647 body.
649 A common alternative to an :class:`IndentedBlock` is a :class:`SimpleStatementSuite`,
650 which can also be used as a :class:`BaseSuite`, meaning that it can be used as the
651 body of many compound statements.
653 An :class:`IndentedBlock` always occurs after a colon in a
654 :class:`BaseCompoundStatement`, so it owns the trailing whitespace for the compound
655 statement's clause.
657 .. code-block::
659 if test: # IndentedBlock's header
660 body
661 """
663 #: Sequence of statements belonging to this indented block.
664 body: Sequence[BaseStatement]
666 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
667 header: TrailingWhitespace = TrailingWhitespace.field()
669 #: A string represents a specific indentation. A ``None`` value uses the modules's
670 #: default indentation. This is included because indentation is allowed to be
671 #: inconsistent across a file, just not ambiguously.
672 indent: Optional[str] = None
674 #: Any trailing comments or lines after the dedent that are owned by this indented
675 #: block. Statements own preceeding and same-line trailing comments, but not
676 #: trailing lines, so it falls on :class:`IndentedBlock` to own it. In the case
677 #: that a statement follows an :class:`IndentedBlock`, that statement will own the
678 #: comments and lines that are at the same indent as the statement, and this
679 #: :class:`IndentedBlock` will own the comments and lines that are indented further.
680 footer: Sequence[EmptyLine] = ()
682 def _validate(self) -> None:
683 indent = self.indent
684 if indent is not None:
685 if len(indent) == 0:
686 raise CSTValidationError(
687 "An indented block must have a non-zero width indent."
688 )
689 if _INDENT_WHITESPACE_RE.fullmatch(indent) is None:
690 raise CSTValidationError(
691 "An indent must be composed of only whitespace characters."
692 )
694 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "IndentedBlock":
695 return IndentedBlock(
696 header=visit_required(self, "header", self.header, visitor),
697 indent=self.indent,
698 body=visit_body_sequence(self, "body", self.body, visitor),
699 footer=visit_sequence(self, "footer", self.footer, visitor),
700 )
702 def _codegen_impl(self, state: CodegenState) -> None:
703 self.header._codegen(state)
705 indent = self.indent
706 state.increase_indent(state.default_indent if indent is None else indent)
708 if self.body:
709 with state.record_syntactic_position(
710 self, start_node=self.body[0], end_node=self.body[-1]
711 ):
712 for stmt in self.body:
713 # IndentedBlock is responsible for adjusting the current indentation level,
714 # but its children are responsible for actually adding that indentation to
715 # the token list.
716 stmt._codegen(state)
717 else:
718 # Empty indented blocks are not syntactically valid in Python unless
719 # they contain a 'pass' statement, so add one here.
720 state.add_indent_tokens()
721 with state.record_syntactic_position(self):
722 state.add_token("pass")
723 state.add_token(state.default_newline)
725 for f in self.footer:
726 f._codegen(state)
728 state.decrease_indent()
731@add_slots
732@dataclass(frozen=True)
733class AsName(CSTNode):
734 """
735 An ``as name`` clause inside an :class:`ExceptHandler`, :class:`ImportAlias` or
736 :class:`WithItem` node.
737 """
739 #: Identifier that the parent node will be aliased to.
740 name: Union[Name, Tuple, List]
742 #: Whitespace between the parent node and the ``as`` keyword.
743 whitespace_before_as: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
745 #: Whitespace between the ``as`` keyword and the name.
746 whitespace_after_as: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
748 def _validate(self) -> None:
749 if (
750 self.whitespace_after_as.empty
751 and not self.name._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
752 ):
753 raise CSTValidationError(
754 "There must be at least one space between 'as' and name."
755 )
757 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AsName":
758 return AsName(
759 whitespace_before_as=visit_required(
760 self, "whitespace_before_as", self.whitespace_before_as, visitor
761 ),
762 name=visit_required(self, "name", self.name, visitor),
763 whitespace_after_as=visit_required(
764 self, "whitespace_after_as", self.whitespace_after_as, visitor
765 ),
766 )
768 def _codegen_impl(self, state: CodegenState) -> None:
769 self.whitespace_before_as._codegen(state)
770 state.add_token("as")
771 self.whitespace_after_as._codegen(state)
772 self.name._codegen(state)
775@add_slots
776@dataclass(frozen=True)
777class ExceptHandler(CSTNode):
778 """
779 An ``except`` clause that appears optionally after a :class:`Try` statement.
780 """
782 #: The body of the except.
783 body: BaseSuite
785 #: The type of exception this catches. Can be a tuple in some cases,
786 #: or ``None`` if the code is catching all exceptions.
787 type: Optional[BaseExpression] = None
789 #: The optional name that a caught exception is assigned to.
790 name: Optional[AsName] = None
792 #: Sequence of empty lines appearing before this compound statement line.
793 leading_lines: Sequence[EmptyLine] = ()
795 #: The whitespace between the ``except`` keyword and the type attribute.
796 whitespace_after_except: SimpleWhitespace = SimpleWhitespace.field(" ")
798 #: The whitespace after any type or name node (whichever comes last) and
799 #: the colon.
800 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
802 def _validate(self) -> None:
803 name = self.name
804 if self.type is None and name is not None:
805 raise CSTValidationError("Cannot have a name for an empty type.")
806 if name is not None and not isinstance(name.name, Name):
807 raise CSTValidationError(
808 "Must use a Name node for AsName name inside ExceptHandler."
809 )
810 type_ = self.type
811 if type_ is not None and self.whitespace_after_except.empty:
812 # Space is only required when the first char in `type` could start
813 # an identifier. In the most common cases, we want to allow
814 # grouping or tuple parens.
815 if isinstance(type_, Name) and not type_.lpar:
816 raise CSTValidationError(
817 "Must have at least one space after except when ExceptHandler has a type."
818 )
819 name = self.name
820 if (
821 type_ is not None
822 and name is not None
823 and name.whitespace_before_as.empty
824 and not type_._safe_to_use_with_word_operator(ExpressionPosition.LEFT)
825 ):
826 raise CSTValidationError(
827 "Must have at least one space before as keyword in an except handler."
828 )
830 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ExceptHandler":
831 return ExceptHandler(
832 leading_lines=visit_sequence(
833 self, "leading_lines", self.leading_lines, visitor
834 ),
835 whitespace_after_except=visit_required(
836 self, "whitespace_after_except", self.whitespace_after_except, visitor
837 ),
838 type=visit_optional(self, "type", self.type, visitor),
839 name=visit_optional(self, "name", self.name, visitor),
840 whitespace_before_colon=visit_required(
841 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
842 ),
843 body=visit_required(self, "body", self.body, visitor),
844 )
846 def _codegen_impl(self, state: CodegenState) -> None:
847 for ll in self.leading_lines:
848 ll._codegen(state)
849 state.add_indent_tokens()
851 with state.record_syntactic_position(self, end_node=self.body):
852 state.add_token("except")
853 self.whitespace_after_except._codegen(state)
854 typenode = self.type
855 if typenode is not None:
856 typenode._codegen(state)
857 namenode = self.name
858 if namenode is not None:
859 namenode._codegen(state)
860 self.whitespace_before_colon._codegen(state)
861 state.add_token(":")
862 self.body._codegen(state)
865@add_slots
866@dataclass(frozen=True)
867class ExceptStarHandler(CSTNode):
868 """
869 An ``except*`` clause that appears after a :class:`TryStar` statement.
870 """
872 #: The body of the except.
873 body: BaseSuite
875 #: The type of exception this catches. Can be a tuple in some cases.
876 type: BaseExpression
878 #: The optional name that a caught exception is assigned to.
879 name: Optional[AsName] = None
881 #: Sequence of empty lines appearing before this compound statement line.
882 leading_lines: Sequence[EmptyLine] = ()
884 #: The whitespace between the ``except`` keyword and the star.
885 whitespace_after_except: SimpleWhitespace = SimpleWhitespace.field("")
887 #: The whitespace between the star and the type.
888 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field(" ")
890 #: The whitespace after any type or name node (whichever comes last) and
891 #: the colon.
892 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
894 def _validate(self) -> None:
895 name = self.name
896 if name is not None and not isinstance(name.name, Name):
897 raise CSTValidationError(
898 "Must use a Name node for AsName name inside ExceptHandler."
899 )
901 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ExceptStarHandler":
902 return ExceptStarHandler(
903 leading_lines=visit_sequence(
904 self, "leading_lines", self.leading_lines, visitor
905 ),
906 whitespace_after_except=visit_required(
907 self, "whitespace_after_except", self.whitespace_after_except, visitor
908 ),
909 whitespace_after_star=visit_required(
910 self, "whitespace_after_star", self.whitespace_after_star, visitor
911 ),
912 type=visit_required(self, "type", self.type, visitor),
913 name=visit_optional(self, "name", self.name, visitor),
914 whitespace_before_colon=visit_required(
915 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
916 ),
917 body=visit_required(self, "body", self.body, visitor),
918 )
920 def _codegen_impl(self, state: CodegenState) -> None:
921 for ll in self.leading_lines:
922 ll._codegen(state)
923 state.add_indent_tokens()
925 with state.record_syntactic_position(self, end_node=self.body):
926 state.add_token("except")
927 self.whitespace_after_except._codegen(state)
928 state.add_token("*")
929 self.whitespace_after_star._codegen(state)
930 typenode = self.type
931 if typenode is not None:
932 typenode._codegen(state)
933 namenode = self.name
934 if namenode is not None:
935 namenode._codegen(state)
936 self.whitespace_before_colon._codegen(state)
937 state.add_token(":")
938 self.body._codegen(state)
941@add_slots
942@dataclass(frozen=True)
943class Finally(CSTNode):
944 """
945 A ``finally`` clause that appears optionally after a :class:`Try` statement.
946 """
948 #: The body of the except.
949 body: BaseSuite
951 #: Sequence of empty lines appearing before this compound statement line.
952 leading_lines: Sequence[EmptyLine] = ()
954 #: The whitespace that appears after the ``finally`` keyword but before
955 #: the colon.
956 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
958 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Finally":
959 return Finally(
960 leading_lines=visit_sequence(
961 self, "leading_lines", self.leading_lines, visitor
962 ),
963 whitespace_before_colon=visit_required(
964 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
965 ),
966 body=visit_required(self, "body", self.body, visitor),
967 )
969 def _codegen_impl(self, state: CodegenState) -> None:
970 for ll in self.leading_lines:
971 ll._codegen(state)
972 state.add_indent_tokens()
974 with state.record_syntactic_position(self, end_node=self.body):
975 state.add_token("finally")
976 self.whitespace_before_colon._codegen(state)
977 state.add_token(":")
978 self.body._codegen(state)
981@add_slots
982@dataclass(frozen=True)
983class Try(BaseCompoundStatement):
984 """
985 A regular ``try`` statement that cannot contain :class:`ExceptStar` blocks. For
986 ``try`` statements that can contain :class:`ExceptStar` blocks, see
987 :class:`TryStar`.
988 """
990 #: The suite that is wrapped with a try statement.
991 body: BaseSuite
993 #: A list of zero or more exception handlers.
994 handlers: Sequence[ExceptHandler] = ()
996 #: An optional else case.
997 orelse: Optional[Else] = None
999 #: An optional finally case.
1000 finalbody: Optional[Finally] = None
1002 #: Sequence of empty lines appearing before this compound statement line.
1003 leading_lines: Sequence[EmptyLine] = ()
1005 #: The whitespace that appears after the ``try`` keyword but before
1006 #: the colon.
1007 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1009 def _validate(self) -> None:
1010 if len(self.handlers) == 0 and self.finalbody is None:
1011 raise CSTValidationError(
1012 "A Try statement must have at least one ExceptHandler or Finally"
1013 )
1014 if len(self.handlers) == 0 and self.orelse is not None:
1015 raise CSTValidationError(
1016 "A Try statement must have at least one ExceptHandler in order "
1017 + "to have an Else."
1018 )
1019 # Check bare excepts are always at the last position
1020 if any(handler.type is None for handler in self.handlers[:-1]):
1021 raise CSTValidationError("The bare except: handler must be the last one.")
1023 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Try":
1024 return Try(
1025 leading_lines=visit_sequence(
1026 self, "leading_lines", self.leading_lines, visitor
1027 ),
1028 whitespace_before_colon=visit_required(
1029 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1030 ),
1031 body=visit_required(self, "body", self.body, visitor),
1032 handlers=visit_sequence(self, "handlers", self.handlers, visitor),
1033 orelse=visit_optional(self, "orelse", self.orelse, visitor),
1034 finalbody=visit_optional(self, "finalbody", self.finalbody, visitor),
1035 )
1037 def _codegen_impl(self, state: CodegenState) -> None:
1038 for ll in self.leading_lines:
1039 ll._codegen(state)
1040 state.add_indent_tokens()
1042 end_node = self.body
1043 if len(self.handlers) > 0:
1044 end_node = self.handlers[-1]
1045 orelse = self.orelse
1046 end_node = end_node if orelse is None else orelse
1047 finalbody = self.finalbody
1048 end_node = end_node if finalbody is None else finalbody
1049 with state.record_syntactic_position(self, end_node=end_node):
1050 state.add_token("try")
1051 self.whitespace_before_colon._codegen(state)
1052 state.add_token(":")
1053 self.body._codegen(state)
1054 for handler in self.handlers:
1055 handler._codegen(state)
1056 if orelse is not None:
1057 orelse._codegen(state)
1058 if finalbody is not None:
1059 finalbody._codegen(state)
1062@add_slots
1063@dataclass(frozen=True)
1064class TryStar(BaseCompoundStatement):
1065 """
1066 A ``try`` statement with ``except*`` blocks.
1067 """
1069 #: The suite that is wrapped with a try statement.
1070 body: BaseSuite
1072 #: A list of one or more exception handlers.
1073 handlers: Sequence[ExceptStarHandler]
1075 #: An optional else case.
1076 orelse: Optional[Else] = None
1078 #: An optional finally case.
1079 finalbody: Optional[Finally] = None
1081 #: Sequence of empty lines appearing before this compound statement line.
1082 leading_lines: Sequence[EmptyLine] = ()
1084 #: The whitespace that appears after the ``try`` keyword but before
1085 #: the colon.
1086 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1088 def _validate(self) -> None:
1089 if len(self.handlers) == 0:
1090 raise CSTValidationError(
1091 "A TryStar statement must have at least one ExceptHandler"
1092 )
1094 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TryStar":
1095 return TryStar(
1096 leading_lines=visit_sequence(
1097 self, "leading_lines", self.leading_lines, visitor
1098 ),
1099 whitespace_before_colon=visit_required(
1100 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1101 ),
1102 body=visit_required(self, "body", self.body, visitor),
1103 handlers=visit_sequence(self, "handlers", self.handlers, visitor),
1104 orelse=visit_optional(self, "orelse", self.orelse, visitor),
1105 finalbody=visit_optional(self, "finalbody", self.finalbody, visitor),
1106 )
1108 def _codegen_impl(self, state: CodegenState) -> None:
1109 for ll in self.leading_lines:
1110 ll._codegen(state)
1111 state.add_indent_tokens()
1113 end_node = self.handlers[-1]
1114 orelse = self.orelse
1115 end_node = end_node if orelse is None else orelse
1116 finalbody = self.finalbody
1117 end_node = end_node if finalbody is None else finalbody
1118 with state.record_syntactic_position(self, end_node=end_node):
1119 state.add_token("try")
1120 self.whitespace_before_colon._codegen(state)
1121 state.add_token(":")
1122 self.body._codegen(state)
1123 for handler in self.handlers:
1124 handler._codegen(state)
1125 if orelse is not None:
1126 orelse._codegen(state)
1127 if finalbody is not None:
1128 finalbody._codegen(state)
1131@add_slots
1132@dataclass(frozen=True)
1133class ImportAlias(CSTNode):
1134 """
1135 An import, with an optional :class:`AsName`. Used in both :class:`Import` and
1136 :class:`ImportFrom` to specify a single import out of another module.
1137 """
1139 #: Name or Attribute node representing the object we are importing.
1140 name: Union[Attribute, Name]
1142 #: Local alias we will import the above object as.
1143 asname: Optional[AsName] = None
1145 #: Any trailing comma that appears after this import. This is optional for the
1146 #: last :class:`ImportAlias` in a :class:`Import` or :class:`ImportFrom`, but all
1147 #: other import aliases inside an import must contain a comma to disambiguate
1148 #: multiple imports.
1149 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
1151 def _validate(self) -> None:
1152 asname = self.asname
1153 if asname is not None:
1154 if not isinstance(asname.name, Name):
1155 raise CSTValidationError(
1156 "Must use a Name node for AsName name inside ImportAlias."
1157 )
1158 if asname.whitespace_before_as.empty:
1159 raise CSTValidationError(
1160 "Must have at least one space before as keyword in an ImportAlias."
1161 )
1162 try:
1163 self.evaluated_name
1164 except Exception as e:
1165 if str(e) == "Logic error!":
1166 raise CSTValidationError(
1167 "The imported name must be a valid qualified name."
1168 )
1169 raise e
1171 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ImportAlias":
1172 return ImportAlias(
1173 name=visit_required(self, "name", self.name, visitor),
1174 asname=visit_optional(self, "asname", self.asname, visitor),
1175 comma=visit_sentinel(self, "comma", self.comma, visitor),
1176 )
1178 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
1179 with state.record_syntactic_position(self):
1180 self.name._codegen(state)
1181 asname = self.asname
1182 if asname is not None:
1183 asname._codegen(state)
1185 comma = self.comma
1186 if comma is MaybeSentinel.DEFAULT and default_comma:
1187 state.add_token(", ")
1188 elif isinstance(comma, Comma):
1189 comma._codegen(state)
1191 def _name(self, node: CSTNode) -> str:
1192 # Unrolled version of get_full_name_for_node to avoid circular imports.
1193 if isinstance(node, Name):
1194 return node.value
1195 elif isinstance(node, Attribute):
1196 return f"{self._name(node.value)}.{node.attr.value}"
1197 else:
1198 raise Exception("Logic error!")
1200 @property
1201 def evaluated_name(self) -> str:
1202 """
1203 Returns the string name this :class:`ImportAlias` represents.
1204 """
1205 return self._name(self.name)
1207 @property
1208 def evaluated_alias(self) -> Optional[str]:
1209 """
1210 Returns the string name for any alias that this :class:`ImportAlias`
1211 has. If there is no ``asname`` attribute, this returns ``None``.
1212 """
1213 asname = self.asname
1214 if asname is not None:
1215 return self._name(asname.name)
1216 return None
1219@add_slots
1220@dataclass(frozen=True)
1221class Import(BaseSmallStatement):
1222 """
1223 An ``import`` statement.
1224 """
1226 #: One or more names that are being imported, with optional local aliases.
1227 names: Sequence[ImportAlias]
1229 #: Optional semicolon when this is used in a statement line. This semicolon
1230 #: owns the whitespace on both sides of it when it is used.
1231 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1233 #: The whitespace that appears after the ``import`` keyword but before
1234 #: the first import alias.
1235 whitespace_after_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1237 def _validate(self) -> None:
1238 if len(self.names) == 0:
1239 raise CSTValidationError(
1240 "An ImportStatement must have at least one ImportAlias"
1241 )
1242 if isinstance(self.names[-1].comma, Comma):
1243 raise CSTValidationError(
1244 "An ImportStatement does not allow a trailing comma"
1245 )
1246 if self.whitespace_after_import.empty:
1247 raise CSTValidationError("Must have at least one space after import.")
1249 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Import":
1250 return Import(
1251 whitespace_after_import=visit_required(
1252 self, "whitespace_after_import", self.whitespace_after_import, visitor
1253 ),
1254 names=visit_sequence(self, "names", self.names, visitor),
1255 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1256 )
1258 def _codegen_impl(
1259 self, state: CodegenState, default_semicolon: bool = False
1260 ) -> None:
1261 with state.record_syntactic_position(self):
1262 state.add_token("import")
1263 self.whitespace_after_import._codegen(state)
1264 lastname = len(self.names) - 1
1265 for i, name in enumerate(self.names):
1266 name._codegen(state, default_comma=(i != lastname))
1268 semicolon = self.semicolon
1269 if isinstance(semicolon, MaybeSentinel):
1270 if default_semicolon:
1271 state.add_token("; ")
1272 elif isinstance(semicolon, Semicolon):
1273 semicolon._codegen(state)
1276@add_slots
1277@dataclass(frozen=True)
1278class ImportFrom(BaseSmallStatement):
1279 """
1280 A ``from x import y`` statement.
1281 """
1283 #: Name or Attribute node representing the module we're importing from.
1284 #: This is optional as :class:`ImportFrom` allows purely relative imports.
1285 module: Optional[Union[Attribute, Name]]
1287 #: One or more names that are being imported from the specified module,
1288 #: with optional local aliases.
1289 names: Union[Sequence[ImportAlias], ImportStar]
1291 #: Sequence of :class:`Dot` nodes indicating relative import level.
1292 relative: Sequence[Dot] = ()
1294 #: Optional open parenthesis for multi-line import continuation.
1295 lpar: Optional[LeftParen] = None
1297 #: Optional close parenthesis for multi-line import continuation.
1298 rpar: Optional[RightParen] = None
1300 #: Optional semicolon when this is used in a statement line. This semicolon
1301 #: owns the whitespace on both sides of it when it is used.
1302 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1304 #: The whitespace that appears after the ``from`` keyword but before
1305 #: the module and any relative import dots.
1306 whitespace_after_from: SimpleWhitespace = SimpleWhitespace.field(" ")
1308 #: The whitespace that appears after the module but before the
1309 #: ``import`` keyword.
1310 whitespace_before_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1312 #: The whitespace that appears after the ``import`` keyword but
1313 #: before the first import name or optional left paren.
1314 whitespace_after_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1316 def _validate_module(self) -> None:
1317 if self.module is None and len(self.relative) == 0:
1318 raise CSTValidationError(
1319 "Must have a module specified if there is no relative import."
1320 )
1322 def _validate_names(self) -> None:
1323 names = self.names
1324 if isinstance(names, Sequence):
1325 if len(names) == 0:
1326 raise CSTValidationError(
1327 "An ImportFrom must have at least one ImportAlias"
1328 )
1329 for name in names[:-1]:
1330 if name.comma is None:
1331 raise CSTValidationError("Non-final ImportAliases require a comma")
1332 if self.lpar is not None and self.rpar is None:
1333 raise CSTValidationError("Cannot have left paren without right paren.")
1334 if self.lpar is None and self.rpar is not None:
1335 raise CSTValidationError("Cannot have right paren without left paren.")
1336 if isinstance(names, ImportStar):
1337 if self.lpar is not None or self.rpar is not None:
1338 raise CSTValidationError(
1339 "An ImportFrom using ImportStar cannot have parens"
1340 )
1342 def _validate_whitespace(self) -> None:
1343 if self.whitespace_after_from.empty and not self.relative:
1344 raise CSTValidationError("Must have at least one space after from.")
1345 if self.whitespace_before_import.empty and not (
1346 self.relative and self.module is None
1347 ):
1348 raise CSTValidationError("Must have at least one space before import.")
1349 if (
1350 self.whitespace_after_import.empty
1351 and self.lpar is None
1352 and not isinstance(self.names, ImportStar)
1353 ):
1354 raise CSTValidationError("Must have at least one space after import.")
1356 def _validate(self) -> None:
1357 self._validate_module()
1358 self._validate_names()
1359 self._validate_whitespace()
1361 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ImportFrom":
1362 names = self.names
1363 return ImportFrom(
1364 whitespace_after_from=visit_required(
1365 self, "whitespace_after_from", self.whitespace_after_from, visitor
1366 ),
1367 relative=visit_sequence(self, "relative", self.relative, visitor),
1368 module=visit_optional(self, "module", self.module, visitor),
1369 whitespace_before_import=visit_required(
1370 self, "whitespace_before_import", self.whitespace_before_import, visitor
1371 ),
1372 whitespace_after_import=visit_required(
1373 self, "whitespace_after_import", self.whitespace_after_import, visitor
1374 ),
1375 lpar=visit_optional(self, "lpar", self.lpar, visitor),
1376 names=(
1377 visit_required(self, "names", names, visitor)
1378 if isinstance(names, ImportStar)
1379 else visit_sequence(self, "names", names, visitor)
1380 ),
1381 rpar=visit_optional(self, "rpar", self.rpar, visitor),
1382 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1383 )
1385 def _codegen_impl(
1386 self, state: CodegenState, default_semicolon: bool = False
1387 ) -> None:
1388 names = self.names
1389 end_node = names[-1] if isinstance(names, Sequence) else names
1390 end_node = end_node if self.rpar is None else self.rpar
1391 with state.record_syntactic_position(self, end_node=end_node):
1392 state.add_token("from")
1393 self.whitespace_after_from._codegen(state)
1394 for dot in self.relative:
1395 dot._codegen(state)
1396 module = self.module
1397 if module is not None:
1398 module._codegen(state)
1399 self.whitespace_before_import._codegen(state)
1400 state.add_token("import")
1401 self.whitespace_after_import._codegen(state)
1402 lpar = self.lpar
1403 if lpar is not None:
1404 lpar._codegen(state)
1405 if isinstance(names, Sequence):
1406 lastname = len(names) - 1
1407 for i, name in enumerate(names):
1408 name._codegen(state, default_comma=(i != lastname))
1409 if isinstance(names, ImportStar):
1410 names._codegen(state)
1411 rpar = self.rpar
1412 if rpar is not None:
1413 rpar._codegen(state)
1415 semicolon = self.semicolon
1416 if isinstance(semicolon, MaybeSentinel):
1417 if default_semicolon:
1418 state.add_token("; ")
1419 elif isinstance(semicolon, Semicolon):
1420 semicolon._codegen(state)
1423@add_slots
1424@dataclass(frozen=True)
1425class AssignTarget(CSTNode):
1426 """
1427 A target for an :class:`Assign`. Owns the equals sign and the whitespace around it.
1428 """
1430 #: The target expression being assigned to.
1431 target: BaseAssignTargetExpression
1433 #: The whitespace appearing before the equals sign.
1434 whitespace_before_equal: SimpleWhitespace = SimpleWhitespace.field(" ")
1436 #: The whitespace appearing after the equals sign.
1437 whitespace_after_equal: SimpleWhitespace = SimpleWhitespace.field(" ")
1439 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AssignTarget":
1440 return AssignTarget(
1441 target=visit_required(self, "target", self.target, visitor),
1442 whitespace_before_equal=visit_required(
1443 self, "whitespace_before_equal", self.whitespace_before_equal, visitor
1444 ),
1445 whitespace_after_equal=visit_required(
1446 self, "whitespace_after_equal", self.whitespace_after_equal, visitor
1447 ),
1448 )
1450 def _codegen_impl(self, state: CodegenState) -> None:
1451 with state.record_syntactic_position(self):
1452 self.target._codegen(state)
1454 self.whitespace_before_equal._codegen(state)
1455 state.add_token("=")
1456 self.whitespace_after_equal._codegen(state)
1459@add_slots
1460@dataclass(frozen=True)
1461class Assign(BaseSmallStatement):
1462 """
1463 An assignment statement such as ``x = y`` or ``x = y = z``. Unlike
1464 :class:`AnnAssign`, this does not allow type annotations but does
1465 allow for multiple targets.
1466 """
1468 #: One or more targets that are being assigned to.
1469 targets: Sequence[AssignTarget]
1471 #: The expression being assigned to the targets.
1472 value: BaseExpression
1474 #: Optional semicolon when this is used in a statement line. This semicolon
1475 #: owns the whitespace on both sides of it when it is used.
1476 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1478 def _validate(self) -> None:
1479 if len(self.targets) == 0:
1480 raise CSTValidationError(
1481 "An Assign statement must have at least one AssignTarget"
1482 )
1484 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Assign":
1485 return Assign(
1486 targets=visit_sequence(self, "targets", self.targets, visitor),
1487 value=visit_required(self, "value", self.value, visitor),
1488 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1489 )
1491 def _codegen_impl(
1492 self, state: CodegenState, default_semicolon: bool = False
1493 ) -> None:
1494 with state.record_syntactic_position(self):
1495 for target in self.targets:
1496 target._codegen(state)
1497 self.value._codegen(state)
1499 semicolon = self.semicolon
1500 if isinstance(semicolon, MaybeSentinel):
1501 if default_semicolon:
1502 state.add_token("; ")
1503 elif isinstance(semicolon, Semicolon):
1504 semicolon._codegen(state)
1507@add_slots
1508@dataclass(frozen=True)
1509class AnnAssign(BaseSmallStatement):
1510 """
1511 An assignment statement such as ``x: int = 5`` or ``x: int``. This only
1512 allows for one assignment target unlike :class:`Assign` but it includes
1513 a variable annotation. Also unlike :class:`Assign`, the assignment target
1514 is optional, as it is possible to annotate an expression without assigning
1515 to it.
1516 """
1518 #: The target that is being annotated and possibly assigned to.
1519 target: BaseAssignTargetExpression
1521 #: The annotation for the target.
1522 annotation: Annotation
1524 #: The optional expression being assigned to the target.
1525 value: Optional[BaseExpression] = None
1527 #: The equals sign used to denote assignment if there is a value.
1528 equal: Union[AssignEqual, MaybeSentinel] = MaybeSentinel.DEFAULT
1530 #: Optional semicolon when this is used in a statement line. This semicolon
1531 #: owns the whitespace on both sides of it when it is used.
1532 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1534 def _validate(self) -> None:
1535 if self.value is None and isinstance(self.equal, AssignEqual):
1536 raise CSTValidationError(
1537 "Must have a value when specifying an AssignEqual."
1538 )
1540 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AnnAssign":
1541 return AnnAssign(
1542 target=visit_required(self, "target", self.target, visitor),
1543 annotation=visit_required(self, "annotation", self.annotation, visitor),
1544 equal=visit_sentinel(self, "equal", self.equal, visitor),
1545 value=visit_optional(self, "value", self.value, visitor),
1546 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1547 )
1549 def _codegen_impl(
1550 self, state: CodegenState, default_semicolon: bool = False
1551 ) -> None:
1552 with state.record_syntactic_position(self):
1553 self.target._codegen(state)
1554 self.annotation._codegen(state, default_indicator=":")
1555 equal = self.equal
1556 if equal is MaybeSentinel.DEFAULT and self.value is not None:
1557 state.add_token(" = ")
1558 elif isinstance(equal, AssignEqual):
1559 equal._codegen(state)
1560 value = self.value
1561 if value is not None:
1562 value._codegen(state)
1564 semicolon = self.semicolon
1565 if isinstance(semicolon, MaybeSentinel):
1566 if default_semicolon:
1567 state.add_token("; ")
1568 elif isinstance(semicolon, Semicolon):
1569 semicolon._codegen(state)
1572@add_slots
1573@dataclass(frozen=True)
1574class AugAssign(BaseSmallStatement):
1575 """
1576 An augmented assignment statement, such as ``x += 5``.
1577 """
1579 #: Target that is being operated on and assigned to.
1580 target: BaseAssignTargetExpression
1582 #: The augmented assignment operation being performed.
1583 operator: BaseAugOp
1585 #: The value used with the above operator to calculate the new assignment.
1586 value: BaseExpression
1588 #: Optional semicolon when this is used in a statement line. This semicolon
1589 #: owns the whitespace on both sides of it when it is used.
1590 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1592 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AugAssign":
1593 return AugAssign(
1594 target=visit_required(self, "target", self.target, visitor),
1595 operator=visit_required(self, "operator", self.operator, visitor),
1596 value=visit_required(self, "value", self.value, visitor),
1597 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1598 )
1600 def _codegen_impl(
1601 self, state: CodegenState, default_semicolon: bool = False
1602 ) -> None:
1603 with state.record_syntactic_position(self):
1604 self.target._codegen(state)
1605 self.operator._codegen(state)
1606 self.value._codegen(state)
1608 semicolon = self.semicolon
1609 if isinstance(semicolon, MaybeSentinel):
1610 if default_semicolon:
1611 state.add_token("; ")
1612 elif isinstance(semicolon, Semicolon):
1613 semicolon._codegen(state)
1616@add_slots
1617@dataclass(frozen=True)
1618class Decorator(CSTNode):
1619 """
1620 A single decorator that decorates a :class:`FunctionDef` or a :class:`ClassDef`.
1621 """
1623 #: The decorator that will return a new function wrapping the parent
1624 #: of this decorator.
1625 decorator: BaseExpression
1627 #: Line comments and empty lines before this decorator. The parent
1628 #: :class:`FunctionDef` or :class:`ClassDef` node owns leading lines before
1629 #: the first decorator so that if the first decorator is removed, spacing is preserved.
1630 leading_lines: Sequence[EmptyLine] = ()
1632 #: Whitespace after the ``@`` and before the decorator expression itself.
1633 whitespace_after_at: SimpleWhitespace = SimpleWhitespace.field("")
1635 #: Optional trailing comment and newline following the decorator before the next line.
1636 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field()
1638 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Decorator":
1639 return Decorator(
1640 leading_lines=visit_sequence(
1641 self, "leading_lines", self.leading_lines, visitor
1642 ),
1643 whitespace_after_at=visit_required(
1644 self, "whitespace_after_at", self.whitespace_after_at, visitor
1645 ),
1646 decorator=visit_required(self, "decorator", self.decorator, visitor),
1647 trailing_whitespace=visit_required(
1648 self, "trailing_whitespace", self.trailing_whitespace, visitor
1649 ),
1650 )
1652 def _codegen_impl(self, state: CodegenState) -> None:
1653 for ll in self.leading_lines:
1654 ll._codegen(state)
1655 state.add_indent_tokens()
1657 with state.record_syntactic_position(self):
1658 state.add_token("@")
1659 self.whitespace_after_at._codegen(state)
1660 self.decorator._codegen(state)
1662 self.trailing_whitespace._codegen(state)
1665def get_docstring_impl(
1666 body: Union[BaseSuite, Sequence[Union[SimpleStatementLine, BaseCompoundStatement]]],
1667 clean: bool,
1668) -> Optional[str]:
1669 """
1670 Implementation Reference:
1671 - :func:`ast.get_docstring` https://docs.python.org/3/library/ast.html#ast.get_docstring
1672 and https://github.com/python/cpython/blob/89aa4694fc8c6d190325ef8ed6ce6a6b8efb3e50/Lib/ast.py#L254
1673 - PEP 257 https://www.python.org/dev/peps/pep-0257/
1674 """
1675 if isinstance(body, Sequence):
1676 if body:
1677 expr = body[0]
1678 else:
1679 return None
1680 else:
1681 expr = body
1682 while isinstance(expr, (BaseSuite, SimpleStatementLine)):
1683 if len(expr.body) == 0:
1684 return None
1685 expr = expr.body[0]
1686 if not isinstance(expr, Expr):
1687 return None
1688 val = expr.value
1689 if isinstance(val, (SimpleString, ConcatenatedString)):
1690 evaluated_value = val.evaluated_value
1691 else:
1692 return None
1693 if isinstance(evaluated_value, bytes):
1694 return None
1696 if evaluated_value is not None and clean:
1697 return inspect.cleandoc(evaluated_value)
1698 return evaluated_value
1701@add_slots
1702@dataclass(frozen=True)
1703class FunctionDef(BaseCompoundStatement):
1704 """
1705 A function definition.
1706 """
1708 #: The function name.
1709 name: Name
1711 #: The function parameters. Present even if there are no params.
1712 params: Parameters
1714 #: The function body.
1715 body: BaseSuite
1717 #: Sequence of decorators applied to this function. Decorators are listed in
1718 #: order that they appear in source (top to bottom) as apposed to the order
1719 #: that they are applied to the function at runtime.
1720 decorators: Sequence[Decorator] = ()
1722 #: An optional return annotation, if the function is annotated.
1723 returns: Optional[Annotation] = None
1725 #: Optional async modifier, if this is an async function.
1726 asynchronous: Optional[Asynchronous] = None
1728 #: Leading empty lines and comments before the first decorator. We
1729 #: assume any comments before the first decorator are owned by the
1730 #: function definition itself. If there are no decorators, this will
1731 #: still contain all of the empty lines and comments before the
1732 #: function definition.
1733 leading_lines: Sequence[EmptyLine] = ()
1735 #: Empty lines and comments between the final decorator and the
1736 #: :class:`FunctionDef` node. In the case of no decorators, this will be empty.
1737 lines_after_decorators: Sequence[EmptyLine] = ()
1739 #: Whitespace after the ``def`` keyword and before the function name.
1740 whitespace_after_def: SimpleWhitespace = SimpleWhitespace.field(" ")
1742 #: Whitespace after the function name and before the type parameters or the opening
1743 #: parenthesis for the parameters.
1744 whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("")
1746 #: Whitespace after the opening parenthesis for the parameters but before
1747 #: the first param itself.
1748 whitespace_before_params: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
1750 #: Whitespace after the closing parenthesis or return annotation and before
1751 #: the colon.
1752 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1754 #: An optional declaration of type parameters.
1755 type_parameters: Optional["TypeParameters"] = None
1757 #: Whitespace between the type parameters and the opening parenthesis for the
1758 #: (non-type) parameters.
1759 whitespace_after_type_parameters: SimpleWhitespace = SimpleWhitespace.field("")
1761 def _validate(self) -> None:
1762 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
1763 raise CSTValidationError("Cannot have parens around Name in a FunctionDef.")
1764 if self.whitespace_after_def.empty:
1765 raise CSTValidationError(
1766 "There must be at least one space between 'def' and name."
1767 )
1769 if (
1770 self.type_parameters is None
1771 and not self.whitespace_after_type_parameters.empty
1772 ):
1773 raise CSTValidationError(
1774 "whitespace_after_type_parameters must be empty if there are no type "
1775 "parameters in FunctionDef"
1776 )
1778 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "FunctionDef":
1779 return FunctionDef(
1780 leading_lines=visit_sequence(
1781 self, "leading_lines", self.leading_lines, visitor
1782 ),
1783 decorators=visit_sequence(self, "decorators", self.decorators, visitor),
1784 lines_after_decorators=visit_sequence(
1785 self, "lines_after_decorators", self.lines_after_decorators, visitor
1786 ),
1787 asynchronous=visit_optional(
1788 self, "asynchronous", self.asynchronous, visitor
1789 ),
1790 whitespace_after_def=visit_required(
1791 self, "whitespace_after_def", self.whitespace_after_def, visitor
1792 ),
1793 name=visit_required(self, "name", self.name, visitor),
1794 whitespace_after_name=visit_required(
1795 self, "whitespace_after_name", self.whitespace_after_name, visitor
1796 ),
1797 type_parameters=visit_optional(
1798 self, "type_parameters", self.type_parameters, visitor
1799 ),
1800 whitespace_after_type_parameters=visit_required(
1801 self,
1802 "whitespace_after_type_parameters",
1803 self.whitespace_after_type_parameters,
1804 visitor,
1805 ),
1806 whitespace_before_params=visit_required(
1807 self, "whitespace_before_params", self.whitespace_before_params, visitor
1808 ),
1809 params=visit_required(self, "params", self.params, visitor),
1810 returns=visit_optional(self, "returns", self.returns, visitor),
1811 whitespace_before_colon=visit_required(
1812 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1813 ),
1814 body=visit_required(self, "body", self.body, visitor),
1815 )
1817 def _codegen_impl(self, state: CodegenState) -> None:
1818 for ll in self.leading_lines:
1819 ll._codegen(state)
1820 for decorator in self.decorators:
1821 decorator._codegen(state)
1822 for lad in self.lines_after_decorators:
1823 lad._codegen(state)
1824 state.add_indent_tokens()
1826 with state.record_syntactic_position(self, end_node=self.body):
1827 asynchronous = self.asynchronous
1828 if asynchronous is not None:
1829 asynchronous._codegen(state)
1830 state.add_token("def")
1831 self.whitespace_after_def._codegen(state)
1832 self.name._codegen(state)
1833 self.whitespace_after_name._codegen(state)
1834 type_params = self.type_parameters
1835 if type_params is not None:
1836 type_params._codegen(state)
1837 self.whitespace_after_type_parameters._codegen(state)
1838 state.add_token("(")
1839 self.whitespace_before_params._codegen(state)
1840 self.params._codegen(state)
1841 state.add_token(")")
1842 returns = self.returns
1843 if returns is not None:
1844 returns._codegen(state, default_indicator="->")
1845 self.whitespace_before_colon._codegen(state)
1846 state.add_token(":")
1847 self.body._codegen(state)
1849 def get_docstring(self, clean: bool = True) -> Optional[str]:
1850 """
1851 When docstring is available, returns a :func:`inspect.cleandoc` cleaned docstring.
1852 Otherwise, returns ``None``.
1853 """
1854 return get_docstring_impl(self.body, clean)
1857@add_slots
1858@dataclass(frozen=True)
1859class ClassDef(BaseCompoundStatement):
1860 """
1861 A class definition.
1862 """
1864 #: The class name.
1865 name: Name
1867 #: The class body.
1868 body: BaseSuite
1870 #: Sequence of base classes this class inherits from.
1871 bases: Sequence[Arg] = ()
1873 #: Sequence of keywords, such as "metaclass".
1874 keywords: Sequence[Arg] = ()
1876 #: Sequence of decorators applied to this class.
1877 decorators: Sequence[Decorator] = ()
1879 #: Optional open parenthesis used when there are bases or keywords.
1880 lpar: Union[LeftParen, MaybeSentinel] = MaybeSentinel.DEFAULT
1882 #: Optional close parenthesis used when there are bases or keywords.
1883 rpar: Union[RightParen, MaybeSentinel] = MaybeSentinel.DEFAULT
1885 #: Leading empty lines and comments before the first decorator. We
1886 #: assume any comments before the first decorator are owned by the
1887 #: class definition itself. If there are no decorators, this will
1888 #: still contain all of the empty lines and comments before the
1889 #: class definition.
1890 leading_lines: Sequence[EmptyLine] = ()
1892 #: Empty lines and comments between the final decorator and the
1893 #: :class:`ClassDef` node. In the case of no decorators, this will be empty.
1894 lines_after_decorators: Sequence[EmptyLine] = ()
1896 #: Whitespace after the ``class`` keyword and before the class name.
1897 whitespace_after_class: SimpleWhitespace = SimpleWhitespace.field(" ")
1899 #: Whitespace after the class name and before the type parameters or the opening
1900 #: parenthesis for the bases and keywords.
1901 whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("")
1903 #: Whitespace after the closing parenthesis or class name and before
1904 #: the colon.
1905 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1907 #: An optional declaration of type parameters.
1908 type_parameters: Optional["TypeParameters"] = None
1910 #: Whitespace between type parameters and opening parenthesis for the bases and
1911 #: keywords.
1912 whitespace_after_type_parameters: SimpleWhitespace = SimpleWhitespace.field("")
1914 def _validate_whitespace(self) -> None:
1915 if self.whitespace_after_class.empty:
1916 raise CSTValidationError(
1917 "There must be at least one space between 'class' and name."
1918 )
1919 if (
1920 self.type_parameters is None
1921 and not self.whitespace_after_type_parameters.empty
1922 ):
1923 raise CSTValidationError(
1924 "whitespace_after_type_parameters must be empty if there are no type"
1925 "parameters in a ClassDef"
1926 )
1928 def _validate_parens(self) -> None:
1929 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
1930 raise CSTValidationError("Cannot have parens around Name in a ClassDef.")
1931 if isinstance(self.lpar, MaybeSentinel) and isinstance(self.rpar, RightParen):
1932 raise CSTValidationError(
1933 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
1934 )
1935 if isinstance(self.lpar, LeftParen) and isinstance(self.rpar, MaybeSentinel):
1936 raise CSTValidationError(
1937 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
1938 )
1940 def _validate_args(self) -> None:
1941 if any((arg.keyword is not None) for arg in self.bases):
1942 raise CSTValidationError("Bases must be arguments without keywords.")
1943 if any((arg.keyword is None and arg.star != "**") for arg in self.keywords):
1944 raise CSTValidationError(
1945 "Keywords must be arguments with keywords or dictionary expansions."
1946 )
1948 def _validate(self) -> None:
1949 self._validate_whitespace()
1950 self._validate_parens()
1951 self._validate_args()
1953 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ClassDef":
1954 return ClassDef(
1955 leading_lines=visit_sequence(
1956 self, "leading_lines", self.leading_lines, visitor
1957 ),
1958 decorators=visit_sequence(self, "decorators", self.decorators, visitor),
1959 lines_after_decorators=visit_sequence(
1960 self, "lines_after_decorators", self.lines_after_decorators, visitor
1961 ),
1962 whitespace_after_class=visit_required(
1963 self, "whitespace_after_class", self.whitespace_after_class, visitor
1964 ),
1965 name=visit_required(self, "name", self.name, visitor),
1966 whitespace_after_name=visit_required(
1967 self, "whitespace_after_name", self.whitespace_after_name, visitor
1968 ),
1969 type_parameters=visit_optional(
1970 self, "type_parameters", self.type_parameters, visitor
1971 ),
1972 whitespace_after_type_parameters=visit_required(
1973 self,
1974 "whitespace_after_type_parameters",
1975 self.whitespace_after_type_parameters,
1976 visitor,
1977 ),
1978 lpar=visit_sentinel(self, "lpar", self.lpar, visitor),
1979 bases=visit_sequence(self, "bases", self.bases, visitor),
1980 keywords=visit_sequence(self, "keywords", self.keywords, visitor),
1981 rpar=visit_sentinel(self, "rpar", self.rpar, visitor),
1982 whitespace_before_colon=visit_required(
1983 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1984 ),
1985 body=visit_required(self, "body", self.body, visitor),
1986 )
1988 def _codegen_impl(self, state: CodegenState) -> None: # noqa: C901
1989 for ll in self.leading_lines:
1990 ll._codegen(state)
1991 for decorator in self.decorators:
1992 decorator._codegen(state)
1993 for lad in self.lines_after_decorators:
1994 lad._codegen(state)
1995 state.add_indent_tokens()
1997 with state.record_syntactic_position(self, end_node=self.body):
1998 state.add_token("class")
1999 self.whitespace_after_class._codegen(state)
2000 self.name._codegen(state)
2001 self.whitespace_after_name._codegen(state)
2002 type_params = self.type_parameters
2003 if type_params is not None:
2004 type_params._codegen(state)
2005 self.whitespace_after_type_parameters._codegen(state)
2006 lpar = self.lpar
2007 if isinstance(lpar, MaybeSentinel):
2008 if self.bases or self.keywords:
2009 state.add_token("(")
2010 elif isinstance(lpar, LeftParen):
2011 lpar._codegen(state)
2012 args = [*self.bases, *self.keywords]
2013 last_arg = len(args) - 1
2014 for i, arg in enumerate(args):
2015 arg._codegen(state, default_comma=(i != last_arg))
2016 rpar = self.rpar
2017 if isinstance(rpar, MaybeSentinel):
2018 if self.bases or self.keywords:
2019 state.add_token(")")
2020 elif isinstance(rpar, RightParen):
2021 rpar._codegen(state)
2022 self.whitespace_before_colon._codegen(state)
2023 state.add_token(":")
2024 self.body._codegen(state)
2026 def get_docstring(self, clean: bool = True) -> Optional[str]:
2027 """
2028 Returns a :func:`inspect.cleandoc` cleaned docstring if the docstring is available, ``None`` otherwise.
2029 """
2030 return get_docstring_impl(self.body, clean)
2033@add_slots
2034@dataclass(frozen=True)
2035class WithItem(CSTNode):
2036 """
2037 A single context manager in a :class:`With` block, with an optional variable name.
2038 """
2040 #: Expression that evaluates to a context manager.
2041 item: BaseExpression
2043 #: Variable to assign the context manager to, if it is needed in the
2044 #: :class:`With` body.
2045 asname: Optional[AsName] = None
2047 #: This is forbidden for the last :class:`WithItem` in a :class:`With`, but all
2048 #: other items inside a with block must contain a comma to separate them.
2049 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2051 def _validate(self) -> None:
2052 asname = self.asname
2053 if (
2054 asname is not None
2055 and asname.whitespace_before_as.empty
2056 and not self.item._safe_to_use_with_word_operator(ExpressionPosition.LEFT)
2057 ):
2058 raise CSTValidationError("Must have at least one space before as keyword.")
2060 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "WithItem":
2061 return WithItem(
2062 item=visit_required(self, "item", self.item, visitor),
2063 asname=visit_optional(self, "asname", self.asname, visitor),
2064 comma=visit_sentinel(self, "comma", self.comma, visitor),
2065 )
2067 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
2068 with state.record_syntactic_position(self):
2069 self.item._codegen(state)
2070 asname = self.asname
2071 if asname is not None:
2072 asname._codegen(state)
2074 comma = self.comma
2075 if comma is MaybeSentinel.DEFAULT and default_comma:
2076 state.add_token(", ")
2077 elif isinstance(comma, Comma):
2078 comma._codegen(state)
2081@add_slots
2082@dataclass(frozen=True)
2083class With(BaseCompoundStatement):
2084 """
2085 A ``with`` statement.
2086 """
2088 #: A sequence of one or more items that evaluate to context managers.
2089 items: Sequence[WithItem]
2091 #: The suite that is wrapped with this statement.
2092 body: BaseSuite
2094 #: Optional async modifier if this is an ``async with`` statement.
2095 asynchronous: Optional[Asynchronous] = None
2097 #: Sequence of empty lines appearing before this with statement.
2098 leading_lines: Sequence[EmptyLine] = ()
2100 #: Optional open parenthesis for multi-line with bindings
2101 lpar: Union[LeftParen, MaybeSentinel] = MaybeSentinel.DEFAULT
2103 #: Optional close parenthesis for multi-line with bindings
2104 rpar: Union[RightParen, MaybeSentinel] = MaybeSentinel.DEFAULT
2106 #: Whitespace after the ``with`` keyword and before the first item.
2107 whitespace_after_with: SimpleWhitespace = SimpleWhitespace.field(" ")
2109 #: Whitespace after the last item and before the colon.
2110 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2112 def _validate_parens(self) -> None:
2113 if isinstance(self.lpar, MaybeSentinel) and isinstance(self.rpar, RightParen):
2114 raise CSTValidationError(
2115 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
2116 )
2117 if isinstance(self.lpar, LeftParen) and isinstance(self.rpar, MaybeSentinel):
2118 raise CSTValidationError(
2119 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
2120 )
2122 def _validate(self) -> None:
2123 self._validate_parens()
2124 if len(self.items) == 0:
2125 raise CSTValidationError(
2126 "A With statement must have at least one WithItem."
2127 )
2128 if (
2129 isinstance(self.rpar, MaybeSentinel)
2130 and self.items[-1].comma != MaybeSentinel.DEFAULT
2131 ):
2132 raise CSTValidationError(
2133 "The last WithItem in an unparenthesized With cannot have a trailing comma."
2134 )
2135 if self.whitespace_after_with.empty and not (
2136 isinstance(self.lpar, LeftParen)
2137 or self.items[0].item._safe_to_use_with_word_operator(
2138 ExpressionPosition.RIGHT
2139 )
2140 ):
2141 raise CSTValidationError("Must have at least one space after with keyword.")
2143 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "With":
2144 return With(
2145 leading_lines=visit_sequence(
2146 self, "leading_lines", self.leading_lines, visitor
2147 ),
2148 asynchronous=visit_optional(
2149 self, "asynchronous", self.asynchronous, visitor
2150 ),
2151 whitespace_after_with=visit_required(
2152 self, "whitespace_after_with", self.whitespace_after_with, visitor
2153 ),
2154 lpar=visit_sentinel(self, "lpar", self.lpar, visitor),
2155 items=visit_sequence(self, "items", self.items, visitor),
2156 rpar=visit_sentinel(self, "rpar", self.rpar, visitor),
2157 whitespace_before_colon=visit_required(
2158 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2159 ),
2160 body=visit_required(self, "body", self.body, visitor),
2161 )
2163 def _codegen_impl(self, state: CodegenState) -> None:
2164 for ll in self.leading_lines:
2165 ll._codegen(state)
2166 state.add_indent_tokens()
2168 needs_paren = False
2169 for item in self.items:
2170 comma = item.comma
2171 if isinstance(comma, Comma):
2172 if isinstance(
2173 comma.whitespace_after,
2174 (EmptyLine, TrailingWhitespace, ParenthesizedWhitespace),
2175 ):
2176 needs_paren = True
2177 break
2179 with state.record_syntactic_position(self, end_node=self.body):
2180 asynchronous = self.asynchronous
2181 if asynchronous is not None:
2182 asynchronous._codegen(state)
2183 state.add_token("with")
2184 self.whitespace_after_with._codegen(state)
2185 lpar = self.lpar
2186 if isinstance(lpar, LeftParen):
2187 lpar._codegen(state)
2188 elif needs_paren:
2189 state.add_token("(")
2190 last_item = len(self.items) - 1
2191 for i, item in enumerate(self.items):
2192 item._codegen(state, default_comma=(i != last_item))
2193 rpar = self.rpar
2194 if isinstance(rpar, RightParen):
2195 rpar._codegen(state)
2196 elif needs_paren:
2197 state.add_token(")")
2198 self.whitespace_before_colon._codegen(state)
2199 state.add_token(":")
2200 self.body._codegen(state)
2203@add_slots
2204@dataclass(frozen=True)
2205class For(BaseCompoundStatement):
2206 """
2207 A ``for target in iter`` statement.
2208 """
2210 #: The target of the iterator in the for statement.
2211 target: BaseAssignTargetExpression
2213 #: The iterable expression we will loop over.
2214 iter: BaseExpression
2216 #: The suite that is wrapped with this statement.
2217 body: BaseSuite
2219 #: An optional else case which will be executed if there is no
2220 #: :class:`Break` statement encountered while looping.
2221 orelse: Optional[Else] = None
2223 #: Optional async modifier, if this is an `async for` statement.
2224 asynchronous: Optional[Asynchronous] = None
2226 #: Sequence of empty lines appearing before this for statement.
2227 leading_lines: Sequence[EmptyLine] = ()
2229 #: Whitespace after the ``for`` keyword and before the target.
2230 whitespace_after_for: SimpleWhitespace = SimpleWhitespace.field(" ")
2232 #: Whitespace after the target and before the ``in`` keyword.
2233 whitespace_before_in: SimpleWhitespace = SimpleWhitespace.field(" ")
2235 #: Whitespace after the ``in`` keyword and before the iter.
2236 whitespace_after_in: SimpleWhitespace = SimpleWhitespace.field(" ")
2238 #: Whitespace after the iter and before the colon.
2239 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2241 def _validate(self) -> None:
2242 if (
2243 self.whitespace_after_for.empty
2244 and not self.target._safe_to_use_with_word_operator(
2245 ExpressionPosition.RIGHT
2246 )
2247 ):
2248 raise CSTValidationError(
2249 "Must have at least one space after 'for' keyword."
2250 )
2252 if (
2253 self.whitespace_before_in.empty
2254 and not self.target._safe_to_use_with_word_operator(ExpressionPosition.LEFT)
2255 ):
2256 raise CSTValidationError(
2257 "Must have at least one space before 'in' keyword."
2258 )
2260 if (
2261 self.whitespace_after_in.empty
2262 and not self.iter._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2263 ):
2264 raise CSTValidationError("Must have at least one space after 'in' keyword.")
2266 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "For":
2267 return For(
2268 leading_lines=visit_sequence(
2269 self, "leading_lines", self.leading_lines, visitor
2270 ),
2271 asynchronous=visit_optional(
2272 self, "asynchronous", self.asynchronous, visitor
2273 ),
2274 whitespace_after_for=visit_required(
2275 self, "whitespace_after_for", self.whitespace_after_for, visitor
2276 ),
2277 target=visit_required(self, "target", self.target, visitor),
2278 whitespace_before_in=visit_required(
2279 self, "whitespace_before_in", self.whitespace_before_in, visitor
2280 ),
2281 whitespace_after_in=visit_required(
2282 self, "whitespace_after_in", self.whitespace_after_in, visitor
2283 ),
2284 iter=visit_required(self, "iter", self.iter, visitor),
2285 whitespace_before_colon=visit_required(
2286 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2287 ),
2288 body=visit_required(self, "body", self.body, visitor),
2289 orelse=visit_optional(self, "orelse", self.orelse, visitor),
2290 )
2292 def _codegen_impl(self, state: CodegenState) -> None:
2293 for ll in self.leading_lines:
2294 ll._codegen(state)
2295 state.add_indent_tokens()
2297 end_node = self.body if self.orelse is None else self.orelse
2298 with state.record_syntactic_position(self, end_node=end_node):
2299 asynchronous = self.asynchronous
2300 if asynchronous is not None:
2301 asynchronous._codegen(state)
2302 state.add_token("for")
2303 self.whitespace_after_for._codegen(state)
2304 self.target._codegen(state)
2305 self.whitespace_before_in._codegen(state)
2306 state.add_token("in")
2307 self.whitespace_after_in._codegen(state)
2308 self.iter._codegen(state)
2309 self.whitespace_before_colon._codegen(state)
2310 state.add_token(":")
2311 self.body._codegen(state)
2312 orelse = self.orelse
2313 if orelse is not None:
2314 orelse._codegen(state)
2317@add_slots
2318@dataclass(frozen=True)
2319class While(BaseCompoundStatement):
2320 """
2321 A ``while`` statement.
2322 """
2324 #: The test we will loop against.
2325 test: BaseExpression
2327 #: The suite that is wrapped with this statement.
2328 body: BaseSuite
2330 #: An optional else case which will be executed if there is no
2331 #: :class:`Break` statement encountered while looping.
2332 orelse: Optional[Else] = None
2334 #: Sequence of empty lines appearing before this while statement.
2335 leading_lines: Sequence[EmptyLine] = ()
2337 #: Whitespace after the ``while`` keyword and before the test.
2338 whitespace_after_while: SimpleWhitespace = SimpleWhitespace.field(" ")
2340 #: Whitespace after the test and before the colon.
2341 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2343 def _validate(self) -> None:
2344 if (
2345 self.whitespace_after_while.empty
2346 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2347 ):
2348 raise CSTValidationError(
2349 "Must have at least one space after 'while' keyword."
2350 )
2352 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "While":
2353 return While(
2354 leading_lines=visit_sequence(
2355 self, "leading_lines", self.leading_lines, visitor
2356 ),
2357 whitespace_after_while=visit_required(
2358 self, "whitespace_after_while", self.whitespace_after_while, visitor
2359 ),
2360 test=visit_required(self, "test", self.test, visitor),
2361 whitespace_before_colon=visit_required(
2362 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2363 ),
2364 body=visit_required(self, "body", self.body, visitor),
2365 orelse=visit_optional(self, "orelse", self.orelse, visitor),
2366 )
2368 def _codegen_impl(self, state: CodegenState) -> None:
2369 for ll in self.leading_lines:
2370 ll._codegen(state)
2371 state.add_indent_tokens()
2373 end_node = self.body if self.orelse is None else self.orelse
2374 with state.record_syntactic_position(self, end_node=end_node):
2375 state.add_token("while")
2376 self.whitespace_after_while._codegen(state)
2377 self.test._codegen(state)
2378 self.whitespace_before_colon._codegen(state)
2379 state.add_token(":")
2380 self.body._codegen(state)
2381 orelse = self.orelse
2382 if orelse is not None:
2383 orelse._codegen(state)
2386@add_slots
2387@dataclass(frozen=True)
2388class Raise(BaseSmallStatement):
2389 """
2390 A ``raise exc`` or ``raise exc from cause`` statement.
2391 """
2393 #: The exception that we should raise.
2394 exc: Optional[BaseExpression] = None
2396 #: Optionally, a ``from cause`` clause to allow us to raise an exception
2397 #: out of another exception's context.
2398 cause: Optional[From] = None
2400 #: Any whitespace appearing between the ``raise`` keyword and the exception.
2401 whitespace_after_raise: Union[
2402 SimpleWhitespace, MaybeSentinel
2403 ] = MaybeSentinel.DEFAULT
2405 #: Optional semicolon when this is used in a statement line. This semicolon
2406 #: owns the whitespace on both sides of it when it is used.
2407 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2409 def _validate(self) -> None:
2410 # Validate correct construction
2411 if self.exc is None and self.cause is not None:
2412 raise CSTValidationError(
2413 "Must have an 'exc' when specifying 'clause'. on Raise."
2414 )
2416 # Validate spacing between "raise" and "exc"
2417 exc = self.exc
2418 if exc is not None:
2419 whitespace_after_raise = self.whitespace_after_raise
2420 has_no_gap = (
2421 not isinstance(whitespace_after_raise, MaybeSentinel)
2422 and whitespace_after_raise.empty
2423 )
2424 if has_no_gap and not exc._safe_to_use_with_word_operator(
2425 ExpressionPosition.RIGHT
2426 ):
2427 raise CSTValidationError("Must have at least one space after 'raise'.")
2429 # Validate spacing between "exc" and "from"
2430 cause = self.cause
2431 if exc is not None and cause is not None:
2432 whitespace_before_from = cause.whitespace_before_from
2433 has_no_gap = (
2434 not isinstance(whitespace_before_from, MaybeSentinel)
2435 and whitespace_before_from.empty
2436 )
2437 if has_no_gap and not exc._safe_to_use_with_word_operator(
2438 ExpressionPosition.LEFT
2439 ):
2440 raise CSTValidationError("Must have at least one space before 'from'.")
2442 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Raise":
2443 return Raise(
2444 whitespace_after_raise=visit_sentinel(
2445 self, "whitespace_after_raise", self.whitespace_after_raise, visitor
2446 ),
2447 exc=visit_optional(self, "exc", self.exc, visitor),
2448 cause=visit_optional(self, "cause", self.cause, visitor),
2449 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2450 )
2452 def _codegen_impl(
2453 self, state: CodegenState, default_semicolon: bool = False
2454 ) -> None:
2455 with state.record_syntactic_position(self):
2456 exc = self.exc
2457 cause = self.cause
2458 state.add_token("raise")
2459 whitespace_after_raise = self.whitespace_after_raise
2460 if isinstance(whitespace_after_raise, MaybeSentinel):
2461 if exc is not None:
2462 state.add_token(" ")
2463 else:
2464 whitespace_after_raise._codegen(state)
2465 if exc is not None:
2466 exc._codegen(state)
2467 if cause is not None:
2468 cause._codegen(state, default_space=" ")
2470 semicolon = self.semicolon
2471 if isinstance(semicolon, MaybeSentinel):
2472 if default_semicolon:
2473 state.add_token("; ")
2474 elif isinstance(semicolon, Semicolon):
2475 semicolon._codegen(state)
2478@add_slots
2479@dataclass(frozen=True)
2480class Assert(BaseSmallStatement):
2481 """
2482 An assert statement such as ``assert x > 5`` or ``assert x > 5, 'Uh oh!'``
2483 """
2485 #: The test we are going to assert on.
2486 test: BaseExpression
2488 #: The optional message to display if the test evaluates to a falsey value.
2489 msg: Optional[BaseExpression] = None
2491 #: A comma separating test and message, if there is a message.
2492 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2494 #: Whitespace appearing after the ``assert`` keyword and before the test.
2495 whitespace_after_assert: SimpleWhitespace = SimpleWhitespace.field(" ")
2497 #: Optional semicolon when this is used in a statement line. This semicolon
2498 #: owns the whitespace on both sides of it when it is used.
2499 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2501 def _validate(self) -> None:
2502 # Validate whitespace
2503 if (
2504 self.whitespace_after_assert.empty
2505 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2506 ):
2507 raise CSTValidationError("Must have at least one space after 'assert'.")
2509 # Validate comma rules
2510 if self.msg is None and isinstance(self.comma, Comma):
2511 raise CSTValidationError("Cannot have trailing comma after 'test'.")
2513 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Assert":
2514 return Assert(
2515 whitespace_after_assert=visit_required(
2516 self, "whitespace_after_assert", self.whitespace_after_assert, visitor
2517 ),
2518 test=visit_required(self, "test", self.test, visitor),
2519 comma=visit_sentinel(self, "comma", self.comma, visitor),
2520 msg=visit_optional(self, "msg", self.msg, visitor),
2521 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2522 )
2524 def _codegen_impl(
2525 self, state: CodegenState, default_semicolon: bool = False
2526 ) -> None:
2527 with state.record_syntactic_position(self):
2528 state.add_token("assert")
2529 self.whitespace_after_assert._codegen(state)
2530 self.test._codegen(state)
2532 comma = self.comma
2533 msg = self.msg
2534 if isinstance(comma, MaybeSentinel):
2535 if msg is not None:
2536 state.add_token(", ")
2537 else:
2538 comma._codegen(state)
2539 if msg is not None:
2540 msg._codegen(state)
2542 semicolon = self.semicolon
2543 if isinstance(semicolon, MaybeSentinel):
2544 if default_semicolon:
2545 state.add_token("; ")
2546 elif isinstance(semicolon, Semicolon):
2547 semicolon._codegen(state)
2550@add_slots
2551@dataclass(frozen=True)
2552class NameItem(CSTNode):
2553 """
2554 A single identifier name inside a :class:`Global` or :class:`Nonlocal` statement.
2556 This exists because a list of names in a ``global`` or ``nonlocal`` statement need
2557 to be separated by a comma, which ends up owned by the :class:`NameItem` node.
2558 """
2560 #: Identifier name.
2561 name: Name
2563 #: This is forbidden for the last :class:`NameItem` in a
2564 #: :class:`Global`/:class:`Nonlocal`, but all other tems inside a ``global`` or
2565 #: ``nonlocal`` statement must contain a comma to separate them.
2566 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2568 def _validate(self) -> None:
2569 # No parens around names here
2570 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
2571 raise CSTValidationError("Cannot have parens around names in NameItem.")
2573 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "NameItem":
2574 return NameItem(
2575 name=visit_required(self, "name", self.name, visitor),
2576 comma=visit_sentinel(self, "comma", self.comma, visitor),
2577 )
2579 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
2580 with state.record_syntactic_position(self):
2581 self.name._codegen(state)
2583 comma = self.comma
2584 if comma is MaybeSentinel.DEFAULT and default_comma:
2585 state.add_token(", ")
2586 elif isinstance(comma, Comma):
2587 comma._codegen(state)
2590@add_slots
2591@dataclass(frozen=True)
2592class Global(BaseSmallStatement):
2593 """
2594 A ``global`` statement.
2595 """
2597 #: A list of one or more names.
2598 names: Sequence[NameItem]
2600 #: Whitespace appearing after the ``global`` keyword and before the first name.
2601 whitespace_after_global: SimpleWhitespace = SimpleWhitespace.field(" ")
2603 #: Optional semicolon when this is used in a statement line. This semicolon
2604 #: owns the whitespace on both sides of it when it is used.
2605 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2607 def _validate(self) -> None:
2608 if len(self.names) == 0:
2609 raise CSTValidationError(
2610 "A Global statement must have at least one NameItem."
2611 )
2612 if self.names[-1].comma != MaybeSentinel.DEFAULT:
2613 raise CSTValidationError(
2614 "The last NameItem in a Global cannot have a trailing comma."
2615 )
2616 if self.whitespace_after_global.empty:
2617 raise CSTValidationError(
2618 "Must have at least one space after 'global' keyword."
2619 )
2621 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Global":
2622 return Global(
2623 whitespace_after_global=visit_required(
2624 self, "whitespace_after_global", self.whitespace_after_global, 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("global")
2635 self.whitespace_after_global._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)
2648@add_slots
2649@dataclass(frozen=True)
2650class Nonlocal(BaseSmallStatement):
2651 """
2652 A ``nonlocal`` statement.
2653 """
2655 #: A list of one or more names.
2656 names: Sequence[NameItem]
2658 #: Whitespace appearing after the ``global`` keyword and before the first name.
2659 whitespace_after_nonlocal: SimpleWhitespace = SimpleWhitespace.field(" ")
2661 #: Optional semicolon when this is used in a statement line. This semicolon
2662 #: owns the whitespace on both sides of it when it is used.
2663 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2665 def _validate(self) -> None:
2666 if len(self.names) == 0:
2667 raise CSTValidationError(
2668 "A Nonlocal statement must have at least one NameItem."
2669 )
2670 if self.names[-1].comma != MaybeSentinel.DEFAULT:
2671 raise CSTValidationError(
2672 "The last NameItem in a Nonlocal cannot have a trailing comma."
2673 )
2674 if self.whitespace_after_nonlocal.empty:
2675 raise CSTValidationError(
2676 "Must have at least one space after 'nonlocal' keyword."
2677 )
2679 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Nonlocal":
2680 return Nonlocal(
2681 whitespace_after_nonlocal=visit_required(
2682 self,
2683 "whitespace_after_nonlocal",
2684 self.whitespace_after_nonlocal,
2685 visitor,
2686 ),
2687 names=visit_sequence(self, "names", self.names, visitor),
2688 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2689 )
2691 def _codegen_impl(
2692 self, state: CodegenState, default_semicolon: bool = False
2693 ) -> None:
2694 with state.record_syntactic_position(self):
2695 state.add_token("nonlocal")
2696 self.whitespace_after_nonlocal._codegen(state)
2697 last_name = len(self.names) - 1
2698 for i, name in enumerate(self.names):
2699 name._codegen(state, default_comma=(i != last_name))
2701 semicolon = self.semicolon
2702 if isinstance(semicolon, MaybeSentinel):
2703 if default_semicolon:
2704 state.add_token("; ")
2705 elif isinstance(semicolon, Semicolon):
2706 semicolon._codegen(state)
2709class MatchPattern(_BaseParenthesizedNode, ABC):
2710 """
2711 A base class for anything that can appear as a pattern in a :class:`Match`
2712 statement.
2713 """
2715 __slots__ = ()
2718@add_slots
2719@dataclass(frozen=True)
2720# pyre-fixme[13]: Attribute `body` is never initialized.
2721class Match(BaseCompoundStatement):
2722 """
2723 A ``match`` statement.
2724 """
2726 #: The subject of the match.
2727 subject: BaseExpression
2729 #: A non-empty list of match cases.
2730 cases: Sequence["MatchCase"]
2732 #: Sequence of empty lines appearing before this compound statement line.
2733 leading_lines: Sequence[EmptyLine] = ()
2735 #: Whitespace between the ``match`` keyword and the subject.
2736 whitespace_after_match: SimpleWhitespace = SimpleWhitespace.field(" ")
2738 #: Whitespace after the subject but before the colon.
2739 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2741 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
2742 whitespace_after_colon: TrailingWhitespace = TrailingWhitespace.field()
2744 #: A string represents a specific indentation. A ``None`` value uses the modules's
2745 #: default indentation. This is included because indentation is allowed to be
2746 #: inconsistent across a file, just not ambiguously.
2747 indent: Optional[str] = None
2749 #: Any trailing comments or lines after the dedent that are owned by this match
2750 #: block. Statements own preceeding and same-line trailing comments, but not
2751 #: trailing lines, so it falls on :class:`Match` to own it. In the case
2752 #: that a statement follows a :class:`Match` block, that statement will own the
2753 #: comments and lines that are at the same indent as the statement, and this
2754 #: :class:`Match` will own the comments and lines that are indented further.
2755 footer: Sequence[EmptyLine] = ()
2757 def _validate(self) -> None:
2758 if len(self.cases) == 0:
2759 raise CSTValidationError("A match statement must have at least one case.")
2761 indent = self.indent
2762 if indent is not None:
2763 if len(indent) == 0:
2764 raise CSTValidationError(
2765 "A match statement must have a non-zero width indent."
2766 )
2767 if _INDENT_WHITESPACE_RE.fullmatch(indent) is None:
2768 raise CSTValidationError(
2769 "An indent must be composed of only whitespace characters."
2770 )
2772 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Match":
2773 return Match(
2774 leading_lines=visit_sequence(
2775 self, "leading_lines", self.leading_lines, visitor
2776 ),
2777 whitespace_after_match=visit_required(
2778 self, "whitespace_after_match", self.whitespace_after_match, visitor
2779 ),
2780 subject=visit_required(self, "subject", self.subject, visitor),
2781 whitespace_before_colon=visit_required(
2782 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2783 ),
2784 whitespace_after_colon=visit_required(
2785 self, "whitespace_after_colon", self.whitespace_after_colon, visitor
2786 ),
2787 indent=self.indent,
2788 cases=visit_sequence(self, "cases", self.cases, visitor),
2789 footer=visit_sequence(self, "footer", self.footer, visitor),
2790 )
2792 def _codegen_impl(self, state: CodegenState) -> None:
2793 for ll in self.leading_lines:
2794 ll._codegen(state)
2795 state.add_indent_tokens()
2797 with state.record_syntactic_position(self, end_node=self.cases[-1]):
2798 state.add_token("match")
2799 self.whitespace_after_match._codegen(state)
2800 self.subject._codegen(state)
2801 self.whitespace_before_colon._codegen(state)
2802 state.add_token(":")
2803 self.whitespace_after_colon._codegen(state)
2805 indent = self.indent
2806 state.increase_indent(state.default_indent if indent is None else indent)
2807 for c in self.cases:
2808 c._codegen(state)
2810 for f in self.footer:
2811 f._codegen(state)
2813 state.decrease_indent()
2816@add_slots
2817@dataclass(frozen=True)
2818class MatchCase(CSTNode):
2819 """
2820 A single ``case`` block of a :class:`Match` statement.
2821 """
2823 #: The pattern that ``subject`` will be matched against.
2824 pattern: MatchPattern
2826 #: The body of this case block, to be evaluated if ``pattern`` matches ``subject``
2827 #: and ``guard`` evaluates to a truthy value.
2828 body: BaseSuite
2830 #: Optional expression that will be evaluated if ``pattern`` matches ``subject``.
2831 guard: Optional[BaseExpression] = None
2833 #: Sequence of empty lines appearing before this case block.
2834 leading_lines: Sequence[EmptyLine] = ()
2836 #: Whitespace directly after the ``case`` keyword.
2837 whitespace_after_case: SimpleWhitespace = SimpleWhitespace.field(" ")
2839 #: Whitespace before the ``if`` keyword in case there's a guard expression.
2840 whitespace_before_if: SimpleWhitespace = SimpleWhitespace.field("")
2842 #: Whitespace after the ``if`` keyword in case there's a guard expression.
2843 whitespace_after_if: SimpleWhitespace = SimpleWhitespace.field("")
2845 #: Whitespace before the colon.
2846 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2848 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
2849 return MatchCase(
2850 leading_lines=visit_sequence(
2851 self, "leading_lines", self.leading_lines, visitor
2852 ),
2853 whitespace_after_case=visit_required(
2854 self, "whitespace_after_case", self.whitespace_after_case, visitor
2855 ),
2856 pattern=visit_required(self, "pattern", self.pattern, visitor),
2857 # pyre-fixme[6]: Expected `SimpleWhitespace` for 4th param but got
2858 # `Optional[SimpleWhitespace]`.
2859 whitespace_before_if=visit_optional(
2860 self, "whitespace_before_if", self.whitespace_before_if, visitor
2861 ),
2862 # pyre-fixme[6]: Expected `SimpleWhitespace` for 5th param but got
2863 # `Optional[SimpleWhitespace]`.
2864 whitespace_after_if=visit_optional(
2865 self, "whitespace_after_if", self.whitespace_after_if, visitor
2866 ),
2867 guard=visit_optional(self, "guard", self.guard, visitor),
2868 body=visit_required(self, "body", self.body, visitor),
2869 )
2871 def _codegen_impl(self, state: CodegenState) -> None:
2872 for ll in self.leading_lines:
2873 ll._codegen(state)
2874 state.add_indent_tokens()
2875 with state.record_syntactic_position(self, end_node=self.body):
2876 state.add_token("case")
2877 self.whitespace_after_case._codegen(state)
2878 self.pattern._codegen(state)
2880 guard = self.guard
2881 if guard is not None:
2882 self.whitespace_before_if._codegen(state)
2883 state.add_token("if")
2884 self.whitespace_after_if._codegen(state)
2885 guard._codegen(state)
2887 self.whitespace_before_colon._codegen(state)
2888 state.add_token(":")
2889 self.body._codegen(state)
2892@add_slots
2893@dataclass(frozen=True)
2894class MatchValue(MatchPattern):
2895 """
2896 A match literal or value pattern that compares by equality.
2897 """
2899 #: an expression to compare to
2900 value: BaseExpression
2902 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
2903 return MatchValue(value=visit_required(self, "value", self.value, visitor))
2905 def _codegen_impl(self, state: CodegenState) -> None:
2906 with state.record_syntactic_position(self):
2907 self.value._codegen(state)
2909 @property
2910 def lpar(self) -> Sequence[LeftParen]:
2911 return self.value.lpar
2913 @lpar.setter
2914 def lpar(self, value: Sequence[LeftParen]) -> None:
2915 self.value.lpar = value
2917 @property
2918 def rpar(self) -> Sequence[RightParen]:
2919 return self.value.rpar
2921 @rpar.setter
2922 def rpar(self, value: Sequence[RightParen]) -> None:
2923 self.value.rpar = value
2926@add_slots
2927@dataclass(frozen=True)
2928class MatchSingleton(MatchPattern):
2929 """
2930 A match literal pattern that compares by identity.
2931 """
2933 #: a literal to compare to
2934 value: Name
2936 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
2937 return MatchSingleton(value=visit_required(self, "value", self.value, visitor))
2939 def _validate(self) -> None:
2940 if self.value.value not in {"True", "False", "None"}:
2941 raise CSTValidationError(
2942 "A match singleton can only be True, False, or None"
2943 )
2945 def _codegen_impl(self, state: CodegenState) -> None:
2946 with state.record_syntactic_position(self):
2947 self.value._codegen(state)
2949 @property
2950 def lpar(self) -> Sequence[LeftParen]:
2951 return self.value.lpar
2953 @lpar.setter
2954 def lpar(self, value: Sequence[LeftParen]) -> None:
2955 # pyre-fixme[41]: Cannot reassign final attribute `lpar`.
2956 self.value.lpar = value
2958 @property
2959 def rpar(self) -> Sequence[RightParen]:
2960 return self.value.rpar
2962 @rpar.setter
2963 def rpar(self, value: Sequence[RightParen]) -> None:
2964 # pyre-fixme[41]: Cannot reassign final attribute `rpar`.
2965 self.value.rpar = value
2968@add_slots
2969@dataclass(frozen=True)
2970class MatchSequenceElement(CSTNode):
2971 """
2972 An element in a sequence match pattern.
2973 """
2975 value: MatchPattern
2977 #: An optional trailing comma.
2978 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2980 def _visit_and_replace_children(
2981 self, visitor: CSTVisitorT
2982 ) -> "MatchSequenceElement":
2983 return MatchSequenceElement(
2984 value=visit_required(self, "value", self.value, visitor),
2985 comma=visit_sentinel(self, "comma", self.comma, visitor),
2986 )
2988 def _codegen_impl(
2989 self,
2990 state: CodegenState,
2991 default_comma: bool = False,
2992 default_comma_whitespace: bool = True,
2993 ) -> None:
2994 with state.record_syntactic_position(self):
2995 self.value._codegen(state)
2996 comma = self.comma
2997 if comma is MaybeSentinel.DEFAULT and default_comma:
2998 state.add_token(", " if default_comma_whitespace else ",")
2999 elif isinstance(comma, Comma):
3000 comma._codegen(state)
3003@add_slots
3004@dataclass(frozen=True)
3005class MatchStar(CSTNode):
3006 """
3007 A starred element in a sequence match pattern. Matches the rest of the sequence.
3008 """
3010 #: The name of the pattern binding. A ``None`` value represents ``*_``.
3011 name: Optional[Name] = None
3013 #: An optional trailing comma.
3014 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3016 #: Optional whitespace between the star and the name.
3017 whitespace_before_name: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3019 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchStar":
3020 return MatchStar(
3021 whitespace_before_name=visit_required(
3022 self, "whitespace_before_name", self.whitespace_before_name, visitor
3023 ),
3024 name=visit_optional(self, "name", self.name, visitor),
3025 comma=visit_sentinel(self, "comma", self.comma, visitor),
3026 )
3028 def _codegen_impl(
3029 self,
3030 state: CodegenState,
3031 default_comma: bool = False,
3032 default_comma_whitespace: bool = True,
3033 ) -> None:
3034 with state.record_syntactic_position(self):
3035 state.add_token("*")
3036 self.whitespace_before_name._codegen(state)
3037 name = self.name
3038 if name is None:
3039 state.add_token("_")
3040 else:
3041 name._codegen(state)
3042 comma = self.comma
3043 if comma is MaybeSentinel.DEFAULT and default_comma:
3044 state.add_token(", " if default_comma_whitespace else ",")
3045 elif isinstance(comma, Comma):
3046 comma._codegen(state)
3049class MatchSequence(MatchPattern, ABC):
3050 """
3051 A match sequence pattern. It's either a :class:`MatchList` or a :class:`MatchTuple`.
3052 Matches a variable length sequence if one of the patterns is a :class:`MatchStar`,
3053 otherwise matches a fixed length sequence.
3054 """
3056 __slots__ = ()
3058 #: Patterns to be matched against the subject elements if it is a sequence.
3059 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3062@add_slots
3063@dataclass(frozen=True)
3064class MatchList(MatchSequence):
3065 """
3066 A list match pattern. It's either an "open sequence pattern" (without brackets) or a
3067 regular list literal (with brackets).
3068 """
3070 #: Patterns to be matched against the subject elements if it is a sequence.
3071 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3073 #: An optional left bracket. If missing, this is an open sequence pattern.
3074 lbracket: Optional[LeftSquareBracket] = LeftSquareBracket.field()
3076 #: An optional left bracket. If missing, this is an open sequence pattern.
3077 rbracket: Optional[RightSquareBracket] = RightSquareBracket.field()
3079 #: Parenthesis at the beginning of the node
3080 lpar: Sequence[LeftParen] = ()
3081 #: Parentheses after the pattern, but before a comma (if there is one).
3082 rpar: Sequence[RightParen] = ()
3084 def _validate(self) -> None:
3085 if self.lbracket and not self.rbracket:
3086 raise CSTValidationError("Cannot have left bracket without right bracket")
3087 if self.rbracket and not self.lbracket:
3088 raise CSTValidationError("Cannot have right bracket without left bracket")
3090 if not self.patterns and not self.lbracket:
3091 raise CSTValidationError(
3092 "Must have brackets if matching against empty list"
3093 )
3095 super(MatchList, self)._validate()
3097 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchList":
3098 return MatchList(
3099 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3100 lbracket=visit_optional(self, "lbracket", self.lbracket, visitor),
3101 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3102 rbracket=visit_optional(self, "rbracket", self.rbracket, visitor),
3103 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3104 )
3106 def _codegen_impl(self, state: CodegenState) -> None:
3107 with self._parenthesize(state):
3108 lbracket = self.lbracket
3109 if lbracket is not None:
3110 lbracket._codegen(state)
3111 pats = self.patterns
3112 for idx, pat in enumerate(pats):
3113 pat._codegen(state, default_comma=(idx < len(pats) - 1))
3114 rbracket = self.rbracket
3115 if rbracket is not None:
3116 rbracket._codegen(state)
3119@add_slots
3120@dataclass(frozen=True)
3121class MatchTuple(MatchSequence):
3122 """
3123 A tuple match pattern.
3124 """
3126 #: Patterns to be matched against the subject elements if it is a sequence.
3127 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3129 #: Parenthesis at the beginning of the node
3130 lpar: Sequence[LeftParen] = field(default_factory=lambda: (LeftParen(),))
3131 #: Parentheses after the pattern, but before a comma (if there is one).
3132 rpar: Sequence[RightParen] = field(default_factory=lambda: (RightParen(),))
3134 def _validate(self) -> None:
3135 if len(self.lpar) < 1:
3136 raise CSTValidationError(
3137 "Tuple patterns must have at least pair of parenthesis"
3138 )
3140 super(MatchTuple, self)._validate()
3142 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchTuple":
3143 return MatchTuple(
3144 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3145 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3146 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3147 )
3149 def _codegen_impl(self, state: CodegenState) -> None:
3150 with self._parenthesize(state):
3151 pats = self.patterns
3152 patlen = len(pats)
3153 for idx, pat in enumerate(pats):
3154 pat._codegen(
3155 state,
3156 default_comma=patlen == 1 or (idx < patlen - 1),
3157 default_comma_whitespace=patlen != 1,
3158 )
3161@add_slots
3162@dataclass(frozen=True)
3163class MatchMappingElement(CSTNode):
3164 """
3165 A ``key: value`` pair in a match mapping pattern.
3166 """
3168 key: BaseExpression
3170 #: The pattern to be matched corresponding to ``key``.
3171 pattern: MatchPattern
3173 #: An optional trailing comma.
3174 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3176 #: Whitespace between ``key`` and the colon.
3177 whitespace_before_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3179 #: Whitespace between the colon and ``pattern``.
3180 whitespace_after_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
3182 def _visit_and_replace_children(
3183 self, visitor: CSTVisitorT
3184 ) -> "MatchMappingElement":
3185 return MatchMappingElement(
3186 key=visit_required(self, "key", self.key, visitor),
3187 whitespace_before_colon=visit_required(
3188 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
3189 ),
3190 whitespace_after_colon=visit_required(
3191 self, "whitespace_after_colon", self.whitespace_after_colon, visitor
3192 ),
3193 pattern=visit_required(self, "pattern", self.pattern, visitor),
3194 comma=visit_sentinel(self, "comma", self.comma, visitor),
3195 )
3197 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
3198 with state.record_syntactic_position(self):
3199 self.key._codegen(state)
3200 self.whitespace_before_colon._codegen(state)
3201 state.add_token(":")
3202 self.whitespace_after_colon._codegen(state)
3203 self.pattern._codegen(state)
3204 comma = self.comma
3205 if comma is MaybeSentinel.DEFAULT and default_comma:
3206 state.add_token(", ")
3207 elif isinstance(comma, Comma):
3208 comma._codegen(state)
3211@add_slots
3212@dataclass(frozen=True)
3213class MatchMapping(MatchPattern):
3214 """
3215 A match mapping pattern.
3216 """
3218 #: A sequence of mapping elements.
3219 elements: Sequence[MatchMappingElement] = ()
3221 #: Left curly brace at the beginning of the pattern.
3222 lbrace: LeftCurlyBrace = LeftCurlyBrace.field()
3224 #: Right curly brace at the end of the pattern.
3225 rbrace: RightCurlyBrace = RightCurlyBrace.field()
3227 #: An optional name to capture the remaining elements of the mapping.
3228 rest: Optional[Name] = None
3230 #: Optional whitespace between stars and ``rest``.
3231 whitespace_before_rest: SimpleWhitespace = SimpleWhitespace.field("")
3233 #: An optional trailing comma attached to ``rest``.
3234 trailing_comma: Optional[Comma] = None
3236 #: Parenthesis at the beginning of the node
3237 lpar: Sequence[LeftParen] = ()
3238 #: Parentheses after the pattern
3239 rpar: Sequence[RightParen] = ()
3241 def _validate(self) -> None:
3242 if isinstance(self.trailing_comma, Comma) and self.rest is not None:
3243 raise CSTValidationError("Cannot have a trailing comma without **rest")
3244 super(MatchMapping, self)._validate()
3246 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchMapping":
3247 return MatchMapping(
3248 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3249 lbrace=visit_required(self, "lbrace", self.lbrace, visitor),
3250 elements=visit_sequence(self, "elements", self.elements, visitor),
3251 whitespace_before_rest=visit_required(
3252 self, "whitespace_before_rest", self.whitespace_before_rest, visitor
3253 ),
3254 rest=visit_optional(self, "rest", self.rest, visitor),
3255 trailing_comma=visit_optional(
3256 self, "trailing_comma", self.trailing_comma, visitor
3257 ),
3258 rbrace=visit_required(self, "rbrace", self.rbrace, visitor),
3259 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3260 )
3262 def _codegen_impl(self, state: CodegenState) -> None:
3263 with self._parenthesize(state):
3264 self.lbrace._codegen(state)
3265 elems = self.elements
3266 rest = self.rest
3267 for idx, el in enumerate(elems):
3268 el._codegen(
3269 state, default_comma=rest is not None or idx < len(elems) - 1
3270 )
3272 if rest is not None:
3273 state.add_token("**")
3274 self.whitespace_before_rest._codegen(state)
3275 rest._codegen(state)
3276 comma = self.trailing_comma
3277 if comma is not None:
3278 comma._codegen(state)
3280 self.rbrace._codegen(state)
3283@add_slots
3284@dataclass(frozen=True)
3285class MatchKeywordElement(CSTNode):
3286 """
3287 A key=value pair in a :class:`MatchClass`.
3288 """
3290 key: Name
3292 #: The pattern to be matched against the attribute named ``key``.
3293 pattern: MatchPattern
3295 #: An optional trailing comma.
3296 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3298 #: Whitespace between ``key`` and the equals sign.
3299 whitespace_before_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3301 #: Whitespace between the equals sign and ``pattern``.
3302 whitespace_after_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3304 def _visit_and_replace_children(
3305 self, visitor: CSTVisitorT
3306 ) -> "MatchKeywordElement":
3307 return MatchKeywordElement(
3308 key=visit_required(self, "key", self.key, visitor),
3309 whitespace_before_equal=visit_required(
3310 self, "whitespace_before_equal", self.whitespace_before_equal, visitor
3311 ),
3312 whitespace_after_equal=visit_required(
3313 self, "whitespace_after_equal", self.whitespace_after_equal, visitor
3314 ),
3315 pattern=visit_required(self, "pattern", self.pattern, visitor),
3316 comma=visit_sentinel(self, "comma", self.comma, visitor),
3317 )
3319 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
3320 with state.record_syntactic_position(self):
3321 self.key._codegen(state)
3322 self.whitespace_before_equal._codegen(state)
3323 state.add_token("=")
3324 self.whitespace_after_equal._codegen(state)
3325 self.pattern._codegen(state)
3326 comma = self.comma
3327 if comma is MaybeSentinel.DEFAULT and default_comma:
3328 state.add_token(", ")
3329 elif isinstance(comma, Comma):
3330 comma._codegen(state)
3333@add_slots
3334@dataclass(frozen=True)
3335class MatchClass(MatchPattern):
3336 """
3337 A match class pattern.
3338 """
3340 #: An expression giving the nominal class to be matched.
3341 cls: BaseExpression
3343 #: A sequence of patterns to be matched against the class defined sequence of
3344 #: pattern matching attributes.
3345 patterns: Sequence[MatchSequenceElement] = ()
3347 #: A sequence of additional attribute names and corresponding patterns to be
3348 #: matched.
3349 kwds: Sequence[MatchKeywordElement] = ()
3351 #: Whitespace between the class name and the left parenthesis.
3352 whitespace_after_cls: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3354 #: Whitespace between the left parenthesis and the first pattern.
3355 whitespace_before_patterns: BaseParenthesizableWhitespace = SimpleWhitespace.field(
3356 ""
3357 )
3359 #: Whitespace between the last pattern and the right parenthesis.
3360 whitespace_after_kwds: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3362 #: Parenthesis at the beginning of the node
3363 lpar: Sequence[LeftParen] = ()
3364 #: Parentheses after the pattern
3365 rpar: Sequence[RightParen] = ()
3367 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchClass":
3368 return MatchClass(
3369 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3370 cls=visit_required(self, "cls", self.cls, visitor),
3371 whitespace_after_cls=visit_required(
3372 self, "whitespace_after_cls", self.whitespace_after_cls, visitor
3373 ),
3374 whitespace_before_patterns=visit_required(
3375 self,
3376 "whitespace_before_patterns",
3377 self.whitespace_before_patterns,
3378 visitor,
3379 ),
3380 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3381 kwds=visit_sequence(self, "kwds", self.kwds, visitor),
3382 whitespace_after_kwds=visit_required(
3383 self, "whitespace_after_kwds", self.whitespace_after_kwds, visitor
3384 ),
3385 )
3387 def _codegen_impl(self, state: CodegenState) -> None:
3388 with self._parenthesize(state):
3389 self.cls._codegen(state)
3390 self.whitespace_after_cls._codegen(state)
3391 state.add_token("(")
3392 self.whitespace_before_patterns._codegen(state)
3393 pats = self.patterns
3394 kwds = self.kwds
3395 for idx, pat in enumerate(pats):
3396 pat._codegen(state, default_comma=idx + 1 < len(pats) + len(kwds))
3397 for idx, kwd in enumerate(kwds):
3398 kwd._codegen(state, default_comma=idx + 1 < len(kwds))
3399 self.whitespace_after_kwds._codegen(state)
3400 state.add_token(")")
3403@add_slots
3404@dataclass(frozen=True)
3405class MatchAs(MatchPattern):
3406 """
3407 A match "as-pattern", capture pattern, or wildcard pattern.
3408 """
3410 #: The match pattern that the subject will be matched against. If this is ``None``,
3411 #: the node represents a capture pattern (i.e. a bare name) and will always succeed.
3412 pattern: Optional[MatchPattern] = None
3414 #: The name that will be bound if the pattern is successful. If this is ``None``,
3415 #: ``pattern`` must also be ``None`` and the node represents the wildcard pattern
3416 #: (i.e. ``_``).
3417 name: Optional[Name] = None
3419 #: Whitespace between ``pattern`` and the ``as`` keyword (if ``pattern`` is not
3420 #: ``None``)
3421 whitespace_before_as: Union[
3422 BaseParenthesizableWhitespace, MaybeSentinel
3423 ] = MaybeSentinel.DEFAULT
3425 #: Whitespace between the ``as`` keyword and ``name`` (if ``pattern`` is not
3426 #: ``None``)
3427 whitespace_after_as: Union[
3428 BaseParenthesizableWhitespace, MaybeSentinel
3429 ] = MaybeSentinel.DEFAULT
3431 #: Parenthesis at the beginning of the node
3432 lpar: Sequence[LeftParen] = ()
3433 #: Parentheses after the pattern
3434 rpar: Sequence[RightParen] = ()
3436 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchAs":
3437 return MatchAs(
3438 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3439 pattern=visit_optional(self, "pattern", self.pattern, visitor),
3440 whitespace_before_as=visit_sentinel(
3441 self, "whitespace_before_as", self.whitespace_before_as, visitor
3442 ),
3443 whitespace_after_as=visit_sentinel(
3444 self, "whitespace_after_as", self.whitespace_after_as, visitor
3445 ),
3446 name=visit_optional(self, "name", self.name, visitor),
3447 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3448 )
3450 def _validate(self) -> None:
3451 if self.name is None and self.pattern is not None:
3452 raise CSTValidationError("Pattern must be None if name is None")
3453 super(MatchAs, self)._validate()
3455 def _codegen_impl(self, state: CodegenState) -> None:
3456 with self._parenthesize(state):
3457 pat = self.pattern
3458 name = self.name
3459 if pat is not None:
3460 pat._codegen(state)
3461 ws_before = self.whitespace_before_as
3462 if ws_before is MaybeSentinel.DEFAULT:
3463 state.add_token(" ")
3464 elif isinstance(ws_before, BaseParenthesizableWhitespace):
3465 ws_before._codegen(state)
3466 state.add_token("as")
3467 ws_after = self.whitespace_after_as
3468 if ws_after is MaybeSentinel.DEFAULT:
3469 state.add_token(" ")
3470 elif isinstance(ws_after, BaseParenthesizableWhitespace):
3471 ws_after._codegen(state)
3472 if name is None:
3473 state.add_token("_")
3474 else:
3475 name._codegen(state)
3478@add_slots
3479@dataclass(frozen=True)
3480class MatchOrElement(CSTNode):
3481 """
3482 An element in a :class:`MatchOr` node.
3483 """
3485 pattern: MatchPattern
3487 #: An optional ``|`` separator.
3488 separator: Union[BitOr, MaybeSentinel] = MaybeSentinel.DEFAULT
3490 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOrElement":
3491 return MatchOrElement(
3492 pattern=visit_required(self, "pattern", self.pattern, visitor),
3493 separator=visit_sentinel(self, "separator", self.separator, visitor),
3494 )
3496 def _codegen_impl(
3497 self, state: CodegenState, default_separator: bool = False
3498 ) -> None:
3499 with state.record_syntactic_position(self):
3500 self.pattern._codegen(state)
3501 sep = self.separator
3502 if sep is MaybeSentinel.DEFAULT and default_separator:
3503 state.add_token(" | ")
3504 elif isinstance(sep, BitOr):
3505 sep._codegen(state)
3508@add_slots
3509@dataclass(frozen=True)
3510class MatchOr(MatchPattern):
3511 """
3512 A match "or-pattern". It matches each of its subpatterns in turn to the subject,
3513 until one succeeds. The or-pattern is then deemed to succeed. If none of the
3514 subpatterns succeed the or-pattern fails.
3515 """
3517 #: The subpatterns to be tried in turn.
3518 patterns: Sequence[MatchOrElement]
3520 #: Parenthesis at the beginning of the node
3521 lpar: Sequence[LeftParen] = ()
3522 #: Parentheses after the pattern
3523 rpar: Sequence[RightParen] = ()
3525 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOr":
3526 return MatchOr(
3527 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3528 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3529 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3530 )
3532 def _codegen_impl(self, state: CodegenState) -> None:
3533 with self._parenthesize(state):
3534 pats = self.patterns
3535 for idx, pat in enumerate(pats):
3536 pat._codegen(state, default_separator=idx + 1 < len(pats))
3539@add_slots
3540@dataclass(frozen=True)
3541class TypeVar(CSTNode):
3542 """
3543 A simple (non-variadic) type variable.
3545 Note: this node represents type a variable when declared using PEP-695 syntax.
3546 """
3548 #: The name of the type variable.
3549 name: Name
3551 #: An optional bound on the type.
3552 bound: Optional[BaseExpression] = None
3554 #: The colon used to separate the name and bound. If not specified,
3555 #: :class:`MaybeSentinel` will be replaced with a colon if there is a bound,
3556 #: otherwise will be left empty.
3557 colon: Union[Colon, MaybeSentinel] = MaybeSentinel.DEFAULT
3559 def _codegen_impl(self, state: CodegenState) -> None:
3560 with state.record_syntactic_position(self):
3561 self.name._codegen(state)
3562 bound = self.bound
3563 colon = self.colon
3564 if not isinstance(colon, MaybeSentinel):
3565 colon._codegen(state)
3566 else:
3567 if bound is not None:
3568 state.add_token(": ")
3570 if bound is not None:
3571 bound._codegen(state)
3573 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeVar":
3574 return TypeVar(
3575 name=visit_required(self, "name", self.name, visitor),
3576 colon=visit_sentinel(self, "colon", self.colon, visitor),
3577 bound=visit_optional(self, "bound", self.bound, visitor),
3578 )
3581@add_slots
3582@dataclass(frozen=True)
3583class TypeVarTuple(CSTNode):
3584 """
3585 A variadic type variable.
3586 """
3588 #: The name of this type variable.
3589 name: Name
3591 #: The (optional) whitespace between the star declaring this type variable as
3592 #: variadic, and the variable's name.
3593 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("")
3595 def _codegen_impl(self, state: CodegenState) -> None:
3596 with state.record_syntactic_position(self):
3597 state.add_token("*")
3598 self.whitespace_after_star._codegen(state)
3599 self.name._codegen(state)
3601 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeVarTuple":
3602 return TypeVarTuple(
3603 name=visit_required(self, "name", self.name, visitor),
3604 whitespace_after_star=visit_required(
3605 self, "whitespace_after_star", self.whitespace_after_star, visitor
3606 ),
3607 )
3610@add_slots
3611@dataclass(frozen=True)
3612class ParamSpec(CSTNode):
3613 """
3614 A parameter specification.
3616 Note: this node represents a parameter specification when declared using PEP-695
3617 syntax.
3618 """
3620 #: The name of this parameter specification.
3621 name: Name
3623 #: The (optional) whitespace between the double star declaring this type variable as
3624 #: a parameter specification, and the name.
3625 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("")
3627 def _codegen_impl(self, state: CodegenState) -> None:
3628 with state.record_syntactic_position(self):
3629 state.add_token("**")
3630 self.whitespace_after_star._codegen(state)
3631 self.name._codegen(state)
3633 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ParamSpec":
3634 return ParamSpec(
3635 name=visit_required(self, "name", self.name, visitor),
3636 whitespace_after_star=visit_required(
3637 self, "whitespace_after_star", self.whitespace_after_star, visitor
3638 ),
3639 )
3642@add_slots
3643@dataclass(frozen=True)
3644class TypeParam(CSTNode):
3645 """
3646 A single type parameter that is contained in a :class:`TypeParameters` list.
3647 """
3649 #: The actual parameter.
3650 param: Union[TypeVar, TypeVarTuple, ParamSpec]
3652 #: A trailing comma. If one is not provided, :class:`MaybeSentinel` will be replaced
3653 #: with a comma only if a comma is required.
3654 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3656 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
3657 self.param._codegen(state)
3658 comma = self.comma
3659 if isinstance(comma, MaybeSentinel):
3660 if default_comma:
3661 state.add_token(", ")
3662 else:
3663 comma._codegen(state)
3665 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeParam":
3666 return TypeParam(
3667 param=visit_required(self, "param", self.param, visitor),
3668 comma=visit_sentinel(self, "comma", self.comma, visitor),
3669 )
3672@add_slots
3673@dataclass(frozen=True)
3674class TypeParameters(CSTNode):
3675 """
3676 Type parameters when specified with PEP-695 syntax.
3678 This node captures all specified parameters that are enclosed with square brackets.
3679 """
3681 #: The parameters within the square brackets.
3682 params: Sequence[TypeParam] = ()
3684 #: Opening square bracket that marks the start of these parameters.
3685 lbracket: LeftSquareBracket = LeftSquareBracket.field()
3686 #: Closing square bracket that marks the end of these parameters.
3687 rbracket: RightSquareBracket = RightSquareBracket.field()
3689 def _codegen_impl(self, state: CodegenState) -> None:
3690 self.lbracket._codegen(state)
3691 params_len = len(self.params)
3692 for idx, param in enumerate(self.params):
3693 param._codegen(state, default_comma=idx + 1 < params_len)
3694 self.rbracket._codegen(state)
3696 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeParameters":
3697 return TypeParameters(
3698 lbracket=visit_required(self, "lbracket", self.lbracket, visitor),
3699 params=visit_sequence(self, "params", self.params, visitor),
3700 rbracket=visit_required(self, "rbracket", self.rbracket, visitor),
3701 )
3704@add_slots
3705@dataclass(frozen=True)
3706class TypeAlias(BaseSmallStatement):
3707 """
3708 A type alias statement.
3710 This node represents the ``type`` statement as specified initially by PEP-695.
3711 Example: ``type ListOrSet[T] = list[T] | set[T]``.
3712 """
3714 #: The name being introduced in this statement.
3715 name: Name
3717 #: Everything on the right hand side of the ``=``.
3718 value: BaseExpression
3720 #: An optional list of type parameters, specified after the name.
3721 type_parameters: Optional[TypeParameters] = None
3723 #: Whitespace between the ``type`` soft keyword and the name.
3724 whitespace_after_type: SimpleWhitespace = SimpleWhitespace.field(" ")
3726 #: Whitespace between the name and the type parameters (if they exist) or the ``=``.
3727 #: If not specified, :class:`MaybeSentinel` will be replaced with a single space if
3728 #: there are no type parameters, otherwise no spaces.
3729 whitespace_after_name: Union[
3730 SimpleWhitespace, MaybeSentinel
3731 ] = MaybeSentinel.DEFAULT
3733 #: Whitespace between the type parameters and the ``=``. Always empty if there are
3734 #: no type parameters. If not specified, :class:`MaybeSentinel` will be replaced
3735 #: with a single space if there are type parameters.
3736 whitespace_after_type_parameters: Union[
3737 SimpleWhitespace, MaybeSentinel
3738 ] = MaybeSentinel.DEFAULT
3740 #: Whitespace between the ``=`` and the value.
3741 whitespace_after_equals: SimpleWhitespace = SimpleWhitespace.field(" ")
3743 #: Optional semicolon when this is used in a statement line. This semicolon
3744 #: owns the whitespace on both sides of it when it is used.
3745 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
3747 def _validate(self) -> None:
3748 if (
3749 self.type_parameters is None
3750 and self.whitespace_after_type_parameters
3751 not in {
3752 SimpleWhitespace(""),
3753 MaybeSentinel.DEFAULT,
3754 }
3755 ):
3756 raise CSTValidationError(
3757 "whitespace_after_type_parameters must be empty when there are no type parameters in a TypeAlias"
3758 )
3760 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeAlias":
3761 return TypeAlias(
3762 whitespace_after_type=visit_required(
3763 self, "whitespace_after_type", self.whitespace_after_type, visitor
3764 ),
3765 name=visit_required(self, "name", self.name, visitor),
3766 whitespace_after_name=visit_sentinel(
3767 self, "whitespace_after_name", self.whitespace_after_name, visitor
3768 ),
3769 type_parameters=visit_optional(
3770 self, "type_parameters", self.type_parameters, visitor
3771 ),
3772 whitespace_after_type_parameters=visit_sentinel(
3773 self,
3774 "whitespace_after_type_parameters",
3775 self.whitespace_after_type_parameters,
3776 visitor,
3777 ),
3778 whitespace_after_equals=visit_required(
3779 self, "whitespace_after_equals", self.whitespace_after_equals, visitor
3780 ),
3781 value=visit_required(self, "value", self.value, visitor),
3782 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
3783 )
3785 def _codegen_impl(
3786 self, state: CodegenState, default_semicolon: bool = False
3787 ) -> None:
3788 with state.record_syntactic_position(self):
3789 state.add_token("type")
3790 self.whitespace_after_type._codegen(state)
3791 self.name._codegen(state)
3792 ws_after_name = self.whitespace_after_name
3793 if isinstance(ws_after_name, MaybeSentinel):
3794 if self.type_parameters is None:
3795 state.add_token(" ")
3796 else:
3797 ws_after_name._codegen(state)
3799 ws_after_type_params = self.whitespace_after_type_parameters
3800 if self.type_parameters is not None:
3801 self.type_parameters._codegen(state)
3802 if isinstance(ws_after_type_params, MaybeSentinel):
3803 state.add_token(" ")
3804 else:
3805 ws_after_type_params._codegen(state)
3807 state.add_token("=")
3808 self.whitespace_after_equals._codegen(state)
3809 self.value._codegen(state)
3811 semi = self.semicolon
3812 if isinstance(semi, MaybeSentinel):
3813 if default_semicolon:
3814 state.add_token("; ")
3815 else:
3816 semi._codegen(state)