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.
5
6import inspect
7import re
8from abc import ABC, abstractmethod
9from dataclasses import dataclass, field
10from typing import Literal, Optional, Pattern, Sequence, Union
11
12from libcst import CSTLogicError
13
14from libcst._add_slots import add_slots
15from libcst._maybe_sentinel import MaybeSentinel
16from libcst._nodes.base import CSTNode, CSTValidationError
17from libcst._nodes.expression import (
18 _BaseParenthesizedNode,
19 Annotation,
20 Arg,
21 Asynchronous,
22 Attribute,
23 BaseAssignTargetExpression,
24 BaseDelTargetExpression,
25 BaseExpression,
26 ConcatenatedString,
27 ExpressionPosition,
28 From,
29 LeftCurlyBrace,
30 LeftParen,
31 LeftSquareBracket,
32 List,
33 Name,
34 Parameters,
35 RightCurlyBrace,
36 RightParen,
37 RightSquareBracket,
38 SimpleString,
39 Tuple,
40)
41from libcst._nodes.internal import (
42 CodegenState,
43 visit_body_sequence,
44 visit_optional,
45 visit_required,
46 visit_sentinel,
47 visit_sequence,
48)
49from libcst._nodes.op import (
50 AssignEqual,
51 BaseAugOp,
52 BitOr,
53 Colon,
54 Comma,
55 Dot,
56 ImportStar,
57 Semicolon,
58)
59from libcst._nodes.whitespace import (
60 BaseParenthesizableWhitespace,
61 EmptyLine,
62 ParenthesizedWhitespace,
63 SimpleWhitespace,
64 TrailingWhitespace,
65)
66from libcst._visitors import CSTVisitorT
67
68_INDENT_WHITESPACE_RE: Pattern[str] = re.compile(r"[ \f\t]+", re.UNICODE)
69
70
71class BaseSuite(CSTNode, ABC):
72 """
73 A dummy base-class for both :class:`SimpleStatementSuite` and :class:`IndentedBlock`.
74 This exists to simplify type definitions and isinstance checks.
75
76 A suite is a group of statements controlled by a clause. A suite can be one or
77 more semicolon-separated simple statements on the same line as the header,
78 following the header’s colon, or it can be one or more indented statements on
79 subsequent lines.
80
81 -- https://docs.python.org/3/reference/compound_stmts.html
82 """
83
84 __slots__ = ()
85
86 body: Union[Sequence["BaseStatement"], Sequence["BaseSmallStatement"]]
87
88
89class BaseStatement(CSTNode, ABC):
90 """
91 A class that exists to allow for typing to specify that any statement is allowed
92 in a particular location.
93 """
94
95 __slots__ = ()
96
97
98class BaseSmallStatement(CSTNode, ABC):
99 """
100 Encapsulates a small statement, like ``del`` or ``pass``, and optionally adds a
101 trailing semicolon. A small statement is always contained inside a
102 :class:`SimpleStatementLine` or :class:`SimpleStatementSuite`. This exists to
103 simplify type definitions and isinstance checks.
104 """
105
106 __slots__ = ()
107
108 #: An optional semicolon that appears after a small statement. This is optional
109 #: for the last small statement in a :class:`SimpleStatementLine` or
110 #: :class:`SimpleStatementSuite`, but all other small statements inside a simple
111 #: statement must contain a semicolon to disambiguate multiple small statements
112 #: on the same line.
113 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
114
115 @abstractmethod
116 def _codegen_impl(
117 self, state: CodegenState, default_semicolon: bool = False
118 ) -> None: ...
119
120
121@add_slots
122@dataclass(frozen=True)
123class Del(BaseSmallStatement):
124 """
125 Represents a ``del`` statement. ``del`` is always followed by a target.
126 """
127
128 #: The target expression will be deleted. This can be a name, a tuple,
129 #: an item of a list, an item of a dictionary, or an attribute.
130 target: BaseDelTargetExpression
131
132 #: The whitespace after the ``del`` keyword.
133 whitespace_after_del: SimpleWhitespace = SimpleWhitespace.field(" ")
134
135 #: Optional semicolon when this is used in a statement line. This semicolon
136 #: owns the whitespace on both sides of it when it is used.
137 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
138
139 def _validate(self) -> None:
140 if (
141 self.whitespace_after_del.empty
142 and not self.target._safe_to_use_with_word_operator(
143 ExpressionPosition.RIGHT
144 )
145 ):
146 raise CSTValidationError("Must have at least one space after 'del'.")
147
148 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Del":
149 return Del(
150 target=visit_required(self, "target", self.target, visitor),
151 whitespace_after_del=visit_required(
152 self, "whitespace_after_del", self.whitespace_after_del, visitor
153 ),
154 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
155 )
156
157 def _codegen_impl(
158 self, state: CodegenState, default_semicolon: bool = False
159 ) -> None:
160 with state.record_syntactic_position(self):
161 state.add_token("del")
162 self.whitespace_after_del._codegen(state)
163 self.target._codegen(state)
164
165 semicolon = self.semicolon
166 if isinstance(semicolon, MaybeSentinel):
167 if default_semicolon:
168 state.add_token("; ")
169 elif isinstance(semicolon, Semicolon):
170 semicolon._codegen(state)
171
172
173@add_slots
174@dataclass(frozen=True)
175class Pass(BaseSmallStatement):
176 """
177 Represents a ``pass`` statement.
178 """
179
180 #: Optional semicolon when this is used in a statement line. This semicolon
181 #: owns the whitespace on both sides of it when it is used.
182 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
183
184 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Pass":
185 return Pass(
186 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor)
187 )
188
189 def _codegen_impl(
190 self, state: CodegenState, default_semicolon: bool = False
191 ) -> None:
192 with state.record_syntactic_position(self):
193 state.add_token("pass")
194
195 semicolon = self.semicolon
196 if isinstance(semicolon, MaybeSentinel):
197 if default_semicolon:
198 state.add_token("; ")
199 elif isinstance(semicolon, Semicolon):
200 semicolon._codegen(state)
201
202
203@add_slots
204@dataclass(frozen=True)
205class Break(BaseSmallStatement):
206 """
207 Represents a ``break`` statement, which is used to break out of a :class:`For`
208 or :class:`While` loop early.
209 """
210
211 #: Optional semicolon when this is used in a statement line. This semicolon
212 #: owns the whitespace on both sides of it when it is used.
213 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
214
215 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Break":
216 return Break(
217 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor)
218 )
219
220 def _codegen_impl(
221 self, state: CodegenState, default_semicolon: bool = False
222 ) -> None:
223 with state.record_syntactic_position(self):
224 state.add_token("break")
225
226 semicolon = self.semicolon
227 if isinstance(semicolon, MaybeSentinel):
228 if default_semicolon:
229 state.add_token("; ")
230 elif isinstance(semicolon, Semicolon):
231 semicolon._codegen(state)
232
233
234@add_slots
235@dataclass(frozen=True)
236class Continue(BaseSmallStatement):
237 """
238 Represents a ``continue`` statement, which is used to skip to the next iteration
239 in a :class:`For` or :class:`While` loop.
240 """
241
242 #: Optional semicolon when this is used in a statement line. This semicolon
243 #: owns the whitespace on both sides of it when it is used.
244 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
245
246 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Continue":
247 return Continue(
248 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor)
249 )
250
251 def _codegen_impl(
252 self, state: CodegenState, default_semicolon: bool = False
253 ) -> None:
254 with state.record_syntactic_position(self):
255 state.add_token("continue")
256
257 semicolon = self.semicolon
258 if isinstance(semicolon, MaybeSentinel):
259 if default_semicolon:
260 state.add_token("; ")
261 elif isinstance(semicolon, Semicolon):
262 semicolon._codegen(state)
263
264
265@add_slots
266@dataclass(frozen=True)
267class Return(BaseSmallStatement):
268 """
269 Represents a ``return`` or a ``return x`` statement.
270 """
271
272 #: The optional expression that will be evaluated and returned.
273 value: Optional[BaseExpression] = None
274
275 #: Optional whitespace after the ``return`` keyword before the optional
276 #: value expression.
277 whitespace_after_return: Union[SimpleWhitespace, MaybeSentinel] = (
278 MaybeSentinel.DEFAULT
279 )
280
281 #: Optional semicolon when this is used in a statement line. This semicolon
282 #: owns the whitespace on both sides of it when it is used.
283 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
284
285 def _validate(self) -> None:
286 value = self.value
287 if value is not None:
288 whitespace_after_return = self.whitespace_after_return
289 has_no_gap = (
290 not isinstance(whitespace_after_return, MaybeSentinel)
291 and whitespace_after_return.empty
292 )
293 if has_no_gap and not value._safe_to_use_with_word_operator(
294 ExpressionPosition.RIGHT
295 ):
296 raise CSTValidationError("Must have at least one space after 'return'.")
297
298 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Return":
299 return Return(
300 whitespace_after_return=visit_sentinel(
301 self, "whitespace_after_return", self.whitespace_after_return, visitor
302 ),
303 value=visit_optional(self, "value", self.value, visitor),
304 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
305 )
306
307 def _codegen_impl(
308 self, state: CodegenState, default_semicolon: bool = False
309 ) -> None:
310 with state.record_syntactic_position(self):
311 state.add_token("return")
312 whitespace_after_return = self.whitespace_after_return
313 value = self.value
314 if isinstance(whitespace_after_return, MaybeSentinel):
315 if value is not None:
316 state.add_token(" ")
317 else:
318 whitespace_after_return._codegen(state)
319 if value is not None:
320 value._codegen(state)
321
322 semicolon = self.semicolon
323 if isinstance(semicolon, MaybeSentinel):
324 if default_semicolon:
325 state.add_token("; ")
326 elif isinstance(semicolon, Semicolon):
327 semicolon._codegen(state)
328
329
330@add_slots
331@dataclass(frozen=True)
332class Expr(BaseSmallStatement):
333 """
334 An expression used as a statement, where the result is unused and unassigned.
335 The most common place you will find this is in function calls where the return
336 value is unneeded.
337 """
338
339 #: The expression itself. Python will evaluate the expression but not assign
340 #: the result anywhere.
341 value: BaseExpression
342
343 #: Optional semicolon when this is used in a statement line. This semicolon
344 #: owns the whitespace on both sides of it when it is used.
345 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
346
347 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Expr":
348 return Expr(
349 value=visit_required(self, "value", self.value, visitor),
350 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
351 )
352
353 def _codegen_impl(
354 self, state: CodegenState, default_semicolon: bool = False
355 ) -> None:
356 with state.record_syntactic_position(self):
357 self.value._codegen(state)
358
359 semicolon = self.semicolon
360 if isinstance(semicolon, MaybeSentinel):
361 if default_semicolon:
362 state.add_token("; ")
363 elif isinstance(semicolon, Semicolon):
364 semicolon._codegen(state)
365
366
367class _BaseSimpleStatement(CSTNode, ABC):
368 """
369 A simple statement is a series of small statements joined together by semicolons.
370
371 simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
372
373 Whitespace between each small statement is owned by the small statements themselves.
374 It can be found on the required semicolon that will be attached to each non-terminal
375 small statement.
376 """
377
378 __slots__ = ()
379
380 #: Sequence of small statements. All but the last statement are required to have
381 #: a semicolon.
382 body: Sequence[BaseSmallStatement]
383
384 #: Any trailing comment and the final ``NEWLINE``, which is part of small statement's
385 #: grammar.
386 trailing_whitespace: TrailingWhitespace
387
388 def _validate(self) -> None:
389 body = self.body
390 for small_stmt in body[:-1]:
391 if small_stmt.semicolon is None:
392 raise CSTValidationError(
393 "All but the last SmallStatement in a SimpleStatementLine or "
394 + "SimpleStatementSuite must have a trailing semicolon. Otherwise, "
395 + "there's no way to syntatically disambiguate each SmallStatement "
396 + "on the same line."
397 )
398
399 def _codegen_impl(self, state: CodegenState) -> None:
400 body = self.body
401 if body:
402 laststmt = len(body) - 1
403 with state.record_syntactic_position(self, end_node=body[laststmt]):
404 for idx, stmt in enumerate(body):
405 stmt._codegen(state, default_semicolon=(idx != laststmt))
406 else:
407 # Empty simple statement blocks are not syntactically valid in Python
408 # unless they contain a 'pass' statement, so add one here.
409 with state.record_syntactic_position(self):
410 state.add_token("pass")
411
412 self.trailing_whitespace._codegen(state)
413
414
415@add_slots
416@dataclass(frozen=True)
417class SimpleStatementLine(_BaseSimpleStatement, BaseStatement):
418 """
419 A simple statement that's part of an IndentedBlock or Module. A simple statement is
420 a series of small statements joined together by semicolons.
421
422 This isn't differentiated from a :class:`SimpleStatementSuite` in the grammar, but
423 because a :class:`SimpleStatementLine` can own additional whitespace that a
424 :class:`SimpleStatementSuite` doesn't have, we're differentiating it in the CST.
425 """
426
427 #: Sequence of small statements. All but the last statement are required to have
428 #: a semicolon.
429 body: Sequence[BaseSmallStatement]
430
431 #: Sequence of empty lines appearing before this simple statement line.
432 leading_lines: Sequence[EmptyLine] = ()
433
434 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
435 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field()
436
437 def _visit_and_replace_children(
438 self, visitor: CSTVisitorT
439 ) -> "SimpleStatementLine":
440 return SimpleStatementLine(
441 leading_lines=visit_sequence(
442 self, "leading_lines", self.leading_lines, visitor
443 ),
444 body=visit_sequence(self, "body", self.body, visitor),
445 trailing_whitespace=visit_required(
446 self, "trailing_whitespace", self.trailing_whitespace, visitor
447 ),
448 )
449
450 def _is_removable(self) -> bool:
451 # If we have an empty body, we are removable since we don't represent
452 # anything concrete.
453 return not self.body
454
455 def _codegen_impl(self, state: CodegenState) -> None:
456 for ll in self.leading_lines:
457 ll._codegen(state)
458 state.add_indent_tokens()
459 _BaseSimpleStatement._codegen_impl(self, state)
460
461
462@add_slots
463@dataclass(frozen=True)
464class SimpleStatementSuite(_BaseSimpleStatement, BaseSuite):
465 """
466 A simple statement that's used as a suite. A simple statement is a series of small
467 statements joined together by semicolons. A suite is the thing that follows the
468 colon in a compound statement.
469
470 .. code-block::
471
472 if test:<leading_whitespace><body><trailing_whitespace>
473
474 This isn't differentiated from a :class:`SimpleStatementLine` in the grammar, but
475 because the two classes need to track different whitespace, we're differentiating
476 it in the CST.
477 """
478
479 #: Sequence of small statements. All but the last statement are required to have
480 #: a semicolon.
481 body: Sequence[BaseSmallStatement]
482
483 #: The whitespace between the colon in the parent statement and the body.
484 leading_whitespace: SimpleWhitespace = SimpleWhitespace.field(" ")
485
486 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
487 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field()
488
489 def _visit_and_replace_children(
490 self, visitor: CSTVisitorT
491 ) -> "SimpleStatementSuite":
492 return SimpleStatementSuite(
493 leading_whitespace=visit_required(
494 self, "leading_whitespace", self.leading_whitespace, visitor
495 ),
496 body=visit_sequence(self, "body", self.body, visitor),
497 trailing_whitespace=visit_required(
498 self, "trailing_whitespace", self.trailing_whitespace, visitor
499 ),
500 )
501
502 def _codegen_impl(self, state: CodegenState) -> None:
503 self.leading_whitespace._codegen(state)
504 _BaseSimpleStatement._codegen_impl(self, state)
505
506
507@add_slots
508@dataclass(frozen=True)
509class Else(CSTNode):
510 """
511 An ``else`` clause that appears optionally after an :class:`If`, :class:`While`,
512 :class:`Try`, or :class:`For` statement.
513
514 This node does not match ``elif`` clauses in :class:`If` statements. It also
515 does not match the required ``else`` clause in an :class:`IfExp` expression
516 (``a = if b else c``).
517 """
518
519 #: The body of else clause.
520 body: BaseSuite
521
522 #: Sequence of empty lines appearing before this compound statement line.
523 leading_lines: Sequence[EmptyLine] = ()
524
525 #: The whitespace appearing after the ``else`` keyword but before the colon.
526 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
527
528 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Else":
529 return Else(
530 leading_lines=visit_sequence(
531 self, "leading_lines", self.leading_lines, visitor
532 ),
533 whitespace_before_colon=visit_required(
534 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
535 ),
536 body=visit_required(self, "body", self.body, visitor),
537 )
538
539 def _codegen_impl(self, state: CodegenState) -> None:
540 for ll in self.leading_lines:
541 ll._codegen(state)
542 state.add_indent_tokens()
543
544 with state.record_syntactic_position(self, end_node=self.body):
545 state.add_token("else")
546 self.whitespace_before_colon._codegen(state)
547 state.add_token(":")
548 self.body._codegen(state)
549
550
551class BaseCompoundStatement(BaseStatement, ABC):
552 """
553 Encapsulates a compound statement, like ``if True: pass`` or ``while True: pass``.
554 This exists to simplify type definitions and isinstance checks.
555
556 Compound statements contain (groups of) other statements; they affect or control
557 the execution of those other statements in some way. In general, compound
558 statements span multiple lines, although in simple incarnations a whole compound
559 statement may be contained in one line.
560
561 -- https://docs.python.org/3/reference/compound_stmts.html
562 """
563
564 __slots__ = ()
565
566 #: The body of this compound statement.
567 body: BaseSuite
568
569 #: Any empty lines or comments appearing before this statement.
570 leading_lines: Sequence[EmptyLine]
571
572
573@add_slots
574@dataclass(frozen=True)
575class If(BaseCompoundStatement):
576 """
577 An ``if`` statement. ``test`` holds a single test expression.
578
579 ``elif`` clauses don’t have a special representation in the AST, but rather appear as
580 extra :class:`If` nodes within the ``orelse`` section of the previous one.
581 """
582
583 #: The expression that, when evaluated, should give us a truthy/falsey value.
584 test: BaseExpression # TODO: should be a test_nocond
585
586 #: The body of this compound statement.
587 body: BaseSuite
588
589 #: An optional ``elif`` or ``else`` clause. :class:`If` signifies an ``elif`` block.
590 #: :class:`Else` signifies an ``else`` block. ``None`` signifies no ``else`` or
591 #:``elif`` block.
592 orelse: Union["If", Else, None] = None
593
594 #: Sequence of empty lines appearing before this compound statement line.
595 leading_lines: Sequence[EmptyLine] = ()
596
597 #: The whitespace appearing after the ``if`` keyword but before the test expression.
598 whitespace_before_test: SimpleWhitespace = SimpleWhitespace.field(" ")
599
600 #: The whitespace appearing after the test expression but before the colon.
601 whitespace_after_test: SimpleWhitespace = SimpleWhitespace.field("")
602
603 def _validate(self) -> None:
604 if (
605 self.whitespace_before_test.empty
606 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
607 ):
608 raise CSTValidationError("Must have at least one space after 'if' keyword.")
609
610 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "If":
611 return If(
612 leading_lines=visit_sequence(
613 self, "leading_lines", self.leading_lines, visitor
614 ),
615 whitespace_before_test=visit_required(
616 self, "whitespace_before_test", self.whitespace_before_test, visitor
617 ),
618 test=visit_required(self, "test", self.test, visitor),
619 whitespace_after_test=visit_required(
620 self, "whitespace_after_test", self.whitespace_after_test, visitor
621 ),
622 body=visit_required(self, "body", self.body, visitor),
623 orelse=visit_optional(self, "orelse", self.orelse, visitor),
624 )
625
626 def _codegen_impl(self, state: CodegenState, is_elif: bool = False) -> None:
627 for ll in self.leading_lines:
628 ll._codegen(state)
629 state.add_indent_tokens()
630
631 end_node = self.body if self.orelse is None else self.orelse
632 with state.record_syntactic_position(self, end_node=end_node):
633 state.add_token("elif" if is_elif else "if")
634 self.whitespace_before_test._codegen(state)
635 self.test._codegen(state)
636 self.whitespace_after_test._codegen(state)
637 state.add_token(":")
638 self.body._codegen(state)
639 orelse = self.orelse
640 if orelse is not None:
641 if isinstance(orelse, If): # special-case elif
642 orelse._codegen(state, is_elif=True)
643 else: # is an Else clause
644 orelse._codegen(state)
645
646
647@add_slots
648@dataclass(frozen=True)
649class IndentedBlock(BaseSuite):
650 """
651 Represents a block of statements beginning with an ``INDENT`` token and ending in a
652 ``DEDENT`` token. Used as the body of compound statements, such as an if statement's
653 body.
654
655 A common alternative to an :class:`IndentedBlock` is a :class:`SimpleStatementSuite`,
656 which can also be used as a :class:`BaseSuite`, meaning that it can be used as the
657 body of many compound statements.
658
659 An :class:`IndentedBlock` always occurs after a colon in a
660 :class:`BaseCompoundStatement`, so it owns the trailing whitespace for the compound
661 statement's clause.
662
663 .. code-block::
664
665 if test: # IndentedBlock's header
666 body
667 """
668
669 #: Sequence of statements belonging to this indented block.
670 body: Sequence[BaseStatement]
671
672 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
673 header: TrailingWhitespace = TrailingWhitespace.field()
674
675 #: A string represents a specific indentation. A ``None`` value uses the modules's
676 #: default indentation. This is included because indentation is allowed to be
677 #: inconsistent across a file, just not ambiguously.
678 indent: Optional[str] = None
679
680 #: Any trailing comments or lines after the dedent that are owned by this indented
681 #: block. Statements own preceeding and same-line trailing comments, but not
682 #: trailing lines, so it falls on :class:`IndentedBlock` to own it. In the case
683 #: that a statement follows an :class:`IndentedBlock`, that statement will own the
684 #: comments and lines that are at the same indent as the statement, and this
685 #: :class:`IndentedBlock` will own the comments and lines that are indented further.
686 footer: Sequence[EmptyLine] = ()
687
688 def _validate(self) -> None:
689 indent = self.indent
690 if indent is not None:
691 if len(indent) == 0:
692 raise CSTValidationError(
693 "An indented block must have a non-zero width indent."
694 )
695 if _INDENT_WHITESPACE_RE.fullmatch(indent) is None:
696 raise CSTValidationError(
697 "An indent must be composed of only whitespace characters."
698 )
699
700 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "IndentedBlock":
701 return IndentedBlock(
702 header=visit_required(self, "header", self.header, visitor),
703 indent=self.indent,
704 body=visit_body_sequence(self, "body", self.body, visitor),
705 footer=visit_sequence(self, "footer", self.footer, visitor),
706 )
707
708 def _codegen_impl(self, state: CodegenState) -> None:
709 self.header._codegen(state)
710
711 indent = self.indent
712 state.increase_indent(state.default_indent if indent is None else indent)
713
714 if self.body:
715 with state.record_syntactic_position(
716 self, start_node=self.body[0], end_node=self.body[-1]
717 ):
718 for stmt in self.body:
719 # IndentedBlock is responsible for adjusting the current indentation level,
720 # but its children are responsible for actually adding that indentation to
721 # the token list.
722 stmt._codegen(state)
723 else:
724 # Empty indented blocks are not syntactically valid in Python unless
725 # they contain a 'pass' statement, so add one here.
726 state.add_indent_tokens()
727 with state.record_syntactic_position(self):
728 state.add_token("pass")
729 state.add_token(state.default_newline)
730
731 for f in self.footer:
732 f._codegen(state)
733
734 state.decrease_indent()
735
736
737@add_slots
738@dataclass(frozen=True)
739class AsName(CSTNode):
740 """
741 An ``as name`` clause inside an :class:`ExceptHandler`, :class:`ImportAlias` or
742 :class:`WithItem` node.
743 """
744
745 #: Identifier that the parent node will be aliased to.
746 name: Union[Name, Tuple, List]
747
748 #: Whitespace between the parent node and the ``as`` keyword.
749 whitespace_before_as: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
750
751 #: Whitespace between the ``as`` keyword and the name.
752 whitespace_after_as: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
753
754 def _validate(self) -> None:
755 if (
756 self.whitespace_after_as.empty
757 and not self.name._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
758 ):
759 raise CSTValidationError(
760 "There must be at least one space between 'as' and name."
761 )
762
763 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AsName":
764 return AsName(
765 whitespace_before_as=visit_required(
766 self, "whitespace_before_as", self.whitespace_before_as, visitor
767 ),
768 name=visit_required(self, "name", self.name, visitor),
769 whitespace_after_as=visit_required(
770 self, "whitespace_after_as", self.whitespace_after_as, visitor
771 ),
772 )
773
774 def _codegen_impl(self, state: CodegenState) -> None:
775 self.whitespace_before_as._codegen(state)
776 state.add_token("as")
777 self.whitespace_after_as._codegen(state)
778 self.name._codegen(state)
779
780
781@add_slots
782@dataclass(frozen=True)
783class ExceptHandler(CSTNode):
784 """
785 An ``except`` clause that appears optionally after a :class:`Try` statement.
786 """
787
788 #: The body of the except.
789 body: BaseSuite
790
791 #: The type of exception this catches. Can be a tuple in some cases,
792 #: or ``None`` if the code is catching all exceptions.
793 type: Optional[BaseExpression] = None
794
795 #: The optional name that a caught exception is assigned to.
796 name: Optional[AsName] = None
797
798 #: Sequence of empty lines appearing before this compound statement line.
799 leading_lines: Sequence[EmptyLine] = ()
800
801 #: The whitespace between the ``except`` keyword and the type attribute.
802 whitespace_after_except: SimpleWhitespace = SimpleWhitespace.field(" ")
803
804 #: The whitespace after any type or name node (whichever comes last) and
805 #: the colon.
806 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
807
808 def _validate(self) -> None:
809 name = self.name
810 if self.type is None and name is not None:
811 raise CSTValidationError("Cannot have a name for an empty type.")
812 if name is not None and not isinstance(name.name, Name):
813 raise CSTValidationError(
814 "Must use a Name node for AsName name inside ExceptHandler."
815 )
816 type_ = self.type
817 if type_ is not None and self.whitespace_after_except.empty:
818 # Space is only required when the first char in `type` could start
819 # an identifier. In the most common cases, we want to allow
820 # grouping or tuple parens.
821 if isinstance(type_, Name) and not type_.lpar:
822 raise CSTValidationError(
823 "Must have at least one space after except when ExceptHandler has a type."
824 )
825 name = self.name
826 if (
827 type_ is not None
828 and name is not None
829 and name.whitespace_before_as.empty
830 and not type_._safe_to_use_with_word_operator(ExpressionPosition.LEFT)
831 ):
832 raise CSTValidationError(
833 "Must have at least one space before as keyword in an except handler."
834 )
835
836 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ExceptHandler":
837 return ExceptHandler(
838 leading_lines=visit_sequence(
839 self, "leading_lines", self.leading_lines, visitor
840 ),
841 whitespace_after_except=visit_required(
842 self, "whitespace_after_except", self.whitespace_after_except, visitor
843 ),
844 type=visit_optional(self, "type", self.type, visitor),
845 name=visit_optional(self, "name", self.name, visitor),
846 whitespace_before_colon=visit_required(
847 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
848 ),
849 body=visit_required(self, "body", self.body, visitor),
850 )
851
852 def _codegen_impl(self, state: CodegenState) -> None:
853 for ll in self.leading_lines:
854 ll._codegen(state)
855 state.add_indent_tokens()
856
857 with state.record_syntactic_position(self, end_node=self.body):
858 state.add_token("except")
859 self.whitespace_after_except._codegen(state)
860 typenode = self.type
861 if typenode is not None:
862 typenode._codegen(state)
863 namenode = self.name
864 if namenode is not None:
865 namenode._codegen(state)
866 self.whitespace_before_colon._codegen(state)
867 state.add_token(":")
868 self.body._codegen(state)
869
870
871@add_slots
872@dataclass(frozen=True)
873class ExceptStarHandler(CSTNode):
874 """
875 An ``except*`` clause that appears after a :class:`TryStar` statement.
876 """
877
878 #: The body of the except.
879 body: BaseSuite
880
881 #: The type of exception this catches. Can be a tuple in some cases.
882 type: BaseExpression
883
884 #: The optional name that a caught exception is assigned to.
885 name: Optional[AsName] = None
886
887 #: Sequence of empty lines appearing before this compound statement line.
888 leading_lines: Sequence[EmptyLine] = ()
889
890 #: The whitespace between the ``except`` keyword and the star.
891 whitespace_after_except: SimpleWhitespace = SimpleWhitespace.field("")
892
893 #: The whitespace between the star and the type.
894 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field(" ")
895
896 #: The whitespace after any type or name node (whichever comes last) and
897 #: the colon.
898 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
899
900 def _validate(self) -> None:
901 name = self.name
902 if name is not None and not isinstance(name.name, Name):
903 raise CSTValidationError(
904 "Must use a Name node for AsName name inside ExceptHandler."
905 )
906
907 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ExceptStarHandler":
908 return ExceptStarHandler(
909 leading_lines=visit_sequence(
910 self, "leading_lines", self.leading_lines, visitor
911 ),
912 whitespace_after_except=visit_required(
913 self, "whitespace_after_except", self.whitespace_after_except, visitor
914 ),
915 whitespace_after_star=visit_required(
916 self, "whitespace_after_star", self.whitespace_after_star, visitor
917 ),
918 type=visit_required(self, "type", self.type, visitor),
919 name=visit_optional(self, "name", self.name, visitor),
920 whitespace_before_colon=visit_required(
921 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
922 ),
923 body=visit_required(self, "body", self.body, visitor),
924 )
925
926 def _codegen_impl(self, state: CodegenState) -> None:
927 for ll in self.leading_lines:
928 ll._codegen(state)
929 state.add_indent_tokens()
930
931 with state.record_syntactic_position(self, end_node=self.body):
932 state.add_token("except")
933 self.whitespace_after_except._codegen(state)
934 state.add_token("*")
935 self.whitespace_after_star._codegen(state)
936 typenode = self.type
937 if typenode is not None:
938 typenode._codegen(state)
939 namenode = self.name
940 if namenode is not None:
941 namenode._codegen(state)
942 self.whitespace_before_colon._codegen(state)
943 state.add_token(":")
944 self.body._codegen(state)
945
946
947@add_slots
948@dataclass(frozen=True)
949class Finally(CSTNode):
950 """
951 A ``finally`` clause that appears optionally after a :class:`Try` statement.
952 """
953
954 #: The body of the except.
955 body: BaseSuite
956
957 #: Sequence of empty lines appearing before this compound statement line.
958 leading_lines: Sequence[EmptyLine] = ()
959
960 #: The whitespace that appears after the ``finally`` keyword but before
961 #: the colon.
962 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
963
964 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Finally":
965 return Finally(
966 leading_lines=visit_sequence(
967 self, "leading_lines", self.leading_lines, visitor
968 ),
969 whitespace_before_colon=visit_required(
970 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
971 ),
972 body=visit_required(self, "body", self.body, visitor),
973 )
974
975 def _codegen_impl(self, state: CodegenState) -> None:
976 for ll in self.leading_lines:
977 ll._codegen(state)
978 state.add_indent_tokens()
979
980 with state.record_syntactic_position(self, end_node=self.body):
981 state.add_token("finally")
982 self.whitespace_before_colon._codegen(state)
983 state.add_token(":")
984 self.body._codegen(state)
985
986
987@add_slots
988@dataclass(frozen=True)
989class Try(BaseCompoundStatement):
990 """
991 A regular ``try`` statement that cannot contain :class:`ExceptStar` blocks. For
992 ``try`` statements that can contain :class:`ExceptStar` blocks, see
993 :class:`TryStar`.
994 """
995
996 #: The suite that is wrapped with a try statement.
997 body: BaseSuite
998
999 #: A list of zero or more exception handlers.
1000 handlers: Sequence[ExceptHandler] = ()
1001
1002 #: An optional else case.
1003 orelse: Optional[Else] = None
1004
1005 #: An optional finally case.
1006 finalbody: Optional[Finally] = None
1007
1008 #: Sequence of empty lines appearing before this compound statement line.
1009 leading_lines: Sequence[EmptyLine] = ()
1010
1011 #: The whitespace that appears after the ``try`` keyword but before
1012 #: the colon.
1013 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1014
1015 def _validate(self) -> None:
1016 if len(self.handlers) == 0 and self.finalbody is None:
1017 raise CSTValidationError(
1018 "A Try statement must have at least one ExceptHandler or Finally"
1019 )
1020 if len(self.handlers) == 0 and self.orelse is not None:
1021 raise CSTValidationError(
1022 "A Try statement must have at least one ExceptHandler in order "
1023 + "to have an Else."
1024 )
1025 # Check bare excepts are always at the last position
1026 if any(handler.type is None for handler in self.handlers[:-1]):
1027 raise CSTValidationError("The bare except: handler must be the last one.")
1028
1029 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Try":
1030 return Try(
1031 leading_lines=visit_sequence(
1032 self, "leading_lines", self.leading_lines, visitor
1033 ),
1034 whitespace_before_colon=visit_required(
1035 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1036 ),
1037 body=visit_required(self, "body", self.body, visitor),
1038 handlers=visit_sequence(self, "handlers", self.handlers, visitor),
1039 orelse=visit_optional(self, "orelse", self.orelse, visitor),
1040 finalbody=visit_optional(self, "finalbody", self.finalbody, visitor),
1041 )
1042
1043 def _codegen_impl(self, state: CodegenState) -> None:
1044 for ll in self.leading_lines:
1045 ll._codegen(state)
1046 state.add_indent_tokens()
1047
1048 end_node = self.body
1049 if len(self.handlers) > 0:
1050 end_node = self.handlers[-1]
1051 orelse = self.orelse
1052 end_node = end_node if orelse is None else orelse
1053 finalbody = self.finalbody
1054 end_node = end_node if finalbody is None else finalbody
1055 with state.record_syntactic_position(self, end_node=end_node):
1056 state.add_token("try")
1057 self.whitespace_before_colon._codegen(state)
1058 state.add_token(":")
1059 self.body._codegen(state)
1060 for handler in self.handlers:
1061 handler._codegen(state)
1062 if orelse is not None:
1063 orelse._codegen(state)
1064 if finalbody is not None:
1065 finalbody._codegen(state)
1066
1067
1068@add_slots
1069@dataclass(frozen=True)
1070class TryStar(BaseCompoundStatement):
1071 """
1072 A ``try`` statement with ``except*`` blocks.
1073 """
1074
1075 #: The suite that is wrapped with a try statement.
1076 body: BaseSuite
1077
1078 #: A list of one or more exception handlers.
1079 handlers: Sequence[ExceptStarHandler]
1080
1081 #: An optional else case.
1082 orelse: Optional[Else] = None
1083
1084 #: An optional finally case.
1085 finalbody: Optional[Finally] = None
1086
1087 #: Sequence of empty lines appearing before this compound statement line.
1088 leading_lines: Sequence[EmptyLine] = ()
1089
1090 #: The whitespace that appears after the ``try`` keyword but before
1091 #: the colon.
1092 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1093
1094 def _validate(self) -> None:
1095 if len(self.handlers) == 0:
1096 raise CSTValidationError(
1097 "A TryStar statement must have at least one ExceptHandler"
1098 )
1099
1100 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TryStar":
1101 return TryStar(
1102 leading_lines=visit_sequence(
1103 self, "leading_lines", self.leading_lines, visitor
1104 ),
1105 whitespace_before_colon=visit_required(
1106 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1107 ),
1108 body=visit_required(self, "body", self.body, visitor),
1109 handlers=visit_sequence(self, "handlers", self.handlers, visitor),
1110 orelse=visit_optional(self, "orelse", self.orelse, visitor),
1111 finalbody=visit_optional(self, "finalbody", self.finalbody, visitor),
1112 )
1113
1114 def _codegen_impl(self, state: CodegenState) -> None:
1115 for ll in self.leading_lines:
1116 ll._codegen(state)
1117 state.add_indent_tokens()
1118
1119 end_node = self.handlers[-1]
1120 orelse = self.orelse
1121 end_node = end_node if orelse is None else orelse
1122 finalbody = self.finalbody
1123 end_node = end_node if finalbody is None else finalbody
1124 with state.record_syntactic_position(self, end_node=end_node):
1125 state.add_token("try")
1126 self.whitespace_before_colon._codegen(state)
1127 state.add_token(":")
1128 self.body._codegen(state)
1129 for handler in self.handlers:
1130 handler._codegen(state)
1131 if orelse is not None:
1132 orelse._codegen(state)
1133 if finalbody is not None:
1134 finalbody._codegen(state)
1135
1136
1137@add_slots
1138@dataclass(frozen=True)
1139class ImportAlias(CSTNode):
1140 """
1141 An import, with an optional :class:`AsName`. Used in both :class:`Import` and
1142 :class:`ImportFrom` to specify a single import out of another module.
1143 """
1144
1145 #: Name or Attribute node representing the object we are importing.
1146 name: Union[Attribute, Name]
1147
1148 #: Local alias we will import the above object as.
1149 asname: Optional[AsName] = None
1150
1151 #: Any trailing comma that appears after this import. This is optional for the
1152 #: last :class:`ImportAlias` in a :class:`Import` or :class:`ImportFrom`, but all
1153 #: other import aliases inside an import must contain a comma to disambiguate
1154 #: multiple imports.
1155 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
1156
1157 def _validate(self) -> None:
1158 asname = self.asname
1159 if asname is not None:
1160 if not isinstance(asname.name, Name):
1161 raise CSTValidationError(
1162 "Must use a Name node for AsName name inside ImportAlias."
1163 )
1164 if asname.whitespace_before_as.empty:
1165 raise CSTValidationError(
1166 "Must have at least one space before as keyword in an ImportAlias."
1167 )
1168 try:
1169 self.evaluated_name
1170 except CSTLogicError as e:
1171 raise CSTValidationError(
1172 "The imported name must be a valid qualified name."
1173 ) from e
1174
1175 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ImportAlias":
1176 return ImportAlias(
1177 name=visit_required(self, "name", self.name, visitor),
1178 asname=visit_optional(self, "asname", self.asname, visitor),
1179 comma=visit_sentinel(self, "comma", self.comma, visitor),
1180 )
1181
1182 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
1183 with state.record_syntactic_position(self):
1184 self.name._codegen(state)
1185 asname = self.asname
1186 if asname is not None:
1187 asname._codegen(state)
1188
1189 comma = self.comma
1190 if comma is MaybeSentinel.DEFAULT and default_comma:
1191 state.add_token(", ")
1192 elif isinstance(comma, Comma):
1193 comma._codegen(state)
1194
1195 def _name(self, node: CSTNode) -> str:
1196 # Unrolled version of get_full_name_for_node to avoid circular imports.
1197 if isinstance(node, Name):
1198 return node.value
1199 elif isinstance(node, Attribute):
1200 return f"{self._name(node.value)}.{node.attr.value}"
1201 else:
1202 raise CSTLogicError("Logic error!")
1203
1204 @property
1205 def evaluated_name(self) -> str:
1206 """
1207 Returns the string name this :class:`ImportAlias` represents.
1208 """
1209 return self._name(self.name)
1210
1211 @property
1212 def evaluated_alias(self) -> Optional[str]:
1213 """
1214 Returns the string name for any alias that this :class:`ImportAlias`
1215 has. If there is no ``asname`` attribute, this returns ``None``.
1216 """
1217 asname = self.asname
1218 if asname is not None:
1219 return self._name(asname.name)
1220 return None
1221
1222
1223@add_slots
1224@dataclass(frozen=True)
1225class Import(BaseSmallStatement):
1226 """
1227 An ``import`` statement.
1228 """
1229
1230 #: One or more names that are being imported, with optional local aliases.
1231 names: Sequence[ImportAlias]
1232
1233 #: Optional semicolon when this is used in a statement line. This semicolon
1234 #: owns the whitespace on both sides of it when it is used.
1235 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1236
1237 #: The whitespace that appears after the ``import`` keyword but before
1238 #: the first import alias.
1239 whitespace_after_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1240
1241 def _validate(self) -> None:
1242 if len(self.names) == 0:
1243 raise CSTValidationError(
1244 "An ImportStatement must have at least one ImportAlias"
1245 )
1246 if isinstance(self.names[-1].comma, Comma):
1247 raise CSTValidationError(
1248 "An ImportStatement does not allow a trailing comma"
1249 )
1250 if self.whitespace_after_import.empty:
1251 raise CSTValidationError("Must have at least one space after import.")
1252
1253 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Import":
1254 return Import(
1255 whitespace_after_import=visit_required(
1256 self, "whitespace_after_import", self.whitespace_after_import, visitor
1257 ),
1258 names=visit_sequence(self, "names", self.names, visitor),
1259 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1260 )
1261
1262 def _codegen_impl(
1263 self, state: CodegenState, default_semicolon: bool = False
1264 ) -> None:
1265 with state.record_syntactic_position(self):
1266 state.add_token("import")
1267 self.whitespace_after_import._codegen(state)
1268 lastname = len(self.names) - 1
1269 for i, name in enumerate(self.names):
1270 name._codegen(state, default_comma=(i != lastname))
1271
1272 semicolon = self.semicolon
1273 if isinstance(semicolon, MaybeSentinel):
1274 if default_semicolon:
1275 state.add_token("; ")
1276 elif isinstance(semicolon, Semicolon):
1277 semicolon._codegen(state)
1278
1279
1280@add_slots
1281@dataclass(frozen=True)
1282class ImportFrom(BaseSmallStatement):
1283 """
1284 A ``from x import y`` statement.
1285 """
1286
1287 #: Name or Attribute node representing the module we're importing from.
1288 #: This is optional as :class:`ImportFrom` allows purely relative imports.
1289 module: Optional[Union[Attribute, Name]]
1290
1291 #: One or more names that are being imported from the specified module,
1292 #: with optional local aliases.
1293 names: Union[Sequence[ImportAlias], ImportStar]
1294
1295 #: Sequence of :class:`Dot` nodes indicating relative import level.
1296 relative: Sequence[Dot] = ()
1297
1298 #: Optional open parenthesis for multi-line import continuation.
1299 lpar: Optional[LeftParen] = None
1300
1301 #: Optional close parenthesis for multi-line import continuation.
1302 rpar: Optional[RightParen] = None
1303
1304 #: Optional semicolon when this is used in a statement line. This semicolon
1305 #: owns the whitespace on both sides of it when it is used.
1306 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1307
1308 #: The whitespace that appears after the ``from`` keyword but before
1309 #: the module and any relative import dots.
1310 whitespace_after_from: SimpleWhitespace = SimpleWhitespace.field(" ")
1311
1312 #: The whitespace that appears after the module but before the
1313 #: ``import`` keyword.
1314 whitespace_before_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1315
1316 #: The whitespace that appears after the ``import`` keyword but
1317 #: before the first import name or optional left paren.
1318 whitespace_after_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1319
1320 def _validate_module(self) -> None:
1321 if self.module is None and len(self.relative) == 0:
1322 raise CSTValidationError(
1323 "Must have a module specified if there is no relative import."
1324 )
1325
1326 def _validate_names(self) -> None:
1327 names = self.names
1328 if isinstance(names, Sequence):
1329 if len(names) == 0:
1330 raise CSTValidationError(
1331 "An ImportFrom must have at least one ImportAlias"
1332 )
1333 for name in names[:-1]:
1334 if name.comma is None:
1335 raise CSTValidationError("Non-final ImportAliases require a comma")
1336 if self.lpar is not None and self.rpar is None:
1337 raise CSTValidationError("Cannot have left paren without right paren.")
1338 if self.lpar is None and self.rpar is not None:
1339 raise CSTValidationError("Cannot have right paren without left paren.")
1340 if isinstance(names, ImportStar):
1341 if self.lpar is not None or self.rpar is not None:
1342 raise CSTValidationError(
1343 "An ImportFrom using ImportStar cannot have parens"
1344 )
1345
1346 def _validate_whitespace(self) -> None:
1347 if self.whitespace_after_from.empty and not self.relative:
1348 raise CSTValidationError("Must have at least one space after from.")
1349 if self.whitespace_before_import.empty and not (
1350 self.relative and self.module is None
1351 ):
1352 raise CSTValidationError("Must have at least one space before import.")
1353 if (
1354 self.whitespace_after_import.empty
1355 and self.lpar is None
1356 and not isinstance(self.names, ImportStar)
1357 ):
1358 raise CSTValidationError("Must have at least one space after import.")
1359
1360 def _validate(self) -> None:
1361 self._validate_module()
1362 self._validate_names()
1363 self._validate_whitespace()
1364
1365 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ImportFrom":
1366 names = self.names
1367 return ImportFrom(
1368 whitespace_after_from=visit_required(
1369 self, "whitespace_after_from", self.whitespace_after_from, visitor
1370 ),
1371 relative=visit_sequence(self, "relative", self.relative, visitor),
1372 module=visit_optional(self, "module", self.module, visitor),
1373 whitespace_before_import=visit_required(
1374 self, "whitespace_before_import", self.whitespace_before_import, visitor
1375 ),
1376 whitespace_after_import=visit_required(
1377 self, "whitespace_after_import", self.whitespace_after_import, visitor
1378 ),
1379 lpar=visit_optional(self, "lpar", self.lpar, visitor),
1380 names=(
1381 visit_required(self, "names", names, visitor)
1382 if isinstance(names, ImportStar)
1383 else visit_sequence(self, "names", names, visitor)
1384 ),
1385 rpar=visit_optional(self, "rpar", self.rpar, visitor),
1386 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1387 )
1388
1389 def _codegen_impl(
1390 self, state: CodegenState, default_semicolon: bool = False
1391 ) -> None:
1392 names = self.names
1393 end_node = names[-1] if isinstance(names, Sequence) else names
1394 end_node = end_node if self.rpar is None else self.rpar
1395 with state.record_syntactic_position(self, end_node=end_node):
1396 state.add_token("from")
1397 self.whitespace_after_from._codegen(state)
1398 for dot in self.relative:
1399 dot._codegen(state)
1400 module = self.module
1401 if module is not None:
1402 module._codegen(state)
1403 self.whitespace_before_import._codegen(state)
1404 state.add_token("import")
1405 self.whitespace_after_import._codegen(state)
1406 lpar = self.lpar
1407 if lpar is not None:
1408 lpar._codegen(state)
1409 if isinstance(names, Sequence):
1410 lastname = len(names) - 1
1411 for i, name in enumerate(names):
1412 name._codegen(state, default_comma=(i != lastname))
1413 if isinstance(names, ImportStar):
1414 names._codegen(state)
1415 rpar = self.rpar
1416 if rpar is not None:
1417 rpar._codegen(state)
1418
1419 semicolon = self.semicolon
1420 if isinstance(semicolon, MaybeSentinel):
1421 if default_semicolon:
1422 state.add_token("; ")
1423 elif isinstance(semicolon, Semicolon):
1424 semicolon._codegen(state)
1425
1426
1427@add_slots
1428@dataclass(frozen=True)
1429class LazyImport(BaseSmallStatement):
1430 """
1431 A ``lazy import x`` statement (PEP 810).
1432
1433 Lazy imports defer loading of the imported module until first use.
1434 ``lazy`` is a soft keyword that is only special immediately before
1435 ``import`` or ``from``.
1436 """
1437
1438 #: One or more names that are being imported lazily, with optional local aliases.
1439 names: Sequence[ImportAlias]
1440
1441 #: Optional semicolon when this is used in a statement line.
1442 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1443
1444 #: The whitespace between ``lazy`` and ``import``.
1445 whitespace_after_lazy: SimpleWhitespace = SimpleWhitespace.field(" ")
1446
1447 #: The whitespace after ``import`` and before the first alias.
1448 whitespace_after_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1449
1450 def _validate(self) -> None:
1451 if len(self.names) == 0:
1452 raise CSTValidationError("A LazyImport must have at least one ImportAlias")
1453 if isinstance(self.names[-1].comma, Comma):
1454 raise CSTValidationError("A LazyImport does not allow a trailing comma")
1455 if self.whitespace_after_lazy.empty:
1456 raise CSTValidationError("Must have at least one space after lazy.")
1457 if self.whitespace_after_import.empty:
1458 raise CSTValidationError("Must have at least one space after import.")
1459
1460 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "LazyImport":
1461 return LazyImport(
1462 whitespace_after_lazy=visit_required(
1463 self, "whitespace_after_lazy", self.whitespace_after_lazy, visitor
1464 ),
1465 whitespace_after_import=visit_required(
1466 self, "whitespace_after_import", self.whitespace_after_import, visitor
1467 ),
1468 names=visit_sequence(self, "names", self.names, visitor),
1469 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1470 )
1471
1472 def _codegen_impl(
1473 self, state: CodegenState, default_semicolon: bool = False
1474 ) -> None:
1475 with state.record_syntactic_position(self):
1476 state.add_token("lazy")
1477 self.whitespace_after_lazy._codegen(state)
1478 state.add_token("import")
1479 self.whitespace_after_import._codegen(state)
1480 lastname = len(self.names) - 1
1481 for i, name in enumerate(self.names):
1482 name._codegen(state, default_comma=(i != lastname))
1483
1484 semicolon = self.semicolon
1485 if isinstance(semicolon, MaybeSentinel):
1486 if default_semicolon:
1487 state.add_token("; ")
1488 elif isinstance(semicolon, Semicolon):
1489 semicolon._codegen(state)
1490
1491
1492@add_slots
1493@dataclass(frozen=True)
1494class LazyImportFrom(BaseSmallStatement):
1495 """
1496 A ``lazy from x import y`` statement (PEP 810).
1497
1498 Lazy imports defer loading of the imported names until first use.
1499 ``lazy`` is a soft keyword that is only special immediately before ``from``.
1500 """
1501
1502 #: Name or Attribute node representing the module we're importing from.
1503 module: Optional[Union[Attribute, Name]]
1504
1505 #: One or more names being imported lazily.
1506 names: Union[Sequence[ImportAlias], ImportStar]
1507
1508 #: Sequence of :class:`Dot` nodes indicating relative import level.
1509 relative: Sequence[Dot] = ()
1510
1511 #: Optional open parenthesis for multi-line import continuation.
1512 lpar: Optional[LeftParen] = None
1513
1514 #: Optional close parenthesis for multi-line import continuation.
1515 rpar: Optional[RightParen] = None
1516
1517 #: Optional semicolon when this is used in a statement line.
1518 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1519
1520 #: The whitespace between ``lazy`` and ``from``.
1521 whitespace_after_lazy: SimpleWhitespace = SimpleWhitespace.field(" ")
1522
1523 #: The whitespace after ``from`` and before the module / dots.
1524 whitespace_after_from: SimpleWhitespace = SimpleWhitespace.field(" ")
1525
1526 #: The whitespace before ``import``.
1527 whitespace_before_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1528
1529 #: The whitespace after ``import`` and before the first name or left paren.
1530 whitespace_after_import: SimpleWhitespace = SimpleWhitespace.field(" ")
1531
1532 def _validate_module(self) -> None:
1533 if self.module is None and len(self.relative) == 0:
1534 raise CSTValidationError(
1535 "Must have a module specified if there is no relative import."
1536 )
1537
1538 def _validate_names(self) -> None:
1539 names = self.names
1540 if isinstance(names, Sequence):
1541 if len(names) == 0:
1542 raise CSTValidationError(
1543 "A LazyImportFrom must have at least one ImportAlias"
1544 )
1545 for name in names[:-1]:
1546 if name.comma is None:
1547 raise CSTValidationError("Non-final ImportAliases require a comma")
1548 if self.lpar is not None and self.rpar is None:
1549 raise CSTValidationError("Cannot have left paren without right paren.")
1550 if self.lpar is None and self.rpar is not None:
1551 raise CSTValidationError("Cannot have right paren without left paren.")
1552
1553 def _validate_whitespace(self) -> None:
1554 if self.whitespace_after_lazy.empty:
1555 raise CSTValidationError("Must have at least one space after lazy.")
1556 if self.whitespace_after_from.empty and not self.relative:
1557 raise CSTValidationError("Must have at least one space after from.")
1558 if self.whitespace_before_import.empty and not (
1559 self.relative and self.module is None
1560 ):
1561 raise CSTValidationError("Must have at least one space before import.")
1562 if (
1563 self.whitespace_after_import.empty
1564 and self.lpar is None
1565 and not isinstance(self.names, ImportStar)
1566 ):
1567 raise CSTValidationError("Must have at least one space after import.")
1568
1569 def _validate(self) -> None:
1570 self._validate_module()
1571 self._validate_names()
1572 self._validate_whitespace()
1573
1574 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "LazyImportFrom":
1575 names = self.names
1576 return LazyImportFrom(
1577 whitespace_after_lazy=visit_required(
1578 self, "whitespace_after_lazy", self.whitespace_after_lazy, visitor
1579 ),
1580 whitespace_after_from=visit_required(
1581 self, "whitespace_after_from", self.whitespace_after_from, visitor
1582 ),
1583 relative=visit_sequence(self, "relative", self.relative, visitor),
1584 module=visit_optional(self, "module", self.module, visitor),
1585 whitespace_before_import=visit_required(
1586 self, "whitespace_before_import", self.whitespace_before_import, visitor
1587 ),
1588 whitespace_after_import=visit_required(
1589 self, "whitespace_after_import", self.whitespace_after_import, visitor
1590 ),
1591 lpar=visit_optional(self, "lpar", self.lpar, visitor),
1592 names=(
1593 visit_required(self, "names", names, visitor)
1594 if isinstance(names, ImportStar)
1595 else visit_sequence(self, "names", names, visitor)
1596 ),
1597 rpar=visit_optional(self, "rpar", self.rpar, visitor),
1598 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1599 )
1600
1601 def _codegen_impl(
1602 self, state: CodegenState, default_semicolon: bool = False
1603 ) -> None:
1604 names = self.names
1605 end_node = names[-1] if isinstance(names, Sequence) else names
1606 end_node = end_node if self.rpar is None else self.rpar
1607 with state.record_syntactic_position(self, end_node=end_node):
1608 state.add_token("lazy")
1609 self.whitespace_after_lazy._codegen(state)
1610 state.add_token("from")
1611 self.whitespace_after_from._codegen(state)
1612 for dot in self.relative:
1613 dot._codegen(state)
1614 module = self.module
1615 if module is not None:
1616 module._codegen(state)
1617 self.whitespace_before_import._codegen(state)
1618 state.add_token("import")
1619 self.whitespace_after_import._codegen(state)
1620 lpar = self.lpar
1621 if lpar is not None:
1622 lpar._codegen(state)
1623 if isinstance(names, Sequence):
1624 lastname = len(names) - 1
1625 for i, name in enumerate(names):
1626 name._codegen(state, default_comma=(i != lastname))
1627 if isinstance(names, ImportStar):
1628 names._codegen(state)
1629 rpar = self.rpar
1630 if rpar is not None:
1631 rpar._codegen(state)
1632
1633 semicolon = self.semicolon
1634 if isinstance(semicolon, MaybeSentinel):
1635 if default_semicolon:
1636 state.add_token("; ")
1637 elif isinstance(semicolon, Semicolon):
1638 semicolon._codegen(state)
1639
1640
1641@add_slots
1642@dataclass(frozen=True)
1643class AssignTarget(CSTNode):
1644 """
1645 A target for an :class:`Assign`. Owns the equals sign and the whitespace around it.
1646 """
1647
1648 #: The target expression being assigned to.
1649 target: BaseAssignTargetExpression
1650
1651 #: The whitespace appearing before the equals sign.
1652 whitespace_before_equal: SimpleWhitespace = SimpleWhitespace.field(" ")
1653
1654 #: The whitespace appearing after the equals sign.
1655 whitespace_after_equal: SimpleWhitespace = SimpleWhitespace.field(" ")
1656
1657 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AssignTarget":
1658 return AssignTarget(
1659 target=visit_required(self, "target", self.target, visitor),
1660 whitespace_before_equal=visit_required(
1661 self, "whitespace_before_equal", self.whitespace_before_equal, visitor
1662 ),
1663 whitespace_after_equal=visit_required(
1664 self, "whitespace_after_equal", self.whitespace_after_equal, visitor
1665 ),
1666 )
1667
1668 def _codegen_impl(self, state: CodegenState) -> None:
1669 with state.record_syntactic_position(self):
1670 self.target._codegen(state)
1671
1672 self.whitespace_before_equal._codegen(state)
1673 state.add_token("=")
1674 self.whitespace_after_equal._codegen(state)
1675
1676
1677@add_slots
1678@dataclass(frozen=True)
1679class Assign(BaseSmallStatement):
1680 """
1681 An assignment statement such as ``x = y`` or ``x = y = z``. Unlike
1682 :class:`AnnAssign`, this does not allow type annotations but does
1683 allow for multiple targets.
1684 """
1685
1686 #: One or more targets that are being assigned to.
1687 targets: Sequence[AssignTarget]
1688
1689 #: The expression being assigned to the targets.
1690 value: BaseExpression
1691
1692 #: Optional semicolon when this is used in a statement line. This semicolon
1693 #: owns the whitespace on both sides of it when it is used.
1694 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1695
1696 def _validate(self) -> None:
1697 if len(self.targets) == 0:
1698 raise CSTValidationError(
1699 "An Assign statement must have at least one AssignTarget"
1700 )
1701
1702 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Assign":
1703 return Assign(
1704 targets=visit_sequence(self, "targets", self.targets, visitor),
1705 value=visit_required(self, "value", self.value, visitor),
1706 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1707 )
1708
1709 def _codegen_impl(
1710 self, state: CodegenState, default_semicolon: bool = False
1711 ) -> None:
1712 with state.record_syntactic_position(self):
1713 for target in self.targets:
1714 target._codegen(state)
1715 self.value._codegen(state)
1716
1717 semicolon = self.semicolon
1718 if isinstance(semicolon, MaybeSentinel):
1719 if default_semicolon:
1720 state.add_token("; ")
1721 elif isinstance(semicolon, Semicolon):
1722 semicolon._codegen(state)
1723
1724
1725@add_slots
1726@dataclass(frozen=True)
1727class AnnAssign(BaseSmallStatement):
1728 """
1729 An assignment statement such as ``x: int = 5`` or ``x: int``. This only
1730 allows for one assignment target unlike :class:`Assign` but it includes
1731 a variable annotation. Also unlike :class:`Assign`, the assignment target
1732 is optional, as it is possible to annotate an expression without assigning
1733 to it.
1734 """
1735
1736 #: The target that is being annotated and possibly assigned to.
1737 target: BaseAssignTargetExpression
1738
1739 #: The annotation for the target.
1740 annotation: Annotation
1741
1742 #: The optional expression being assigned to the target.
1743 value: Optional[BaseExpression] = None
1744
1745 #: The equals sign used to denote assignment if there is a value.
1746 equal: Union[AssignEqual, MaybeSentinel] = MaybeSentinel.DEFAULT
1747
1748 #: Optional semicolon when this is used in a statement line. This semicolon
1749 #: owns the whitespace on both sides of it when it is used.
1750 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1751
1752 def _validate(self) -> None:
1753 if self.value is None and isinstance(self.equal, AssignEqual):
1754 raise CSTValidationError(
1755 "Must have a value when specifying an AssignEqual."
1756 )
1757
1758 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AnnAssign":
1759 return AnnAssign(
1760 target=visit_required(self, "target", self.target, visitor),
1761 annotation=visit_required(self, "annotation", self.annotation, visitor),
1762 equal=visit_sentinel(self, "equal", self.equal, visitor),
1763 value=visit_optional(self, "value", self.value, visitor),
1764 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1765 )
1766
1767 def _codegen_impl(
1768 self, state: CodegenState, default_semicolon: bool = False
1769 ) -> None:
1770 with state.record_syntactic_position(self):
1771 self.target._codegen(state)
1772 self.annotation._codegen(state, default_indicator=":")
1773 equal = self.equal
1774 if equal is MaybeSentinel.DEFAULT and self.value is not None:
1775 state.add_token(" = ")
1776 elif isinstance(equal, AssignEqual):
1777 equal._codegen(state)
1778 value = self.value
1779 if value is not None:
1780 value._codegen(state)
1781
1782 semicolon = self.semicolon
1783 if isinstance(semicolon, MaybeSentinel):
1784 if default_semicolon:
1785 state.add_token("; ")
1786 elif isinstance(semicolon, Semicolon):
1787 semicolon._codegen(state)
1788
1789
1790@add_slots
1791@dataclass(frozen=True)
1792class AugAssign(BaseSmallStatement):
1793 """
1794 An augmented assignment statement, such as ``x += 5``.
1795 """
1796
1797 #: Target that is being operated on and assigned to.
1798 target: BaseAssignTargetExpression
1799
1800 #: The augmented assignment operation being performed.
1801 operator: BaseAugOp
1802
1803 #: The value used with the above operator to calculate the new assignment.
1804 value: BaseExpression
1805
1806 #: Optional semicolon when this is used in a statement line. This semicolon
1807 #: owns the whitespace on both sides of it when it is used.
1808 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1809
1810 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AugAssign":
1811 return AugAssign(
1812 target=visit_required(self, "target", self.target, visitor),
1813 operator=visit_required(self, "operator", self.operator, visitor),
1814 value=visit_required(self, "value", self.value, visitor),
1815 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1816 )
1817
1818 def _codegen_impl(
1819 self, state: CodegenState, default_semicolon: bool = False
1820 ) -> None:
1821 with state.record_syntactic_position(self):
1822 self.target._codegen(state)
1823 self.operator._codegen(state)
1824 self.value._codegen(state)
1825
1826 semicolon = self.semicolon
1827 if isinstance(semicolon, MaybeSentinel):
1828 if default_semicolon:
1829 state.add_token("; ")
1830 elif isinstance(semicolon, Semicolon):
1831 semicolon._codegen(state)
1832
1833
1834@add_slots
1835@dataclass(frozen=True)
1836class Decorator(CSTNode):
1837 """
1838 A single decorator that decorates a :class:`FunctionDef` or a :class:`ClassDef`.
1839 """
1840
1841 #: The decorator that will return a new function wrapping the parent
1842 #: of this decorator.
1843 decorator: BaseExpression
1844
1845 #: Line comments and empty lines before this decorator. The parent
1846 #: :class:`FunctionDef` or :class:`ClassDef` node owns leading lines before
1847 #: the first decorator so that if the first decorator is removed, spacing is preserved.
1848 leading_lines: Sequence[EmptyLine] = ()
1849
1850 #: Whitespace after the ``@`` and before the decorator expression itself.
1851 whitespace_after_at: SimpleWhitespace = SimpleWhitespace.field("")
1852
1853 #: Optional trailing comment and newline following the decorator before the next line.
1854 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field()
1855
1856 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Decorator":
1857 return Decorator(
1858 leading_lines=visit_sequence(
1859 self, "leading_lines", self.leading_lines, visitor
1860 ),
1861 whitespace_after_at=visit_required(
1862 self, "whitespace_after_at", self.whitespace_after_at, visitor
1863 ),
1864 decorator=visit_required(self, "decorator", self.decorator, visitor),
1865 trailing_whitespace=visit_required(
1866 self, "trailing_whitespace", self.trailing_whitespace, visitor
1867 ),
1868 )
1869
1870 def _codegen_impl(self, state: CodegenState) -> None:
1871 for ll in self.leading_lines:
1872 ll._codegen(state)
1873 state.add_indent_tokens()
1874
1875 with state.record_syntactic_position(self):
1876 state.add_token("@")
1877 self.whitespace_after_at._codegen(state)
1878 self.decorator._codegen(state)
1879
1880 self.trailing_whitespace._codegen(state)
1881
1882
1883def get_docstring_impl(
1884 body: Union[BaseSuite, Sequence[Union[SimpleStatementLine, BaseCompoundStatement]]],
1885 clean: bool,
1886) -> Optional[str]:
1887 """
1888 Implementation Reference:
1889 - :func:`ast.get_docstring` https://docs.python.org/3/library/ast.html#ast.get_docstring
1890 and https://github.com/python/cpython/blob/89aa4694fc8c6d190325ef8ed6ce6a6b8efb3e50/Lib/ast.py#L254
1891 - PEP 257 https://www.python.org/dev/peps/pep-0257/
1892 """
1893 if isinstance(body, Sequence):
1894 if body:
1895 expr = body[0]
1896 else:
1897 return None
1898 else:
1899 expr = body
1900 while isinstance(expr, (BaseSuite, SimpleStatementLine)):
1901 if len(expr.body) == 0:
1902 return None
1903 expr = expr.body[0]
1904 if not isinstance(expr, Expr):
1905 return None
1906 val = expr.value
1907 if isinstance(val, (SimpleString, ConcatenatedString)):
1908 evaluated_value = val.evaluated_value
1909 else:
1910 return None
1911 if isinstance(evaluated_value, bytes):
1912 return None
1913
1914 if evaluated_value is not None and clean:
1915 return inspect.cleandoc(evaluated_value)
1916 return evaluated_value
1917
1918
1919@add_slots
1920@dataclass(frozen=True)
1921class FunctionDef(BaseCompoundStatement):
1922 """
1923 A function definition.
1924 """
1925
1926 #: The function name.
1927 name: Name
1928
1929 #: The function parameters. Present even if there are no params.
1930 params: Parameters
1931
1932 #: The function body.
1933 body: BaseSuite
1934
1935 #: Sequence of decorators applied to this function. Decorators are listed in
1936 #: order that they appear in source (top to bottom) as apposed to the order
1937 #: that they are applied to the function at runtime.
1938 decorators: Sequence[Decorator] = ()
1939
1940 #: An optional return annotation, if the function is annotated.
1941 returns: Optional[Annotation] = None
1942
1943 #: Optional async modifier, if this is an async function.
1944 asynchronous: Optional[Asynchronous] = None
1945
1946 #: Leading empty lines and comments before the first decorator. We
1947 #: assume any comments before the first decorator are owned by the
1948 #: function definition itself. If there are no decorators, this will
1949 #: still contain all of the empty lines and comments before the
1950 #: function definition.
1951 leading_lines: Sequence[EmptyLine] = ()
1952
1953 #: Empty lines and comments between the final decorator and the
1954 #: :class:`FunctionDef` node. In the case of no decorators, this will be empty.
1955 lines_after_decorators: Sequence[EmptyLine] = ()
1956
1957 #: Whitespace after the ``def`` keyword and before the function name.
1958 whitespace_after_def: SimpleWhitespace = SimpleWhitespace.field(" ")
1959
1960 #: Whitespace after the function name and before the type parameters or the opening
1961 #: parenthesis for the parameters.
1962 whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("")
1963
1964 #: Whitespace after the opening parenthesis for the parameters but before
1965 #: the first param itself.
1966 whitespace_before_params: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
1967
1968 #: Whitespace after the closing parenthesis or return annotation and before
1969 #: the colon.
1970 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1971
1972 #: An optional declaration of type parameters.
1973 type_parameters: Optional["TypeParameters"] = None
1974
1975 #: Whitespace between the type parameters and the opening parenthesis for the
1976 #: (non-type) parameters.
1977 whitespace_after_type_parameters: SimpleWhitespace = SimpleWhitespace.field("")
1978
1979 def _validate(self) -> None:
1980 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
1981 raise CSTValidationError("Cannot have parens around Name in a FunctionDef.")
1982 if self.whitespace_after_def.empty:
1983 raise CSTValidationError(
1984 "There must be at least one space between 'def' and name."
1985 )
1986
1987 if (
1988 self.type_parameters is None
1989 and not self.whitespace_after_type_parameters.empty
1990 ):
1991 raise CSTValidationError(
1992 "whitespace_after_type_parameters must be empty if there are no type "
1993 "parameters in FunctionDef"
1994 )
1995
1996 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "FunctionDef":
1997 return FunctionDef(
1998 leading_lines=visit_sequence(
1999 self, "leading_lines", self.leading_lines, visitor
2000 ),
2001 decorators=visit_sequence(self, "decorators", self.decorators, visitor),
2002 lines_after_decorators=visit_sequence(
2003 self, "lines_after_decorators", self.lines_after_decorators, visitor
2004 ),
2005 asynchronous=visit_optional(
2006 self, "asynchronous", self.asynchronous, visitor
2007 ),
2008 whitespace_after_def=visit_required(
2009 self, "whitespace_after_def", self.whitespace_after_def, visitor
2010 ),
2011 name=visit_required(self, "name", self.name, visitor),
2012 whitespace_after_name=visit_required(
2013 self, "whitespace_after_name", self.whitespace_after_name, visitor
2014 ),
2015 type_parameters=visit_optional(
2016 self, "type_parameters", self.type_parameters, visitor
2017 ),
2018 whitespace_after_type_parameters=visit_required(
2019 self,
2020 "whitespace_after_type_parameters",
2021 self.whitespace_after_type_parameters,
2022 visitor,
2023 ),
2024 whitespace_before_params=visit_required(
2025 self, "whitespace_before_params", self.whitespace_before_params, visitor
2026 ),
2027 params=visit_required(self, "params", self.params, visitor),
2028 returns=visit_optional(self, "returns", self.returns, visitor),
2029 whitespace_before_colon=visit_required(
2030 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2031 ),
2032 body=visit_required(self, "body", self.body, visitor),
2033 )
2034
2035 def _codegen_impl(self, state: CodegenState) -> None:
2036 for ll in self.leading_lines:
2037 ll._codegen(state)
2038 for decorator in self.decorators:
2039 decorator._codegen(state)
2040 for lad in self.lines_after_decorators:
2041 lad._codegen(state)
2042 state.add_indent_tokens()
2043
2044 with state.record_syntactic_position(self, end_node=self.body):
2045 asynchronous = self.asynchronous
2046 if asynchronous is not None:
2047 asynchronous._codegen(state)
2048 state.add_token("def")
2049 self.whitespace_after_def._codegen(state)
2050 self.name._codegen(state)
2051 self.whitespace_after_name._codegen(state)
2052 type_params = self.type_parameters
2053 if type_params is not None:
2054 type_params._codegen(state)
2055 self.whitespace_after_type_parameters._codegen(state)
2056 state.add_token("(")
2057 self.whitespace_before_params._codegen(state)
2058 self.params._codegen(state)
2059 state.add_token(")")
2060 returns = self.returns
2061 if returns is not None:
2062 returns._codegen(state, default_indicator="->")
2063 self.whitespace_before_colon._codegen(state)
2064 state.add_token(":")
2065 self.body._codegen(state)
2066
2067 def get_docstring(self, clean: bool = True) -> Optional[str]:
2068 """
2069 When docstring is available, returns a :func:`inspect.cleandoc` cleaned docstring.
2070 Otherwise, returns ``None``.
2071 """
2072 return get_docstring_impl(self.body, clean)
2073
2074
2075@add_slots
2076@dataclass(frozen=True)
2077class ClassDef(BaseCompoundStatement):
2078 """
2079 A class definition.
2080 """
2081
2082 #: The class name.
2083 name: Name
2084
2085 #: The class body.
2086 body: BaseSuite
2087
2088 #: Sequence of base classes this class inherits from.
2089 bases: Sequence[Arg] = ()
2090
2091 #: Sequence of keywords, such as "metaclass".
2092 keywords: Sequence[Arg] = ()
2093
2094 #: Sequence of decorators applied to this class.
2095 decorators: Sequence[Decorator] = ()
2096
2097 #: Optional open parenthesis used when there are bases or keywords.
2098 lpar: Union[LeftParen, MaybeSentinel] = MaybeSentinel.DEFAULT
2099
2100 #: Optional close parenthesis used when there are bases or keywords.
2101 rpar: Union[RightParen, MaybeSentinel] = MaybeSentinel.DEFAULT
2102
2103 #: Leading empty lines and comments before the first decorator. We
2104 #: assume any comments before the first decorator are owned by the
2105 #: class definition itself. If there are no decorators, this will
2106 #: still contain all of the empty lines and comments before the
2107 #: class definition.
2108 leading_lines: Sequence[EmptyLine] = ()
2109
2110 #: Empty lines and comments between the final decorator and the
2111 #: :class:`ClassDef` node. In the case of no decorators, this will be empty.
2112 lines_after_decorators: Sequence[EmptyLine] = ()
2113
2114 #: Whitespace after the ``class`` keyword and before the class name.
2115 whitespace_after_class: SimpleWhitespace = SimpleWhitespace.field(" ")
2116
2117 #: Whitespace after the class name and before the type parameters or the opening
2118 #: parenthesis for the bases and keywords.
2119 whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("")
2120
2121 #: Whitespace after the closing parenthesis or class name and before
2122 #: the colon.
2123 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2124
2125 #: An optional declaration of type parameters.
2126 type_parameters: Optional["TypeParameters"] = None
2127
2128 #: Whitespace between type parameters and opening parenthesis for the bases and
2129 #: keywords.
2130 whitespace_after_type_parameters: SimpleWhitespace = SimpleWhitespace.field("")
2131
2132 def _validate_whitespace(self) -> None:
2133 if self.whitespace_after_class.empty:
2134 raise CSTValidationError(
2135 "There must be at least one space between 'class' and name."
2136 )
2137 if (
2138 self.type_parameters is None
2139 and not self.whitespace_after_type_parameters.empty
2140 ):
2141 raise CSTValidationError(
2142 "whitespace_after_type_parameters must be empty if there are no type"
2143 "parameters in a ClassDef"
2144 )
2145
2146 def _validate_parens(self) -> None:
2147 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
2148 raise CSTValidationError("Cannot have parens around Name in a ClassDef.")
2149 if isinstance(self.lpar, MaybeSentinel) and isinstance(self.rpar, RightParen):
2150 raise CSTValidationError(
2151 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
2152 )
2153 if isinstance(self.lpar, LeftParen) and isinstance(self.rpar, MaybeSentinel):
2154 raise CSTValidationError(
2155 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
2156 )
2157
2158 def _validate_args(self) -> None:
2159 if any((arg.keyword is not None) for arg in self.bases):
2160 raise CSTValidationError("Bases must be arguments without keywords.")
2161 if any((arg.keyword is None and arg.star != "**") for arg in self.keywords):
2162 raise CSTValidationError(
2163 "Keywords must be arguments with keywords or dictionary expansions."
2164 )
2165
2166 def _validate(self) -> None:
2167 self._validate_whitespace()
2168 self._validate_parens()
2169 self._validate_args()
2170
2171 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ClassDef":
2172 return ClassDef(
2173 leading_lines=visit_sequence(
2174 self, "leading_lines", self.leading_lines, visitor
2175 ),
2176 decorators=visit_sequence(self, "decorators", self.decorators, visitor),
2177 lines_after_decorators=visit_sequence(
2178 self, "lines_after_decorators", self.lines_after_decorators, visitor
2179 ),
2180 whitespace_after_class=visit_required(
2181 self, "whitespace_after_class", self.whitespace_after_class, visitor
2182 ),
2183 name=visit_required(self, "name", self.name, visitor),
2184 whitespace_after_name=visit_required(
2185 self, "whitespace_after_name", self.whitespace_after_name, visitor
2186 ),
2187 type_parameters=visit_optional(
2188 self, "type_parameters", self.type_parameters, visitor
2189 ),
2190 whitespace_after_type_parameters=visit_required(
2191 self,
2192 "whitespace_after_type_parameters",
2193 self.whitespace_after_type_parameters,
2194 visitor,
2195 ),
2196 lpar=visit_sentinel(self, "lpar", self.lpar, visitor),
2197 bases=visit_sequence(self, "bases", self.bases, visitor),
2198 keywords=visit_sequence(self, "keywords", self.keywords, visitor),
2199 rpar=visit_sentinel(self, "rpar", self.rpar, visitor),
2200 whitespace_before_colon=visit_required(
2201 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2202 ),
2203 body=visit_required(self, "body", self.body, visitor),
2204 )
2205
2206 def _codegen_impl(self, state: CodegenState) -> None: # noqa: C901
2207 for ll in self.leading_lines:
2208 ll._codegen(state)
2209 for decorator in self.decorators:
2210 decorator._codegen(state)
2211 for lad in self.lines_after_decorators:
2212 lad._codegen(state)
2213 state.add_indent_tokens()
2214
2215 with state.record_syntactic_position(self, end_node=self.body):
2216 state.add_token("class")
2217 self.whitespace_after_class._codegen(state)
2218 self.name._codegen(state)
2219 self.whitespace_after_name._codegen(state)
2220 type_params = self.type_parameters
2221 if type_params is not None:
2222 type_params._codegen(state)
2223 self.whitespace_after_type_parameters._codegen(state)
2224 lpar = self.lpar
2225 if isinstance(lpar, MaybeSentinel):
2226 if self.bases or self.keywords:
2227 state.add_token("(")
2228 elif isinstance(lpar, LeftParen):
2229 lpar._codegen(state)
2230 args = [*self.bases, *self.keywords]
2231 last_arg = len(args) - 1
2232 for i, arg in enumerate(args):
2233 arg._codegen(state, default_comma=(i != last_arg))
2234 rpar = self.rpar
2235 if isinstance(rpar, MaybeSentinel):
2236 if self.bases or self.keywords:
2237 state.add_token(")")
2238 elif isinstance(rpar, RightParen):
2239 rpar._codegen(state)
2240 self.whitespace_before_colon._codegen(state)
2241 state.add_token(":")
2242 self.body._codegen(state)
2243
2244 def get_docstring(self, clean: bool = True) -> Optional[str]:
2245 """
2246 Returns a :func:`inspect.cleandoc` cleaned docstring if the docstring is available, ``None`` otherwise.
2247 """
2248 return get_docstring_impl(self.body, clean)
2249
2250
2251@add_slots
2252@dataclass(frozen=True)
2253class WithItem(CSTNode):
2254 """
2255 A single context manager in a :class:`With` block, with an optional variable name.
2256 """
2257
2258 #: Expression that evaluates to a context manager.
2259 item: BaseExpression
2260
2261 #: Variable to assign the context manager to, if it is needed in the
2262 #: :class:`With` body.
2263 asname: Optional[AsName] = None
2264
2265 #: This is forbidden for the last :class:`WithItem` in a :class:`With`, but all
2266 #: other items inside a with block must contain a comma to separate them.
2267 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2268
2269 def _validate(self) -> None:
2270 asname = self.asname
2271 if (
2272 asname is not None
2273 and asname.whitespace_before_as.empty
2274 and not self.item._safe_to_use_with_word_operator(ExpressionPosition.LEFT)
2275 ):
2276 raise CSTValidationError("Must have at least one space before as keyword.")
2277
2278 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "WithItem":
2279 return WithItem(
2280 item=visit_required(self, "item", self.item, visitor),
2281 asname=visit_optional(self, "asname", self.asname, visitor),
2282 comma=visit_sentinel(self, "comma", self.comma, visitor),
2283 )
2284
2285 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
2286 with state.record_syntactic_position(self):
2287 self.item._codegen(state)
2288 asname = self.asname
2289 if asname is not None:
2290 asname._codegen(state)
2291
2292 comma = self.comma
2293 if comma is MaybeSentinel.DEFAULT and default_comma:
2294 state.add_token(", ")
2295 elif isinstance(comma, Comma):
2296 comma._codegen(state)
2297
2298
2299@add_slots
2300@dataclass(frozen=True)
2301class With(BaseCompoundStatement):
2302 """
2303 A ``with`` statement.
2304 """
2305
2306 #: A sequence of one or more items that evaluate to context managers.
2307 items: Sequence[WithItem]
2308
2309 #: The suite that is wrapped with this statement.
2310 body: BaseSuite
2311
2312 #: Optional async modifier if this is an ``async with`` statement.
2313 asynchronous: Optional[Asynchronous] = None
2314
2315 #: Sequence of empty lines appearing before this with statement.
2316 leading_lines: Sequence[EmptyLine] = ()
2317
2318 #: Optional open parenthesis for multi-line with bindings
2319 lpar: Union[LeftParen, MaybeSentinel] = MaybeSentinel.DEFAULT
2320
2321 #: Optional close parenthesis for multi-line with bindings
2322 rpar: Union[RightParen, MaybeSentinel] = MaybeSentinel.DEFAULT
2323
2324 #: Whitespace after the ``with`` keyword and before the first item.
2325 whitespace_after_with: SimpleWhitespace = SimpleWhitespace.field(" ")
2326
2327 #: Whitespace after the last item and before the colon.
2328 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2329
2330 def _validate_parens(self) -> None:
2331 if isinstance(self.lpar, MaybeSentinel) and isinstance(self.rpar, RightParen):
2332 raise CSTValidationError(
2333 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
2334 )
2335 if isinstance(self.lpar, LeftParen) and isinstance(self.rpar, MaybeSentinel):
2336 raise CSTValidationError(
2337 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
2338 )
2339
2340 def _validate(self) -> None:
2341 self._validate_parens()
2342 if len(self.items) == 0:
2343 raise CSTValidationError(
2344 "A With statement must have at least one WithItem."
2345 )
2346 if (
2347 isinstance(self.rpar, MaybeSentinel)
2348 and self.items[-1].comma != MaybeSentinel.DEFAULT
2349 ):
2350 raise CSTValidationError(
2351 "The last WithItem in an unparenthesized With cannot have a trailing comma."
2352 )
2353 if self.whitespace_after_with.empty and not (
2354 isinstance(self.lpar, LeftParen)
2355 or self.items[0].item._safe_to_use_with_word_operator(
2356 ExpressionPosition.RIGHT
2357 )
2358 ):
2359 raise CSTValidationError("Must have at least one space after with keyword.")
2360
2361 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "With":
2362 return With(
2363 leading_lines=visit_sequence(
2364 self, "leading_lines", self.leading_lines, visitor
2365 ),
2366 asynchronous=visit_optional(
2367 self, "asynchronous", self.asynchronous, visitor
2368 ),
2369 whitespace_after_with=visit_required(
2370 self, "whitespace_after_with", self.whitespace_after_with, visitor
2371 ),
2372 lpar=visit_sentinel(self, "lpar", self.lpar, visitor),
2373 items=visit_sequence(self, "items", self.items, visitor),
2374 rpar=visit_sentinel(self, "rpar", self.rpar, visitor),
2375 whitespace_before_colon=visit_required(
2376 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2377 ),
2378 body=visit_required(self, "body", self.body, visitor),
2379 )
2380
2381 def _codegen_impl(self, state: CodegenState) -> None:
2382 for ll in self.leading_lines:
2383 ll._codegen(state)
2384 state.add_indent_tokens()
2385
2386 needs_paren = False
2387 for item in self.items:
2388 comma = item.comma
2389 if isinstance(comma, Comma):
2390 if isinstance(
2391 comma.whitespace_after,
2392 (EmptyLine, TrailingWhitespace, ParenthesizedWhitespace),
2393 ):
2394 needs_paren = True
2395 break
2396
2397 with state.record_syntactic_position(self, end_node=self.body):
2398 asynchronous = self.asynchronous
2399 if asynchronous is not None:
2400 asynchronous._codegen(state)
2401 state.add_token("with")
2402 self.whitespace_after_with._codegen(state)
2403 lpar = self.lpar
2404 if isinstance(lpar, LeftParen):
2405 lpar._codegen(state)
2406 elif needs_paren:
2407 state.add_token("(")
2408 last_item = len(self.items) - 1
2409 for i, item in enumerate(self.items):
2410 item._codegen(state, default_comma=(i != last_item))
2411 rpar = self.rpar
2412 if isinstance(rpar, RightParen):
2413 rpar._codegen(state)
2414 elif needs_paren:
2415 state.add_token(")")
2416 self.whitespace_before_colon._codegen(state)
2417 state.add_token(":")
2418 self.body._codegen(state)
2419
2420
2421@add_slots
2422@dataclass(frozen=True)
2423class For(BaseCompoundStatement):
2424 """
2425 A ``for target in iter`` statement.
2426 """
2427
2428 #: The target of the iterator in the for statement.
2429 target: BaseAssignTargetExpression
2430
2431 #: The iterable expression we will loop over.
2432 iter: BaseExpression
2433
2434 #: The suite that is wrapped with this statement.
2435 body: BaseSuite
2436
2437 #: An optional else case which will be executed if there is no
2438 #: :class:`Break` statement encountered while looping.
2439 orelse: Optional[Else] = None
2440
2441 #: Optional async modifier, if this is an `async for` statement.
2442 asynchronous: Optional[Asynchronous] = None
2443
2444 #: Sequence of empty lines appearing before this for statement.
2445 leading_lines: Sequence[EmptyLine] = ()
2446
2447 #: Whitespace after the ``for`` keyword and before the target.
2448 whitespace_after_for: SimpleWhitespace = SimpleWhitespace.field(" ")
2449
2450 #: Whitespace after the target and before the ``in`` keyword.
2451 whitespace_before_in: SimpleWhitespace = SimpleWhitespace.field(" ")
2452
2453 #: Whitespace after the ``in`` keyword and before the iter.
2454 whitespace_after_in: SimpleWhitespace = SimpleWhitespace.field(" ")
2455
2456 #: Whitespace after the iter and before the colon.
2457 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2458
2459 def _validate(self) -> None:
2460 if (
2461 self.whitespace_after_for.empty
2462 and not self.target._safe_to_use_with_word_operator(
2463 ExpressionPosition.RIGHT
2464 )
2465 ):
2466 raise CSTValidationError(
2467 "Must have at least one space after 'for' keyword."
2468 )
2469
2470 if (
2471 self.whitespace_before_in.empty
2472 and not self.target._safe_to_use_with_word_operator(ExpressionPosition.LEFT)
2473 ):
2474 raise CSTValidationError(
2475 "Must have at least one space before 'in' keyword."
2476 )
2477
2478 if (
2479 self.whitespace_after_in.empty
2480 and not self.iter._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2481 ):
2482 raise CSTValidationError("Must have at least one space after 'in' keyword.")
2483
2484 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "For":
2485 return For(
2486 leading_lines=visit_sequence(
2487 self, "leading_lines", self.leading_lines, visitor
2488 ),
2489 asynchronous=visit_optional(
2490 self, "asynchronous", self.asynchronous, visitor
2491 ),
2492 whitespace_after_for=visit_required(
2493 self, "whitespace_after_for", self.whitespace_after_for, visitor
2494 ),
2495 target=visit_required(self, "target", self.target, visitor),
2496 whitespace_before_in=visit_required(
2497 self, "whitespace_before_in", self.whitespace_before_in, visitor
2498 ),
2499 whitespace_after_in=visit_required(
2500 self, "whitespace_after_in", self.whitespace_after_in, visitor
2501 ),
2502 iter=visit_required(self, "iter", self.iter, visitor),
2503 whitespace_before_colon=visit_required(
2504 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2505 ),
2506 body=visit_required(self, "body", self.body, visitor),
2507 orelse=visit_optional(self, "orelse", self.orelse, visitor),
2508 )
2509
2510 def _codegen_impl(self, state: CodegenState) -> None:
2511 for ll in self.leading_lines:
2512 ll._codegen(state)
2513 state.add_indent_tokens()
2514
2515 end_node = self.body if self.orelse is None else self.orelse
2516 with state.record_syntactic_position(self, end_node=end_node):
2517 asynchronous = self.asynchronous
2518 if asynchronous is not None:
2519 asynchronous._codegen(state)
2520 state.add_token("for")
2521 self.whitespace_after_for._codegen(state)
2522 self.target._codegen(state)
2523 self.whitespace_before_in._codegen(state)
2524 state.add_token("in")
2525 self.whitespace_after_in._codegen(state)
2526 self.iter._codegen(state)
2527 self.whitespace_before_colon._codegen(state)
2528 state.add_token(":")
2529 self.body._codegen(state)
2530 orelse = self.orelse
2531 if orelse is not None:
2532 orelse._codegen(state)
2533
2534
2535@add_slots
2536@dataclass(frozen=True)
2537class While(BaseCompoundStatement):
2538 """
2539 A ``while`` statement.
2540 """
2541
2542 #: The test we will loop against.
2543 test: BaseExpression
2544
2545 #: The suite that is wrapped with this statement.
2546 body: BaseSuite
2547
2548 #: An optional else case which will be executed if there is no
2549 #: :class:`Break` statement encountered while looping.
2550 orelse: Optional[Else] = None
2551
2552 #: Sequence of empty lines appearing before this while statement.
2553 leading_lines: Sequence[EmptyLine] = ()
2554
2555 #: Whitespace after the ``while`` keyword and before the test.
2556 whitespace_after_while: SimpleWhitespace = SimpleWhitespace.field(" ")
2557
2558 #: Whitespace after the test and before the colon.
2559 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2560
2561 def _validate(self) -> None:
2562 if (
2563 self.whitespace_after_while.empty
2564 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2565 ):
2566 raise CSTValidationError(
2567 "Must have at least one space after 'while' keyword."
2568 )
2569
2570 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "While":
2571 return While(
2572 leading_lines=visit_sequence(
2573 self, "leading_lines", self.leading_lines, visitor
2574 ),
2575 whitespace_after_while=visit_required(
2576 self, "whitespace_after_while", self.whitespace_after_while, visitor
2577 ),
2578 test=visit_required(self, "test", self.test, visitor),
2579 whitespace_before_colon=visit_required(
2580 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2581 ),
2582 body=visit_required(self, "body", self.body, visitor),
2583 orelse=visit_optional(self, "orelse", self.orelse, visitor),
2584 )
2585
2586 def _codegen_impl(self, state: CodegenState) -> None:
2587 for ll in self.leading_lines:
2588 ll._codegen(state)
2589 state.add_indent_tokens()
2590
2591 end_node = self.body if self.orelse is None else self.orelse
2592 with state.record_syntactic_position(self, end_node=end_node):
2593 state.add_token("while")
2594 self.whitespace_after_while._codegen(state)
2595 self.test._codegen(state)
2596 self.whitespace_before_colon._codegen(state)
2597 state.add_token(":")
2598 self.body._codegen(state)
2599 orelse = self.orelse
2600 if orelse is not None:
2601 orelse._codegen(state)
2602
2603
2604@add_slots
2605@dataclass(frozen=True)
2606class Raise(BaseSmallStatement):
2607 """
2608 A ``raise exc`` or ``raise exc from cause`` statement.
2609 """
2610
2611 #: The exception that we should raise.
2612 exc: Optional[BaseExpression] = None
2613
2614 #: Optionally, a ``from cause`` clause to allow us to raise an exception
2615 #: out of another exception's context.
2616 cause: Optional[From] = None
2617
2618 #: Any whitespace appearing between the ``raise`` keyword and the exception.
2619 whitespace_after_raise: Union[SimpleWhitespace, MaybeSentinel] = (
2620 MaybeSentinel.DEFAULT
2621 )
2622
2623 #: Optional semicolon when this is used in a statement line. This semicolon
2624 #: owns the whitespace on both sides of it when it is used.
2625 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2626
2627 def _validate(self) -> None:
2628 # Validate correct construction
2629 if self.exc is None and self.cause is not None:
2630 raise CSTValidationError(
2631 "Must have an 'exc' when specifying 'clause'. on Raise."
2632 )
2633
2634 # Validate spacing between "raise" and "exc"
2635 exc = self.exc
2636 if exc is not None:
2637 whitespace_after_raise = self.whitespace_after_raise
2638 has_no_gap = (
2639 not isinstance(whitespace_after_raise, MaybeSentinel)
2640 and whitespace_after_raise.empty
2641 )
2642 if has_no_gap and not exc._safe_to_use_with_word_operator(
2643 ExpressionPosition.RIGHT
2644 ):
2645 raise CSTValidationError("Must have at least one space after 'raise'.")
2646
2647 # Validate spacing between "exc" and "from"
2648 cause = self.cause
2649 if exc is not None and cause is not None:
2650 whitespace_before_from = cause.whitespace_before_from
2651 has_no_gap = (
2652 not isinstance(whitespace_before_from, MaybeSentinel)
2653 and whitespace_before_from.empty
2654 )
2655 if has_no_gap and not exc._safe_to_use_with_word_operator(
2656 ExpressionPosition.LEFT
2657 ):
2658 raise CSTValidationError("Must have at least one space before 'from'.")
2659
2660 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Raise":
2661 return Raise(
2662 whitespace_after_raise=visit_sentinel(
2663 self, "whitespace_after_raise", self.whitespace_after_raise, visitor
2664 ),
2665 exc=visit_optional(self, "exc", self.exc, visitor),
2666 cause=visit_optional(self, "cause", self.cause, visitor),
2667 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2668 )
2669
2670 def _codegen_impl(
2671 self, state: CodegenState, default_semicolon: bool = False
2672 ) -> None:
2673 with state.record_syntactic_position(self):
2674 exc = self.exc
2675 cause = self.cause
2676 state.add_token("raise")
2677 whitespace_after_raise = self.whitespace_after_raise
2678 if isinstance(whitespace_after_raise, MaybeSentinel):
2679 if exc is not None:
2680 state.add_token(" ")
2681 else:
2682 whitespace_after_raise._codegen(state)
2683 if exc is not None:
2684 exc._codegen(state)
2685 if cause is not None:
2686 cause._codegen(state, default_space=" ")
2687
2688 semicolon = self.semicolon
2689 if isinstance(semicolon, MaybeSentinel):
2690 if default_semicolon:
2691 state.add_token("; ")
2692 elif isinstance(semicolon, Semicolon):
2693 semicolon._codegen(state)
2694
2695
2696@add_slots
2697@dataclass(frozen=True)
2698class Assert(BaseSmallStatement):
2699 """
2700 An assert statement such as ``assert x > 5`` or ``assert x > 5, 'Uh oh!'``
2701 """
2702
2703 #: The test we are going to assert on.
2704 test: BaseExpression
2705
2706 #: The optional message to display if the test evaluates to a falsey value.
2707 msg: Optional[BaseExpression] = None
2708
2709 #: A comma separating test and message, if there is a message.
2710 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2711
2712 #: Whitespace appearing after the ``assert`` keyword and before the test.
2713 whitespace_after_assert: SimpleWhitespace = SimpleWhitespace.field(" ")
2714
2715 #: Optional semicolon when this is used in a statement line. This semicolon
2716 #: owns the whitespace on both sides of it when it is used.
2717 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2718
2719 def _validate(self) -> None:
2720 # Validate whitespace
2721 if (
2722 self.whitespace_after_assert.empty
2723 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2724 ):
2725 raise CSTValidationError("Must have at least one space after 'assert'.")
2726
2727 # Validate comma rules
2728 if self.msg is None and isinstance(self.comma, Comma):
2729 raise CSTValidationError("Cannot have trailing comma after 'test'.")
2730
2731 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Assert":
2732 return Assert(
2733 whitespace_after_assert=visit_required(
2734 self, "whitespace_after_assert", self.whitespace_after_assert, visitor
2735 ),
2736 test=visit_required(self, "test", self.test, visitor),
2737 comma=visit_sentinel(self, "comma", self.comma, visitor),
2738 msg=visit_optional(self, "msg", self.msg, visitor),
2739 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2740 )
2741
2742 def _codegen_impl(
2743 self, state: CodegenState, default_semicolon: bool = False
2744 ) -> None:
2745 with state.record_syntactic_position(self):
2746 state.add_token("assert")
2747 self.whitespace_after_assert._codegen(state)
2748 self.test._codegen(state)
2749
2750 comma = self.comma
2751 msg = self.msg
2752 if isinstance(comma, MaybeSentinel):
2753 if msg is not None:
2754 state.add_token(", ")
2755 else:
2756 comma._codegen(state)
2757 if msg is not None:
2758 msg._codegen(state)
2759
2760 semicolon = self.semicolon
2761 if isinstance(semicolon, MaybeSentinel):
2762 if default_semicolon:
2763 state.add_token("; ")
2764 elif isinstance(semicolon, Semicolon):
2765 semicolon._codegen(state)
2766
2767
2768@add_slots
2769@dataclass(frozen=True)
2770class NameItem(CSTNode):
2771 """
2772 A single identifier name inside a :class:`Global` or :class:`Nonlocal` statement.
2773
2774 This exists because a list of names in a ``global`` or ``nonlocal`` statement need
2775 to be separated by a comma, which ends up owned by the :class:`NameItem` node.
2776 """
2777
2778 #: Identifier name.
2779 name: Name
2780
2781 #: This is forbidden for the last :class:`NameItem` in a
2782 #: :class:`Global`/:class:`Nonlocal`, but all other tems inside a ``global`` or
2783 #: ``nonlocal`` statement must contain a comma to separate them.
2784 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2785
2786 def _validate(self) -> None:
2787 # No parens around names here
2788 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
2789 raise CSTValidationError("Cannot have parens around names in NameItem.")
2790
2791 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "NameItem":
2792 return NameItem(
2793 name=visit_required(self, "name", self.name, visitor),
2794 comma=visit_sentinel(self, "comma", self.comma, visitor),
2795 )
2796
2797 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
2798 with state.record_syntactic_position(self):
2799 self.name._codegen(state)
2800
2801 comma = self.comma
2802 if comma is MaybeSentinel.DEFAULT and default_comma:
2803 state.add_token(", ")
2804 elif isinstance(comma, Comma):
2805 comma._codegen(state)
2806
2807
2808@add_slots
2809@dataclass(frozen=True)
2810class Global(BaseSmallStatement):
2811 """
2812 A ``global`` statement.
2813 """
2814
2815 #: A list of one or more names.
2816 names: Sequence[NameItem]
2817
2818 #: Whitespace appearing after the ``global`` keyword and before the first name.
2819 whitespace_after_global: SimpleWhitespace = SimpleWhitespace.field(" ")
2820
2821 #: Optional semicolon when this is used in a statement line. This semicolon
2822 #: owns the whitespace on both sides of it when it is used.
2823 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2824
2825 def _validate(self) -> None:
2826 if len(self.names) == 0:
2827 raise CSTValidationError(
2828 "A Global statement must have at least one NameItem."
2829 )
2830 if self.names[-1].comma != MaybeSentinel.DEFAULT:
2831 raise CSTValidationError(
2832 "The last NameItem in a Global cannot have a trailing comma."
2833 )
2834 if self.whitespace_after_global.empty:
2835 raise CSTValidationError(
2836 "Must have at least one space after 'global' keyword."
2837 )
2838
2839 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Global":
2840 return Global(
2841 whitespace_after_global=visit_required(
2842 self, "whitespace_after_global", self.whitespace_after_global, visitor
2843 ),
2844 names=visit_sequence(self, "names", self.names, visitor),
2845 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2846 )
2847
2848 def _codegen_impl(
2849 self, state: CodegenState, default_semicolon: bool = False
2850 ) -> None:
2851 with state.record_syntactic_position(self):
2852 state.add_token("global")
2853 self.whitespace_after_global._codegen(state)
2854 last_name = len(self.names) - 1
2855 for i, name in enumerate(self.names):
2856 name._codegen(state, default_comma=(i != last_name))
2857
2858 semicolon = self.semicolon
2859 if isinstance(semicolon, MaybeSentinel):
2860 if default_semicolon:
2861 state.add_token("; ")
2862 elif isinstance(semicolon, Semicolon):
2863 semicolon._codegen(state)
2864
2865
2866@add_slots
2867@dataclass(frozen=True)
2868class Nonlocal(BaseSmallStatement):
2869 """
2870 A ``nonlocal`` statement.
2871 """
2872
2873 #: A list of one or more names.
2874 names: Sequence[NameItem]
2875
2876 #: Whitespace appearing after the ``global`` keyword and before the first name.
2877 whitespace_after_nonlocal: SimpleWhitespace = SimpleWhitespace.field(" ")
2878
2879 #: Optional semicolon when this is used in a statement line. This semicolon
2880 #: owns the whitespace on both sides of it when it is used.
2881 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2882
2883 def _validate(self) -> None:
2884 if len(self.names) == 0:
2885 raise CSTValidationError(
2886 "A Nonlocal statement must have at least one NameItem."
2887 )
2888 if self.names[-1].comma != MaybeSentinel.DEFAULT:
2889 raise CSTValidationError(
2890 "The last NameItem in a Nonlocal cannot have a trailing comma."
2891 )
2892 if self.whitespace_after_nonlocal.empty:
2893 raise CSTValidationError(
2894 "Must have at least one space after 'nonlocal' keyword."
2895 )
2896
2897 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Nonlocal":
2898 return Nonlocal(
2899 whitespace_after_nonlocal=visit_required(
2900 self,
2901 "whitespace_after_nonlocal",
2902 self.whitespace_after_nonlocal,
2903 visitor,
2904 ),
2905 names=visit_sequence(self, "names", self.names, visitor),
2906 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2907 )
2908
2909 def _codegen_impl(
2910 self, state: CodegenState, default_semicolon: bool = False
2911 ) -> None:
2912 with state.record_syntactic_position(self):
2913 state.add_token("nonlocal")
2914 self.whitespace_after_nonlocal._codegen(state)
2915 last_name = len(self.names) - 1
2916 for i, name in enumerate(self.names):
2917 name._codegen(state, default_comma=(i != last_name))
2918
2919 semicolon = self.semicolon
2920 if isinstance(semicolon, MaybeSentinel):
2921 if default_semicolon:
2922 state.add_token("; ")
2923 elif isinstance(semicolon, Semicolon):
2924 semicolon._codegen(state)
2925
2926
2927class MatchPattern(_BaseParenthesizedNode, ABC):
2928 """
2929 A base class for anything that can appear as a pattern in a :class:`Match`
2930 statement.
2931 """
2932
2933 __slots__ = ()
2934
2935
2936@add_slots
2937@dataclass(frozen=True)
2938# pyre-fixme[13]: Attribute `body` is never initialized.
2939class Match(BaseCompoundStatement):
2940 """
2941 A ``match`` statement.
2942 """
2943
2944 #: The subject of the match.
2945 subject: BaseExpression
2946
2947 #: A non-empty list of match cases.
2948 cases: Sequence["MatchCase"]
2949
2950 #: Sequence of empty lines appearing before this compound statement line.
2951 leading_lines: Sequence[EmptyLine] = ()
2952
2953 #: Whitespace between the ``match`` keyword and the subject.
2954 whitespace_after_match: SimpleWhitespace = SimpleWhitespace.field(" ")
2955
2956 #: Whitespace after the subject but before the colon.
2957 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2958
2959 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
2960 whitespace_after_colon: TrailingWhitespace = TrailingWhitespace.field()
2961
2962 #: A string represents a specific indentation. A ``None`` value uses the modules's
2963 #: default indentation. This is included because indentation is allowed to be
2964 #: inconsistent across a file, just not ambiguously.
2965 indent: Optional[str] = None
2966
2967 #: Any trailing comments or lines after the dedent that are owned by this match
2968 #: block. Statements own preceeding and same-line trailing comments, but not
2969 #: trailing lines, so it falls on :class:`Match` to own it. In the case
2970 #: that a statement follows a :class:`Match` block, that statement will own the
2971 #: comments and lines that are at the same indent as the statement, and this
2972 #: :class:`Match` will own the comments and lines that are indented further.
2973 footer: Sequence[EmptyLine] = ()
2974
2975 def _validate(self) -> None:
2976 if len(self.cases) == 0:
2977 raise CSTValidationError("A match statement must have at least one case.")
2978
2979 indent = self.indent
2980 if indent is not None:
2981 if len(indent) == 0:
2982 raise CSTValidationError(
2983 "A match statement must have a non-zero width indent."
2984 )
2985 if _INDENT_WHITESPACE_RE.fullmatch(indent) is None:
2986 raise CSTValidationError(
2987 "An indent must be composed of only whitespace characters."
2988 )
2989
2990 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Match":
2991 return Match(
2992 leading_lines=visit_sequence(
2993 self, "leading_lines", self.leading_lines, visitor
2994 ),
2995 whitespace_after_match=visit_required(
2996 self, "whitespace_after_match", self.whitespace_after_match, visitor
2997 ),
2998 subject=visit_required(self, "subject", self.subject, visitor),
2999 whitespace_before_colon=visit_required(
3000 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
3001 ),
3002 whitespace_after_colon=visit_required(
3003 self, "whitespace_after_colon", self.whitespace_after_colon, visitor
3004 ),
3005 indent=self.indent,
3006 cases=visit_sequence(self, "cases", self.cases, visitor),
3007 footer=visit_sequence(self, "footer", self.footer, visitor),
3008 )
3009
3010 def _codegen_impl(self, state: CodegenState) -> None:
3011 for ll in self.leading_lines:
3012 ll._codegen(state)
3013 state.add_indent_tokens()
3014
3015 with state.record_syntactic_position(self, end_node=self.cases[-1]):
3016 state.add_token("match")
3017 self.whitespace_after_match._codegen(state)
3018 self.subject._codegen(state)
3019 self.whitespace_before_colon._codegen(state)
3020 state.add_token(":")
3021 self.whitespace_after_colon._codegen(state)
3022
3023 indent = self.indent
3024 state.increase_indent(state.default_indent if indent is None else indent)
3025 for c in self.cases:
3026 c._codegen(state)
3027
3028 for f in self.footer:
3029 f._codegen(state)
3030
3031 state.decrease_indent()
3032
3033
3034@add_slots
3035@dataclass(frozen=True)
3036class MatchCase(CSTNode):
3037 """
3038 A single ``case`` block of a :class:`Match` statement.
3039 """
3040
3041 #: The pattern that ``subject`` will be matched against.
3042 pattern: MatchPattern
3043
3044 #: The body of this case block, to be evaluated if ``pattern`` matches ``subject``
3045 #: and ``guard`` evaluates to a truthy value.
3046 body: BaseSuite
3047
3048 #: Optional expression that will be evaluated if ``pattern`` matches ``subject``.
3049 guard: Optional[BaseExpression] = None
3050
3051 #: Sequence of empty lines appearing before this case block.
3052 leading_lines: Sequence[EmptyLine] = ()
3053
3054 #: Whitespace directly after the ``case`` keyword.
3055 whitespace_after_case: SimpleWhitespace = SimpleWhitespace.field(" ")
3056
3057 #: Whitespace before the ``if`` keyword in case there's a guard expression.
3058 whitespace_before_if: SimpleWhitespace = SimpleWhitespace.field("")
3059
3060 #: Whitespace after the ``if`` keyword in case there's a guard expression.
3061 whitespace_after_if: SimpleWhitespace = SimpleWhitespace.field("")
3062
3063 #: Whitespace before the colon.
3064 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
3065
3066 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
3067 return MatchCase(
3068 leading_lines=visit_sequence(
3069 self, "leading_lines", self.leading_lines, visitor
3070 ),
3071 whitespace_after_case=visit_required(
3072 self, "whitespace_after_case", self.whitespace_after_case, visitor
3073 ),
3074 pattern=visit_required(self, "pattern", self.pattern, visitor),
3075 whitespace_before_if=visit_required(
3076 self, "whitespace_before_if", self.whitespace_before_if, visitor
3077 ),
3078 whitespace_after_if=visit_required(
3079 self, "whitespace_after_if", self.whitespace_after_if, visitor
3080 ),
3081 guard=visit_optional(self, "guard", self.guard, visitor),
3082 whitespace_before_colon=visit_required(
3083 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
3084 ),
3085 body=visit_required(self, "body", self.body, visitor),
3086 )
3087
3088 def _codegen_impl(self, state: CodegenState) -> None:
3089 for ll in self.leading_lines:
3090 ll._codegen(state)
3091 state.add_indent_tokens()
3092 with state.record_syntactic_position(self, end_node=self.body):
3093 state.add_token("case")
3094 self.whitespace_after_case._codegen(state)
3095 self.pattern._codegen(state)
3096
3097 guard = self.guard
3098 if guard is not None:
3099 self.whitespace_before_if._codegen(state)
3100 state.add_token("if")
3101 self.whitespace_after_if._codegen(state)
3102 guard._codegen(state)
3103 else:
3104 self.whitespace_before_if._codegen(state)
3105 self.whitespace_after_if._codegen(state)
3106
3107 self.whitespace_before_colon._codegen(state)
3108 state.add_token(":")
3109 self.body._codegen(state)
3110
3111
3112@add_slots
3113@dataclass(frozen=True)
3114class MatchValue(MatchPattern):
3115 """
3116 A match literal or value pattern that compares by equality.
3117 """
3118
3119 #: an expression to compare to
3120 value: BaseExpression
3121
3122 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
3123 return MatchValue(value=visit_required(self, "value", self.value, visitor))
3124
3125 def _codegen_impl(self, state: CodegenState) -> None:
3126 with state.record_syntactic_position(self):
3127 self.value._codegen(state)
3128
3129 @property
3130 def lpar(self) -> Sequence[LeftParen]:
3131 return self.value.lpar
3132
3133 @lpar.setter
3134 def lpar(self, value: Sequence[LeftParen]) -> None:
3135 self.value.lpar = value
3136
3137 @property
3138 def rpar(self) -> Sequence[RightParen]:
3139 return self.value.rpar
3140
3141 @rpar.setter
3142 def rpar(self, value: Sequence[RightParen]) -> None:
3143 self.value.rpar = value
3144
3145
3146@add_slots
3147@dataclass(frozen=True)
3148class MatchSingleton(MatchPattern):
3149 """
3150 A match literal pattern that compares by identity.
3151 """
3152
3153 #: a literal to compare to
3154 value: Name
3155
3156 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
3157 return MatchSingleton(value=visit_required(self, "value", self.value, visitor))
3158
3159 def _validate(self) -> None:
3160 if self.value.value not in {"True", "False", "None"}:
3161 raise CSTValidationError(
3162 "A match singleton can only be True, False, or None"
3163 )
3164
3165 def _codegen_impl(self, state: CodegenState) -> None:
3166 with state.record_syntactic_position(self):
3167 self.value._codegen(state)
3168
3169 @property
3170 def lpar(self) -> Sequence[LeftParen]:
3171 return self.value.lpar
3172
3173 @lpar.setter
3174 def lpar(self, value: Sequence[LeftParen]) -> None:
3175 # pyre-fixme[41]: Cannot reassign final attribute `lpar`.
3176 self.value.lpar = value
3177
3178 @property
3179 def rpar(self) -> Sequence[RightParen]:
3180 return self.value.rpar
3181
3182 @rpar.setter
3183 def rpar(self, value: Sequence[RightParen]) -> None:
3184 # pyre-fixme[41]: Cannot reassign final attribute `rpar`.
3185 self.value.rpar = value
3186
3187
3188@add_slots
3189@dataclass(frozen=True)
3190class MatchSequenceElement(CSTNode):
3191 """
3192 An element in a sequence match pattern.
3193 """
3194
3195 value: MatchPattern
3196
3197 #: An optional trailing comma.
3198 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3199
3200 def _visit_and_replace_children(
3201 self, visitor: CSTVisitorT
3202 ) -> "MatchSequenceElement":
3203 return MatchSequenceElement(
3204 value=visit_required(self, "value", self.value, visitor),
3205 comma=visit_sentinel(self, "comma", self.comma, visitor),
3206 )
3207
3208 def _codegen_impl(
3209 self,
3210 state: CodegenState,
3211 default_comma: bool = False,
3212 default_comma_whitespace: bool = True,
3213 ) -> None:
3214 with state.record_syntactic_position(self):
3215 self.value._codegen(state)
3216 comma = self.comma
3217 if comma is MaybeSentinel.DEFAULT and default_comma:
3218 state.add_token(", " if default_comma_whitespace else ",")
3219 elif isinstance(comma, Comma):
3220 comma._codegen(state)
3221
3222
3223@add_slots
3224@dataclass(frozen=True)
3225class MatchStar(CSTNode):
3226 """
3227 A starred element in a sequence match pattern. Matches the rest of the sequence.
3228 """
3229
3230 #: The name of the pattern binding. A ``None`` value represents ``*_``.
3231 name: Optional[Name] = None
3232
3233 #: An optional trailing comma.
3234 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3235
3236 #: Optional whitespace between the star and the name.
3237 whitespace_before_name: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3238
3239 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchStar":
3240 return MatchStar(
3241 whitespace_before_name=visit_required(
3242 self, "whitespace_before_name", self.whitespace_before_name, visitor
3243 ),
3244 name=visit_optional(self, "name", self.name, visitor),
3245 comma=visit_sentinel(self, "comma", self.comma, visitor),
3246 )
3247
3248 def _codegen_impl(
3249 self,
3250 state: CodegenState,
3251 default_comma: bool = False,
3252 default_comma_whitespace: bool = True,
3253 ) -> None:
3254 with state.record_syntactic_position(self):
3255 state.add_token("*")
3256 self.whitespace_before_name._codegen(state)
3257 name = self.name
3258 if name is None:
3259 state.add_token("_")
3260 else:
3261 name._codegen(state)
3262 comma = self.comma
3263 if comma is MaybeSentinel.DEFAULT and default_comma:
3264 state.add_token(", " if default_comma_whitespace else ",")
3265 elif isinstance(comma, Comma):
3266 comma._codegen(state)
3267
3268
3269class MatchSequence(MatchPattern, ABC):
3270 """
3271 A match sequence pattern. It's either a :class:`MatchList` or a :class:`MatchTuple`.
3272 Matches a variable length sequence if one of the patterns is a :class:`MatchStar`,
3273 otherwise matches a fixed length sequence.
3274 """
3275
3276 __slots__ = ()
3277
3278 #: Patterns to be matched against the subject elements if it is a sequence.
3279 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3280
3281
3282@add_slots
3283@dataclass(frozen=True)
3284class MatchList(MatchSequence):
3285 """
3286 A list match pattern. It's either an "open sequence pattern" (without brackets) or a
3287 regular list literal (with brackets).
3288 """
3289
3290 #: Patterns to be matched against the subject elements if it is a sequence.
3291 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3292
3293 #: An optional left bracket. If missing, this is an open sequence pattern.
3294 lbracket: Optional[LeftSquareBracket] = None
3295
3296 #: An optional left bracket. If missing, this is an open sequence pattern.
3297 rbracket: Optional[RightSquareBracket] = None
3298
3299 #: Parenthesis at the beginning of the node
3300 lpar: Sequence[LeftParen] = ()
3301 #: Parentheses after the pattern, but before a comma (if there is one).
3302 rpar: Sequence[RightParen] = ()
3303
3304 def _validate(self) -> None:
3305 if self.lbracket and not self.rbracket:
3306 raise CSTValidationError("Cannot have left bracket without right bracket")
3307 if self.rbracket and not self.lbracket:
3308 raise CSTValidationError("Cannot have right bracket without left bracket")
3309
3310 if not self.patterns and not self.lbracket:
3311 raise CSTValidationError(
3312 "Must have brackets if matching against empty list"
3313 )
3314
3315 super(MatchList, self)._validate()
3316
3317 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchList":
3318 return MatchList(
3319 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3320 lbracket=visit_optional(self, "lbracket", self.lbracket, visitor),
3321 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3322 rbracket=visit_optional(self, "rbracket", self.rbracket, visitor),
3323 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3324 )
3325
3326 def _codegen_impl(self, state: CodegenState) -> None:
3327 with self._parenthesize(state):
3328 lbracket = self.lbracket
3329 if lbracket is not None:
3330 lbracket._codegen(state)
3331 pats = self.patterns
3332 for idx, pat in enumerate(pats):
3333 pat._codegen(state, default_comma=(idx < len(pats) - 1))
3334 rbracket = self.rbracket
3335 if rbracket is not None:
3336 rbracket._codegen(state)
3337
3338
3339@add_slots
3340@dataclass(frozen=True)
3341class MatchTuple(MatchSequence):
3342 """
3343 A tuple match pattern.
3344 """
3345
3346 #: Patterns to be matched against the subject elements if it is a sequence.
3347 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3348
3349 #: Parenthesis at the beginning of the node
3350 lpar: Sequence[LeftParen] = field(default_factory=lambda: (LeftParen(),))
3351 #: Parentheses after the pattern, but before a comma (if there is one).
3352 rpar: Sequence[RightParen] = field(default_factory=lambda: (RightParen(),))
3353
3354 def _validate(self) -> None:
3355 if len(self.lpar) < 1:
3356 raise CSTValidationError(
3357 "Tuple patterns must have at least pair of parenthesis"
3358 )
3359
3360 super(MatchTuple, self)._validate()
3361
3362 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchTuple":
3363 return MatchTuple(
3364 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3365 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3366 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3367 )
3368
3369 def _codegen_impl(self, state: CodegenState) -> None:
3370 with self._parenthesize(state):
3371 pats = self.patterns
3372 patlen = len(pats)
3373 for idx, pat in enumerate(pats):
3374 pat._codegen(
3375 state,
3376 default_comma=patlen == 1 or (idx < patlen - 1),
3377 default_comma_whitespace=patlen != 1,
3378 )
3379
3380
3381@add_slots
3382@dataclass(frozen=True)
3383class MatchMappingElement(CSTNode):
3384 """
3385 A ``key: value`` pair in a match mapping pattern.
3386 """
3387
3388 key: BaseExpression
3389
3390 #: The pattern to be matched corresponding to ``key``.
3391 pattern: MatchPattern
3392
3393 #: An optional trailing comma.
3394 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3395
3396 #: Whitespace between ``key`` and the colon.
3397 whitespace_before_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3398
3399 #: Whitespace between the colon and ``pattern``.
3400 whitespace_after_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
3401
3402 def _visit_and_replace_children(
3403 self, visitor: CSTVisitorT
3404 ) -> "MatchMappingElement":
3405 return MatchMappingElement(
3406 key=visit_required(self, "key", self.key, visitor),
3407 whitespace_before_colon=visit_required(
3408 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
3409 ),
3410 whitespace_after_colon=visit_required(
3411 self, "whitespace_after_colon", self.whitespace_after_colon, visitor
3412 ),
3413 pattern=visit_required(self, "pattern", self.pattern, visitor),
3414 comma=visit_sentinel(self, "comma", self.comma, visitor),
3415 )
3416
3417 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
3418 with state.record_syntactic_position(self):
3419 self.key._codegen(state)
3420 self.whitespace_before_colon._codegen(state)
3421 state.add_token(":")
3422 self.whitespace_after_colon._codegen(state)
3423 self.pattern._codegen(state)
3424 comma = self.comma
3425 if comma is MaybeSentinel.DEFAULT and default_comma:
3426 state.add_token(", ")
3427 elif isinstance(comma, Comma):
3428 comma._codegen(state)
3429
3430
3431@add_slots
3432@dataclass(frozen=True)
3433class MatchMapping(MatchPattern):
3434 """
3435 A match mapping pattern.
3436 """
3437
3438 #: A sequence of mapping elements.
3439 elements: Sequence[MatchMappingElement] = ()
3440
3441 #: Left curly brace at the beginning of the pattern.
3442 lbrace: LeftCurlyBrace = LeftCurlyBrace.field()
3443
3444 #: Right curly brace at the end of the pattern.
3445 rbrace: RightCurlyBrace = RightCurlyBrace.field()
3446
3447 #: An optional name to capture the remaining elements of the mapping.
3448 rest: Optional[Name] = None
3449
3450 #: Optional whitespace between stars and ``rest``.
3451 whitespace_before_rest: SimpleWhitespace = SimpleWhitespace.field("")
3452
3453 #: An optional trailing comma attached to ``rest``.
3454 trailing_comma: Optional[Comma] = None
3455
3456 #: Parenthesis at the beginning of the node
3457 lpar: Sequence[LeftParen] = ()
3458 #: Parentheses after the pattern
3459 rpar: Sequence[RightParen] = ()
3460
3461 def _validate(self) -> None:
3462 super(MatchMapping, self)._validate()
3463
3464 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchMapping":
3465 return MatchMapping(
3466 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3467 lbrace=visit_required(self, "lbrace", self.lbrace, visitor),
3468 elements=visit_sequence(self, "elements", self.elements, visitor),
3469 whitespace_before_rest=visit_required(
3470 self, "whitespace_before_rest", self.whitespace_before_rest, visitor
3471 ),
3472 rest=visit_optional(self, "rest", self.rest, visitor),
3473 trailing_comma=visit_optional(
3474 self, "trailing_comma", self.trailing_comma, visitor
3475 ),
3476 rbrace=visit_required(self, "rbrace", self.rbrace, visitor),
3477 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3478 )
3479
3480 def _codegen_impl(self, state: CodegenState) -> None:
3481 with self._parenthesize(state):
3482 self.lbrace._codegen(state)
3483 elems = self.elements
3484 rest = self.rest
3485 for idx, el in enumerate(elems):
3486 el._codegen(
3487 state, default_comma=rest is not None or idx < len(elems) - 1
3488 )
3489
3490 if rest is not None:
3491 state.add_token("**")
3492 self.whitespace_before_rest._codegen(state)
3493 rest._codegen(state)
3494 comma = self.trailing_comma
3495 if comma is not None:
3496 comma._codegen(state)
3497
3498 self.rbrace._codegen(state)
3499
3500
3501@add_slots
3502@dataclass(frozen=True)
3503class MatchKeywordElement(CSTNode):
3504 """
3505 A key=value pair in a :class:`MatchClass`.
3506 """
3507
3508 key: Name
3509
3510 #: The pattern to be matched against the attribute named ``key``.
3511 pattern: MatchPattern
3512
3513 #: An optional trailing comma.
3514 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3515
3516 #: Whitespace between ``key`` and the equals sign.
3517 whitespace_before_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3518
3519 #: Whitespace between the equals sign and ``pattern``.
3520 whitespace_after_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3521
3522 def _visit_and_replace_children(
3523 self, visitor: CSTVisitorT
3524 ) -> "MatchKeywordElement":
3525 return MatchKeywordElement(
3526 key=visit_required(self, "key", self.key, visitor),
3527 whitespace_before_equal=visit_required(
3528 self, "whitespace_before_equal", self.whitespace_before_equal, visitor
3529 ),
3530 whitespace_after_equal=visit_required(
3531 self, "whitespace_after_equal", self.whitespace_after_equal, visitor
3532 ),
3533 pattern=visit_required(self, "pattern", self.pattern, visitor),
3534 comma=visit_sentinel(self, "comma", self.comma, visitor),
3535 )
3536
3537 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
3538 with state.record_syntactic_position(self):
3539 self.key._codegen(state)
3540 self.whitespace_before_equal._codegen(state)
3541 state.add_token("=")
3542 self.whitespace_after_equal._codegen(state)
3543 self.pattern._codegen(state)
3544 comma = self.comma
3545 if comma is MaybeSentinel.DEFAULT and default_comma:
3546 state.add_token(", ")
3547 elif isinstance(comma, Comma):
3548 comma._codegen(state)
3549
3550
3551@add_slots
3552@dataclass(frozen=True)
3553class MatchClass(MatchPattern):
3554 """
3555 A match class pattern.
3556 """
3557
3558 #: An expression giving the nominal class to be matched.
3559 cls: BaseExpression
3560
3561 #: A sequence of patterns to be matched against the class defined sequence of
3562 #: pattern matching attributes.
3563 patterns: Sequence[MatchSequenceElement] = ()
3564
3565 #: A sequence of additional attribute names and corresponding patterns to be
3566 #: matched.
3567 kwds: Sequence[MatchKeywordElement] = ()
3568
3569 #: Whitespace between the class name and the left parenthesis.
3570 whitespace_after_cls: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3571
3572 #: Whitespace between the left parenthesis and the first pattern.
3573 whitespace_before_patterns: BaseParenthesizableWhitespace = SimpleWhitespace.field(
3574 ""
3575 )
3576
3577 #: Whitespace between the last pattern and the right parenthesis.
3578 whitespace_after_kwds: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3579
3580 #: Parenthesis at the beginning of the node
3581 lpar: Sequence[LeftParen] = ()
3582 #: Parentheses after the pattern
3583 rpar: Sequence[RightParen] = ()
3584
3585 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchClass":
3586 return MatchClass(
3587 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3588 cls=visit_required(self, "cls", self.cls, visitor),
3589 whitespace_after_cls=visit_required(
3590 self, "whitespace_after_cls", self.whitespace_after_cls, visitor
3591 ),
3592 whitespace_before_patterns=visit_required(
3593 self,
3594 "whitespace_before_patterns",
3595 self.whitespace_before_patterns,
3596 visitor,
3597 ),
3598 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3599 kwds=visit_sequence(self, "kwds", self.kwds, visitor),
3600 whitespace_after_kwds=visit_required(
3601 self, "whitespace_after_kwds", self.whitespace_after_kwds, visitor
3602 ),
3603 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3604 )
3605
3606 def _codegen_impl(self, state: CodegenState) -> None:
3607 with self._parenthesize(state):
3608 self.cls._codegen(state)
3609 self.whitespace_after_cls._codegen(state)
3610 state.add_token("(")
3611 self.whitespace_before_patterns._codegen(state)
3612 pats = self.patterns
3613 kwds = self.kwds
3614 for idx, pat in enumerate(pats):
3615 pat._codegen(state, default_comma=idx + 1 < len(pats) + len(kwds))
3616 for idx, kwd in enumerate(kwds):
3617 kwd._codegen(state, default_comma=idx + 1 < len(kwds))
3618 self.whitespace_after_kwds._codegen(state)
3619 state.add_token(")")
3620
3621
3622@add_slots
3623@dataclass(frozen=True)
3624class MatchAs(MatchPattern):
3625 """
3626 A match "as-pattern", capture pattern, or wildcard pattern.
3627 """
3628
3629 #: The match pattern that the subject will be matched against. If this is ``None``,
3630 #: the node represents a capture pattern (i.e. a bare name) and will always succeed.
3631 pattern: Optional[MatchPattern] = None
3632
3633 #: The name that will be bound if the pattern is successful. If this is ``None``,
3634 #: ``pattern`` must also be ``None`` and the node represents the wildcard pattern
3635 #: (i.e. ``_``).
3636 name: Optional[Name] = None
3637
3638 #: Whitespace between ``pattern`` and the ``as`` keyword (if ``pattern`` is not
3639 #: ``None``)
3640 whitespace_before_as: Union[BaseParenthesizableWhitespace, MaybeSentinel] = (
3641 MaybeSentinel.DEFAULT
3642 )
3643
3644 #: Whitespace between the ``as`` keyword and ``name`` (if ``pattern`` is not
3645 #: ``None``)
3646 whitespace_after_as: Union[BaseParenthesizableWhitespace, MaybeSentinel] = (
3647 MaybeSentinel.DEFAULT
3648 )
3649
3650 #: Parenthesis at the beginning of the node
3651 lpar: Sequence[LeftParen] = ()
3652 #: Parentheses after the pattern
3653 rpar: Sequence[RightParen] = ()
3654
3655 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchAs":
3656 return MatchAs(
3657 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3658 pattern=visit_optional(self, "pattern", self.pattern, visitor),
3659 whitespace_before_as=visit_sentinel(
3660 self, "whitespace_before_as", self.whitespace_before_as, visitor
3661 ),
3662 whitespace_after_as=visit_sentinel(
3663 self, "whitespace_after_as", self.whitespace_after_as, visitor
3664 ),
3665 name=visit_optional(self, "name", self.name, visitor),
3666 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3667 )
3668
3669 def _validate(self) -> None:
3670 if self.name is None and self.pattern is not None:
3671 raise CSTValidationError("Pattern must be None if name is None")
3672 super(MatchAs, self)._validate()
3673
3674 def _codegen_impl(self, state: CodegenState) -> None:
3675 with self._parenthesize(state):
3676 pat = self.pattern
3677 name = self.name
3678 if pat is not None:
3679 pat._codegen(state)
3680 ws_before = self.whitespace_before_as
3681 if ws_before is MaybeSentinel.DEFAULT:
3682 state.add_token(" ")
3683 elif isinstance(ws_before, BaseParenthesizableWhitespace):
3684 ws_before._codegen(state)
3685 state.add_token("as")
3686 ws_after = self.whitespace_after_as
3687 if ws_after is MaybeSentinel.DEFAULT:
3688 state.add_token(" ")
3689 elif isinstance(ws_after, BaseParenthesizableWhitespace):
3690 ws_after._codegen(state)
3691 else:
3692 ws_before = self.whitespace_before_as
3693 if isinstance(ws_before, BaseParenthesizableWhitespace):
3694 ws_before._codegen(state)
3695 ws_after = self.whitespace_after_as
3696 if isinstance(ws_after, BaseParenthesizableWhitespace):
3697 ws_after._codegen(state)
3698 if name is None:
3699 state.add_token("_")
3700 else:
3701 name._codegen(state)
3702
3703
3704@add_slots
3705@dataclass(frozen=True)
3706class MatchOrElement(CSTNode):
3707 """
3708 An element in a :class:`MatchOr` node.
3709 """
3710
3711 pattern: MatchPattern
3712
3713 #: An optional ``|`` separator.
3714 separator: Union[BitOr, MaybeSentinel] = MaybeSentinel.DEFAULT
3715
3716 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOrElement":
3717 return MatchOrElement(
3718 pattern=visit_required(self, "pattern", self.pattern, visitor),
3719 separator=visit_sentinel(self, "separator", self.separator, visitor),
3720 )
3721
3722 def _codegen_impl(
3723 self, state: CodegenState, default_separator: bool = False
3724 ) -> None:
3725 with state.record_syntactic_position(self):
3726 self.pattern._codegen(state)
3727 sep = self.separator
3728 if sep is MaybeSentinel.DEFAULT and default_separator:
3729 state.add_token(" | ")
3730 elif isinstance(sep, BitOr):
3731 sep._codegen(state)
3732
3733
3734@add_slots
3735@dataclass(frozen=True)
3736class MatchOr(MatchPattern):
3737 """
3738 A match "or-pattern". It matches each of its subpatterns in turn to the subject,
3739 until one succeeds. The or-pattern is then deemed to succeed. If none of the
3740 subpatterns succeed the or-pattern fails.
3741 """
3742
3743 #: The subpatterns to be tried in turn.
3744 patterns: Sequence[MatchOrElement]
3745
3746 #: Parenthesis at the beginning of the node
3747 lpar: Sequence[LeftParen] = ()
3748 #: Parentheses after the pattern
3749 rpar: Sequence[RightParen] = ()
3750
3751 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOr":
3752 return MatchOr(
3753 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3754 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3755 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3756 )
3757
3758 def _codegen_impl(self, state: CodegenState) -> None:
3759 with self._parenthesize(state):
3760 pats = self.patterns
3761 for idx, pat in enumerate(pats):
3762 pat._codegen(state, default_separator=idx + 1 < len(pats))
3763
3764
3765@add_slots
3766@dataclass(frozen=True)
3767class TypeVar(CSTNode):
3768 """
3769 A simple (non-variadic) type variable.
3770
3771 Note: this node represents type a variable when declared using PEP-695 syntax.
3772 """
3773
3774 #: The name of the type variable.
3775 name: Name
3776
3777 #: An optional bound on the type.
3778 bound: Optional[BaseExpression] = None
3779
3780 #: The colon used to separate the name and bound. If not specified,
3781 #: :class:`MaybeSentinel` will be replaced with a colon if there is a bound,
3782 #: otherwise will be left empty.
3783 colon: Union[Colon, MaybeSentinel] = MaybeSentinel.DEFAULT
3784
3785 def _codegen_impl(self, state: CodegenState) -> None:
3786 with state.record_syntactic_position(self):
3787 self.name._codegen(state)
3788 bound = self.bound
3789 colon = self.colon
3790 if not isinstance(colon, MaybeSentinel):
3791 colon._codegen(state)
3792 else:
3793 if bound is not None:
3794 state.add_token(": ")
3795
3796 if bound is not None:
3797 bound._codegen(state)
3798
3799 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeVar":
3800 return TypeVar(
3801 name=visit_required(self, "name", self.name, visitor),
3802 colon=visit_sentinel(self, "colon", self.colon, visitor),
3803 bound=visit_optional(self, "bound", self.bound, visitor),
3804 )
3805
3806
3807@add_slots
3808@dataclass(frozen=True)
3809class TypeVarTuple(CSTNode):
3810 """
3811 A variadic type variable.
3812 """
3813
3814 #: The name of this type variable.
3815 name: Name
3816
3817 #: The (optional) whitespace between the star declaring this type variable as
3818 #: variadic, and the variable's name.
3819 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("")
3820
3821 def _codegen_impl(self, state: CodegenState) -> None:
3822 with state.record_syntactic_position(self):
3823 state.add_token("*")
3824 self.whitespace_after_star._codegen(state)
3825 self.name._codegen(state)
3826
3827 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeVarTuple":
3828 return TypeVarTuple(
3829 name=visit_required(self, "name", self.name, visitor),
3830 whitespace_after_star=visit_required(
3831 self, "whitespace_after_star", self.whitespace_after_star, visitor
3832 ),
3833 )
3834
3835
3836@add_slots
3837@dataclass(frozen=True)
3838class ParamSpec(CSTNode):
3839 """
3840 A parameter specification.
3841
3842 Note: this node represents a parameter specification when declared using PEP-695
3843 syntax.
3844 """
3845
3846 #: The name of this parameter specification.
3847 name: Name
3848
3849 #: The (optional) whitespace between the double star declaring this type variable as
3850 #: a parameter specification, and the name.
3851 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("")
3852
3853 def _codegen_impl(self, state: CodegenState) -> None:
3854 with state.record_syntactic_position(self):
3855 state.add_token("**")
3856 self.whitespace_after_star._codegen(state)
3857 self.name._codegen(state)
3858
3859 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ParamSpec":
3860 return ParamSpec(
3861 name=visit_required(self, "name", self.name, visitor),
3862 whitespace_after_star=visit_required(
3863 self, "whitespace_after_star", self.whitespace_after_star, visitor
3864 ),
3865 )
3866
3867
3868@add_slots
3869@dataclass(frozen=True)
3870class TypeParam(CSTNode):
3871 """
3872 A single type parameter that is contained in a :class:`TypeParameters` list.
3873 """
3874
3875 #: The actual parameter.
3876 param: Union[TypeVar, TypeVarTuple, ParamSpec]
3877
3878 #: A trailing comma. If one is not provided, :class:`MaybeSentinel` will be replaced
3879 #: with a comma only if a comma is required.
3880 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3881
3882 #: The equal sign used to denote assignment if there is a default.
3883 equal: Union[AssignEqual, MaybeSentinel] = MaybeSentinel.DEFAULT
3884
3885 #: The star used to denote a variadic default
3886 star: Literal["", "*"] = ""
3887
3888 #: The whitespace between the star and the type.
3889 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("")
3890
3891 #: Any optional default value, used when the argument is not supplied.
3892 default: Optional[BaseExpression] = None
3893
3894 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
3895 self.param._codegen(state)
3896
3897 equal = self.equal
3898 if equal is MaybeSentinel.DEFAULT and self.default is not None:
3899 state.add_token(" = ")
3900 elif isinstance(equal, AssignEqual):
3901 equal._codegen(state)
3902
3903 state.add_token(self.star)
3904 self.whitespace_after_star._codegen(state)
3905
3906 default = self.default
3907 if default is not None:
3908 default._codegen(state)
3909
3910 comma = self.comma
3911 if isinstance(comma, MaybeSentinel):
3912 if default_comma:
3913 state.add_token(", ")
3914 else:
3915 comma._codegen(state)
3916
3917 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeParam":
3918 ret = TypeParam(
3919 param=visit_required(self, "param", self.param, visitor),
3920 equal=visit_sentinel(self, "equal", self.equal, visitor),
3921 star=self.star,
3922 whitespace_after_star=visit_required(
3923 self, "whitespace_after_star", self.whitespace_after_star, visitor
3924 ),
3925 default=visit_optional(self, "default", self.default, visitor),
3926 comma=visit_sentinel(self, "comma", self.comma, visitor),
3927 )
3928 return ret
3929
3930 def _validate(self) -> None:
3931 if self.default is None and isinstance(self.equal, AssignEqual):
3932 raise CSTValidationError(
3933 "Must have a default when specifying an AssignEqual."
3934 )
3935 if self.star and not (self.default or isinstance(self.equal, AssignEqual)):
3936 raise CSTValidationError("Star can only be present if a default")
3937 if isinstance(self.star, str) and self.star not in ("", "*"):
3938 raise CSTValidationError("Must specify either '' or '*' for star.")
3939
3940
3941@add_slots
3942@dataclass(frozen=True)
3943class TypeParameters(CSTNode):
3944 """
3945 Type parameters when specified with PEP-695 syntax.
3946
3947 This node captures all specified parameters that are enclosed with square brackets.
3948 """
3949
3950 #: The parameters within the square brackets.
3951 params: Sequence[TypeParam] = ()
3952
3953 #: Opening square bracket that marks the start of these parameters.
3954 lbracket: LeftSquareBracket = LeftSquareBracket.field()
3955 #: Closing square bracket that marks the end of these parameters.
3956 rbracket: RightSquareBracket = RightSquareBracket.field()
3957
3958 def _codegen_impl(self, state: CodegenState) -> None:
3959 self.lbracket._codegen(state)
3960 params_len = len(self.params)
3961 for idx, param in enumerate(self.params):
3962 param._codegen(state, default_comma=idx + 1 < params_len)
3963 self.rbracket._codegen(state)
3964
3965 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeParameters":
3966 return TypeParameters(
3967 lbracket=visit_required(self, "lbracket", self.lbracket, visitor),
3968 params=visit_sequence(self, "params", self.params, visitor),
3969 rbracket=visit_required(self, "rbracket", self.rbracket, visitor),
3970 )
3971
3972
3973@add_slots
3974@dataclass(frozen=True)
3975class TypeAlias(BaseSmallStatement):
3976 """
3977 A type alias statement.
3978
3979 This node represents the ``type`` statement as specified initially by PEP-695.
3980 Example: ``type ListOrSet[T] = list[T] | set[T]``.
3981 """
3982
3983 #: The name being introduced in this statement.
3984 name: Name
3985
3986 #: Everything on the right hand side of the ``=``.
3987 value: BaseExpression
3988
3989 #: An optional list of type parameters, specified after the name.
3990 type_parameters: Optional[TypeParameters] = None
3991
3992 #: Whitespace between the ``type`` soft keyword and the name.
3993 whitespace_after_type: SimpleWhitespace = SimpleWhitespace.field(" ")
3994
3995 #: Whitespace between the name and the type parameters (if they exist) or the ``=``.
3996 #: If not specified, :class:`MaybeSentinel` will be replaced with a single space if
3997 #: there are no type parameters, otherwise no spaces.
3998 whitespace_after_name: Union[SimpleWhitespace, MaybeSentinel] = (
3999 MaybeSentinel.DEFAULT
4000 )
4001
4002 #: Whitespace between the type parameters and the ``=``. Always empty if there are
4003 #: no type parameters. If not specified, :class:`MaybeSentinel` will be replaced
4004 #: with a single space if there are type parameters.
4005 whitespace_after_type_parameters: Union[SimpleWhitespace, MaybeSentinel] = (
4006 MaybeSentinel.DEFAULT
4007 )
4008
4009 #: Whitespace between the ``=`` and the value.
4010 whitespace_after_equals: SimpleWhitespace = SimpleWhitespace.field(" ")
4011
4012 #: Optional semicolon when this is used in a statement line. This semicolon
4013 #: owns the whitespace on both sides of it when it is used.
4014 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
4015
4016 def _validate(self) -> None:
4017 if (
4018 self.type_parameters is None
4019 and self.whitespace_after_type_parameters
4020 not in {
4021 SimpleWhitespace(""),
4022 MaybeSentinel.DEFAULT,
4023 }
4024 ):
4025 raise CSTValidationError(
4026 "whitespace_after_type_parameters must be empty when there are no type parameters in a TypeAlias"
4027 )
4028
4029 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeAlias":
4030 return TypeAlias(
4031 whitespace_after_type=visit_required(
4032 self, "whitespace_after_type", self.whitespace_after_type, visitor
4033 ),
4034 name=visit_required(self, "name", self.name, visitor),
4035 whitespace_after_name=visit_sentinel(
4036 self, "whitespace_after_name", self.whitespace_after_name, visitor
4037 ),
4038 type_parameters=visit_optional(
4039 self, "type_parameters", self.type_parameters, visitor
4040 ),
4041 whitespace_after_type_parameters=visit_sentinel(
4042 self,
4043 "whitespace_after_type_parameters",
4044 self.whitespace_after_type_parameters,
4045 visitor,
4046 ),
4047 whitespace_after_equals=visit_required(
4048 self, "whitespace_after_equals", self.whitespace_after_equals, visitor
4049 ),
4050 value=visit_required(self, "value", self.value, visitor),
4051 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
4052 )
4053
4054 def _codegen_impl(
4055 self, state: CodegenState, default_semicolon: bool = False
4056 ) -> None:
4057 with state.record_syntactic_position(self):
4058 state.add_token("type")
4059 self.whitespace_after_type._codegen(state)
4060 self.name._codegen(state)
4061 ws_after_name = self.whitespace_after_name
4062 if isinstance(ws_after_name, MaybeSentinel):
4063 if self.type_parameters is None:
4064 state.add_token(" ")
4065 else:
4066 ws_after_name._codegen(state)
4067
4068 ws_after_type_params = self.whitespace_after_type_parameters
4069 if self.type_parameters is not None:
4070 self.type_parameters._codegen(state)
4071 if isinstance(ws_after_type_params, MaybeSentinel):
4072 state.add_token(" ")
4073 else:
4074 ws_after_type_params._codegen(state)
4075
4076 state.add_token("=")
4077 self.whitespace_after_equals._codegen(state)
4078 self.value._codegen(state)
4079
4080 semi = self.semicolon
4081 if isinstance(semi, MaybeSentinel):
4082 if default_semicolon:
4083 state.add_token("; ")
4084 else:
4085 semi._codegen(state)