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 AssignTarget(CSTNode):
1430 """
1431 A target for an :class:`Assign`. Owns the equals sign and the whitespace around it.
1432 """
1433
1434 #: The target expression being assigned to.
1435 target: BaseAssignTargetExpression
1436
1437 #: The whitespace appearing before the equals sign.
1438 whitespace_before_equal: SimpleWhitespace = SimpleWhitespace.field(" ")
1439
1440 #: The whitespace appearing after the equals sign.
1441 whitespace_after_equal: SimpleWhitespace = SimpleWhitespace.field(" ")
1442
1443 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AssignTarget":
1444 return AssignTarget(
1445 target=visit_required(self, "target", self.target, visitor),
1446 whitespace_before_equal=visit_required(
1447 self, "whitespace_before_equal", self.whitespace_before_equal, visitor
1448 ),
1449 whitespace_after_equal=visit_required(
1450 self, "whitespace_after_equal", self.whitespace_after_equal, visitor
1451 ),
1452 )
1453
1454 def _codegen_impl(self, state: CodegenState) -> None:
1455 with state.record_syntactic_position(self):
1456 self.target._codegen(state)
1457
1458 self.whitespace_before_equal._codegen(state)
1459 state.add_token("=")
1460 self.whitespace_after_equal._codegen(state)
1461
1462
1463@add_slots
1464@dataclass(frozen=True)
1465class Assign(BaseSmallStatement):
1466 """
1467 An assignment statement such as ``x = y`` or ``x = y = z``. Unlike
1468 :class:`AnnAssign`, this does not allow type annotations but does
1469 allow for multiple targets.
1470 """
1471
1472 #: One or more targets that are being assigned to.
1473 targets: Sequence[AssignTarget]
1474
1475 #: The expression being assigned to the targets.
1476 value: BaseExpression
1477
1478 #: Optional semicolon when this is used in a statement line. This semicolon
1479 #: owns the whitespace on both sides of it when it is used.
1480 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1481
1482 def _validate(self) -> None:
1483 if len(self.targets) == 0:
1484 raise CSTValidationError(
1485 "An Assign statement must have at least one AssignTarget"
1486 )
1487
1488 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Assign":
1489 return Assign(
1490 targets=visit_sequence(self, "targets", self.targets, visitor),
1491 value=visit_required(self, "value", self.value, visitor),
1492 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1493 )
1494
1495 def _codegen_impl(
1496 self, state: CodegenState, default_semicolon: bool = False
1497 ) -> None:
1498 with state.record_syntactic_position(self):
1499 for target in self.targets:
1500 target._codegen(state)
1501 self.value._codegen(state)
1502
1503 semicolon = self.semicolon
1504 if isinstance(semicolon, MaybeSentinel):
1505 if default_semicolon:
1506 state.add_token("; ")
1507 elif isinstance(semicolon, Semicolon):
1508 semicolon._codegen(state)
1509
1510
1511@add_slots
1512@dataclass(frozen=True)
1513class AnnAssign(BaseSmallStatement):
1514 """
1515 An assignment statement such as ``x: int = 5`` or ``x: int``. This only
1516 allows for one assignment target unlike :class:`Assign` but it includes
1517 a variable annotation. Also unlike :class:`Assign`, the assignment target
1518 is optional, as it is possible to annotate an expression without assigning
1519 to it.
1520 """
1521
1522 #: The target that is being annotated and possibly assigned to.
1523 target: BaseAssignTargetExpression
1524
1525 #: The annotation for the target.
1526 annotation: Annotation
1527
1528 #: The optional expression being assigned to the target.
1529 value: Optional[BaseExpression] = None
1530
1531 #: The equals sign used to denote assignment if there is a value.
1532 equal: Union[AssignEqual, MaybeSentinel] = MaybeSentinel.DEFAULT
1533
1534 #: Optional semicolon when this is used in a statement line. This semicolon
1535 #: owns the whitespace on both sides of it when it is used.
1536 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1537
1538 def _validate(self) -> None:
1539 if self.value is None and isinstance(self.equal, AssignEqual):
1540 raise CSTValidationError(
1541 "Must have a value when specifying an AssignEqual."
1542 )
1543
1544 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AnnAssign":
1545 return AnnAssign(
1546 target=visit_required(self, "target", self.target, visitor),
1547 annotation=visit_required(self, "annotation", self.annotation, visitor),
1548 equal=visit_sentinel(self, "equal", self.equal, visitor),
1549 value=visit_optional(self, "value", self.value, visitor),
1550 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1551 )
1552
1553 def _codegen_impl(
1554 self, state: CodegenState, default_semicolon: bool = False
1555 ) -> None:
1556 with state.record_syntactic_position(self):
1557 self.target._codegen(state)
1558 self.annotation._codegen(state, default_indicator=":")
1559 equal = self.equal
1560 if equal is MaybeSentinel.DEFAULT and self.value is not None:
1561 state.add_token(" = ")
1562 elif isinstance(equal, AssignEqual):
1563 equal._codegen(state)
1564 value = self.value
1565 if value is not None:
1566 value._codegen(state)
1567
1568 semicolon = self.semicolon
1569 if isinstance(semicolon, MaybeSentinel):
1570 if default_semicolon:
1571 state.add_token("; ")
1572 elif isinstance(semicolon, Semicolon):
1573 semicolon._codegen(state)
1574
1575
1576@add_slots
1577@dataclass(frozen=True)
1578class AugAssign(BaseSmallStatement):
1579 """
1580 An augmented assignment statement, such as ``x += 5``.
1581 """
1582
1583 #: Target that is being operated on and assigned to.
1584 target: BaseAssignTargetExpression
1585
1586 #: The augmented assignment operation being performed.
1587 operator: BaseAugOp
1588
1589 #: The value used with the above operator to calculate the new assignment.
1590 value: BaseExpression
1591
1592 #: Optional semicolon when this is used in a statement line. This semicolon
1593 #: owns the whitespace on both sides of it when it is used.
1594 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
1595
1596 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AugAssign":
1597 return AugAssign(
1598 target=visit_required(self, "target", self.target, visitor),
1599 operator=visit_required(self, "operator", self.operator, visitor),
1600 value=visit_required(self, "value", self.value, visitor),
1601 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
1602 )
1603
1604 def _codegen_impl(
1605 self, state: CodegenState, default_semicolon: bool = False
1606 ) -> None:
1607 with state.record_syntactic_position(self):
1608 self.target._codegen(state)
1609 self.operator._codegen(state)
1610 self.value._codegen(state)
1611
1612 semicolon = self.semicolon
1613 if isinstance(semicolon, MaybeSentinel):
1614 if default_semicolon:
1615 state.add_token("; ")
1616 elif isinstance(semicolon, Semicolon):
1617 semicolon._codegen(state)
1618
1619
1620@add_slots
1621@dataclass(frozen=True)
1622class Decorator(CSTNode):
1623 """
1624 A single decorator that decorates a :class:`FunctionDef` or a :class:`ClassDef`.
1625 """
1626
1627 #: The decorator that will return a new function wrapping the parent
1628 #: of this decorator.
1629 decorator: BaseExpression
1630
1631 #: Line comments and empty lines before this decorator. The parent
1632 #: :class:`FunctionDef` or :class:`ClassDef` node owns leading lines before
1633 #: the first decorator so that if the first decorator is removed, spacing is preserved.
1634 leading_lines: Sequence[EmptyLine] = ()
1635
1636 #: Whitespace after the ``@`` and before the decorator expression itself.
1637 whitespace_after_at: SimpleWhitespace = SimpleWhitespace.field("")
1638
1639 #: Optional trailing comment and newline following the decorator before the next line.
1640 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field()
1641
1642 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Decorator":
1643 return Decorator(
1644 leading_lines=visit_sequence(
1645 self, "leading_lines", self.leading_lines, visitor
1646 ),
1647 whitespace_after_at=visit_required(
1648 self, "whitespace_after_at", self.whitespace_after_at, visitor
1649 ),
1650 decorator=visit_required(self, "decorator", self.decorator, visitor),
1651 trailing_whitespace=visit_required(
1652 self, "trailing_whitespace", self.trailing_whitespace, visitor
1653 ),
1654 )
1655
1656 def _codegen_impl(self, state: CodegenState) -> None:
1657 for ll in self.leading_lines:
1658 ll._codegen(state)
1659 state.add_indent_tokens()
1660
1661 with state.record_syntactic_position(self):
1662 state.add_token("@")
1663 self.whitespace_after_at._codegen(state)
1664 self.decorator._codegen(state)
1665
1666 self.trailing_whitespace._codegen(state)
1667
1668
1669def get_docstring_impl(
1670 body: Union[BaseSuite, Sequence[Union[SimpleStatementLine, BaseCompoundStatement]]],
1671 clean: bool,
1672) -> Optional[str]:
1673 """
1674 Implementation Reference:
1675 - :func:`ast.get_docstring` https://docs.python.org/3/library/ast.html#ast.get_docstring
1676 and https://github.com/python/cpython/blob/89aa4694fc8c6d190325ef8ed6ce6a6b8efb3e50/Lib/ast.py#L254
1677 - PEP 257 https://www.python.org/dev/peps/pep-0257/
1678 """
1679 if isinstance(body, Sequence):
1680 if body:
1681 expr = body[0]
1682 else:
1683 return None
1684 else:
1685 expr = body
1686 while isinstance(expr, (BaseSuite, SimpleStatementLine)):
1687 if len(expr.body) == 0:
1688 return None
1689 expr = expr.body[0]
1690 if not isinstance(expr, Expr):
1691 return None
1692 val = expr.value
1693 if isinstance(val, (SimpleString, ConcatenatedString)):
1694 evaluated_value = val.evaluated_value
1695 else:
1696 return None
1697 if isinstance(evaluated_value, bytes):
1698 return None
1699
1700 if evaluated_value is not None and clean:
1701 return inspect.cleandoc(evaluated_value)
1702 return evaluated_value
1703
1704
1705@add_slots
1706@dataclass(frozen=True)
1707class FunctionDef(BaseCompoundStatement):
1708 """
1709 A function definition.
1710 """
1711
1712 #: The function name.
1713 name: Name
1714
1715 #: The function parameters. Present even if there are no params.
1716 params: Parameters
1717
1718 #: The function body.
1719 body: BaseSuite
1720
1721 #: Sequence of decorators applied to this function. Decorators are listed in
1722 #: order that they appear in source (top to bottom) as apposed to the order
1723 #: that they are applied to the function at runtime.
1724 decorators: Sequence[Decorator] = ()
1725
1726 #: An optional return annotation, if the function is annotated.
1727 returns: Optional[Annotation] = None
1728
1729 #: Optional async modifier, if this is an async function.
1730 asynchronous: Optional[Asynchronous] = None
1731
1732 #: Leading empty lines and comments before the first decorator. We
1733 #: assume any comments before the first decorator are owned by the
1734 #: function definition itself. If there are no decorators, this will
1735 #: still contain all of the empty lines and comments before the
1736 #: function definition.
1737 leading_lines: Sequence[EmptyLine] = ()
1738
1739 #: Empty lines and comments between the final decorator and the
1740 #: :class:`FunctionDef` node. In the case of no decorators, this will be empty.
1741 lines_after_decorators: Sequence[EmptyLine] = ()
1742
1743 #: Whitespace after the ``def`` keyword and before the function name.
1744 whitespace_after_def: SimpleWhitespace = SimpleWhitespace.field(" ")
1745
1746 #: Whitespace after the function name and before the type parameters or the opening
1747 #: parenthesis for the parameters.
1748 whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("")
1749
1750 #: Whitespace after the opening parenthesis for the parameters but before
1751 #: the first param itself.
1752 whitespace_before_params: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
1753
1754 #: Whitespace after the closing parenthesis or return annotation and before
1755 #: the colon.
1756 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1757
1758 #: An optional declaration of type parameters.
1759 type_parameters: Optional["TypeParameters"] = None
1760
1761 #: Whitespace between the type parameters and the opening parenthesis for the
1762 #: (non-type) parameters.
1763 whitespace_after_type_parameters: SimpleWhitespace = SimpleWhitespace.field("")
1764
1765 def _validate(self) -> None:
1766 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
1767 raise CSTValidationError("Cannot have parens around Name in a FunctionDef.")
1768 if self.whitespace_after_def.empty:
1769 raise CSTValidationError(
1770 "There must be at least one space between 'def' and name."
1771 )
1772
1773 if (
1774 self.type_parameters is None
1775 and not self.whitespace_after_type_parameters.empty
1776 ):
1777 raise CSTValidationError(
1778 "whitespace_after_type_parameters must be empty if there are no type "
1779 "parameters in FunctionDef"
1780 )
1781
1782 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "FunctionDef":
1783 return FunctionDef(
1784 leading_lines=visit_sequence(
1785 self, "leading_lines", self.leading_lines, visitor
1786 ),
1787 decorators=visit_sequence(self, "decorators", self.decorators, visitor),
1788 lines_after_decorators=visit_sequence(
1789 self, "lines_after_decorators", self.lines_after_decorators, visitor
1790 ),
1791 asynchronous=visit_optional(
1792 self, "asynchronous", self.asynchronous, visitor
1793 ),
1794 whitespace_after_def=visit_required(
1795 self, "whitespace_after_def", self.whitespace_after_def, visitor
1796 ),
1797 name=visit_required(self, "name", self.name, visitor),
1798 whitespace_after_name=visit_required(
1799 self, "whitespace_after_name", self.whitespace_after_name, visitor
1800 ),
1801 type_parameters=visit_optional(
1802 self, "type_parameters", self.type_parameters, visitor
1803 ),
1804 whitespace_after_type_parameters=visit_required(
1805 self,
1806 "whitespace_after_type_parameters",
1807 self.whitespace_after_type_parameters,
1808 visitor,
1809 ),
1810 whitespace_before_params=visit_required(
1811 self, "whitespace_before_params", self.whitespace_before_params, visitor
1812 ),
1813 params=visit_required(self, "params", self.params, visitor),
1814 returns=visit_optional(self, "returns", self.returns, visitor),
1815 whitespace_before_colon=visit_required(
1816 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1817 ),
1818 body=visit_required(self, "body", self.body, visitor),
1819 )
1820
1821 def _codegen_impl(self, state: CodegenState) -> None:
1822 for ll in self.leading_lines:
1823 ll._codegen(state)
1824 for decorator in self.decorators:
1825 decorator._codegen(state)
1826 for lad in self.lines_after_decorators:
1827 lad._codegen(state)
1828 state.add_indent_tokens()
1829
1830 with state.record_syntactic_position(self, end_node=self.body):
1831 asynchronous = self.asynchronous
1832 if asynchronous is not None:
1833 asynchronous._codegen(state)
1834 state.add_token("def")
1835 self.whitespace_after_def._codegen(state)
1836 self.name._codegen(state)
1837 self.whitespace_after_name._codegen(state)
1838 type_params = self.type_parameters
1839 if type_params is not None:
1840 type_params._codegen(state)
1841 self.whitespace_after_type_parameters._codegen(state)
1842 state.add_token("(")
1843 self.whitespace_before_params._codegen(state)
1844 self.params._codegen(state)
1845 state.add_token(")")
1846 returns = self.returns
1847 if returns is not None:
1848 returns._codegen(state, default_indicator="->")
1849 self.whitespace_before_colon._codegen(state)
1850 state.add_token(":")
1851 self.body._codegen(state)
1852
1853 def get_docstring(self, clean: bool = True) -> Optional[str]:
1854 """
1855 When docstring is available, returns a :func:`inspect.cleandoc` cleaned docstring.
1856 Otherwise, returns ``None``.
1857 """
1858 return get_docstring_impl(self.body, clean)
1859
1860
1861@add_slots
1862@dataclass(frozen=True)
1863class ClassDef(BaseCompoundStatement):
1864 """
1865 A class definition.
1866 """
1867
1868 #: The class name.
1869 name: Name
1870
1871 #: The class body.
1872 body: BaseSuite
1873
1874 #: Sequence of base classes this class inherits from.
1875 bases: Sequence[Arg] = ()
1876
1877 #: Sequence of keywords, such as "metaclass".
1878 keywords: Sequence[Arg] = ()
1879
1880 #: Sequence of decorators applied to this class.
1881 decorators: Sequence[Decorator] = ()
1882
1883 #: Optional open parenthesis used when there are bases or keywords.
1884 lpar: Union[LeftParen, MaybeSentinel] = MaybeSentinel.DEFAULT
1885
1886 #: Optional close parenthesis used when there are bases or keywords.
1887 rpar: Union[RightParen, MaybeSentinel] = MaybeSentinel.DEFAULT
1888
1889 #: Leading empty lines and comments before the first decorator. We
1890 #: assume any comments before the first decorator are owned by the
1891 #: class definition itself. If there are no decorators, this will
1892 #: still contain all of the empty lines and comments before the
1893 #: class definition.
1894 leading_lines: Sequence[EmptyLine] = ()
1895
1896 #: Empty lines and comments between the final decorator and the
1897 #: :class:`ClassDef` node. In the case of no decorators, this will be empty.
1898 lines_after_decorators: Sequence[EmptyLine] = ()
1899
1900 #: Whitespace after the ``class`` keyword and before the class name.
1901 whitespace_after_class: SimpleWhitespace = SimpleWhitespace.field(" ")
1902
1903 #: Whitespace after the class name and before the type parameters or the opening
1904 #: parenthesis for the bases and keywords.
1905 whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("")
1906
1907 #: Whitespace after the closing parenthesis or class name and before
1908 #: the colon.
1909 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
1910
1911 #: An optional declaration of type parameters.
1912 type_parameters: Optional["TypeParameters"] = None
1913
1914 #: Whitespace between type parameters and opening parenthesis for the bases and
1915 #: keywords.
1916 whitespace_after_type_parameters: SimpleWhitespace = SimpleWhitespace.field("")
1917
1918 def _validate_whitespace(self) -> None:
1919 if self.whitespace_after_class.empty:
1920 raise CSTValidationError(
1921 "There must be at least one space between 'class' and name."
1922 )
1923 if (
1924 self.type_parameters is None
1925 and not self.whitespace_after_type_parameters.empty
1926 ):
1927 raise CSTValidationError(
1928 "whitespace_after_type_parameters must be empty if there are no type"
1929 "parameters in a ClassDef"
1930 )
1931
1932 def _validate_parens(self) -> None:
1933 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
1934 raise CSTValidationError("Cannot have parens around Name in a ClassDef.")
1935 if isinstance(self.lpar, MaybeSentinel) and isinstance(self.rpar, RightParen):
1936 raise CSTValidationError(
1937 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
1938 )
1939 if isinstance(self.lpar, LeftParen) and isinstance(self.rpar, MaybeSentinel):
1940 raise CSTValidationError(
1941 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
1942 )
1943
1944 def _validate_args(self) -> None:
1945 if any((arg.keyword is not None) for arg in self.bases):
1946 raise CSTValidationError("Bases must be arguments without keywords.")
1947 if any((arg.keyword is None and arg.star != "**") for arg in self.keywords):
1948 raise CSTValidationError(
1949 "Keywords must be arguments with keywords or dictionary expansions."
1950 )
1951
1952 def _validate(self) -> None:
1953 self._validate_whitespace()
1954 self._validate_parens()
1955 self._validate_args()
1956
1957 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ClassDef":
1958 return ClassDef(
1959 leading_lines=visit_sequence(
1960 self, "leading_lines", self.leading_lines, visitor
1961 ),
1962 decorators=visit_sequence(self, "decorators", self.decorators, visitor),
1963 lines_after_decorators=visit_sequence(
1964 self, "lines_after_decorators", self.lines_after_decorators, visitor
1965 ),
1966 whitespace_after_class=visit_required(
1967 self, "whitespace_after_class", self.whitespace_after_class, visitor
1968 ),
1969 name=visit_required(self, "name", self.name, visitor),
1970 whitespace_after_name=visit_required(
1971 self, "whitespace_after_name", self.whitespace_after_name, visitor
1972 ),
1973 type_parameters=visit_optional(
1974 self, "type_parameters", self.type_parameters, visitor
1975 ),
1976 whitespace_after_type_parameters=visit_required(
1977 self,
1978 "whitespace_after_type_parameters",
1979 self.whitespace_after_type_parameters,
1980 visitor,
1981 ),
1982 lpar=visit_sentinel(self, "lpar", self.lpar, visitor),
1983 bases=visit_sequence(self, "bases", self.bases, visitor),
1984 keywords=visit_sequence(self, "keywords", self.keywords, visitor),
1985 rpar=visit_sentinel(self, "rpar", self.rpar, visitor),
1986 whitespace_before_colon=visit_required(
1987 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
1988 ),
1989 body=visit_required(self, "body", self.body, visitor),
1990 )
1991
1992 def _codegen_impl(self, state: CodegenState) -> None: # noqa: C901
1993 for ll in self.leading_lines:
1994 ll._codegen(state)
1995 for decorator in self.decorators:
1996 decorator._codegen(state)
1997 for lad in self.lines_after_decorators:
1998 lad._codegen(state)
1999 state.add_indent_tokens()
2000
2001 with state.record_syntactic_position(self, end_node=self.body):
2002 state.add_token("class")
2003 self.whitespace_after_class._codegen(state)
2004 self.name._codegen(state)
2005 self.whitespace_after_name._codegen(state)
2006 type_params = self.type_parameters
2007 if type_params is not None:
2008 type_params._codegen(state)
2009 self.whitespace_after_type_parameters._codegen(state)
2010 lpar = self.lpar
2011 if isinstance(lpar, MaybeSentinel):
2012 if self.bases or self.keywords:
2013 state.add_token("(")
2014 elif isinstance(lpar, LeftParen):
2015 lpar._codegen(state)
2016 args = [*self.bases, *self.keywords]
2017 last_arg = len(args) - 1
2018 for i, arg in enumerate(args):
2019 arg._codegen(state, default_comma=(i != last_arg))
2020 rpar = self.rpar
2021 if isinstance(rpar, MaybeSentinel):
2022 if self.bases or self.keywords:
2023 state.add_token(")")
2024 elif isinstance(rpar, RightParen):
2025 rpar._codegen(state)
2026 self.whitespace_before_colon._codegen(state)
2027 state.add_token(":")
2028 self.body._codegen(state)
2029
2030 def get_docstring(self, clean: bool = True) -> Optional[str]:
2031 """
2032 Returns a :func:`inspect.cleandoc` cleaned docstring if the docstring is available, ``None`` otherwise.
2033 """
2034 return get_docstring_impl(self.body, clean)
2035
2036
2037@add_slots
2038@dataclass(frozen=True)
2039class WithItem(CSTNode):
2040 """
2041 A single context manager in a :class:`With` block, with an optional variable name.
2042 """
2043
2044 #: Expression that evaluates to a context manager.
2045 item: BaseExpression
2046
2047 #: Variable to assign the context manager to, if it is needed in the
2048 #: :class:`With` body.
2049 asname: Optional[AsName] = None
2050
2051 #: This is forbidden for the last :class:`WithItem` in a :class:`With`, but all
2052 #: other items inside a with block must contain a comma to separate them.
2053 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2054
2055 def _validate(self) -> None:
2056 asname = self.asname
2057 if (
2058 asname is not None
2059 and asname.whitespace_before_as.empty
2060 and not self.item._safe_to_use_with_word_operator(ExpressionPosition.LEFT)
2061 ):
2062 raise CSTValidationError("Must have at least one space before as keyword.")
2063
2064 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "WithItem":
2065 return WithItem(
2066 item=visit_required(self, "item", self.item, visitor),
2067 asname=visit_optional(self, "asname", self.asname, visitor),
2068 comma=visit_sentinel(self, "comma", self.comma, visitor),
2069 )
2070
2071 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
2072 with state.record_syntactic_position(self):
2073 self.item._codegen(state)
2074 asname = self.asname
2075 if asname is not None:
2076 asname._codegen(state)
2077
2078 comma = self.comma
2079 if comma is MaybeSentinel.DEFAULT and default_comma:
2080 state.add_token(", ")
2081 elif isinstance(comma, Comma):
2082 comma._codegen(state)
2083
2084
2085@add_slots
2086@dataclass(frozen=True)
2087class With(BaseCompoundStatement):
2088 """
2089 A ``with`` statement.
2090 """
2091
2092 #: A sequence of one or more items that evaluate to context managers.
2093 items: Sequence[WithItem]
2094
2095 #: The suite that is wrapped with this statement.
2096 body: BaseSuite
2097
2098 #: Optional async modifier if this is an ``async with`` statement.
2099 asynchronous: Optional[Asynchronous] = None
2100
2101 #: Sequence of empty lines appearing before this with statement.
2102 leading_lines: Sequence[EmptyLine] = ()
2103
2104 #: Optional open parenthesis for multi-line with bindings
2105 lpar: Union[LeftParen, MaybeSentinel] = MaybeSentinel.DEFAULT
2106
2107 #: Optional close parenthesis for multi-line with bindings
2108 rpar: Union[RightParen, MaybeSentinel] = MaybeSentinel.DEFAULT
2109
2110 #: Whitespace after the ``with`` keyword and before the first item.
2111 whitespace_after_with: SimpleWhitespace = SimpleWhitespace.field(" ")
2112
2113 #: Whitespace after the last item and before the colon.
2114 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2115
2116 def _validate_parens(self) -> None:
2117 if isinstance(self.lpar, MaybeSentinel) and isinstance(self.rpar, RightParen):
2118 raise CSTValidationError(
2119 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
2120 )
2121 if isinstance(self.lpar, LeftParen) and isinstance(self.rpar, MaybeSentinel):
2122 raise CSTValidationError(
2123 "Do not mix concrete LeftParen/RightParen with MaybeSentinel."
2124 )
2125
2126 def _validate(self) -> None:
2127 self._validate_parens()
2128 if len(self.items) == 0:
2129 raise CSTValidationError(
2130 "A With statement must have at least one WithItem."
2131 )
2132 if (
2133 isinstance(self.rpar, MaybeSentinel)
2134 and self.items[-1].comma != MaybeSentinel.DEFAULT
2135 ):
2136 raise CSTValidationError(
2137 "The last WithItem in an unparenthesized With cannot have a trailing comma."
2138 )
2139 if self.whitespace_after_with.empty and not (
2140 isinstance(self.lpar, LeftParen)
2141 or self.items[0].item._safe_to_use_with_word_operator(
2142 ExpressionPosition.RIGHT
2143 )
2144 ):
2145 raise CSTValidationError("Must have at least one space after with keyword.")
2146
2147 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "With":
2148 return With(
2149 leading_lines=visit_sequence(
2150 self, "leading_lines", self.leading_lines, visitor
2151 ),
2152 asynchronous=visit_optional(
2153 self, "asynchronous", self.asynchronous, visitor
2154 ),
2155 whitespace_after_with=visit_required(
2156 self, "whitespace_after_with", self.whitespace_after_with, visitor
2157 ),
2158 lpar=visit_sentinel(self, "lpar", self.lpar, visitor),
2159 items=visit_sequence(self, "items", self.items, visitor),
2160 rpar=visit_sentinel(self, "rpar", self.rpar, visitor),
2161 whitespace_before_colon=visit_required(
2162 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2163 ),
2164 body=visit_required(self, "body", self.body, visitor),
2165 )
2166
2167 def _codegen_impl(self, state: CodegenState) -> None:
2168 for ll in self.leading_lines:
2169 ll._codegen(state)
2170 state.add_indent_tokens()
2171
2172 needs_paren = False
2173 for item in self.items:
2174 comma = item.comma
2175 if isinstance(comma, Comma):
2176 if isinstance(
2177 comma.whitespace_after,
2178 (EmptyLine, TrailingWhitespace, ParenthesizedWhitespace),
2179 ):
2180 needs_paren = True
2181 break
2182
2183 with state.record_syntactic_position(self, end_node=self.body):
2184 asynchronous = self.asynchronous
2185 if asynchronous is not None:
2186 asynchronous._codegen(state)
2187 state.add_token("with")
2188 self.whitespace_after_with._codegen(state)
2189 lpar = self.lpar
2190 if isinstance(lpar, LeftParen):
2191 lpar._codegen(state)
2192 elif needs_paren:
2193 state.add_token("(")
2194 last_item = len(self.items) - 1
2195 for i, item in enumerate(self.items):
2196 item._codegen(state, default_comma=(i != last_item))
2197 rpar = self.rpar
2198 if isinstance(rpar, RightParen):
2199 rpar._codegen(state)
2200 elif needs_paren:
2201 state.add_token(")")
2202 self.whitespace_before_colon._codegen(state)
2203 state.add_token(":")
2204 self.body._codegen(state)
2205
2206
2207@add_slots
2208@dataclass(frozen=True)
2209class For(BaseCompoundStatement):
2210 """
2211 A ``for target in iter`` statement.
2212 """
2213
2214 #: The target of the iterator in the for statement.
2215 target: BaseAssignTargetExpression
2216
2217 #: The iterable expression we will loop over.
2218 iter: BaseExpression
2219
2220 #: The suite that is wrapped with this statement.
2221 body: BaseSuite
2222
2223 #: An optional else case which will be executed if there is no
2224 #: :class:`Break` statement encountered while looping.
2225 orelse: Optional[Else] = None
2226
2227 #: Optional async modifier, if this is an `async for` statement.
2228 asynchronous: Optional[Asynchronous] = None
2229
2230 #: Sequence of empty lines appearing before this for statement.
2231 leading_lines: Sequence[EmptyLine] = ()
2232
2233 #: Whitespace after the ``for`` keyword and before the target.
2234 whitespace_after_for: SimpleWhitespace = SimpleWhitespace.field(" ")
2235
2236 #: Whitespace after the target and before the ``in`` keyword.
2237 whitespace_before_in: SimpleWhitespace = SimpleWhitespace.field(" ")
2238
2239 #: Whitespace after the ``in`` keyword and before the iter.
2240 whitespace_after_in: SimpleWhitespace = SimpleWhitespace.field(" ")
2241
2242 #: Whitespace after the iter and before the colon.
2243 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2244
2245 def _validate(self) -> None:
2246 if (
2247 self.whitespace_after_for.empty
2248 and not self.target._safe_to_use_with_word_operator(
2249 ExpressionPosition.RIGHT
2250 )
2251 ):
2252 raise CSTValidationError(
2253 "Must have at least one space after 'for' keyword."
2254 )
2255
2256 if (
2257 self.whitespace_before_in.empty
2258 and not self.target._safe_to_use_with_word_operator(ExpressionPosition.LEFT)
2259 ):
2260 raise CSTValidationError(
2261 "Must have at least one space before 'in' keyword."
2262 )
2263
2264 if (
2265 self.whitespace_after_in.empty
2266 and not self.iter._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2267 ):
2268 raise CSTValidationError("Must have at least one space after 'in' keyword.")
2269
2270 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "For":
2271 return For(
2272 leading_lines=visit_sequence(
2273 self, "leading_lines", self.leading_lines, visitor
2274 ),
2275 asynchronous=visit_optional(
2276 self, "asynchronous", self.asynchronous, visitor
2277 ),
2278 whitespace_after_for=visit_required(
2279 self, "whitespace_after_for", self.whitespace_after_for, visitor
2280 ),
2281 target=visit_required(self, "target", self.target, visitor),
2282 whitespace_before_in=visit_required(
2283 self, "whitespace_before_in", self.whitespace_before_in, visitor
2284 ),
2285 whitespace_after_in=visit_required(
2286 self, "whitespace_after_in", self.whitespace_after_in, visitor
2287 ),
2288 iter=visit_required(self, "iter", self.iter, visitor),
2289 whitespace_before_colon=visit_required(
2290 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2291 ),
2292 body=visit_required(self, "body", self.body, visitor),
2293 orelse=visit_optional(self, "orelse", self.orelse, visitor),
2294 )
2295
2296 def _codegen_impl(self, state: CodegenState) -> None:
2297 for ll in self.leading_lines:
2298 ll._codegen(state)
2299 state.add_indent_tokens()
2300
2301 end_node = self.body if self.orelse is None else self.orelse
2302 with state.record_syntactic_position(self, end_node=end_node):
2303 asynchronous = self.asynchronous
2304 if asynchronous is not None:
2305 asynchronous._codegen(state)
2306 state.add_token("for")
2307 self.whitespace_after_for._codegen(state)
2308 self.target._codegen(state)
2309 self.whitespace_before_in._codegen(state)
2310 state.add_token("in")
2311 self.whitespace_after_in._codegen(state)
2312 self.iter._codegen(state)
2313 self.whitespace_before_colon._codegen(state)
2314 state.add_token(":")
2315 self.body._codegen(state)
2316 orelse = self.orelse
2317 if orelse is not None:
2318 orelse._codegen(state)
2319
2320
2321@add_slots
2322@dataclass(frozen=True)
2323class While(BaseCompoundStatement):
2324 """
2325 A ``while`` statement.
2326 """
2327
2328 #: The test we will loop against.
2329 test: BaseExpression
2330
2331 #: The suite that is wrapped with this statement.
2332 body: BaseSuite
2333
2334 #: An optional else case which will be executed if there is no
2335 #: :class:`Break` statement encountered while looping.
2336 orelse: Optional[Else] = None
2337
2338 #: Sequence of empty lines appearing before this while statement.
2339 leading_lines: Sequence[EmptyLine] = ()
2340
2341 #: Whitespace after the ``while`` keyword and before the test.
2342 whitespace_after_while: SimpleWhitespace = SimpleWhitespace.field(" ")
2343
2344 #: Whitespace after the test and before the colon.
2345 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2346
2347 def _validate(self) -> None:
2348 if (
2349 self.whitespace_after_while.empty
2350 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2351 ):
2352 raise CSTValidationError(
2353 "Must have at least one space after 'while' keyword."
2354 )
2355
2356 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "While":
2357 return While(
2358 leading_lines=visit_sequence(
2359 self, "leading_lines", self.leading_lines, visitor
2360 ),
2361 whitespace_after_while=visit_required(
2362 self, "whitespace_after_while", self.whitespace_after_while, visitor
2363 ),
2364 test=visit_required(self, "test", self.test, visitor),
2365 whitespace_before_colon=visit_required(
2366 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2367 ),
2368 body=visit_required(self, "body", self.body, visitor),
2369 orelse=visit_optional(self, "orelse", self.orelse, visitor),
2370 )
2371
2372 def _codegen_impl(self, state: CodegenState) -> None:
2373 for ll in self.leading_lines:
2374 ll._codegen(state)
2375 state.add_indent_tokens()
2376
2377 end_node = self.body if self.orelse is None else self.orelse
2378 with state.record_syntactic_position(self, end_node=end_node):
2379 state.add_token("while")
2380 self.whitespace_after_while._codegen(state)
2381 self.test._codegen(state)
2382 self.whitespace_before_colon._codegen(state)
2383 state.add_token(":")
2384 self.body._codegen(state)
2385 orelse = self.orelse
2386 if orelse is not None:
2387 orelse._codegen(state)
2388
2389
2390@add_slots
2391@dataclass(frozen=True)
2392class Raise(BaseSmallStatement):
2393 """
2394 A ``raise exc`` or ``raise exc from cause`` statement.
2395 """
2396
2397 #: The exception that we should raise.
2398 exc: Optional[BaseExpression] = None
2399
2400 #: Optionally, a ``from cause`` clause to allow us to raise an exception
2401 #: out of another exception's context.
2402 cause: Optional[From] = None
2403
2404 #: Any whitespace appearing between the ``raise`` keyword and the exception.
2405 whitespace_after_raise: Union[SimpleWhitespace, MaybeSentinel] = (
2406 MaybeSentinel.DEFAULT
2407 )
2408
2409 #: Optional semicolon when this is used in a statement line. This semicolon
2410 #: owns the whitespace on both sides of it when it is used.
2411 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2412
2413 def _validate(self) -> None:
2414 # Validate correct construction
2415 if self.exc is None and self.cause is not None:
2416 raise CSTValidationError(
2417 "Must have an 'exc' when specifying 'clause'. on Raise."
2418 )
2419
2420 # Validate spacing between "raise" and "exc"
2421 exc = self.exc
2422 if exc is not None:
2423 whitespace_after_raise = self.whitespace_after_raise
2424 has_no_gap = (
2425 not isinstance(whitespace_after_raise, MaybeSentinel)
2426 and whitespace_after_raise.empty
2427 )
2428 if has_no_gap and not exc._safe_to_use_with_word_operator(
2429 ExpressionPosition.RIGHT
2430 ):
2431 raise CSTValidationError("Must have at least one space after 'raise'.")
2432
2433 # Validate spacing between "exc" and "from"
2434 cause = self.cause
2435 if exc is not None and cause is not None:
2436 whitespace_before_from = cause.whitespace_before_from
2437 has_no_gap = (
2438 not isinstance(whitespace_before_from, MaybeSentinel)
2439 and whitespace_before_from.empty
2440 )
2441 if has_no_gap and not exc._safe_to_use_with_word_operator(
2442 ExpressionPosition.LEFT
2443 ):
2444 raise CSTValidationError("Must have at least one space before 'from'.")
2445
2446 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Raise":
2447 return Raise(
2448 whitespace_after_raise=visit_sentinel(
2449 self, "whitespace_after_raise", self.whitespace_after_raise, visitor
2450 ),
2451 exc=visit_optional(self, "exc", self.exc, visitor),
2452 cause=visit_optional(self, "cause", self.cause, visitor),
2453 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2454 )
2455
2456 def _codegen_impl(
2457 self, state: CodegenState, default_semicolon: bool = False
2458 ) -> None:
2459 with state.record_syntactic_position(self):
2460 exc = self.exc
2461 cause = self.cause
2462 state.add_token("raise")
2463 whitespace_after_raise = self.whitespace_after_raise
2464 if isinstance(whitespace_after_raise, MaybeSentinel):
2465 if exc is not None:
2466 state.add_token(" ")
2467 else:
2468 whitespace_after_raise._codegen(state)
2469 if exc is not None:
2470 exc._codegen(state)
2471 if cause is not None:
2472 cause._codegen(state, default_space=" ")
2473
2474 semicolon = self.semicolon
2475 if isinstance(semicolon, MaybeSentinel):
2476 if default_semicolon:
2477 state.add_token("; ")
2478 elif isinstance(semicolon, Semicolon):
2479 semicolon._codegen(state)
2480
2481
2482@add_slots
2483@dataclass(frozen=True)
2484class Assert(BaseSmallStatement):
2485 """
2486 An assert statement such as ``assert x > 5`` or ``assert x > 5, 'Uh oh!'``
2487 """
2488
2489 #: The test we are going to assert on.
2490 test: BaseExpression
2491
2492 #: The optional message to display if the test evaluates to a falsey value.
2493 msg: Optional[BaseExpression] = None
2494
2495 #: A comma separating test and message, if there is a message.
2496 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2497
2498 #: Whitespace appearing after the ``assert`` keyword and before the test.
2499 whitespace_after_assert: SimpleWhitespace = SimpleWhitespace.field(" ")
2500
2501 #: Optional semicolon when this is used in a statement line. This semicolon
2502 #: owns the whitespace on both sides of it when it is used.
2503 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2504
2505 def _validate(self) -> None:
2506 # Validate whitespace
2507 if (
2508 self.whitespace_after_assert.empty
2509 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
2510 ):
2511 raise CSTValidationError("Must have at least one space after 'assert'.")
2512
2513 # Validate comma rules
2514 if self.msg is None and isinstance(self.comma, Comma):
2515 raise CSTValidationError("Cannot have trailing comma after 'test'.")
2516
2517 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Assert":
2518 return Assert(
2519 whitespace_after_assert=visit_required(
2520 self, "whitespace_after_assert", self.whitespace_after_assert, visitor
2521 ),
2522 test=visit_required(self, "test", self.test, visitor),
2523 comma=visit_sentinel(self, "comma", self.comma, visitor),
2524 msg=visit_optional(self, "msg", self.msg, visitor),
2525 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2526 )
2527
2528 def _codegen_impl(
2529 self, state: CodegenState, default_semicolon: bool = False
2530 ) -> None:
2531 with state.record_syntactic_position(self):
2532 state.add_token("assert")
2533 self.whitespace_after_assert._codegen(state)
2534 self.test._codegen(state)
2535
2536 comma = self.comma
2537 msg = self.msg
2538 if isinstance(comma, MaybeSentinel):
2539 if msg is not None:
2540 state.add_token(", ")
2541 else:
2542 comma._codegen(state)
2543 if msg is not None:
2544 msg._codegen(state)
2545
2546 semicolon = self.semicolon
2547 if isinstance(semicolon, MaybeSentinel):
2548 if default_semicolon:
2549 state.add_token("; ")
2550 elif isinstance(semicolon, Semicolon):
2551 semicolon._codegen(state)
2552
2553
2554@add_slots
2555@dataclass(frozen=True)
2556class NameItem(CSTNode):
2557 """
2558 A single identifier name inside a :class:`Global` or :class:`Nonlocal` statement.
2559
2560 This exists because a list of names in a ``global`` or ``nonlocal`` statement need
2561 to be separated by a comma, which ends up owned by the :class:`NameItem` node.
2562 """
2563
2564 #: Identifier name.
2565 name: Name
2566
2567 #: This is forbidden for the last :class:`NameItem` in a
2568 #: :class:`Global`/:class:`Nonlocal`, but all other tems inside a ``global`` or
2569 #: ``nonlocal`` statement must contain a comma to separate them.
2570 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2571
2572 def _validate(self) -> None:
2573 # No parens around names here
2574 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
2575 raise CSTValidationError("Cannot have parens around names in NameItem.")
2576
2577 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "NameItem":
2578 return NameItem(
2579 name=visit_required(self, "name", self.name, visitor),
2580 comma=visit_sentinel(self, "comma", self.comma, visitor),
2581 )
2582
2583 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
2584 with state.record_syntactic_position(self):
2585 self.name._codegen(state)
2586
2587 comma = self.comma
2588 if comma is MaybeSentinel.DEFAULT and default_comma:
2589 state.add_token(", ")
2590 elif isinstance(comma, Comma):
2591 comma._codegen(state)
2592
2593
2594@add_slots
2595@dataclass(frozen=True)
2596class Global(BaseSmallStatement):
2597 """
2598 A ``global`` statement.
2599 """
2600
2601 #: A list of one or more names.
2602 names: Sequence[NameItem]
2603
2604 #: Whitespace appearing after the ``global`` keyword and before the first name.
2605 whitespace_after_global: SimpleWhitespace = SimpleWhitespace.field(" ")
2606
2607 #: Optional semicolon when this is used in a statement line. This semicolon
2608 #: owns the whitespace on both sides of it when it is used.
2609 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2610
2611 def _validate(self) -> None:
2612 if len(self.names) == 0:
2613 raise CSTValidationError(
2614 "A Global statement must have at least one NameItem."
2615 )
2616 if self.names[-1].comma != MaybeSentinel.DEFAULT:
2617 raise CSTValidationError(
2618 "The last NameItem in a Global cannot have a trailing comma."
2619 )
2620 if self.whitespace_after_global.empty:
2621 raise CSTValidationError(
2622 "Must have at least one space after 'global' keyword."
2623 )
2624
2625 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Global":
2626 return Global(
2627 whitespace_after_global=visit_required(
2628 self, "whitespace_after_global", self.whitespace_after_global, visitor
2629 ),
2630 names=visit_sequence(self, "names", self.names, visitor),
2631 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2632 )
2633
2634 def _codegen_impl(
2635 self, state: CodegenState, default_semicolon: bool = False
2636 ) -> None:
2637 with state.record_syntactic_position(self):
2638 state.add_token("global")
2639 self.whitespace_after_global._codegen(state)
2640 last_name = len(self.names) - 1
2641 for i, name in enumerate(self.names):
2642 name._codegen(state, default_comma=(i != last_name))
2643
2644 semicolon = self.semicolon
2645 if isinstance(semicolon, MaybeSentinel):
2646 if default_semicolon:
2647 state.add_token("; ")
2648 elif isinstance(semicolon, Semicolon):
2649 semicolon._codegen(state)
2650
2651
2652@add_slots
2653@dataclass(frozen=True)
2654class Nonlocal(BaseSmallStatement):
2655 """
2656 A ``nonlocal`` statement.
2657 """
2658
2659 #: A list of one or more names.
2660 names: Sequence[NameItem]
2661
2662 #: Whitespace appearing after the ``global`` keyword and before the first name.
2663 whitespace_after_nonlocal: SimpleWhitespace = SimpleWhitespace.field(" ")
2664
2665 #: Optional semicolon when this is used in a statement line. This semicolon
2666 #: owns the whitespace on both sides of it when it is used.
2667 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
2668
2669 def _validate(self) -> None:
2670 if len(self.names) == 0:
2671 raise CSTValidationError(
2672 "A Nonlocal statement must have at least one NameItem."
2673 )
2674 if self.names[-1].comma != MaybeSentinel.DEFAULT:
2675 raise CSTValidationError(
2676 "The last NameItem in a Nonlocal cannot have a trailing comma."
2677 )
2678 if self.whitespace_after_nonlocal.empty:
2679 raise CSTValidationError(
2680 "Must have at least one space after 'nonlocal' keyword."
2681 )
2682
2683 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Nonlocal":
2684 return Nonlocal(
2685 whitespace_after_nonlocal=visit_required(
2686 self,
2687 "whitespace_after_nonlocal",
2688 self.whitespace_after_nonlocal,
2689 visitor,
2690 ),
2691 names=visit_sequence(self, "names", self.names, visitor),
2692 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
2693 )
2694
2695 def _codegen_impl(
2696 self, state: CodegenState, default_semicolon: bool = False
2697 ) -> None:
2698 with state.record_syntactic_position(self):
2699 state.add_token("nonlocal")
2700 self.whitespace_after_nonlocal._codegen(state)
2701 last_name = len(self.names) - 1
2702 for i, name in enumerate(self.names):
2703 name._codegen(state, default_comma=(i != last_name))
2704
2705 semicolon = self.semicolon
2706 if isinstance(semicolon, MaybeSentinel):
2707 if default_semicolon:
2708 state.add_token("; ")
2709 elif isinstance(semicolon, Semicolon):
2710 semicolon._codegen(state)
2711
2712
2713class MatchPattern(_BaseParenthesizedNode, ABC):
2714 """
2715 A base class for anything that can appear as a pattern in a :class:`Match`
2716 statement.
2717 """
2718
2719 __slots__ = ()
2720
2721
2722@add_slots
2723@dataclass(frozen=True)
2724# pyre-fixme[13]: Attribute `body` is never initialized.
2725class Match(BaseCompoundStatement):
2726 """
2727 A ``match`` statement.
2728 """
2729
2730 #: The subject of the match.
2731 subject: BaseExpression
2732
2733 #: A non-empty list of match cases.
2734 cases: Sequence["MatchCase"]
2735
2736 #: Sequence of empty lines appearing before this compound statement line.
2737 leading_lines: Sequence[EmptyLine] = ()
2738
2739 #: Whitespace between the ``match`` keyword and the subject.
2740 whitespace_after_match: SimpleWhitespace = SimpleWhitespace.field(" ")
2741
2742 #: Whitespace after the subject but before the colon.
2743 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2744
2745 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
2746 whitespace_after_colon: TrailingWhitespace = TrailingWhitespace.field()
2747
2748 #: A string represents a specific indentation. A ``None`` value uses the modules's
2749 #: default indentation. This is included because indentation is allowed to be
2750 #: inconsistent across a file, just not ambiguously.
2751 indent: Optional[str] = None
2752
2753 #: Any trailing comments or lines after the dedent that are owned by this match
2754 #: block. Statements own preceeding and same-line trailing comments, but not
2755 #: trailing lines, so it falls on :class:`Match` to own it. In the case
2756 #: that a statement follows a :class:`Match` block, that statement will own the
2757 #: comments and lines that are at the same indent as the statement, and this
2758 #: :class:`Match` will own the comments and lines that are indented further.
2759 footer: Sequence[EmptyLine] = ()
2760
2761 def _validate(self) -> None:
2762 if len(self.cases) == 0:
2763 raise CSTValidationError("A match statement must have at least one case.")
2764
2765 indent = self.indent
2766 if indent is not None:
2767 if len(indent) == 0:
2768 raise CSTValidationError(
2769 "A match statement must have a non-zero width indent."
2770 )
2771 if _INDENT_WHITESPACE_RE.fullmatch(indent) is None:
2772 raise CSTValidationError(
2773 "An indent must be composed of only whitespace characters."
2774 )
2775
2776 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Match":
2777 return Match(
2778 leading_lines=visit_sequence(
2779 self, "leading_lines", self.leading_lines, visitor
2780 ),
2781 whitespace_after_match=visit_required(
2782 self, "whitespace_after_match", self.whitespace_after_match, visitor
2783 ),
2784 subject=visit_required(self, "subject", self.subject, visitor),
2785 whitespace_before_colon=visit_required(
2786 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2787 ),
2788 whitespace_after_colon=visit_required(
2789 self, "whitespace_after_colon", self.whitespace_after_colon, visitor
2790 ),
2791 indent=self.indent,
2792 cases=visit_sequence(self, "cases", self.cases, visitor),
2793 footer=visit_sequence(self, "footer", self.footer, visitor),
2794 )
2795
2796 def _codegen_impl(self, state: CodegenState) -> None:
2797 for ll in self.leading_lines:
2798 ll._codegen(state)
2799 state.add_indent_tokens()
2800
2801 with state.record_syntactic_position(self, end_node=self.cases[-1]):
2802 state.add_token("match")
2803 self.whitespace_after_match._codegen(state)
2804 self.subject._codegen(state)
2805 self.whitespace_before_colon._codegen(state)
2806 state.add_token(":")
2807 self.whitespace_after_colon._codegen(state)
2808
2809 indent = self.indent
2810 state.increase_indent(state.default_indent if indent is None else indent)
2811 for c in self.cases:
2812 c._codegen(state)
2813
2814 for f in self.footer:
2815 f._codegen(state)
2816
2817 state.decrease_indent()
2818
2819
2820@add_slots
2821@dataclass(frozen=True)
2822class MatchCase(CSTNode):
2823 """
2824 A single ``case`` block of a :class:`Match` statement.
2825 """
2826
2827 #: The pattern that ``subject`` will be matched against.
2828 pattern: MatchPattern
2829
2830 #: The body of this case block, to be evaluated if ``pattern`` matches ``subject``
2831 #: and ``guard`` evaluates to a truthy value.
2832 body: BaseSuite
2833
2834 #: Optional expression that will be evaluated if ``pattern`` matches ``subject``.
2835 guard: Optional[BaseExpression] = None
2836
2837 #: Sequence of empty lines appearing before this case block.
2838 leading_lines: Sequence[EmptyLine] = ()
2839
2840 #: Whitespace directly after the ``case`` keyword.
2841 whitespace_after_case: SimpleWhitespace = SimpleWhitespace.field(" ")
2842
2843 #: Whitespace before the ``if`` keyword in case there's a guard expression.
2844 whitespace_before_if: SimpleWhitespace = SimpleWhitespace.field("")
2845
2846 #: Whitespace after the ``if`` keyword in case there's a guard expression.
2847 whitespace_after_if: SimpleWhitespace = SimpleWhitespace.field("")
2848
2849 #: Whitespace before the colon.
2850 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
2851
2852 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
2853 return MatchCase(
2854 leading_lines=visit_sequence(
2855 self, "leading_lines", self.leading_lines, visitor
2856 ),
2857 whitespace_after_case=visit_required(
2858 self, "whitespace_after_case", self.whitespace_after_case, visitor
2859 ),
2860 pattern=visit_required(self, "pattern", self.pattern, visitor),
2861 whitespace_before_if=visit_required(
2862 self, "whitespace_before_if", self.whitespace_before_if, visitor
2863 ),
2864 whitespace_after_if=visit_required(
2865 self, "whitespace_after_if", self.whitespace_after_if, visitor
2866 ),
2867 guard=visit_optional(self, "guard", self.guard, visitor),
2868 whitespace_before_colon=visit_required(
2869 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
2870 ),
2871 body=visit_required(self, "body", self.body, visitor),
2872 )
2873
2874 def _codegen_impl(self, state: CodegenState) -> None:
2875 for ll in self.leading_lines:
2876 ll._codegen(state)
2877 state.add_indent_tokens()
2878 with state.record_syntactic_position(self, end_node=self.body):
2879 state.add_token("case")
2880 self.whitespace_after_case._codegen(state)
2881 self.pattern._codegen(state)
2882
2883 guard = self.guard
2884 if guard is not None:
2885 self.whitespace_before_if._codegen(state)
2886 state.add_token("if")
2887 self.whitespace_after_if._codegen(state)
2888 guard._codegen(state)
2889 else:
2890 self.whitespace_before_if._codegen(state)
2891 self.whitespace_after_if._codegen(state)
2892
2893 self.whitespace_before_colon._codegen(state)
2894 state.add_token(":")
2895 self.body._codegen(state)
2896
2897
2898@add_slots
2899@dataclass(frozen=True)
2900class MatchValue(MatchPattern):
2901 """
2902 A match literal or value pattern that compares by equality.
2903 """
2904
2905 #: an expression to compare to
2906 value: BaseExpression
2907
2908 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
2909 return MatchValue(value=visit_required(self, "value", self.value, visitor))
2910
2911 def _codegen_impl(self, state: CodegenState) -> None:
2912 with state.record_syntactic_position(self):
2913 self.value._codegen(state)
2914
2915 @property
2916 def lpar(self) -> Sequence[LeftParen]:
2917 return self.value.lpar
2918
2919 @lpar.setter
2920 def lpar(self, value: Sequence[LeftParen]) -> None:
2921 self.value.lpar = value
2922
2923 @property
2924 def rpar(self) -> Sequence[RightParen]:
2925 return self.value.rpar
2926
2927 @rpar.setter
2928 def rpar(self, value: Sequence[RightParen]) -> None:
2929 self.value.rpar = value
2930
2931
2932@add_slots
2933@dataclass(frozen=True)
2934class MatchSingleton(MatchPattern):
2935 """
2936 A match literal pattern that compares by identity.
2937 """
2938
2939 #: a literal to compare to
2940 value: Name
2941
2942 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
2943 return MatchSingleton(value=visit_required(self, "value", self.value, visitor))
2944
2945 def _validate(self) -> None:
2946 if self.value.value not in {"True", "False", "None"}:
2947 raise CSTValidationError(
2948 "A match singleton can only be True, False, or None"
2949 )
2950
2951 def _codegen_impl(self, state: CodegenState) -> None:
2952 with state.record_syntactic_position(self):
2953 self.value._codegen(state)
2954
2955 @property
2956 def lpar(self) -> Sequence[LeftParen]:
2957 return self.value.lpar
2958
2959 @lpar.setter
2960 def lpar(self, value: Sequence[LeftParen]) -> None:
2961 # pyre-fixme[41]: Cannot reassign final attribute `lpar`.
2962 self.value.lpar = value
2963
2964 @property
2965 def rpar(self) -> Sequence[RightParen]:
2966 return self.value.rpar
2967
2968 @rpar.setter
2969 def rpar(self, value: Sequence[RightParen]) -> None:
2970 # pyre-fixme[41]: Cannot reassign final attribute `rpar`.
2971 self.value.rpar = value
2972
2973
2974@add_slots
2975@dataclass(frozen=True)
2976class MatchSequenceElement(CSTNode):
2977 """
2978 An element in a sequence match pattern.
2979 """
2980
2981 value: MatchPattern
2982
2983 #: An optional trailing comma.
2984 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
2985
2986 def _visit_and_replace_children(
2987 self, visitor: CSTVisitorT
2988 ) -> "MatchSequenceElement":
2989 return MatchSequenceElement(
2990 value=visit_required(self, "value", self.value, visitor),
2991 comma=visit_sentinel(self, "comma", self.comma, visitor),
2992 )
2993
2994 def _codegen_impl(
2995 self,
2996 state: CodegenState,
2997 default_comma: bool = False,
2998 default_comma_whitespace: bool = True,
2999 ) -> None:
3000 with state.record_syntactic_position(self):
3001 self.value._codegen(state)
3002 comma = self.comma
3003 if comma is MaybeSentinel.DEFAULT and default_comma:
3004 state.add_token(", " if default_comma_whitespace else ",")
3005 elif isinstance(comma, Comma):
3006 comma._codegen(state)
3007
3008
3009@add_slots
3010@dataclass(frozen=True)
3011class MatchStar(CSTNode):
3012 """
3013 A starred element in a sequence match pattern. Matches the rest of the sequence.
3014 """
3015
3016 #: The name of the pattern binding. A ``None`` value represents ``*_``.
3017 name: Optional[Name] = None
3018
3019 #: An optional trailing comma.
3020 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3021
3022 #: Optional whitespace between the star and the name.
3023 whitespace_before_name: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3024
3025 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchStar":
3026 return MatchStar(
3027 whitespace_before_name=visit_required(
3028 self, "whitespace_before_name", self.whitespace_before_name, visitor
3029 ),
3030 name=visit_optional(self, "name", self.name, visitor),
3031 comma=visit_sentinel(self, "comma", self.comma, visitor),
3032 )
3033
3034 def _codegen_impl(
3035 self,
3036 state: CodegenState,
3037 default_comma: bool = False,
3038 default_comma_whitespace: bool = True,
3039 ) -> None:
3040 with state.record_syntactic_position(self):
3041 state.add_token("*")
3042 self.whitespace_before_name._codegen(state)
3043 name = self.name
3044 if name is None:
3045 state.add_token("_")
3046 else:
3047 name._codegen(state)
3048 comma = self.comma
3049 if comma is MaybeSentinel.DEFAULT and default_comma:
3050 state.add_token(", " if default_comma_whitespace else ",")
3051 elif isinstance(comma, Comma):
3052 comma._codegen(state)
3053
3054
3055class MatchSequence(MatchPattern, ABC):
3056 """
3057 A match sequence pattern. It's either a :class:`MatchList` or a :class:`MatchTuple`.
3058 Matches a variable length sequence if one of the patterns is a :class:`MatchStar`,
3059 otherwise matches a fixed length sequence.
3060 """
3061
3062 __slots__ = ()
3063
3064 #: Patterns to be matched against the subject elements if it is a sequence.
3065 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3066
3067
3068@add_slots
3069@dataclass(frozen=True)
3070class MatchList(MatchSequence):
3071 """
3072 A list match pattern. It's either an "open sequence pattern" (without brackets) or a
3073 regular list literal (with brackets).
3074 """
3075
3076 #: Patterns to be matched against the subject elements if it is a sequence.
3077 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3078
3079 #: An optional left bracket. If missing, this is an open sequence pattern.
3080 lbracket: Optional[LeftSquareBracket] = None
3081
3082 #: An optional left bracket. If missing, this is an open sequence pattern.
3083 rbracket: Optional[RightSquareBracket] = None
3084
3085 #: Parenthesis at the beginning of the node
3086 lpar: Sequence[LeftParen] = ()
3087 #: Parentheses after the pattern, but before a comma (if there is one).
3088 rpar: Sequence[RightParen] = ()
3089
3090 def _validate(self) -> None:
3091 if self.lbracket and not self.rbracket:
3092 raise CSTValidationError("Cannot have left bracket without right bracket")
3093 if self.rbracket and not self.lbracket:
3094 raise CSTValidationError("Cannot have right bracket without left bracket")
3095
3096 if not self.patterns and not self.lbracket:
3097 raise CSTValidationError(
3098 "Must have brackets if matching against empty list"
3099 )
3100
3101 super(MatchList, self)._validate()
3102
3103 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchList":
3104 return MatchList(
3105 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3106 lbracket=visit_optional(self, "lbracket", self.lbracket, visitor),
3107 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3108 rbracket=visit_optional(self, "rbracket", self.rbracket, visitor),
3109 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3110 )
3111
3112 def _codegen_impl(self, state: CodegenState) -> None:
3113 with self._parenthesize(state):
3114 lbracket = self.lbracket
3115 if lbracket is not None:
3116 lbracket._codegen(state)
3117 pats = self.patterns
3118 for idx, pat in enumerate(pats):
3119 pat._codegen(state, default_comma=(idx < len(pats) - 1))
3120 rbracket = self.rbracket
3121 if rbracket is not None:
3122 rbracket._codegen(state)
3123
3124
3125@add_slots
3126@dataclass(frozen=True)
3127class MatchTuple(MatchSequence):
3128 """
3129 A tuple match pattern.
3130 """
3131
3132 #: Patterns to be matched against the subject elements if it is a sequence.
3133 patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
3134
3135 #: Parenthesis at the beginning of the node
3136 lpar: Sequence[LeftParen] = field(default_factory=lambda: (LeftParen(),))
3137 #: Parentheses after the pattern, but before a comma (if there is one).
3138 rpar: Sequence[RightParen] = field(default_factory=lambda: (RightParen(),))
3139
3140 def _validate(self) -> None:
3141 if len(self.lpar) < 1:
3142 raise CSTValidationError(
3143 "Tuple patterns must have at least pair of parenthesis"
3144 )
3145
3146 super(MatchTuple, self)._validate()
3147
3148 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchTuple":
3149 return MatchTuple(
3150 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3151 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3152 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3153 )
3154
3155 def _codegen_impl(self, state: CodegenState) -> None:
3156 with self._parenthesize(state):
3157 pats = self.patterns
3158 patlen = len(pats)
3159 for idx, pat in enumerate(pats):
3160 pat._codegen(
3161 state,
3162 default_comma=patlen == 1 or (idx < patlen - 1),
3163 default_comma_whitespace=patlen != 1,
3164 )
3165
3166
3167@add_slots
3168@dataclass(frozen=True)
3169class MatchMappingElement(CSTNode):
3170 """
3171 A ``key: value`` pair in a match mapping pattern.
3172 """
3173
3174 key: BaseExpression
3175
3176 #: The pattern to be matched corresponding to ``key``.
3177 pattern: MatchPattern
3178
3179 #: An optional trailing comma.
3180 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3181
3182 #: Whitespace between ``key`` and the colon.
3183 whitespace_before_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3184
3185 #: Whitespace between the colon and ``pattern``.
3186 whitespace_after_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
3187
3188 def _visit_and_replace_children(
3189 self, visitor: CSTVisitorT
3190 ) -> "MatchMappingElement":
3191 return MatchMappingElement(
3192 key=visit_required(self, "key", self.key, visitor),
3193 whitespace_before_colon=visit_required(
3194 self, "whitespace_before_colon", self.whitespace_before_colon, visitor
3195 ),
3196 whitespace_after_colon=visit_required(
3197 self, "whitespace_after_colon", self.whitespace_after_colon, visitor
3198 ),
3199 pattern=visit_required(self, "pattern", self.pattern, visitor),
3200 comma=visit_sentinel(self, "comma", self.comma, visitor),
3201 )
3202
3203 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
3204 with state.record_syntactic_position(self):
3205 self.key._codegen(state)
3206 self.whitespace_before_colon._codegen(state)
3207 state.add_token(":")
3208 self.whitespace_after_colon._codegen(state)
3209 self.pattern._codegen(state)
3210 comma = self.comma
3211 if comma is MaybeSentinel.DEFAULT and default_comma:
3212 state.add_token(", ")
3213 elif isinstance(comma, Comma):
3214 comma._codegen(state)
3215
3216
3217@add_slots
3218@dataclass(frozen=True)
3219class MatchMapping(MatchPattern):
3220 """
3221 A match mapping pattern.
3222 """
3223
3224 #: A sequence of mapping elements.
3225 elements: Sequence[MatchMappingElement] = ()
3226
3227 #: Left curly brace at the beginning of the pattern.
3228 lbrace: LeftCurlyBrace = LeftCurlyBrace.field()
3229
3230 #: Right curly brace at the end of the pattern.
3231 rbrace: RightCurlyBrace = RightCurlyBrace.field()
3232
3233 #: An optional name to capture the remaining elements of the mapping.
3234 rest: Optional[Name] = None
3235
3236 #: Optional whitespace between stars and ``rest``.
3237 whitespace_before_rest: SimpleWhitespace = SimpleWhitespace.field("")
3238
3239 #: An optional trailing comma attached to ``rest``.
3240 trailing_comma: Optional[Comma] = None
3241
3242 #: Parenthesis at the beginning of the node
3243 lpar: Sequence[LeftParen] = ()
3244 #: Parentheses after the pattern
3245 rpar: Sequence[RightParen] = ()
3246
3247 def _validate(self) -> None:
3248 if isinstance(self.trailing_comma, Comma) and self.rest is not None:
3249 raise CSTValidationError("Cannot have a trailing comma without **rest")
3250 super(MatchMapping, self)._validate()
3251
3252 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchMapping":
3253 return MatchMapping(
3254 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3255 lbrace=visit_required(self, "lbrace", self.lbrace, visitor),
3256 elements=visit_sequence(self, "elements", self.elements, visitor),
3257 whitespace_before_rest=visit_required(
3258 self, "whitespace_before_rest", self.whitespace_before_rest, visitor
3259 ),
3260 rest=visit_optional(self, "rest", self.rest, visitor),
3261 trailing_comma=visit_optional(
3262 self, "trailing_comma", self.trailing_comma, visitor
3263 ),
3264 rbrace=visit_required(self, "rbrace", self.rbrace, visitor),
3265 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3266 )
3267
3268 def _codegen_impl(self, state: CodegenState) -> None:
3269 with self._parenthesize(state):
3270 self.lbrace._codegen(state)
3271 elems = self.elements
3272 rest = self.rest
3273 for idx, el in enumerate(elems):
3274 el._codegen(
3275 state, default_comma=rest is not None or idx < len(elems) - 1
3276 )
3277
3278 if rest is not None:
3279 state.add_token("**")
3280 self.whitespace_before_rest._codegen(state)
3281 rest._codegen(state)
3282 comma = self.trailing_comma
3283 if comma is not None:
3284 comma._codegen(state)
3285
3286 self.rbrace._codegen(state)
3287
3288
3289@add_slots
3290@dataclass(frozen=True)
3291class MatchKeywordElement(CSTNode):
3292 """
3293 A key=value pair in a :class:`MatchClass`.
3294 """
3295
3296 key: Name
3297
3298 #: The pattern to be matched against the attribute named ``key``.
3299 pattern: MatchPattern
3300
3301 #: An optional trailing comma.
3302 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3303
3304 #: Whitespace between ``key`` and the equals sign.
3305 whitespace_before_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3306
3307 #: Whitespace between the equals sign and ``pattern``.
3308 whitespace_after_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3309
3310 def _visit_and_replace_children(
3311 self, visitor: CSTVisitorT
3312 ) -> "MatchKeywordElement":
3313 return MatchKeywordElement(
3314 key=visit_required(self, "key", self.key, visitor),
3315 whitespace_before_equal=visit_required(
3316 self, "whitespace_before_equal", self.whitespace_before_equal, visitor
3317 ),
3318 whitespace_after_equal=visit_required(
3319 self, "whitespace_after_equal", self.whitespace_after_equal, visitor
3320 ),
3321 pattern=visit_required(self, "pattern", self.pattern, visitor),
3322 comma=visit_sentinel(self, "comma", self.comma, visitor),
3323 )
3324
3325 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
3326 with state.record_syntactic_position(self):
3327 self.key._codegen(state)
3328 self.whitespace_before_equal._codegen(state)
3329 state.add_token("=")
3330 self.whitespace_after_equal._codegen(state)
3331 self.pattern._codegen(state)
3332 comma = self.comma
3333 if comma is MaybeSentinel.DEFAULT and default_comma:
3334 state.add_token(", ")
3335 elif isinstance(comma, Comma):
3336 comma._codegen(state)
3337
3338
3339@add_slots
3340@dataclass(frozen=True)
3341class MatchClass(MatchPattern):
3342 """
3343 A match class pattern.
3344 """
3345
3346 #: An expression giving the nominal class to be matched.
3347 cls: BaseExpression
3348
3349 #: A sequence of patterns to be matched against the class defined sequence of
3350 #: pattern matching attributes.
3351 patterns: Sequence[MatchSequenceElement] = ()
3352
3353 #: A sequence of additional attribute names and corresponding patterns to be
3354 #: matched.
3355 kwds: Sequence[MatchKeywordElement] = ()
3356
3357 #: Whitespace between the class name and the left parenthesis.
3358 whitespace_after_cls: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3359
3360 #: Whitespace between the left parenthesis and the first pattern.
3361 whitespace_before_patterns: BaseParenthesizableWhitespace = SimpleWhitespace.field(
3362 ""
3363 )
3364
3365 #: Whitespace between the last pattern and the right parenthesis.
3366 whitespace_after_kwds: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
3367
3368 #: Parenthesis at the beginning of the node
3369 lpar: Sequence[LeftParen] = ()
3370 #: Parentheses after the pattern
3371 rpar: Sequence[RightParen] = ()
3372
3373 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchClass":
3374 return MatchClass(
3375 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3376 cls=visit_required(self, "cls", self.cls, visitor),
3377 whitespace_after_cls=visit_required(
3378 self, "whitespace_after_cls", self.whitespace_after_cls, visitor
3379 ),
3380 whitespace_before_patterns=visit_required(
3381 self,
3382 "whitespace_before_patterns",
3383 self.whitespace_before_patterns,
3384 visitor,
3385 ),
3386 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3387 kwds=visit_sequence(self, "kwds", self.kwds, visitor),
3388 whitespace_after_kwds=visit_required(
3389 self, "whitespace_after_kwds", self.whitespace_after_kwds, visitor
3390 ),
3391 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3392 )
3393
3394 def _codegen_impl(self, state: CodegenState) -> None:
3395 with self._parenthesize(state):
3396 self.cls._codegen(state)
3397 self.whitespace_after_cls._codegen(state)
3398 state.add_token("(")
3399 self.whitespace_before_patterns._codegen(state)
3400 pats = self.patterns
3401 kwds = self.kwds
3402 for idx, pat in enumerate(pats):
3403 pat._codegen(state, default_comma=idx + 1 < len(pats) + len(kwds))
3404 for idx, kwd in enumerate(kwds):
3405 kwd._codegen(state, default_comma=idx + 1 < len(kwds))
3406 self.whitespace_after_kwds._codegen(state)
3407 state.add_token(")")
3408
3409
3410@add_slots
3411@dataclass(frozen=True)
3412class MatchAs(MatchPattern):
3413 """
3414 A match "as-pattern", capture pattern, or wildcard pattern.
3415 """
3416
3417 #: The match pattern that the subject will be matched against. If this is ``None``,
3418 #: the node represents a capture pattern (i.e. a bare name) and will always succeed.
3419 pattern: Optional[MatchPattern] = None
3420
3421 #: The name that will be bound if the pattern is successful. If this is ``None``,
3422 #: ``pattern`` must also be ``None`` and the node represents the wildcard pattern
3423 #: (i.e. ``_``).
3424 name: Optional[Name] = None
3425
3426 #: Whitespace between ``pattern`` and the ``as`` keyword (if ``pattern`` is not
3427 #: ``None``)
3428 whitespace_before_as: Union[BaseParenthesizableWhitespace, MaybeSentinel] = (
3429 MaybeSentinel.DEFAULT
3430 )
3431
3432 #: Whitespace between the ``as`` keyword and ``name`` (if ``pattern`` is not
3433 #: ``None``)
3434 whitespace_after_as: Union[BaseParenthesizableWhitespace, MaybeSentinel] = (
3435 MaybeSentinel.DEFAULT
3436 )
3437
3438 #: Parenthesis at the beginning of the node
3439 lpar: Sequence[LeftParen] = ()
3440 #: Parentheses after the pattern
3441 rpar: Sequence[RightParen] = ()
3442
3443 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchAs":
3444 return MatchAs(
3445 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3446 pattern=visit_optional(self, "pattern", self.pattern, visitor),
3447 whitespace_before_as=visit_sentinel(
3448 self, "whitespace_before_as", self.whitespace_before_as, visitor
3449 ),
3450 whitespace_after_as=visit_sentinel(
3451 self, "whitespace_after_as", self.whitespace_after_as, visitor
3452 ),
3453 name=visit_optional(self, "name", self.name, visitor),
3454 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3455 )
3456
3457 def _validate(self) -> None:
3458 if self.name is None and self.pattern is not None:
3459 raise CSTValidationError("Pattern must be None if name is None")
3460 super(MatchAs, self)._validate()
3461
3462 def _codegen_impl(self, state: CodegenState) -> None:
3463 with self._parenthesize(state):
3464 pat = self.pattern
3465 name = self.name
3466 if pat is not None:
3467 pat._codegen(state)
3468 ws_before = self.whitespace_before_as
3469 if ws_before is MaybeSentinel.DEFAULT:
3470 state.add_token(" ")
3471 elif isinstance(ws_before, BaseParenthesizableWhitespace):
3472 ws_before._codegen(state)
3473 state.add_token("as")
3474 ws_after = self.whitespace_after_as
3475 if ws_after is MaybeSentinel.DEFAULT:
3476 state.add_token(" ")
3477 elif isinstance(ws_after, BaseParenthesizableWhitespace):
3478 ws_after._codegen(state)
3479 else:
3480 ws_before = self.whitespace_before_as
3481 if isinstance(ws_before, BaseParenthesizableWhitespace):
3482 ws_before._codegen(state)
3483 ws_after = self.whitespace_after_as
3484 if isinstance(ws_after, BaseParenthesizableWhitespace):
3485 ws_after._codegen(state)
3486 if name is None:
3487 state.add_token("_")
3488 else:
3489 name._codegen(state)
3490
3491
3492@add_slots
3493@dataclass(frozen=True)
3494class MatchOrElement(CSTNode):
3495 """
3496 An element in a :class:`MatchOr` node.
3497 """
3498
3499 pattern: MatchPattern
3500
3501 #: An optional ``|`` separator.
3502 separator: Union[BitOr, MaybeSentinel] = MaybeSentinel.DEFAULT
3503
3504 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOrElement":
3505 return MatchOrElement(
3506 pattern=visit_required(self, "pattern", self.pattern, visitor),
3507 separator=visit_sentinel(self, "separator", self.separator, visitor),
3508 )
3509
3510 def _codegen_impl(
3511 self, state: CodegenState, default_separator: bool = False
3512 ) -> None:
3513 with state.record_syntactic_position(self):
3514 self.pattern._codegen(state)
3515 sep = self.separator
3516 if sep is MaybeSentinel.DEFAULT and default_separator:
3517 state.add_token(" | ")
3518 elif isinstance(sep, BitOr):
3519 sep._codegen(state)
3520
3521
3522@add_slots
3523@dataclass(frozen=True)
3524class MatchOr(MatchPattern):
3525 """
3526 A match "or-pattern". It matches each of its subpatterns in turn to the subject,
3527 until one succeeds. The or-pattern is then deemed to succeed. If none of the
3528 subpatterns succeed the or-pattern fails.
3529 """
3530
3531 #: The subpatterns to be tried in turn.
3532 patterns: Sequence[MatchOrElement]
3533
3534 #: Parenthesis at the beginning of the node
3535 lpar: Sequence[LeftParen] = ()
3536 #: Parentheses after the pattern
3537 rpar: Sequence[RightParen] = ()
3538
3539 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOr":
3540 return MatchOr(
3541 lpar=visit_sequence(self, "lpar", self.lpar, visitor),
3542 patterns=visit_sequence(self, "patterns", self.patterns, visitor),
3543 rpar=visit_sequence(self, "rpar", self.rpar, visitor),
3544 )
3545
3546 def _codegen_impl(self, state: CodegenState) -> None:
3547 with self._parenthesize(state):
3548 pats = self.patterns
3549 for idx, pat in enumerate(pats):
3550 pat._codegen(state, default_separator=idx + 1 < len(pats))
3551
3552
3553@add_slots
3554@dataclass(frozen=True)
3555class TypeVar(CSTNode):
3556 """
3557 A simple (non-variadic) type variable.
3558
3559 Note: this node represents type a variable when declared using PEP-695 syntax.
3560 """
3561
3562 #: The name of the type variable.
3563 name: Name
3564
3565 #: An optional bound on the type.
3566 bound: Optional[BaseExpression] = None
3567
3568 #: The colon used to separate the name and bound. If not specified,
3569 #: :class:`MaybeSentinel` will be replaced with a colon if there is a bound,
3570 #: otherwise will be left empty.
3571 colon: Union[Colon, MaybeSentinel] = MaybeSentinel.DEFAULT
3572
3573 def _codegen_impl(self, state: CodegenState) -> None:
3574 with state.record_syntactic_position(self):
3575 self.name._codegen(state)
3576 bound = self.bound
3577 colon = self.colon
3578 if not isinstance(colon, MaybeSentinel):
3579 colon._codegen(state)
3580 else:
3581 if bound is not None:
3582 state.add_token(": ")
3583
3584 if bound is not None:
3585 bound._codegen(state)
3586
3587 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeVar":
3588 return TypeVar(
3589 name=visit_required(self, "name", self.name, visitor),
3590 colon=visit_sentinel(self, "colon", self.colon, visitor),
3591 bound=visit_optional(self, "bound", self.bound, visitor),
3592 )
3593
3594
3595@add_slots
3596@dataclass(frozen=True)
3597class TypeVarTuple(CSTNode):
3598 """
3599 A variadic type variable.
3600 """
3601
3602 #: The name of this type variable.
3603 name: Name
3604
3605 #: The (optional) whitespace between the star declaring this type variable as
3606 #: variadic, and the variable's name.
3607 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("")
3608
3609 def _codegen_impl(self, state: CodegenState) -> None:
3610 with state.record_syntactic_position(self):
3611 state.add_token("*")
3612 self.whitespace_after_star._codegen(state)
3613 self.name._codegen(state)
3614
3615 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeVarTuple":
3616 return TypeVarTuple(
3617 name=visit_required(self, "name", self.name, visitor),
3618 whitespace_after_star=visit_required(
3619 self, "whitespace_after_star", self.whitespace_after_star, visitor
3620 ),
3621 )
3622
3623
3624@add_slots
3625@dataclass(frozen=True)
3626class ParamSpec(CSTNode):
3627 """
3628 A parameter specification.
3629
3630 Note: this node represents a parameter specification when declared using PEP-695
3631 syntax.
3632 """
3633
3634 #: The name of this parameter specification.
3635 name: Name
3636
3637 #: The (optional) whitespace between the double star declaring this type variable as
3638 #: a parameter specification, and the name.
3639 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("")
3640
3641 def _codegen_impl(self, state: CodegenState) -> None:
3642 with state.record_syntactic_position(self):
3643 state.add_token("**")
3644 self.whitespace_after_star._codegen(state)
3645 self.name._codegen(state)
3646
3647 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ParamSpec":
3648 return ParamSpec(
3649 name=visit_required(self, "name", self.name, visitor),
3650 whitespace_after_star=visit_required(
3651 self, "whitespace_after_star", self.whitespace_after_star, visitor
3652 ),
3653 )
3654
3655
3656@add_slots
3657@dataclass(frozen=True)
3658class TypeParam(CSTNode):
3659 """
3660 A single type parameter that is contained in a :class:`TypeParameters` list.
3661 """
3662
3663 #: The actual parameter.
3664 param: Union[TypeVar, TypeVarTuple, ParamSpec]
3665
3666 #: A trailing comma. If one is not provided, :class:`MaybeSentinel` will be replaced
3667 #: with a comma only if a comma is required.
3668 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
3669
3670 #: The equal sign used to denote assignment if there is a default.
3671 equal: Union[AssignEqual, MaybeSentinel] = MaybeSentinel.DEFAULT
3672
3673 #: The star used to denote a variadic default
3674 star: Literal["", "*"] = ""
3675
3676 #: The whitespace between the star and the type.
3677 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("")
3678
3679 #: Any optional default value, used when the argument is not supplied.
3680 default: Optional[BaseExpression] = None
3681
3682 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
3683 self.param._codegen(state)
3684
3685 equal = self.equal
3686 if equal is MaybeSentinel.DEFAULT and self.default is not None:
3687 state.add_token(" = ")
3688 elif isinstance(equal, AssignEqual):
3689 equal._codegen(state)
3690
3691 state.add_token(self.star)
3692 self.whitespace_after_star._codegen(state)
3693
3694 default = self.default
3695 if default is not None:
3696 default._codegen(state)
3697
3698 comma = self.comma
3699 if isinstance(comma, MaybeSentinel):
3700 if default_comma:
3701 state.add_token(", ")
3702 else:
3703 comma._codegen(state)
3704
3705 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeParam":
3706 ret = TypeParam(
3707 param=visit_required(self, "param", self.param, visitor),
3708 equal=visit_sentinel(self, "equal", self.equal, visitor),
3709 star=self.star,
3710 whitespace_after_star=visit_required(
3711 self, "whitespace_after_star", self.whitespace_after_star, visitor
3712 ),
3713 default=visit_optional(self, "default", self.default, visitor),
3714 comma=visit_sentinel(self, "comma", self.comma, visitor),
3715 )
3716 return ret
3717
3718 def _validate(self) -> None:
3719 if self.default is None and isinstance(self.equal, AssignEqual):
3720 raise CSTValidationError(
3721 "Must have a default when specifying an AssignEqual."
3722 )
3723 if self.star and not (self.default or isinstance(self.equal, AssignEqual)):
3724 raise CSTValidationError("Star can only be present if a default")
3725 if isinstance(self.star, str) and self.star not in ("", "*"):
3726 raise CSTValidationError("Must specify either '' or '*' for star.")
3727
3728
3729@add_slots
3730@dataclass(frozen=True)
3731class TypeParameters(CSTNode):
3732 """
3733 Type parameters when specified with PEP-695 syntax.
3734
3735 This node captures all specified parameters that are enclosed with square brackets.
3736 """
3737
3738 #: The parameters within the square brackets.
3739 params: Sequence[TypeParam] = ()
3740
3741 #: Opening square bracket that marks the start of these parameters.
3742 lbracket: LeftSquareBracket = LeftSquareBracket.field()
3743 #: Closing square bracket that marks the end of these parameters.
3744 rbracket: RightSquareBracket = RightSquareBracket.field()
3745
3746 def _codegen_impl(self, state: CodegenState) -> None:
3747 self.lbracket._codegen(state)
3748 params_len = len(self.params)
3749 for idx, param in enumerate(self.params):
3750 param._codegen(state, default_comma=idx + 1 < params_len)
3751 self.rbracket._codegen(state)
3752
3753 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeParameters":
3754 return TypeParameters(
3755 lbracket=visit_required(self, "lbracket", self.lbracket, visitor),
3756 params=visit_sequence(self, "params", self.params, visitor),
3757 rbracket=visit_required(self, "rbracket", self.rbracket, visitor),
3758 )
3759
3760
3761@add_slots
3762@dataclass(frozen=True)
3763class TypeAlias(BaseSmallStatement):
3764 """
3765 A type alias statement.
3766
3767 This node represents the ``type`` statement as specified initially by PEP-695.
3768 Example: ``type ListOrSet[T] = list[T] | set[T]``.
3769 """
3770
3771 #: The name being introduced in this statement.
3772 name: Name
3773
3774 #: Everything on the right hand side of the ``=``.
3775 value: BaseExpression
3776
3777 #: An optional list of type parameters, specified after the name.
3778 type_parameters: Optional[TypeParameters] = None
3779
3780 #: Whitespace between the ``type`` soft keyword and the name.
3781 whitespace_after_type: SimpleWhitespace = SimpleWhitespace.field(" ")
3782
3783 #: Whitespace between the name and the type parameters (if they exist) or the ``=``.
3784 #: If not specified, :class:`MaybeSentinel` will be replaced with a single space if
3785 #: there are no type parameters, otherwise no spaces.
3786 whitespace_after_name: Union[SimpleWhitespace, MaybeSentinel] = (
3787 MaybeSentinel.DEFAULT
3788 )
3789
3790 #: Whitespace between the type parameters and the ``=``. Always empty if there are
3791 #: no type parameters. If not specified, :class:`MaybeSentinel` will be replaced
3792 #: with a single space if there are type parameters.
3793 whitespace_after_type_parameters: Union[SimpleWhitespace, MaybeSentinel] = (
3794 MaybeSentinel.DEFAULT
3795 )
3796
3797 #: Whitespace between the ``=`` and the value.
3798 whitespace_after_equals: SimpleWhitespace = SimpleWhitespace.field(" ")
3799
3800 #: Optional semicolon when this is used in a statement line. This semicolon
3801 #: owns the whitespace on both sides of it when it is used.
3802 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
3803
3804 def _validate(self) -> None:
3805 if (
3806 self.type_parameters is None
3807 and self.whitespace_after_type_parameters
3808 not in {
3809 SimpleWhitespace(""),
3810 MaybeSentinel.DEFAULT,
3811 }
3812 ):
3813 raise CSTValidationError(
3814 "whitespace_after_type_parameters must be empty when there are no type parameters in a TypeAlias"
3815 )
3816
3817 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeAlias":
3818 return TypeAlias(
3819 whitespace_after_type=visit_required(
3820 self, "whitespace_after_type", self.whitespace_after_type, visitor
3821 ),
3822 name=visit_required(self, "name", self.name, visitor),
3823 whitespace_after_name=visit_sentinel(
3824 self, "whitespace_after_name", self.whitespace_after_name, visitor
3825 ),
3826 type_parameters=visit_optional(
3827 self, "type_parameters", self.type_parameters, visitor
3828 ),
3829 whitespace_after_type_parameters=visit_sentinel(
3830 self,
3831 "whitespace_after_type_parameters",
3832 self.whitespace_after_type_parameters,
3833 visitor,
3834 ),
3835 whitespace_after_equals=visit_required(
3836 self, "whitespace_after_equals", self.whitespace_after_equals, visitor
3837 ),
3838 value=visit_required(self, "value", self.value, visitor),
3839 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
3840 )
3841
3842 def _codegen_impl(
3843 self, state: CodegenState, default_semicolon: bool = False
3844 ) -> None:
3845 with state.record_syntactic_position(self):
3846 state.add_token("type")
3847 self.whitespace_after_type._codegen(state)
3848 self.name._codegen(state)
3849 ws_after_name = self.whitespace_after_name
3850 if isinstance(ws_after_name, MaybeSentinel):
3851 if self.type_parameters is None:
3852 state.add_token(" ")
3853 else:
3854 ws_after_name._codegen(state)
3855
3856 ws_after_type_params = self.whitespace_after_type_parameters
3857 if self.type_parameters is not None:
3858 self.type_parameters._codegen(state)
3859 if isinstance(ws_after_type_params, MaybeSentinel):
3860 state.add_token(" ")
3861 else:
3862 ws_after_type_params._codegen(state)
3863
3864 state.add_token("=")
3865 self.whitespace_after_equals._codegen(state)
3866 self.value._codegen(state)
3867
3868 semi = self.semicolon
3869 if isinstance(semi, MaybeSentinel):
3870 if default_semicolon:
3871 state.add_token("; ")
3872 else:
3873 semi._codegen(state)