Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/libcst/_nodes/statement.py: 45%

1520 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 07:09 +0000

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 Optional, Pattern, Sequence, Union 

11 

12from libcst._add_slots import add_slots 

13from libcst._maybe_sentinel import MaybeSentinel 

14from libcst._nodes.base import CSTNode, CSTValidationError 

15from libcst._nodes.expression import ( 

16 _BaseParenthesizedNode, 

17 Annotation, 

18 Arg, 

19 Asynchronous, 

20 Attribute, 

21 BaseAssignTargetExpression, 

22 BaseDelTargetExpression, 

23 BaseExpression, 

24 ConcatenatedString, 

25 ExpressionPosition, 

26 From, 

27 LeftCurlyBrace, 

28 LeftParen, 

29 LeftSquareBracket, 

30 List, 

31 Name, 

32 Parameters, 

33 RightCurlyBrace, 

34 RightParen, 

35 RightSquareBracket, 

36 SimpleString, 

37 Tuple, 

38) 

39from libcst._nodes.internal import ( 

40 CodegenState, 

41 visit_body_sequence, 

42 visit_optional, 

43 visit_required, 

44 visit_sentinel, 

45 visit_sequence, 

46) 

47from libcst._nodes.op import ( 

48 AssignEqual, 

49 BaseAugOp, 

50 BitOr, 

51 Comma, 

52 Dot, 

53 ImportStar, 

54 Semicolon, 

55) 

56from libcst._nodes.whitespace import ( 

57 BaseParenthesizableWhitespace, 

58 EmptyLine, 

59 ParenthesizedWhitespace, 

60 SimpleWhitespace, 

61 TrailingWhitespace, 

62) 

63from libcst._visitors import CSTVisitorT 

64 

65_INDENT_WHITESPACE_RE: Pattern[str] = re.compile(r"[ \f\t]+", re.UNICODE) 

66 

67 

68class BaseSuite(CSTNode, ABC): 

69 """ 

70 A dummy base-class for both :class:`SimpleStatementSuite` and :class:`IndentedBlock`. 

71 This exists to simplify type definitions and isinstance checks. 

72 

73 A suite is a group of statements controlled by a clause. A suite can be one or 

74 more semicolon-separated simple statements on the same line as the header, 

75 following the header’s colon, or it can be one or more indented statements on 

76 subsequent lines. 

77 

78 -- https://docs.python.org/3/reference/compound_stmts.html 

79 """ 

80 

81 __slots__ = () 

82 

83 body: Union[Sequence["BaseStatement"], Sequence["BaseSmallStatement"]] 

84 

85 

86class BaseStatement(CSTNode, ABC): 

87 """ 

88 A class that exists to allow for typing to specify that any statement is allowed 

89 in a particular location. 

90 """ 

91 

92 __slots__ = () 

93 

94 

95class BaseSmallStatement(CSTNode, ABC): 

96 """ 

97 Encapsulates a small statement, like ``del`` or ``pass``, and optionally adds a 

98 trailing semicolon. A small statement is always contained inside a 

99 :class:`SimpleStatementLine` or :class:`SimpleStatementSuite`. This exists to 

100 simplify type definitions and isinstance checks. 

101 """ 

102 

103 __slots__ = () 

104 

105 #: An optional semicolon that appears after a small statement. This is optional 

106 #: for the last small statement in a :class:`SimpleStatementLine` or 

107 #: :class:`SimpleStatementSuite`, but all other small statements inside a simple 

108 #: statement must contain a semicolon to disambiguate multiple small statements 

109 #: on the same line. 

110 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

111 

112 @abstractmethod 

113 def _codegen_impl( 

114 self, state: CodegenState, default_semicolon: bool = False 

115 ) -> None: 

116 ... 

117 

118 

119@add_slots 

120@dataclass(frozen=True) 

121class Del(BaseSmallStatement): 

122 """ 

123 Represents a ``del`` statement. ``del`` is always followed by a target. 

124 """ 

125 

126 #: The target expression will be deleted. This can be a name, a tuple, 

127 #: an item of a list, an item of a dictionary, or an attribute. 

128 target: BaseDelTargetExpression 

129 

130 #: The whitespace after the ``del`` keyword. 

131 whitespace_after_del: SimpleWhitespace = SimpleWhitespace.field(" ") 

132 

133 #: Optional semicolon when this is used in a statement line. This semicolon 

134 #: owns the whitespace on both sides of it when it is used. 

135 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

136 

137 def _validate(self) -> None: 

138 if ( 

139 self.whitespace_after_del.empty 

140 and not self.target._safe_to_use_with_word_operator( 

141 ExpressionPosition.RIGHT 

142 ) 

143 ): 

144 raise CSTValidationError("Must have at least one space after 'del'.") 

145 

146 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Del": 

147 return Del( 

148 target=visit_required(self, "target", self.target, visitor), 

149 whitespace_after_del=visit_required( 

150 self, "whitespace_after_del", self.whitespace_after_del, visitor 

151 ), 

152 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

153 ) 

154 

155 def _codegen_impl( 

156 self, state: CodegenState, default_semicolon: bool = False 

157 ) -> None: 

158 with state.record_syntactic_position(self): 

159 state.add_token("del") 

160 self.whitespace_after_del._codegen(state) 

161 self.target._codegen(state) 

162 

163 semicolon = self.semicolon 

164 if isinstance(semicolon, MaybeSentinel): 

165 if default_semicolon: 

166 state.add_token("; ") 

167 elif isinstance(semicolon, Semicolon): 

168 semicolon._codegen(state) 

169 

170 

171@add_slots 

172@dataclass(frozen=True) 

173class Pass(BaseSmallStatement): 

174 """ 

175 Represents a ``pass`` statement. 

176 """ 

177 

178 #: Optional semicolon when this is used in a statement line. This semicolon 

179 #: owns the whitespace on both sides of it when it is used. 

180 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

181 

182 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Pass": 

183 return Pass( 

184 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor) 

185 ) 

186 

187 def _codegen_impl( 

188 self, state: CodegenState, default_semicolon: bool = False 

189 ) -> None: 

190 with state.record_syntactic_position(self): 

191 state.add_token("pass") 

192 

193 semicolon = self.semicolon 

194 if isinstance(semicolon, MaybeSentinel): 

195 if default_semicolon: 

196 state.add_token("; ") 

197 elif isinstance(semicolon, Semicolon): 

198 semicolon._codegen(state) 

199 

200 

201@add_slots 

202@dataclass(frozen=True) 

203class Break(BaseSmallStatement): 

204 """ 

205 Represents a ``break`` statement, which is used to break out of a :class:`For` 

206 or :class:`While` loop early. 

207 """ 

208 

209 #: Optional semicolon when this is used in a statement line. This semicolon 

210 #: owns the whitespace on both sides of it when it is used. 

211 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

212 

213 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Break": 

214 return Break( 

215 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor) 

216 ) 

217 

218 def _codegen_impl( 

219 self, state: CodegenState, default_semicolon: bool = False 

220 ) -> None: 

221 with state.record_syntactic_position(self): 

222 state.add_token("break") 

223 

224 semicolon = self.semicolon 

225 if isinstance(semicolon, MaybeSentinel): 

226 if default_semicolon: 

227 state.add_token("; ") 

228 elif isinstance(semicolon, Semicolon): 

229 semicolon._codegen(state) 

230 

231 

232@add_slots 

233@dataclass(frozen=True) 

234class Continue(BaseSmallStatement): 

235 """ 

236 Represents a ``continue`` statement, which is used to skip to the next iteration 

237 in a :class:`For` or :class:`While` loop. 

238 """ 

239 

240 #: Optional semicolon when this is used in a statement line. This semicolon 

241 #: owns the whitespace on both sides of it when it is used. 

242 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

243 

244 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Continue": 

245 return Continue( 

246 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor) 

247 ) 

248 

249 def _codegen_impl( 

250 self, state: CodegenState, default_semicolon: bool = False 

251 ) -> None: 

252 with state.record_syntactic_position(self): 

253 state.add_token("continue") 

254 

255 semicolon = self.semicolon 

256 if isinstance(semicolon, MaybeSentinel): 

257 if default_semicolon: 

258 state.add_token("; ") 

259 elif isinstance(semicolon, Semicolon): 

260 semicolon._codegen(state) 

261 

262 

263@add_slots 

264@dataclass(frozen=True) 

265class Return(BaseSmallStatement): 

266 """ 

267 Represents a ``return`` or a ``return x`` statement. 

268 """ 

269 

270 #: The optional expression that will be evaluated and returned. 

271 value: Optional[BaseExpression] = None 

272 

273 #: Optional whitespace after the ``return`` keyword before the optional 

274 #: value expression. 

275 whitespace_after_return: Union[ 

276 SimpleWhitespace, MaybeSentinel 

277 ] = MaybeSentinel.DEFAULT 

278 

279 #: Optional semicolon when this is used in a statement line. This semicolon 

280 #: owns the whitespace on both sides of it when it is used. 

281 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

282 

283 def _validate(self) -> None: 

284 value = self.value 

285 if value is not None: 

286 whitespace_after_return = self.whitespace_after_return 

287 has_no_gap = ( 

288 not isinstance(whitespace_after_return, MaybeSentinel) 

289 and whitespace_after_return.empty 

290 ) 

291 if has_no_gap and not value._safe_to_use_with_word_operator( 

292 ExpressionPosition.RIGHT 

293 ): 

294 raise CSTValidationError("Must have at least one space after 'return'.") 

295 

296 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Return": 

297 return Return( 

298 whitespace_after_return=visit_sentinel( 

299 self, "whitespace_after_return", self.whitespace_after_return, visitor 

300 ), 

301 value=visit_optional(self, "value", self.value, visitor), 

302 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

303 ) 

304 

305 def _codegen_impl( 

306 self, state: CodegenState, default_semicolon: bool = False 

307 ) -> None: 

308 with state.record_syntactic_position(self): 

309 state.add_token("return") 

310 whitespace_after_return = self.whitespace_after_return 

311 value = self.value 

312 if isinstance(whitespace_after_return, MaybeSentinel): 

313 if value is not None: 

314 state.add_token(" ") 

315 else: 

316 whitespace_after_return._codegen(state) 

317 if value is not None: 

318 value._codegen(state) 

319 

320 semicolon = self.semicolon 

321 if isinstance(semicolon, MaybeSentinel): 

322 if default_semicolon: 

323 state.add_token("; ") 

324 elif isinstance(semicolon, Semicolon): 

325 semicolon._codegen(state) 

326 

327 

328@add_slots 

329@dataclass(frozen=True) 

330class Expr(BaseSmallStatement): 

331 """ 

332 An expression used as a statement, where the result is unused and unassigned. 

333 The most common place you will find this is in function calls where the return 

334 value is unneeded. 

335 """ 

336 

337 #: The expression itself. Python will evaluate the expression but not assign 

338 #: the result anywhere. 

339 value: BaseExpression 

340 

341 #: Optional semicolon when this is used in a statement line. This semicolon 

342 #: owns the whitespace on both sides of it when it is used. 

343 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

344 

345 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Expr": 

346 return Expr( 

347 value=visit_required(self, "value", self.value, visitor), 

348 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

349 ) 

350 

351 def _codegen_impl( 

352 self, state: CodegenState, default_semicolon: bool = False 

353 ) -> None: 

354 with state.record_syntactic_position(self): 

355 self.value._codegen(state) 

356 

357 semicolon = self.semicolon 

358 if isinstance(semicolon, MaybeSentinel): 

359 if default_semicolon: 

360 state.add_token("; ") 

361 elif isinstance(semicolon, Semicolon): 

362 semicolon._codegen(state) 

363 

364 

365class _BaseSimpleStatement(CSTNode, ABC): 

366 """ 

367 A simple statement is a series of small statements joined together by semicolons. 

368 

369 simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE 

370 

371 Whitespace between each small statement is owned by the small statements themselves. 

372 It can be found on the required semicolon that will be attached to each non-terminal 

373 small statement. 

374 """ 

375 

376 __slots__ = () 

377 

378 #: Sequence of small statements. All but the last statement are required to have 

379 #: a semicolon. 

380 body: Sequence[BaseSmallStatement] 

381 

382 #: Any trailing comment and the final ``NEWLINE``, which is part of small statement's 

383 #: grammar. 

384 trailing_whitespace: TrailingWhitespace 

385 

386 def _validate(self) -> None: 

387 body = self.body 

388 for small_stmt in body[:-1]: 

389 if small_stmt.semicolon is None: 

390 raise CSTValidationError( 

391 "All but the last SmallStatement in a SimpleStatementLine or " 

392 + "SimpleStatementSuite must have a trailing semicolon. Otherwise, " 

393 + "there's no way to syntatically disambiguate each SmallStatement " 

394 + "on the same line." 

395 ) 

396 

397 def _codegen_impl(self, state: CodegenState) -> None: 

398 body = self.body 

399 if body: 

400 laststmt = len(body) - 1 

401 with state.record_syntactic_position(self, end_node=body[laststmt]): 

402 for idx, stmt in enumerate(body): 

403 stmt._codegen(state, default_semicolon=(idx != laststmt)) 

404 else: 

405 # Empty simple statement blocks are not syntactically valid in Python 

406 # unless they contain a 'pass' statement, so add one here. 

407 with state.record_syntactic_position(self): 

408 state.add_token("pass") 

409 

410 self.trailing_whitespace._codegen(state) 

411 

412 

413@add_slots 

414@dataclass(frozen=True) 

415class SimpleStatementLine(_BaseSimpleStatement, BaseStatement): 

416 """ 

417 A simple statement that's part of an IndentedBlock or Module. A simple statement is 

418 a series of small statements joined together by semicolons. 

419 

420 This isn't differentiated from a :class:`SimpleStatementSuite` in the grammar, but 

421 because a :class:`SimpleStatementLine` can own additional whitespace that a 

422 :class:`SimpleStatementSuite` doesn't have, we're differentiating it in the CST. 

423 """ 

424 

425 #: Sequence of small statements. All but the last statement are required to have 

426 #: a semicolon. 

427 body: Sequence[BaseSmallStatement] 

428 

429 #: Sequence of empty lines appearing before this simple statement line. 

430 leading_lines: Sequence[EmptyLine] = () 

431 

432 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line. 

433 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field() 

434 

435 def _visit_and_replace_children( 

436 self, visitor: CSTVisitorT 

437 ) -> "SimpleStatementLine": 

438 return SimpleStatementLine( 

439 leading_lines=visit_sequence( 

440 self, "leading_lines", self.leading_lines, visitor 

441 ), 

442 body=visit_sequence(self, "body", self.body, visitor), 

443 trailing_whitespace=visit_required( 

444 self, "trailing_whitespace", self.trailing_whitespace, visitor 

445 ), 

446 ) 

447 

448 def _is_removable(self) -> bool: 

449 # If we have an empty body, we are removable since we don't represent 

450 # anything concrete. 

451 return not self.body 

452 

453 def _codegen_impl(self, state: CodegenState) -> None: 

454 for ll in self.leading_lines: 

455 ll._codegen(state) 

456 state.add_indent_tokens() 

457 _BaseSimpleStatement._codegen_impl(self, state) 

458 

459 

460@add_slots 

461@dataclass(frozen=True) 

462class SimpleStatementSuite(_BaseSimpleStatement, BaseSuite): 

463 """ 

464 A simple statement that's used as a suite. A simple statement is a series of small 

465 statements joined together by semicolons. A suite is the thing that follows the 

466 colon in a compound statement. 

467 

468 .. code-block:: 

469 

470 if test:<leading_whitespace><body><trailing_whitespace> 

471 

472 This isn't differentiated from a :class:`SimpleStatementLine` in the grammar, but 

473 because the two classes need to track different whitespace, we're differentiating 

474 it in the CST. 

475 """ 

476 

477 #: Sequence of small statements. All but the last statement are required to have 

478 #: a semicolon. 

479 body: Sequence[BaseSmallStatement] 

480 

481 #: The whitespace between the colon in the parent statement and the body. 

482 leading_whitespace: SimpleWhitespace = SimpleWhitespace.field(" ") 

483 

484 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line. 

485 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field() 

486 

487 def _visit_and_replace_children( 

488 self, visitor: CSTVisitorT 

489 ) -> "SimpleStatementSuite": 

490 return SimpleStatementSuite( 

491 leading_whitespace=visit_required( 

492 self, "leading_whitespace", self.leading_whitespace, visitor 

493 ), 

494 body=visit_sequence(self, "body", self.body, visitor), 

495 trailing_whitespace=visit_required( 

496 self, "trailing_whitespace", self.trailing_whitespace, visitor 

497 ), 

498 ) 

499 

500 def _codegen_impl(self, state: CodegenState) -> None: 

501 self.leading_whitespace._codegen(state) 

502 _BaseSimpleStatement._codegen_impl(self, state) 

503 

504 

505@add_slots 

506@dataclass(frozen=True) 

507class Else(CSTNode): 

508 """ 

509 An ``else`` clause that appears optionally after an :class:`If`, :class:`While`, 

510 :class:`Try`, or :class:`For` statement. 

511 

512 This node does not match ``elif`` clauses in :class:`If` statements. It also 

513 does not match the required ``else`` clause in an :class:`IfExp` expression 

514 (``a = if b else c``). 

515 """ 

516 

517 #: The body of else clause. 

518 body: BaseSuite 

519 

520 #: Sequence of empty lines appearing before this compound statement line. 

521 leading_lines: Sequence[EmptyLine] = () 

522 

523 #: The whitespace appearing after the ``else`` keyword but before the colon. 

524 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

525 

526 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Else": 

527 return Else( 

528 leading_lines=visit_sequence( 

529 self, "leading_lines", self.leading_lines, visitor 

530 ), 

531 whitespace_before_colon=visit_required( 

532 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

533 ), 

534 body=visit_required(self, "body", self.body, visitor), 

535 ) 

536 

537 def _codegen_impl(self, state: CodegenState) -> None: 

538 for ll in self.leading_lines: 

539 ll._codegen(state) 

540 state.add_indent_tokens() 

541 

542 with state.record_syntactic_position(self, end_node=self.body): 

543 state.add_token("else") 

544 self.whitespace_before_colon._codegen(state) 

545 state.add_token(":") 

546 self.body._codegen(state) 

547 

548 

549class BaseCompoundStatement(BaseStatement, ABC): 

550 """ 

551 Encapsulates a compound statement, like ``if True: pass`` or ``while True: pass``. 

552 This exists to simplify type definitions and isinstance checks. 

553 

554 Compound statements contain (groups of) other statements; they affect or control 

555 the execution of those other statements in some way. In general, compound 

556 statements span multiple lines, although in simple incarnations a whole compound 

557 statement may be contained in one line. 

558 

559 -- https://docs.python.org/3/reference/compound_stmts.html 

560 """ 

561 

562 __slots__ = () 

563 

564 #: The body of this compound statement. 

565 body: BaseSuite 

566 

567 #: Any empty lines or comments appearing before this statement. 

568 leading_lines: Sequence[EmptyLine] 

569 

570 

571@add_slots 

572@dataclass(frozen=True) 

573class If(BaseCompoundStatement): 

574 """ 

575 An ``if`` statement. ``test`` holds a single test expression. 

576 

577 ``elif`` clauses don’t have a special representation in the AST, but rather appear as 

578 extra :class:`If` nodes within the ``orelse`` section of the previous one. 

579 """ 

580 

581 #: The expression that, when evaluated, should give us a truthy/falsey value. 

582 test: BaseExpression # TODO: should be a test_nocond 

583 

584 #: The body of this compound statement. 

585 body: BaseSuite 

586 

587 #: An optional ``elif`` or ``else`` clause. :class:`If` signifies an ``elif`` block. 

588 #: :class:`Else` signifies an ``else`` block. ``None`` signifies no ``else`` or 

589 #:``elif`` block. 

590 orelse: Union["If", Else, None] = None 

591 

592 #: Sequence of empty lines appearing before this compound statement line. 

593 leading_lines: Sequence[EmptyLine] = () 

594 

595 #: The whitespace appearing after the ``if`` keyword but before the test expression. 

596 whitespace_before_test: SimpleWhitespace = SimpleWhitespace.field(" ") 

597 

598 #: The whitespace appearing after the test expression but before the colon. 

599 whitespace_after_test: SimpleWhitespace = SimpleWhitespace.field("") 

600 

601 # TODO: _validate 

602 

603 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "If": 

604 return If( 

605 leading_lines=visit_sequence( 

606 self, "leading_lines", self.leading_lines, visitor 

607 ), 

608 whitespace_before_test=visit_required( 

609 self, "whitespace_before_test", self.whitespace_before_test, visitor 

610 ), 

611 test=visit_required(self, "test", self.test, visitor), 

612 whitespace_after_test=visit_required( 

613 self, "whitespace_after_test", self.whitespace_after_test, visitor 

614 ), 

615 body=visit_required(self, "body", self.body, visitor), 

616 orelse=visit_optional(self, "orelse", self.orelse, visitor), 

617 ) 

618 

619 def _codegen_impl(self, state: CodegenState, is_elif: bool = False) -> None: 

620 for ll in self.leading_lines: 

621 ll._codegen(state) 

622 state.add_indent_tokens() 

623 

624 end_node = self.body if self.orelse is None else self.orelse 

625 with state.record_syntactic_position(self, end_node=end_node): 

626 state.add_token("elif" if is_elif else "if") 

627 self.whitespace_before_test._codegen(state) 

628 self.test._codegen(state) 

629 self.whitespace_after_test._codegen(state) 

630 state.add_token(":") 

631 self.body._codegen(state) 

632 orelse = self.orelse 

633 if orelse is not None: 

634 if isinstance(orelse, If): # special-case elif 

635 orelse._codegen(state, is_elif=True) 

636 else: # is an Else clause 

637 orelse._codegen(state) 

638 

639 

640@add_slots 

641@dataclass(frozen=True) 

642class IndentedBlock(BaseSuite): 

643 """ 

644 Represents a block of statements beginning with an ``INDENT`` token and ending in a 

645 ``DEDENT`` token. Used as the body of compound statements, such as an if statement's 

646 body. 

647 

648 A common alternative to an :class:`IndentedBlock` is a :class:`SimpleStatementSuite`, 

649 which can also be used as a :class:`BaseSuite`, meaning that it can be used as the 

650 body of many compound statements. 

651 

652 An :class:`IndentedBlock` always occurs after a colon in a 

653 :class:`BaseCompoundStatement`, so it owns the trailing whitespace for the compound 

654 statement's clause. 

655 

656 .. code-block:: 

657 

658 if test: # IndentedBlock's header 

659 body 

660 """ 

661 

662 #: Sequence of statements belonging to this indented block. 

663 body: Sequence[BaseStatement] 

664 

665 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line. 

666 header: TrailingWhitespace = TrailingWhitespace.field() 

667 

668 #: A string represents a specific indentation. A ``None`` value uses the modules's 

669 #: default indentation. This is included because indentation is allowed to be 

670 #: inconsistent across a file, just not ambiguously. 

671 indent: Optional[str] = None 

672 

673 #: Any trailing comments or lines after the dedent that are owned by this indented 

674 #: block. Statements own preceeding and same-line trailing comments, but not 

675 #: trailing lines, so it falls on :class:`IndentedBlock` to own it. In the case 

676 #: that a statement follows an :class:`IndentedBlock`, that statement will own the 

677 #: comments and lines that are at the same indent as the statement, and this 

678 #: :class:`IndentedBlock` will own the comments and lines that are indented further. 

679 footer: Sequence[EmptyLine] = () 

680 

681 def _validate(self) -> None: 

682 indent = self.indent 

683 if indent is not None: 

684 if len(indent) == 0: 

685 raise CSTValidationError( 

686 "An indented block must have a non-zero width indent." 

687 ) 

688 if _INDENT_WHITESPACE_RE.fullmatch(indent) is None: 

689 raise CSTValidationError( 

690 "An indent must be composed of only whitespace characters." 

691 ) 

692 

693 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "IndentedBlock": 

694 return IndentedBlock( 

695 header=visit_required(self, "header", self.header, visitor), 

696 indent=self.indent, 

697 body=visit_body_sequence(self, "body", self.body, visitor), 

698 footer=visit_sequence(self, "footer", self.footer, visitor), 

699 ) 

700 

701 def _codegen_impl(self, state: CodegenState) -> None: 

702 self.header._codegen(state) 

703 

704 indent = self.indent 

705 state.increase_indent(state.default_indent if indent is None else indent) 

706 

707 if self.body: 

708 with state.record_syntactic_position( 

709 self, start_node=self.body[0], end_node=self.body[-1] 

710 ): 

711 for stmt in self.body: 

712 # IndentedBlock is responsible for adjusting the current indentation level, 

713 # but its children are responsible for actually adding that indentation to 

714 # the token list. 

715 stmt._codegen(state) 

716 else: 

717 # Empty indented blocks are not syntactically valid in Python unless 

718 # they contain a 'pass' statement, so add one here. 

719 state.add_indent_tokens() 

720 with state.record_syntactic_position(self): 

721 state.add_token("pass") 

722 state.add_token(state.default_newline) 

723 

724 for f in self.footer: 

725 f._codegen(state) 

726 

727 state.decrease_indent() 

728 

729 

730@add_slots 

731@dataclass(frozen=True) 

732class AsName(CSTNode): 

733 """ 

734 An ``as name`` clause inside an :class:`ExceptHandler`, :class:`ImportAlias` or 

735 :class:`WithItem` node. 

736 """ 

737 

738 #: Identifier that the parent node will be aliased to. 

739 name: Union[Name, Tuple, List] 

740 

741 #: Whitespace between the parent node and the ``as`` keyword. 

742 whitespace_before_as: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

743 

744 #: Whitespace between the ``as`` keyword and the name. 

745 whitespace_after_as: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

746 

747 def _validate(self) -> None: 

748 if self.whitespace_after_as.empty: 

749 raise CSTValidationError( 

750 "There must be at least one space between 'as' and name." 

751 ) 

752 

753 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AsName": 

754 return AsName( 

755 whitespace_before_as=visit_required( 

756 self, "whitespace_before_as", self.whitespace_before_as, visitor 

757 ), 

758 name=visit_required(self, "name", self.name, visitor), 

759 whitespace_after_as=visit_required( 

760 self, "whitespace_after_as", self.whitespace_after_as, visitor 

761 ), 

762 ) 

763 

764 def _codegen_impl(self, state: CodegenState) -> None: 

765 self.whitespace_before_as._codegen(state) 

766 state.add_token("as") 

767 self.whitespace_after_as._codegen(state) 

768 self.name._codegen(state) 

769 

770 

771@add_slots 

772@dataclass(frozen=True) 

773class ExceptHandler(CSTNode): 

774 """ 

775 An ``except`` clause that appears optionally after a :class:`Try` statement. 

776 """ 

777 

778 #: The body of the except. 

779 body: BaseSuite 

780 

781 #: The type of exception this catches. Can be a tuple in some cases, 

782 #: or ``None`` if the code is catching all exceptions. 

783 type: Optional[BaseExpression] = None 

784 

785 #: The optional name that a caught exception is assigned to. 

786 name: Optional[AsName] = None 

787 

788 #: Sequence of empty lines appearing before this compound statement line. 

789 leading_lines: Sequence[EmptyLine] = () 

790 

791 #: The whitespace between the ``except`` keyword and the type attribute. 

792 whitespace_after_except: SimpleWhitespace = SimpleWhitespace.field(" ") 

793 

794 #: The whitespace after any type or name node (whichever comes last) and 

795 #: the colon. 

796 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

797 

798 def _validate(self) -> None: 

799 name = self.name 

800 if self.type is None and name is not None: 

801 raise CSTValidationError("Cannot have a name for an empty type.") 

802 if name is not None and not isinstance(name.name, Name): 

803 raise CSTValidationError( 

804 "Must use a Name node for AsName name inside ExceptHandler." 

805 ) 

806 type_ = self.type 

807 if type_ is not None and self.whitespace_after_except.empty: 

808 # Space is only required when the first char in `type` could start 

809 # an identifier. In the most common cases, we want to allow 

810 # grouping or tuple parens. 

811 if isinstance(type_, Name) and not type_.lpar: 

812 raise CSTValidationError( 

813 "Must have at least one space after except when ExceptHandler has a type." 

814 ) 

815 name = self.name 

816 if ( 

817 type_ is not None 

818 and name is not None 

819 and name.whitespace_before_as.empty 

820 and not type_._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

821 ): 

822 raise CSTValidationError( 

823 "Must have at least one space before as keyword in an except handler." 

824 ) 

825 

826 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ExceptHandler": 

827 return ExceptHandler( 

828 leading_lines=visit_sequence( 

829 self, "leading_lines", self.leading_lines, visitor 

830 ), 

831 whitespace_after_except=visit_required( 

832 self, "whitespace_after_except", self.whitespace_after_except, visitor 

833 ), 

834 type=visit_optional(self, "type", self.type, visitor), 

835 name=visit_optional(self, "name", self.name, visitor), 

836 whitespace_before_colon=visit_required( 

837 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

838 ), 

839 body=visit_required(self, "body", self.body, visitor), 

840 ) 

841 

842 def _codegen_impl(self, state: CodegenState) -> None: 

843 for ll in self.leading_lines: 

844 ll._codegen(state) 

845 state.add_indent_tokens() 

846 

847 with state.record_syntactic_position(self, end_node=self.body): 

848 state.add_token("except") 

849 self.whitespace_after_except._codegen(state) 

850 typenode = self.type 

851 if typenode is not None: 

852 typenode._codegen(state) 

853 namenode = self.name 

854 if namenode is not None: 

855 namenode._codegen(state) 

856 self.whitespace_before_colon._codegen(state) 

857 state.add_token(":") 

858 self.body._codegen(state) 

859 

860 

861@add_slots 

862@dataclass(frozen=True) 

863class ExceptStarHandler(CSTNode): 

864 """ 

865 An ``except*`` clause that appears after a :class:`TryStar` statement. 

866 """ 

867 

868 #: The body of the except. 

869 body: BaseSuite 

870 

871 #: The type of exception this catches. Can be a tuple in some cases. 

872 type: BaseExpression 

873 

874 #: The optional name that a caught exception is assigned to. 

875 name: Optional[AsName] = None 

876 

877 #: Sequence of empty lines appearing before this compound statement line. 

878 leading_lines: Sequence[EmptyLine] = () 

879 

880 #: The whitespace between the ``except`` keyword and the star. 

881 whitespace_after_except: SimpleWhitespace = SimpleWhitespace.field("") 

882 

883 #: The whitespace between the star and the type. 

884 whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field(" ") 

885 

886 #: The whitespace after any type or name node (whichever comes last) and 

887 #: the colon. 

888 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

889 

890 def _validate(self) -> None: 

891 name = self.name 

892 if name is not None and not isinstance(name.name, Name): 

893 raise CSTValidationError( 

894 "Must use a Name node for AsName name inside ExceptHandler." 

895 ) 

896 

897 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ExceptStarHandler": 

898 return ExceptStarHandler( 

899 leading_lines=visit_sequence( 

900 self, "leading_lines", self.leading_lines, visitor 

901 ), 

902 whitespace_after_except=visit_required( 

903 self, "whitespace_after_except", self.whitespace_after_except, visitor 

904 ), 

905 whitespace_after_star=visit_required( 

906 self, "whitespace_after_star", self.whitespace_after_star, visitor 

907 ), 

908 type=visit_required(self, "type", self.type, visitor), 

909 name=visit_optional(self, "name", self.name, visitor), 

910 whitespace_before_colon=visit_required( 

911 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

912 ), 

913 body=visit_required(self, "body", self.body, visitor), 

914 ) 

915 

916 def _codegen_impl(self, state: CodegenState) -> None: 

917 for ll in self.leading_lines: 

918 ll._codegen(state) 

919 state.add_indent_tokens() 

920 

921 with state.record_syntactic_position(self, end_node=self.body): 

922 state.add_token("except") 

923 self.whitespace_after_except._codegen(state) 

924 state.add_token("*") 

925 self.whitespace_after_star._codegen(state) 

926 typenode = self.type 

927 if typenode is not None: 

928 typenode._codegen(state) 

929 namenode = self.name 

930 if namenode is not None: 

931 namenode._codegen(state) 

932 self.whitespace_before_colon._codegen(state) 

933 state.add_token(":") 

934 self.body._codegen(state) 

935 

936 

937@add_slots 

938@dataclass(frozen=True) 

939class Finally(CSTNode): 

940 """ 

941 A ``finally`` clause that appears optionally after a :class:`Try` statement. 

942 """ 

943 

944 #: The body of the except. 

945 body: BaseSuite 

946 

947 #: Sequence of empty lines appearing before this compound statement line. 

948 leading_lines: Sequence[EmptyLine] = () 

949 

950 #: The whitespace that appears after the ``finally`` keyword but before 

951 #: the colon. 

952 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

953 

954 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Finally": 

955 return Finally( 

956 leading_lines=visit_sequence( 

957 self, "leading_lines", self.leading_lines, visitor 

958 ), 

959 whitespace_before_colon=visit_required( 

960 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

961 ), 

962 body=visit_required(self, "body", self.body, visitor), 

963 ) 

964 

965 def _codegen_impl(self, state: CodegenState) -> None: 

966 for ll in self.leading_lines: 

967 ll._codegen(state) 

968 state.add_indent_tokens() 

969 

970 with state.record_syntactic_position(self, end_node=self.body): 

971 state.add_token("finally") 

972 self.whitespace_before_colon._codegen(state) 

973 state.add_token(":") 

974 self.body._codegen(state) 

975 

976 

977@add_slots 

978@dataclass(frozen=True) 

979class Try(BaseCompoundStatement): 

980 """ 

981 A regular ``try`` statement that cannot contain :class:`ExceptStar` blocks. For 

982 ``try`` statements that can contain :class:`ExceptStar` blocks, see 

983 :class:`TryStar`. 

984 """ 

985 

986 #: The suite that is wrapped with a try statement. 

987 body: BaseSuite 

988 

989 #: A list of zero or more exception handlers. 

990 handlers: Sequence[ExceptHandler] = () 

991 

992 #: An optional else case. 

993 orelse: Optional[Else] = None 

994 

995 #: An optional finally case. 

996 finalbody: Optional[Finally] = None 

997 

998 #: Sequence of empty lines appearing before this compound statement line. 

999 leading_lines: Sequence[EmptyLine] = () 

1000 

1001 #: The whitespace that appears after the ``try`` keyword but before 

1002 #: the colon. 

1003 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

1004 

1005 def _validate(self) -> None: 

1006 if len(self.handlers) == 0 and self.finalbody is None: 

1007 raise CSTValidationError( 

1008 "A Try statement must have at least one ExceptHandler or Finally" 

1009 ) 

1010 if len(self.handlers) == 0 and self.orelse is not None: 

1011 raise CSTValidationError( 

1012 "A Try statement must have at least one ExceptHandler in order " 

1013 + "to have an Else." 

1014 ) 

1015 # Check bare excepts are always at the last position 

1016 if any(handler.type is None for handler in self.handlers[:-1]): 

1017 raise CSTValidationError("The bare except: handler must be the last one.") 

1018 

1019 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Try": 

1020 return Try( 

1021 leading_lines=visit_sequence( 

1022 self, "leading_lines", self.leading_lines, visitor 

1023 ), 

1024 whitespace_before_colon=visit_required( 

1025 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

1026 ), 

1027 body=visit_required(self, "body", self.body, visitor), 

1028 handlers=visit_sequence(self, "handlers", self.handlers, visitor), 

1029 orelse=visit_optional(self, "orelse", self.orelse, visitor), 

1030 finalbody=visit_optional(self, "finalbody", self.finalbody, visitor), 

1031 ) 

1032 

1033 def _codegen_impl(self, state: CodegenState) -> None: 

1034 for ll in self.leading_lines: 

1035 ll._codegen(state) 

1036 state.add_indent_tokens() 

1037 

1038 end_node = self.body 

1039 if len(self.handlers) > 0: 

1040 end_node = self.handlers[-1] 

1041 orelse = self.orelse 

1042 end_node = end_node if orelse is None else orelse 

1043 finalbody = self.finalbody 

1044 end_node = end_node if finalbody is None else finalbody 

1045 with state.record_syntactic_position(self, end_node=end_node): 

1046 state.add_token("try") 

1047 self.whitespace_before_colon._codegen(state) 

1048 state.add_token(":") 

1049 self.body._codegen(state) 

1050 for handler in self.handlers: 

1051 handler._codegen(state) 

1052 if orelse is not None: 

1053 orelse._codegen(state) 

1054 if finalbody is not None: 

1055 finalbody._codegen(state) 

1056 

1057 

1058@add_slots 

1059@dataclass(frozen=True) 

1060class TryStar(BaseCompoundStatement): 

1061 """ 

1062 A ``try`` statement with ``except*`` blocks. 

1063 """ 

1064 

1065 #: The suite that is wrapped with a try statement. 

1066 body: BaseSuite 

1067 

1068 #: A list of one or more exception handlers. 

1069 handlers: Sequence[ExceptStarHandler] 

1070 

1071 #: An optional else case. 

1072 orelse: Optional[Else] = None 

1073 

1074 #: An optional finally case. 

1075 finalbody: Optional[Finally] = None 

1076 

1077 #: Sequence of empty lines appearing before this compound statement line. 

1078 leading_lines: Sequence[EmptyLine] = () 

1079 

1080 #: The whitespace that appears after the ``try`` keyword but before 

1081 #: the colon. 

1082 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

1083 

1084 def _validate(self) -> None: 

1085 if len(self.handlers) == 0: 

1086 raise CSTValidationError( 

1087 "A TryStar statement must have at least one ExceptHandler" 

1088 ) 

1089 

1090 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TryStar": 

1091 return TryStar( 

1092 leading_lines=visit_sequence( 

1093 self, "leading_lines", self.leading_lines, visitor 

1094 ), 

1095 whitespace_before_colon=visit_required( 

1096 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

1097 ), 

1098 body=visit_required(self, "body", self.body, visitor), 

1099 handlers=visit_sequence(self, "handlers", self.handlers, visitor), 

1100 orelse=visit_optional(self, "orelse", self.orelse, visitor), 

1101 finalbody=visit_optional(self, "finalbody", self.finalbody, visitor), 

1102 ) 

1103 

1104 def _codegen_impl(self, state: CodegenState) -> None: 

1105 for ll in self.leading_lines: 

1106 ll._codegen(state) 

1107 state.add_indent_tokens() 

1108 

1109 end_node = self.handlers[-1] 

1110 orelse = self.orelse 

1111 end_node = end_node if orelse is None else orelse 

1112 finalbody = self.finalbody 

1113 end_node = end_node if finalbody is None else finalbody 

1114 with state.record_syntactic_position(self, end_node=end_node): 

1115 state.add_token("try") 

1116 self.whitespace_before_colon._codegen(state) 

1117 state.add_token(":") 

1118 self.body._codegen(state) 

1119 for handler in self.handlers: 

1120 handler._codegen(state) 

1121 if orelse is not None: 

1122 orelse._codegen(state) 

1123 if finalbody is not None: 

1124 finalbody._codegen(state) 

1125 

1126 

1127@add_slots 

1128@dataclass(frozen=True) 

1129class ImportAlias(CSTNode): 

1130 """ 

1131 An import, with an optional :class:`AsName`. Used in both :class:`Import` and 

1132 :class:`ImportFrom` to specify a single import out of another module. 

1133 """ 

1134 

1135 #: Name or Attribute node representing the object we are importing. 

1136 name: Union[Attribute, Name] 

1137 

1138 #: Local alias we will import the above object as. 

1139 asname: Optional[AsName] = None 

1140 

1141 #: Any trailing comma that appears after this import. This is optional for the 

1142 #: last :class:`ImportAlias` in a :class:`Import` or :class:`ImportFrom`, but all 

1143 #: other import aliases inside an import must contain a comma to disambiguate 

1144 #: multiple imports. 

1145 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT 

1146 

1147 def _validate(self) -> None: 

1148 asname = self.asname 

1149 if asname is not None: 

1150 if not isinstance(asname.name, Name): 

1151 raise CSTValidationError( 

1152 "Must use a Name node for AsName name inside ImportAlias." 

1153 ) 

1154 if asname.whitespace_before_as.empty: 

1155 raise CSTValidationError( 

1156 "Must have at least one space before as keyword in an ImportAlias." 

1157 ) 

1158 try: 

1159 self.evaluated_name 

1160 except Exception as e: 

1161 if str(e) == "Logic error!": 

1162 raise CSTValidationError( 

1163 "The imported name must be a valid qualified name." 

1164 ) 

1165 raise e 

1166 

1167 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ImportAlias": 

1168 return ImportAlias( 

1169 name=visit_required(self, "name", self.name, visitor), 

1170 asname=visit_optional(self, "asname", self.asname, visitor), 

1171 comma=visit_sentinel(self, "comma", self.comma, visitor), 

1172 ) 

1173 

1174 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None: 

1175 with state.record_syntactic_position(self): 

1176 self.name._codegen(state) 

1177 asname = self.asname 

1178 if asname is not None: 

1179 asname._codegen(state) 

1180 

1181 comma = self.comma 

1182 if comma is MaybeSentinel.DEFAULT and default_comma: 

1183 state.add_token(", ") 

1184 elif isinstance(comma, Comma): 

1185 comma._codegen(state) 

1186 

1187 def _name(self, node: CSTNode) -> str: 

1188 # Unrolled version of get_full_name_for_node to avoid circular imports. 

1189 if isinstance(node, Name): 

1190 return node.value 

1191 elif isinstance(node, Attribute): 

1192 return f"{self._name(node.value)}.{node.attr.value}" 

1193 else: 

1194 raise Exception("Logic error!") 

1195 

1196 @property 

1197 def evaluated_name(self) -> str: 

1198 """ 

1199 Returns the string name this :class:`ImportAlias` represents. 

1200 """ 

1201 return self._name(self.name) 

1202 

1203 @property 

1204 def evaluated_alias(self) -> Optional[str]: 

1205 """ 

1206 Returns the string name for any alias that this :class:`ImportAlias` 

1207 has. If there is no ``asname`` attribute, this returns ``None``. 

1208 """ 

1209 asname = self.asname 

1210 if asname is not None: 

1211 return self._name(asname.name) 

1212 return None 

1213 

1214 

1215@add_slots 

1216@dataclass(frozen=True) 

1217class Import(BaseSmallStatement): 

1218 """ 

1219 An ``import`` statement. 

1220 """ 

1221 

1222 #: One or more names that are being imported, with optional local aliases. 

1223 names: Sequence[ImportAlias] 

1224 

1225 #: Optional semicolon when this is used in a statement line. This semicolon 

1226 #: owns the whitespace on both sides of it when it is used. 

1227 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

1228 

1229 #: The whitespace that appears after the ``import`` keyword but before 

1230 #: the first import alias. 

1231 whitespace_after_import: SimpleWhitespace = SimpleWhitespace.field(" ") 

1232 

1233 def _validate(self) -> None: 

1234 if len(self.names) == 0: 

1235 raise CSTValidationError( 

1236 "An ImportStatement must have at least one ImportAlias" 

1237 ) 

1238 if isinstance(self.names[-1].comma, Comma): 

1239 raise CSTValidationError( 

1240 "An ImportStatement does not allow a trailing comma" 

1241 ) 

1242 if self.whitespace_after_import.empty: 

1243 raise CSTValidationError("Must have at least one space after import.") 

1244 

1245 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Import": 

1246 return Import( 

1247 whitespace_after_import=visit_required( 

1248 self, "whitespace_after_import", self.whitespace_after_import, visitor 

1249 ), 

1250 names=visit_sequence(self, "names", self.names, visitor), 

1251 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

1252 ) 

1253 

1254 def _codegen_impl( 

1255 self, state: CodegenState, default_semicolon: bool = False 

1256 ) -> None: 

1257 with state.record_syntactic_position(self): 

1258 state.add_token("import") 

1259 self.whitespace_after_import._codegen(state) 

1260 lastname = len(self.names) - 1 

1261 for i, name in enumerate(self.names): 

1262 name._codegen(state, default_comma=(i != lastname)) 

1263 

1264 semicolon = self.semicolon 

1265 if isinstance(semicolon, MaybeSentinel): 

1266 if default_semicolon: 

1267 state.add_token("; ") 

1268 elif isinstance(semicolon, Semicolon): 

1269 semicolon._codegen(state) 

1270 

1271 

1272@add_slots 

1273@dataclass(frozen=True) 

1274class ImportFrom(BaseSmallStatement): 

1275 """ 

1276 A ``from x import y`` statement. 

1277 """ 

1278 

1279 #: Name or Attribute node representing the module we're importing from. 

1280 #: This is optional as :class:`ImportFrom` allows purely relative imports. 

1281 module: Optional[Union[Attribute, Name]] 

1282 

1283 #: One or more names that are being imported from the specified module, 

1284 #: with optional local aliases. 

1285 names: Union[Sequence[ImportAlias], ImportStar] 

1286 

1287 #: Sequence of :class:`Dot` nodes indicating relative import level. 

1288 relative: Sequence[Dot] = () 

1289 

1290 #: Optional open parenthesis for multi-line import continuation. 

1291 lpar: Optional[LeftParen] = None 

1292 

1293 #: Optional close parenthesis for multi-line import continuation. 

1294 rpar: Optional[RightParen] = None 

1295 

1296 #: Optional semicolon when this is used in a statement line. This semicolon 

1297 #: owns the whitespace on both sides of it when it is used. 

1298 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

1299 

1300 #: The whitespace that appears after the ``from`` keyword but before 

1301 #: the module and any relative import dots. 

1302 whitespace_after_from: SimpleWhitespace = SimpleWhitespace.field(" ") 

1303 

1304 #: The whitespace that appears after the module but before the 

1305 #: ``import`` keyword. 

1306 whitespace_before_import: SimpleWhitespace = SimpleWhitespace.field(" ") 

1307 

1308 #: The whitespace that appears after the ``import`` keyword but 

1309 #: before the first import name or optional left paren. 

1310 whitespace_after_import: SimpleWhitespace = SimpleWhitespace.field(" ") 

1311 

1312 def _validate_module(self) -> None: 

1313 if self.module is None and len(self.relative) == 0: 

1314 raise CSTValidationError( 

1315 "Must have a module specified if there is no relative import." 

1316 ) 

1317 

1318 def _validate_names(self) -> None: 

1319 names = self.names 

1320 if isinstance(names, Sequence): 

1321 if len(names) == 0: 

1322 raise CSTValidationError( 

1323 "An ImportFrom must have at least one ImportAlias" 

1324 ) 

1325 for name in names[:-1]: 

1326 if name.comma is None: 

1327 raise CSTValidationError("Non-final ImportAliases require a comma") 

1328 if self.lpar is not None and self.rpar is None: 

1329 raise CSTValidationError("Cannot have left paren without right paren.") 

1330 if self.lpar is None and self.rpar is not None: 

1331 raise CSTValidationError("Cannot have right paren without left paren.") 

1332 if isinstance(names, ImportStar): 

1333 if self.lpar is not None or self.rpar is not None: 

1334 raise CSTValidationError( 

1335 "An ImportFrom using ImportStar cannot have parens" 

1336 ) 

1337 

1338 def _validate_whitespace(self) -> None: 

1339 if self.whitespace_after_from.empty and not self.relative: 

1340 raise CSTValidationError("Must have at least one space after from.") 

1341 if self.whitespace_before_import.empty and not ( 

1342 self.relative and self.module is None 

1343 ): 

1344 raise CSTValidationError("Must have at least one space before import.") 

1345 if ( 

1346 self.whitespace_after_import.empty 

1347 and self.lpar is None 

1348 and not isinstance(self.names, ImportStar) 

1349 ): 

1350 raise CSTValidationError("Must have at least one space after import.") 

1351 

1352 def _validate(self) -> None: 

1353 self._validate_module() 

1354 self._validate_names() 

1355 self._validate_whitespace() 

1356 

1357 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ImportFrom": 

1358 names = self.names 

1359 return ImportFrom( 

1360 whitespace_after_from=visit_required( 

1361 self, "whitespace_after_from", self.whitespace_after_from, visitor 

1362 ), 

1363 relative=visit_sequence(self, "relative", self.relative, visitor), 

1364 module=visit_optional(self, "module", self.module, visitor), 

1365 whitespace_before_import=visit_required( 

1366 self, "whitespace_before_import", self.whitespace_before_import, visitor 

1367 ), 

1368 whitespace_after_import=visit_required( 

1369 self, "whitespace_after_import", self.whitespace_after_import, visitor 

1370 ), 

1371 lpar=visit_optional(self, "lpar", self.lpar, visitor), 

1372 names=( 

1373 visit_required(self, "names", names, visitor) 

1374 if isinstance(names, ImportStar) 

1375 else visit_sequence(self, "names", names, visitor) 

1376 ), 

1377 rpar=visit_optional(self, "rpar", self.rpar, visitor), 

1378 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

1379 ) 

1380 

1381 def _codegen_impl( 

1382 self, state: CodegenState, default_semicolon: bool = False 

1383 ) -> None: 

1384 names = self.names 

1385 end_node = names[-1] if isinstance(names, Sequence) else names 

1386 end_node = end_node if self.rpar is None else self.rpar 

1387 with state.record_syntactic_position(self, end_node=end_node): 

1388 state.add_token("from") 

1389 self.whitespace_after_from._codegen(state) 

1390 for dot in self.relative: 

1391 dot._codegen(state) 

1392 module = self.module 

1393 if module is not None: 

1394 module._codegen(state) 

1395 self.whitespace_before_import._codegen(state) 

1396 state.add_token("import") 

1397 self.whitespace_after_import._codegen(state) 

1398 lpar = self.lpar 

1399 if lpar is not None: 

1400 lpar._codegen(state) 

1401 if isinstance(names, Sequence): 

1402 lastname = len(names) - 1 

1403 for i, name in enumerate(names): 

1404 name._codegen(state, default_comma=(i != lastname)) 

1405 if isinstance(names, ImportStar): 

1406 names._codegen(state) 

1407 rpar = self.rpar 

1408 if rpar is not None: 

1409 rpar._codegen(state) 

1410 

1411 semicolon = self.semicolon 

1412 if isinstance(semicolon, MaybeSentinel): 

1413 if default_semicolon: 

1414 state.add_token("; ") 

1415 elif isinstance(semicolon, Semicolon): 

1416 semicolon._codegen(state) 

1417 

1418 

1419@add_slots 

1420@dataclass(frozen=True) 

1421class AssignTarget(CSTNode): 

1422 """ 

1423 A target for an :class:`Assign`. Owns the equals sign and the whitespace around it. 

1424 """ 

1425 

1426 #: The target expression being assigned to. 

1427 target: BaseAssignTargetExpression 

1428 

1429 #: The whitespace appearing before the equals sign. 

1430 whitespace_before_equal: SimpleWhitespace = SimpleWhitespace.field(" ") 

1431 

1432 #: The whitespace appearing after the equals sign. 

1433 whitespace_after_equal: SimpleWhitespace = SimpleWhitespace.field(" ") 

1434 

1435 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AssignTarget": 

1436 return AssignTarget( 

1437 target=visit_required(self, "target", self.target, visitor), 

1438 whitespace_before_equal=visit_required( 

1439 self, "whitespace_before_equal", self.whitespace_before_equal, visitor 

1440 ), 

1441 whitespace_after_equal=visit_required( 

1442 self, "whitespace_after_equal", self.whitespace_after_equal, visitor 

1443 ), 

1444 ) 

1445 

1446 def _codegen_impl(self, state: CodegenState) -> None: 

1447 with state.record_syntactic_position(self): 

1448 self.target._codegen(state) 

1449 

1450 self.whitespace_before_equal._codegen(state) 

1451 state.add_token("=") 

1452 self.whitespace_after_equal._codegen(state) 

1453 

1454 

1455@add_slots 

1456@dataclass(frozen=True) 

1457class Assign(BaseSmallStatement): 

1458 """ 

1459 An assignment statement such as ``x = y`` or ``x = y = z``. Unlike 

1460 :class:`AnnAssign`, this does not allow type annotations but does 

1461 allow for multiple targets. 

1462 """ 

1463 

1464 #: One or more targets that are being assigned to. 

1465 targets: Sequence[AssignTarget] 

1466 

1467 #: The expression being assigned to the targets. 

1468 value: BaseExpression 

1469 

1470 #: Optional semicolon when this is used in a statement line. This semicolon 

1471 #: owns the whitespace on both sides of it when it is used. 

1472 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

1473 

1474 def _validate(self) -> None: 

1475 if len(self.targets) == 0: 

1476 raise CSTValidationError( 

1477 "An Assign statement must have at least one AssignTarget" 

1478 ) 

1479 

1480 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Assign": 

1481 return Assign( 

1482 targets=visit_sequence(self, "targets", self.targets, visitor), 

1483 value=visit_required(self, "value", self.value, visitor), 

1484 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

1485 ) 

1486 

1487 def _codegen_impl( 

1488 self, state: CodegenState, default_semicolon: bool = False 

1489 ) -> None: 

1490 with state.record_syntactic_position(self): 

1491 for target in self.targets: 

1492 target._codegen(state) 

1493 self.value._codegen(state) 

1494 

1495 semicolon = self.semicolon 

1496 if isinstance(semicolon, MaybeSentinel): 

1497 if default_semicolon: 

1498 state.add_token("; ") 

1499 elif isinstance(semicolon, Semicolon): 

1500 semicolon._codegen(state) 

1501 

1502 

1503@add_slots 

1504@dataclass(frozen=True) 

1505class AnnAssign(BaseSmallStatement): 

1506 """ 

1507 An assignment statement such as ``x: int = 5`` or ``x: int``. This only 

1508 allows for one assignment target unlike :class:`Assign` but it includes 

1509 a variable annotation. Also unlike :class:`Assign`, the assignment target 

1510 is optional, as it is possible to annotate an expression without assigning 

1511 to it. 

1512 """ 

1513 

1514 #: The target that is being annotated and possibly assigned to. 

1515 target: BaseAssignTargetExpression 

1516 

1517 #: The annotation for the target. 

1518 annotation: Annotation 

1519 

1520 #: The optional expression being assigned to the target. 

1521 value: Optional[BaseExpression] = None 

1522 

1523 #: The equals sign used to denote assignment if there is a value. 

1524 equal: Union[AssignEqual, MaybeSentinel] = MaybeSentinel.DEFAULT 

1525 

1526 #: Optional semicolon when this is used in a statement line. This semicolon 

1527 #: owns the whitespace on both sides of it when it is used. 

1528 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

1529 

1530 def _validate(self) -> None: 

1531 if self.value is None and isinstance(self.equal, AssignEqual): 

1532 raise CSTValidationError( 

1533 "Must have a value when specifying an AssignEqual." 

1534 ) 

1535 

1536 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AnnAssign": 

1537 return AnnAssign( 

1538 target=visit_required(self, "target", self.target, visitor), 

1539 annotation=visit_required(self, "annotation", self.annotation, visitor), 

1540 equal=visit_sentinel(self, "equal", self.equal, visitor), 

1541 value=visit_optional(self, "value", self.value, visitor), 

1542 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

1543 ) 

1544 

1545 def _codegen_impl( 

1546 self, state: CodegenState, default_semicolon: bool = False 

1547 ) -> None: 

1548 with state.record_syntactic_position(self): 

1549 self.target._codegen(state) 

1550 self.annotation._codegen(state, default_indicator=":") 

1551 equal = self.equal 

1552 if equal is MaybeSentinel.DEFAULT and self.value is not None: 

1553 state.add_token(" = ") 

1554 elif isinstance(equal, AssignEqual): 

1555 equal._codegen(state) 

1556 value = self.value 

1557 if value is not None: 

1558 value._codegen(state) 

1559 

1560 semicolon = self.semicolon 

1561 if isinstance(semicolon, MaybeSentinel): 

1562 if default_semicolon: 

1563 state.add_token("; ") 

1564 elif isinstance(semicolon, Semicolon): 

1565 semicolon._codegen(state) 

1566 

1567 

1568@add_slots 

1569@dataclass(frozen=True) 

1570class AugAssign(BaseSmallStatement): 

1571 """ 

1572 An augmented assignment statement, such as ``x += 5``. 

1573 """ 

1574 

1575 #: Target that is being operated on and assigned to. 

1576 target: BaseAssignTargetExpression 

1577 

1578 #: The augmented assignment operation being performed. 

1579 operator: BaseAugOp 

1580 

1581 #: The value used with the above operator to calculate the new assignment. 

1582 value: BaseExpression 

1583 

1584 #: Optional semicolon when this is used in a statement line. This semicolon 

1585 #: owns the whitespace on both sides of it when it is used. 

1586 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

1587 

1588 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "AugAssign": 

1589 return AugAssign( 

1590 target=visit_required(self, "target", self.target, visitor), 

1591 operator=visit_required(self, "operator", self.operator, visitor), 

1592 value=visit_required(self, "value", self.value, visitor), 

1593 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

1594 ) 

1595 

1596 def _codegen_impl( 

1597 self, state: CodegenState, default_semicolon: bool = False 

1598 ) -> None: 

1599 with state.record_syntactic_position(self): 

1600 self.target._codegen(state) 

1601 self.operator._codegen(state) 

1602 self.value._codegen(state) 

1603 

1604 semicolon = self.semicolon 

1605 if isinstance(semicolon, MaybeSentinel): 

1606 if default_semicolon: 

1607 state.add_token("; ") 

1608 elif isinstance(semicolon, Semicolon): 

1609 semicolon._codegen(state) 

1610 

1611 

1612@add_slots 

1613@dataclass(frozen=True) 

1614class Decorator(CSTNode): 

1615 """ 

1616 A single decorator that decorates a :class:`FunctionDef` or a :class:`ClassDef`. 

1617 """ 

1618 

1619 #: The decorator that will return a new function wrapping the parent 

1620 #: of this decorator. 

1621 decorator: BaseExpression 

1622 

1623 #: Line comments and empty lines before this decorator. The parent 

1624 #: :class:`FunctionDef` or :class:`ClassDef` node owns leading lines before 

1625 #: the first decorator so that if the first decorator is removed, spacing is preserved. 

1626 leading_lines: Sequence[EmptyLine] = () 

1627 

1628 #: Whitespace after the ``@`` and before the decorator expression itself. 

1629 whitespace_after_at: SimpleWhitespace = SimpleWhitespace.field("") 

1630 

1631 #: Optional trailing comment and newline following the decorator before the next line. 

1632 trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field() 

1633 

1634 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Decorator": 

1635 return Decorator( 

1636 leading_lines=visit_sequence( 

1637 self, "leading_lines", self.leading_lines, visitor 

1638 ), 

1639 whitespace_after_at=visit_required( 

1640 self, "whitespace_after_at", self.whitespace_after_at, visitor 

1641 ), 

1642 decorator=visit_required(self, "decorator", self.decorator, visitor), 

1643 trailing_whitespace=visit_required( 

1644 self, "trailing_whitespace", self.trailing_whitespace, visitor 

1645 ), 

1646 ) 

1647 

1648 def _codegen_impl(self, state: CodegenState) -> None: 

1649 for ll in self.leading_lines: 

1650 ll._codegen(state) 

1651 state.add_indent_tokens() 

1652 

1653 with state.record_syntactic_position(self): 

1654 state.add_token("@") 

1655 self.whitespace_after_at._codegen(state) 

1656 self.decorator._codegen(state) 

1657 

1658 self.trailing_whitespace._codegen(state) 

1659 

1660 

1661def get_docstring_impl( 

1662 body: Union[BaseSuite, Sequence[Union[SimpleStatementLine, BaseCompoundStatement]]], 

1663 clean: bool, 

1664) -> Optional[str]: 

1665 """ 

1666 Implementation Reference: 

1667 - :func:`ast.get_docstring` https://docs.python.org/3/library/ast.html#ast.get_docstring 

1668 and https://github.com/python/cpython/blob/89aa4694fc8c6d190325ef8ed6ce6a6b8efb3e50/Lib/ast.py#L254 

1669 - PEP 257 https://www.python.org/dev/peps/pep-0257/ 

1670 """ 

1671 if isinstance(body, Sequence): 

1672 if body: 

1673 expr = body[0] 

1674 else: 

1675 return None 

1676 else: 

1677 expr = body 

1678 while isinstance(expr, (BaseSuite, SimpleStatementLine)): 

1679 if len(expr.body) == 0: 

1680 return None 

1681 expr = expr.body[0] 

1682 if not isinstance(expr, Expr): 

1683 return None 

1684 val = expr.value 

1685 if isinstance(val, (SimpleString, ConcatenatedString)): 

1686 evaluated_value = val.evaluated_value 

1687 else: 

1688 return None 

1689 if isinstance(evaluated_value, bytes): 

1690 return None 

1691 

1692 if evaluated_value is not None and clean: 

1693 return inspect.cleandoc(evaluated_value) 

1694 return evaluated_value 

1695 

1696 

1697@add_slots 

1698@dataclass(frozen=True) 

1699class FunctionDef(BaseCompoundStatement): 

1700 """ 

1701 A function definition. 

1702 """ 

1703 

1704 #: The function name. 

1705 name: Name 

1706 

1707 #: The function parameters. Present even if there are no params. 

1708 params: Parameters 

1709 

1710 #: The function body. 

1711 body: BaseSuite 

1712 

1713 #: Sequence of decorators applied to this function. Decorators are listed in 

1714 #: order that they appear in source (top to bottom) as apposed to the order 

1715 #: that they are applied to the function at runtime. 

1716 decorators: Sequence[Decorator] = () 

1717 

1718 #: An optional return annotation, if the function is annotated. 

1719 returns: Optional[Annotation] = None 

1720 

1721 #: Optional async modifier, if this is an async function. 

1722 asynchronous: Optional[Asynchronous] = None 

1723 

1724 #: Leading empty lines and comments before the first decorator. We 

1725 #: assume any comments before the first decorator are owned by the 

1726 #: function definition itself. If there are no decorators, this will 

1727 #: still contain all of the empty lines and comments before the 

1728 #: function definition. 

1729 leading_lines: Sequence[EmptyLine] = () 

1730 

1731 #: Empty lines and comments between the final decorator and the 

1732 #: :class:`FunctionDef` node. In the case of no decorators, this will be empty. 

1733 lines_after_decorators: Sequence[EmptyLine] = () 

1734 

1735 #: Whitespace after the ``def`` keyword and before the function name. 

1736 whitespace_after_def: SimpleWhitespace = SimpleWhitespace.field(" ") 

1737 

1738 #: Whitespace after the function name and before the opening parenthesis for 

1739 #: the parameters. 

1740 whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("") 

1741 

1742 #: Whitespace after the opening parenthesis for the parameters but before 

1743 #: the first param itself. 

1744 whitespace_before_params: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

1745 

1746 #: Whitespace after the closing parenthesis or return annotation and before 

1747 #: the colon. 

1748 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

1749 

1750 def _validate(self) -> None: 

1751 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0: 

1752 raise CSTValidationError("Cannot have parens around Name in a FunctionDef.") 

1753 if self.whitespace_after_def.empty: 

1754 raise CSTValidationError( 

1755 "There must be at least one space between 'def' and name." 

1756 ) 

1757 

1758 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "FunctionDef": 

1759 return FunctionDef( 

1760 leading_lines=visit_sequence( 

1761 self, "leading_lines", self.leading_lines, visitor 

1762 ), 

1763 decorators=visit_sequence(self, "decorators", self.decorators, visitor), 

1764 lines_after_decorators=visit_sequence( 

1765 self, "lines_after_decorators", self.lines_after_decorators, visitor 

1766 ), 

1767 asynchronous=visit_optional( 

1768 self, "asynchronous", self.asynchronous, visitor 

1769 ), 

1770 whitespace_after_def=visit_required( 

1771 self, "whitespace_after_def", self.whitespace_after_def, visitor 

1772 ), 

1773 name=visit_required(self, "name", self.name, visitor), 

1774 whitespace_after_name=visit_required( 

1775 self, "whitespace_after_name", self.whitespace_after_name, visitor 

1776 ), 

1777 whitespace_before_params=visit_required( 

1778 self, "whitespace_before_params", self.whitespace_before_params, visitor 

1779 ), 

1780 params=visit_required(self, "params", self.params, visitor), 

1781 returns=visit_optional(self, "returns", self.returns, visitor), 

1782 whitespace_before_colon=visit_required( 

1783 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

1784 ), 

1785 body=visit_required(self, "body", self.body, visitor), 

1786 ) 

1787 

1788 def _codegen_impl(self, state: CodegenState) -> None: 

1789 for ll in self.leading_lines: 

1790 ll._codegen(state) 

1791 for decorator in self.decorators: 

1792 decorator._codegen(state) 

1793 for lad in self.lines_after_decorators: 

1794 lad._codegen(state) 

1795 state.add_indent_tokens() 

1796 

1797 with state.record_syntactic_position(self, end_node=self.body): 

1798 asynchronous = self.asynchronous 

1799 if asynchronous is not None: 

1800 asynchronous._codegen(state) 

1801 state.add_token("def") 

1802 self.whitespace_after_def._codegen(state) 

1803 self.name._codegen(state) 

1804 self.whitespace_after_name._codegen(state) 

1805 state.add_token("(") 

1806 self.whitespace_before_params._codegen(state) 

1807 self.params._codegen(state) 

1808 state.add_token(")") 

1809 returns = self.returns 

1810 if returns is not None: 

1811 returns._codegen(state, default_indicator="->") 

1812 self.whitespace_before_colon._codegen(state) 

1813 state.add_token(":") 

1814 self.body._codegen(state) 

1815 

1816 def get_docstring(self, clean: bool = True) -> Optional[str]: 

1817 """ 

1818 When docstring is available, returns a :func:`inspect.cleandoc` cleaned docstring. 

1819 Otherwise, returns ``None``. 

1820 """ 

1821 return get_docstring_impl(self.body, clean) 

1822 

1823 

1824@add_slots 

1825@dataclass(frozen=True) 

1826class ClassDef(BaseCompoundStatement): 

1827 """ 

1828 A class definition. 

1829 """ 

1830 

1831 #: The class name. 

1832 name: Name 

1833 

1834 #: The class body. 

1835 body: BaseSuite 

1836 

1837 #: Sequence of base classes this class inherits from. 

1838 bases: Sequence[Arg] = () 

1839 

1840 #: Sequence of keywords, such as "metaclass". 

1841 keywords: Sequence[Arg] = () 

1842 

1843 #: Sequence of decorators applied to this class. 

1844 decorators: Sequence[Decorator] = () 

1845 

1846 #: Optional open parenthesis used when there are bases or keywords. 

1847 lpar: Union[LeftParen, MaybeSentinel] = MaybeSentinel.DEFAULT 

1848 

1849 #: Optional close parenthesis used when there are bases or keywords. 

1850 rpar: Union[RightParen, MaybeSentinel] = MaybeSentinel.DEFAULT 

1851 

1852 #: Leading empty lines and comments before the first decorator. We 

1853 #: assume any comments before the first decorator are owned by the 

1854 #: class definition itself. If there are no decorators, this will 

1855 #: still contain all of the empty lines and comments before the 

1856 #: class definition. 

1857 leading_lines: Sequence[EmptyLine] = () 

1858 

1859 #: Empty lines and comments between the final decorator and the 

1860 #: :class:`ClassDef` node. In the case of no decorators, this will be empty. 

1861 lines_after_decorators: Sequence[EmptyLine] = () 

1862 

1863 #: Whitespace after the ``class`` keyword and before the class name. 

1864 whitespace_after_class: SimpleWhitespace = SimpleWhitespace.field(" ") 

1865 

1866 #: Whitespace after the class name and before the opening parenthesis for 

1867 #: the bases and keywords. 

1868 whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("") 

1869 

1870 #: Whitespace after the closing parenthesis or class name and before 

1871 #: the colon. 

1872 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

1873 

1874 def _validate_whitespace(self) -> None: 

1875 if self.whitespace_after_class.empty: 

1876 raise CSTValidationError( 

1877 "There must be at least one space between 'class' and name." 

1878 ) 

1879 

1880 def _validate_parens(self) -> None: 

1881 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0: 

1882 raise CSTValidationError("Cannot have parens around Name in a ClassDef.") 

1883 if isinstance(self.lpar, MaybeSentinel) and isinstance(self.rpar, RightParen): 

1884 raise CSTValidationError( 

1885 "Do not mix concrete LeftParen/RightParen with MaybeSentinel." 

1886 ) 

1887 if isinstance(self.lpar, LeftParen) and isinstance(self.rpar, MaybeSentinel): 

1888 raise CSTValidationError( 

1889 "Do not mix concrete LeftParen/RightParen with MaybeSentinel." 

1890 ) 

1891 

1892 def _validate_args(self) -> None: 

1893 if any((arg.keyword is not None) for arg in self.bases): 

1894 raise CSTValidationError("Bases must be arguments without keywords.") 

1895 if any((arg.keyword is None and arg.star != "**") for arg in self.keywords): 

1896 raise CSTValidationError( 

1897 "Keywords must be arguments with keywords or dictionary expansions." 

1898 ) 

1899 

1900 def _validate(self) -> None: 

1901 self._validate_whitespace() 

1902 self._validate_parens() 

1903 self._validate_args() 

1904 

1905 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ClassDef": 

1906 return ClassDef( 

1907 leading_lines=visit_sequence( 

1908 self, "leading_lines", self.leading_lines, visitor 

1909 ), 

1910 decorators=visit_sequence(self, "decorators", self.decorators, visitor), 

1911 lines_after_decorators=visit_sequence( 

1912 self, "lines_after_decorators", self.lines_after_decorators, visitor 

1913 ), 

1914 whitespace_after_class=visit_required( 

1915 self, "whitespace_after_class", self.whitespace_after_class, visitor 

1916 ), 

1917 name=visit_required(self, "name", self.name, visitor), 

1918 whitespace_after_name=visit_required( 

1919 self, "whitespace_after_name", self.whitespace_after_name, visitor 

1920 ), 

1921 lpar=visit_sentinel(self, "lpar", self.lpar, visitor), 

1922 bases=visit_sequence(self, "bases", self.bases, visitor), 

1923 keywords=visit_sequence(self, "keywords", self.keywords, visitor), 

1924 rpar=visit_sentinel(self, "rpar", self.rpar, visitor), 

1925 whitespace_before_colon=visit_required( 

1926 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

1927 ), 

1928 body=visit_required(self, "body", self.body, visitor), 

1929 ) 

1930 

1931 def _codegen_impl(self, state: CodegenState) -> None: # noqa: C901 

1932 for ll in self.leading_lines: 

1933 ll._codegen(state) 

1934 for decorator in self.decorators: 

1935 decorator._codegen(state) 

1936 for lad in self.lines_after_decorators: 

1937 lad._codegen(state) 

1938 state.add_indent_tokens() 

1939 

1940 with state.record_syntactic_position(self, end_node=self.body): 

1941 state.add_token("class") 

1942 self.whitespace_after_class._codegen(state) 

1943 self.name._codegen(state) 

1944 self.whitespace_after_name._codegen(state) 

1945 lpar = self.lpar 

1946 if isinstance(lpar, MaybeSentinel): 

1947 if self.bases or self.keywords: 

1948 state.add_token("(") 

1949 elif isinstance(lpar, LeftParen): 

1950 lpar._codegen(state) 

1951 args = [*self.bases, *self.keywords] 

1952 last_arg = len(args) - 1 

1953 for i, arg in enumerate(args): 

1954 arg._codegen(state, default_comma=(i != last_arg)) 

1955 rpar = self.rpar 

1956 if isinstance(rpar, MaybeSentinel): 

1957 if self.bases or self.keywords: 

1958 state.add_token(")") 

1959 elif isinstance(rpar, RightParen): 

1960 rpar._codegen(state) 

1961 self.whitespace_before_colon._codegen(state) 

1962 state.add_token(":") 

1963 self.body._codegen(state) 

1964 

1965 def get_docstring(self, clean: bool = True) -> Optional[str]: 

1966 """ 

1967 Returns a :func:`inspect.cleandoc` cleaned docstring if the docstring is available, ``None`` otherwise. 

1968 """ 

1969 return get_docstring_impl(self.body, clean) 

1970 

1971 

1972@add_slots 

1973@dataclass(frozen=True) 

1974class WithItem(CSTNode): 

1975 """ 

1976 A single context manager in a :class:`With` block, with an optional variable name. 

1977 """ 

1978 

1979 #: Expression that evaluates to a context manager. 

1980 item: BaseExpression 

1981 

1982 #: Variable to assign the context manager to, if it is needed in the 

1983 #: :class:`With` body. 

1984 asname: Optional[AsName] = None 

1985 

1986 #: This is forbidden for the last :class:`WithItem` in a :class:`With`, but all 

1987 #: other items inside a with block must contain a comma to separate them. 

1988 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT 

1989 

1990 def _validate(self) -> None: 

1991 asname = self.asname 

1992 if ( 

1993 asname is not None 

1994 and asname.whitespace_before_as.empty 

1995 and not self.item._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

1996 ): 

1997 raise CSTValidationError("Must have at least one space before as keyword.") 

1998 

1999 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "WithItem": 

2000 return WithItem( 

2001 item=visit_required(self, "item", self.item, visitor), 

2002 asname=visit_optional(self, "asname", self.asname, visitor), 

2003 comma=visit_sentinel(self, "comma", self.comma, visitor), 

2004 ) 

2005 

2006 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None: 

2007 with state.record_syntactic_position(self): 

2008 self.item._codegen(state) 

2009 asname = self.asname 

2010 if asname is not None: 

2011 asname._codegen(state) 

2012 

2013 comma = self.comma 

2014 if comma is MaybeSentinel.DEFAULT and default_comma: 

2015 state.add_token(", ") 

2016 elif isinstance(comma, Comma): 

2017 comma._codegen(state) 

2018 

2019 

2020@add_slots 

2021@dataclass(frozen=True) 

2022class With(BaseCompoundStatement): 

2023 """ 

2024 A ``with`` statement. 

2025 """ 

2026 

2027 #: A sequence of one or more items that evaluate to context managers. 

2028 items: Sequence[WithItem] 

2029 

2030 #: The suite that is wrapped with this statement. 

2031 body: BaseSuite 

2032 

2033 #: Optional async modifier if this is an ``async with`` statement. 

2034 asynchronous: Optional[Asynchronous] = None 

2035 

2036 #: Sequence of empty lines appearing before this with statement. 

2037 leading_lines: Sequence[EmptyLine] = () 

2038 

2039 #: Optional open parenthesis for multi-line with bindings 

2040 lpar: Union[LeftParen, MaybeSentinel] = MaybeSentinel.DEFAULT 

2041 

2042 #: Optional close parenthesis for multi-line with bindings 

2043 rpar: Union[RightParen, MaybeSentinel] = MaybeSentinel.DEFAULT 

2044 

2045 #: Whitespace after the ``with`` keyword and before the first item. 

2046 whitespace_after_with: SimpleWhitespace = SimpleWhitespace.field(" ") 

2047 

2048 #: Whitespace after the last item and before the colon. 

2049 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

2050 

2051 def _validate_parens(self) -> None: 

2052 if isinstance(self.lpar, MaybeSentinel) and isinstance(self.rpar, RightParen): 

2053 raise CSTValidationError( 

2054 "Do not mix concrete LeftParen/RightParen with MaybeSentinel." 

2055 ) 

2056 if isinstance(self.lpar, LeftParen) and isinstance(self.rpar, MaybeSentinel): 

2057 raise CSTValidationError( 

2058 "Do not mix concrete LeftParen/RightParen with MaybeSentinel." 

2059 ) 

2060 

2061 def _validate(self) -> None: 

2062 self._validate_parens() 

2063 if len(self.items) == 0: 

2064 raise CSTValidationError( 

2065 "A With statement must have at least one WithItem." 

2066 ) 

2067 if ( 

2068 isinstance(self.rpar, MaybeSentinel) 

2069 and self.items[-1].comma != MaybeSentinel.DEFAULT 

2070 ): 

2071 raise CSTValidationError( 

2072 "The last WithItem in an unparenthesized With cannot have a trailing comma." 

2073 ) 

2074 if self.whitespace_after_with.empty and not ( 

2075 isinstance(self.lpar, LeftParen) 

2076 or self.items[0].item._safe_to_use_with_word_operator( 

2077 ExpressionPosition.RIGHT 

2078 ) 

2079 ): 

2080 raise CSTValidationError("Must have at least one space after with keyword.") 

2081 

2082 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "With": 

2083 return With( 

2084 leading_lines=visit_sequence( 

2085 self, "leading_lines", self.leading_lines, visitor 

2086 ), 

2087 asynchronous=visit_optional( 

2088 self, "asynchronous", self.asynchronous, visitor 

2089 ), 

2090 whitespace_after_with=visit_required( 

2091 self, "whitespace_after_with", self.whitespace_after_with, visitor 

2092 ), 

2093 lpar=visit_sentinel(self, "lpar", self.lpar, visitor), 

2094 items=visit_sequence(self, "items", self.items, visitor), 

2095 rpar=visit_sentinel(self, "rpar", self.rpar, visitor), 

2096 whitespace_before_colon=visit_required( 

2097 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

2098 ), 

2099 body=visit_required(self, "body", self.body, visitor), 

2100 ) 

2101 

2102 def _codegen_impl(self, state: CodegenState) -> None: 

2103 for ll in self.leading_lines: 

2104 ll._codegen(state) 

2105 state.add_indent_tokens() 

2106 

2107 needs_paren = False 

2108 for item in self.items: 

2109 comma = item.comma 

2110 if isinstance(comma, Comma): 

2111 if isinstance( 

2112 comma.whitespace_after, 

2113 (EmptyLine, TrailingWhitespace, ParenthesizedWhitespace), 

2114 ): 

2115 needs_paren = True 

2116 break 

2117 

2118 with state.record_syntactic_position(self, end_node=self.body): 

2119 asynchronous = self.asynchronous 

2120 if asynchronous is not None: 

2121 asynchronous._codegen(state) 

2122 state.add_token("with") 

2123 self.whitespace_after_with._codegen(state) 

2124 lpar = self.lpar 

2125 if isinstance(lpar, LeftParen): 

2126 lpar._codegen(state) 

2127 elif needs_paren: 

2128 state.add_token("(") 

2129 last_item = len(self.items) - 1 

2130 for i, item in enumerate(self.items): 

2131 item._codegen(state, default_comma=(i != last_item)) 

2132 rpar = self.rpar 

2133 if isinstance(rpar, RightParen): 

2134 rpar._codegen(state) 

2135 elif needs_paren: 

2136 state.add_token(")") 

2137 self.whitespace_before_colon._codegen(state) 

2138 state.add_token(":") 

2139 self.body._codegen(state) 

2140 

2141 

2142@add_slots 

2143@dataclass(frozen=True) 

2144class For(BaseCompoundStatement): 

2145 """ 

2146 A ``for target in iter`` statement. 

2147 """ 

2148 

2149 #: The target of the iterator in the for statement. 

2150 target: BaseAssignTargetExpression 

2151 

2152 #: The iterable expression we will loop over. 

2153 iter: BaseExpression 

2154 

2155 #: The suite that is wrapped with this statement. 

2156 body: BaseSuite 

2157 

2158 #: An optional else case which will be executed if there is no 

2159 #: :class:`Break` statement encountered while looping. 

2160 orelse: Optional[Else] = None 

2161 

2162 #: Optional async modifier, if this is an `async for` statement. 

2163 asynchronous: Optional[Asynchronous] = None 

2164 

2165 #: Sequence of empty lines appearing before this for statement. 

2166 leading_lines: Sequence[EmptyLine] = () 

2167 

2168 #: Whitespace after the ``for`` keyword and before the target. 

2169 whitespace_after_for: SimpleWhitespace = SimpleWhitespace.field(" ") 

2170 

2171 #: Whitespace after the target and before the ``in`` keyword. 

2172 whitespace_before_in: SimpleWhitespace = SimpleWhitespace.field(" ") 

2173 

2174 #: Whitespace after the ``in`` keyword and before the iter. 

2175 whitespace_after_in: SimpleWhitespace = SimpleWhitespace.field(" ") 

2176 

2177 #: Whitespace after the iter and before the colon. 

2178 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

2179 

2180 def _validate(self) -> None: 

2181 if ( 

2182 self.whitespace_after_for.empty 

2183 and not self.target._safe_to_use_with_word_operator( 

2184 ExpressionPosition.RIGHT 

2185 ) 

2186 ): 

2187 raise CSTValidationError( 

2188 "Must have at least one space after 'for' keyword." 

2189 ) 

2190 

2191 if ( 

2192 self.whitespace_before_in.empty 

2193 and not self.target._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

2194 ): 

2195 raise CSTValidationError( 

2196 "Must have at least one space before 'in' keyword." 

2197 ) 

2198 

2199 if ( 

2200 self.whitespace_after_in.empty 

2201 and not self.iter._safe_to_use_with_word_operator(ExpressionPosition.RIGHT) 

2202 ): 

2203 raise CSTValidationError("Must have at least one space after 'in' keyword.") 

2204 

2205 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "For": 

2206 return For( 

2207 leading_lines=visit_sequence( 

2208 self, "leading_lines", self.leading_lines, visitor 

2209 ), 

2210 asynchronous=visit_optional( 

2211 self, "asynchronous", self.asynchronous, visitor 

2212 ), 

2213 whitespace_after_for=visit_required( 

2214 self, "whitespace_after_for", self.whitespace_after_for, visitor 

2215 ), 

2216 target=visit_required(self, "target", self.target, visitor), 

2217 whitespace_before_in=visit_required( 

2218 self, "whitespace_before_in", self.whitespace_before_in, visitor 

2219 ), 

2220 whitespace_after_in=visit_required( 

2221 self, "whitespace_after_in", self.whitespace_after_in, visitor 

2222 ), 

2223 iter=visit_required(self, "iter", self.iter, visitor), 

2224 whitespace_before_colon=visit_required( 

2225 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

2226 ), 

2227 body=visit_required(self, "body", self.body, visitor), 

2228 orelse=visit_optional(self, "orelse", self.orelse, visitor), 

2229 ) 

2230 

2231 def _codegen_impl(self, state: CodegenState) -> None: 

2232 for ll in self.leading_lines: 

2233 ll._codegen(state) 

2234 state.add_indent_tokens() 

2235 

2236 end_node = self.body if self.orelse is None else self.orelse 

2237 with state.record_syntactic_position(self, end_node=end_node): 

2238 asynchronous = self.asynchronous 

2239 if asynchronous is not None: 

2240 asynchronous._codegen(state) 

2241 state.add_token("for") 

2242 self.whitespace_after_for._codegen(state) 

2243 self.target._codegen(state) 

2244 self.whitespace_before_in._codegen(state) 

2245 state.add_token("in") 

2246 self.whitespace_after_in._codegen(state) 

2247 self.iter._codegen(state) 

2248 self.whitespace_before_colon._codegen(state) 

2249 state.add_token(":") 

2250 self.body._codegen(state) 

2251 orelse = self.orelse 

2252 if orelse is not None: 

2253 orelse._codegen(state) 

2254 

2255 

2256@add_slots 

2257@dataclass(frozen=True) 

2258class While(BaseCompoundStatement): 

2259 """ 

2260 A ``while`` statement. 

2261 """ 

2262 

2263 #: The test we will loop against. 

2264 test: BaseExpression 

2265 

2266 #: The suite that is wrapped with this statement. 

2267 body: BaseSuite 

2268 

2269 #: An optional else case which will be executed if there is no 

2270 #: :class:`Break` statement encountered while looping. 

2271 orelse: Optional[Else] = None 

2272 

2273 #: Sequence of empty lines appearing before this while statement. 

2274 leading_lines: Sequence[EmptyLine] = () 

2275 

2276 #: Whitespace after the ``while`` keyword and before the test. 

2277 whitespace_after_while: SimpleWhitespace = SimpleWhitespace.field(" ") 

2278 

2279 #: Whitespace after the test and before the colon. 

2280 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

2281 

2282 def _validate(self) -> None: 

2283 if ( 

2284 self.whitespace_after_while.empty 

2285 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT) 

2286 ): 

2287 raise CSTValidationError( 

2288 "Must have at least one space after 'while' keyword." 

2289 ) 

2290 

2291 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "While": 

2292 return While( 

2293 leading_lines=visit_sequence( 

2294 self, "leading_lines", self.leading_lines, visitor 

2295 ), 

2296 whitespace_after_while=visit_required( 

2297 self, "whitespace_after_while", self.whitespace_after_while, visitor 

2298 ), 

2299 test=visit_required(self, "test", self.test, visitor), 

2300 whitespace_before_colon=visit_required( 

2301 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

2302 ), 

2303 body=visit_required(self, "body", self.body, visitor), 

2304 orelse=visit_optional(self, "orelse", self.orelse, visitor), 

2305 ) 

2306 

2307 def _codegen_impl(self, state: CodegenState) -> None: 

2308 for ll in self.leading_lines: 

2309 ll._codegen(state) 

2310 state.add_indent_tokens() 

2311 

2312 end_node = self.body if self.orelse is None else self.orelse 

2313 with state.record_syntactic_position(self, end_node=end_node): 

2314 state.add_token("while") 

2315 self.whitespace_after_while._codegen(state) 

2316 self.test._codegen(state) 

2317 self.whitespace_before_colon._codegen(state) 

2318 state.add_token(":") 

2319 self.body._codegen(state) 

2320 orelse = self.orelse 

2321 if orelse is not None: 

2322 orelse._codegen(state) 

2323 

2324 

2325@add_slots 

2326@dataclass(frozen=True) 

2327class Raise(BaseSmallStatement): 

2328 """ 

2329 A ``raise exc`` or ``raise exc from cause`` statement. 

2330 """ 

2331 

2332 #: The exception that we should raise. 

2333 exc: Optional[BaseExpression] = None 

2334 

2335 #: Optionally, a ``from cause`` clause to allow us to raise an exception 

2336 #: out of another exception's context. 

2337 cause: Optional[From] = None 

2338 

2339 #: Any whitespace appearing between the ``raise`` keyword and the exception. 

2340 whitespace_after_raise: Union[ 

2341 SimpleWhitespace, MaybeSentinel 

2342 ] = MaybeSentinel.DEFAULT 

2343 

2344 #: Optional semicolon when this is used in a statement line. This semicolon 

2345 #: owns the whitespace on both sides of it when it is used. 

2346 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

2347 

2348 def _validate(self) -> None: 

2349 # Validate correct construction 

2350 if self.exc is None and self.cause is not None: 

2351 raise CSTValidationError( 

2352 "Must have an 'exc' when specifying 'clause'. on Raise." 

2353 ) 

2354 

2355 # Validate spacing between "raise" and "exc" 

2356 exc = self.exc 

2357 if exc is not None: 

2358 whitespace_after_raise = self.whitespace_after_raise 

2359 has_no_gap = ( 

2360 not isinstance(whitespace_after_raise, MaybeSentinel) 

2361 and whitespace_after_raise.empty 

2362 ) 

2363 if has_no_gap and not exc._safe_to_use_with_word_operator( 

2364 ExpressionPosition.RIGHT 

2365 ): 

2366 raise CSTValidationError("Must have at least one space after 'raise'.") 

2367 

2368 # Validate spacing between "exc" and "from" 

2369 cause = self.cause 

2370 if exc is not None and cause is not None: 

2371 whitespace_before_from = cause.whitespace_before_from 

2372 has_no_gap = ( 

2373 not isinstance(whitespace_before_from, MaybeSentinel) 

2374 and whitespace_before_from.empty 

2375 ) 

2376 if has_no_gap and not exc._safe_to_use_with_word_operator( 

2377 ExpressionPosition.LEFT 

2378 ): 

2379 raise CSTValidationError("Must have at least one space before 'from'.") 

2380 

2381 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Raise": 

2382 return Raise( 

2383 whitespace_after_raise=visit_sentinel( 

2384 self, "whitespace_after_raise", self.whitespace_after_raise, visitor 

2385 ), 

2386 exc=visit_optional(self, "exc", self.exc, visitor), 

2387 cause=visit_optional(self, "cause", self.cause, visitor), 

2388 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

2389 ) 

2390 

2391 def _codegen_impl( 

2392 self, state: CodegenState, default_semicolon: bool = False 

2393 ) -> None: 

2394 with state.record_syntactic_position(self): 

2395 exc = self.exc 

2396 cause = self.cause 

2397 state.add_token("raise") 

2398 whitespace_after_raise = self.whitespace_after_raise 

2399 if isinstance(whitespace_after_raise, MaybeSentinel): 

2400 if exc is not None: 

2401 state.add_token(" ") 

2402 else: 

2403 whitespace_after_raise._codegen(state) 

2404 if exc is not None: 

2405 exc._codegen(state) 

2406 if cause is not None: 

2407 cause._codegen(state, default_space=" ") 

2408 

2409 semicolon = self.semicolon 

2410 if isinstance(semicolon, MaybeSentinel): 

2411 if default_semicolon: 

2412 state.add_token("; ") 

2413 elif isinstance(semicolon, Semicolon): 

2414 semicolon._codegen(state) 

2415 

2416 

2417@add_slots 

2418@dataclass(frozen=True) 

2419class Assert(BaseSmallStatement): 

2420 """ 

2421 An assert statement such as ``assert x > 5`` or ``assert x > 5, 'Uh oh!'`` 

2422 """ 

2423 

2424 #: The test we are going to assert on. 

2425 test: BaseExpression 

2426 

2427 #: The optional message to display if the test evaluates to a falsey value. 

2428 msg: Optional[BaseExpression] = None 

2429 

2430 #: A comma separating test and message, if there is a message. 

2431 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT 

2432 

2433 #: Whitespace appearing after the ``assert`` keyword and before the test. 

2434 whitespace_after_assert: SimpleWhitespace = SimpleWhitespace.field(" ") 

2435 

2436 #: Optional semicolon when this is used in a statement line. This semicolon 

2437 #: owns the whitespace on both sides of it when it is used. 

2438 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

2439 

2440 def _validate(self) -> None: 

2441 # Validate whitespace 

2442 if ( 

2443 self.whitespace_after_assert.empty 

2444 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT) 

2445 ): 

2446 raise CSTValidationError("Must have at least one space after 'assert'.") 

2447 

2448 # Validate comma rules 

2449 if self.msg is None and isinstance(self.comma, Comma): 

2450 raise CSTValidationError("Cannot have trailing comma after 'test'.") 

2451 

2452 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Assert": 

2453 return Assert( 

2454 whitespace_after_assert=visit_required( 

2455 self, "whitespace_after_assert", self.whitespace_after_assert, visitor 

2456 ), 

2457 test=visit_required(self, "test", self.test, visitor), 

2458 comma=visit_sentinel(self, "comma", self.comma, visitor), 

2459 msg=visit_optional(self, "msg", self.msg, visitor), 

2460 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

2461 ) 

2462 

2463 def _codegen_impl( 

2464 self, state: CodegenState, default_semicolon: bool = False 

2465 ) -> None: 

2466 with state.record_syntactic_position(self): 

2467 state.add_token("assert") 

2468 self.whitespace_after_assert._codegen(state) 

2469 self.test._codegen(state) 

2470 

2471 comma = self.comma 

2472 msg = self.msg 

2473 if isinstance(comma, MaybeSentinel): 

2474 if msg is not None: 

2475 state.add_token(", ") 

2476 else: 

2477 comma._codegen(state) 

2478 if msg is not None: 

2479 msg._codegen(state) 

2480 

2481 semicolon = self.semicolon 

2482 if isinstance(semicolon, MaybeSentinel): 

2483 if default_semicolon: 

2484 state.add_token("; ") 

2485 elif isinstance(semicolon, Semicolon): 

2486 semicolon._codegen(state) 

2487 

2488 

2489@add_slots 

2490@dataclass(frozen=True) 

2491class NameItem(CSTNode): 

2492 """ 

2493 A single identifier name inside a :class:`Global` or :class:`Nonlocal` statement. 

2494 

2495 This exists because a list of names in a ``global`` or ``nonlocal`` statement need 

2496 to be separated by a comma, which ends up owned by the :class:`NameItem` node. 

2497 """ 

2498 

2499 #: Identifier name. 

2500 name: Name 

2501 

2502 #: This is forbidden for the last :class:`NameItem` in a 

2503 #: :class:`Global`/:class:`Nonlocal`, but all other tems inside a ``global`` or 

2504 #: ``nonlocal`` statement must contain a comma to separate them. 

2505 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT 

2506 

2507 def _validate(self) -> None: 

2508 # No parens around names here 

2509 if len(self.name.lpar) > 0 or len(self.name.rpar) > 0: 

2510 raise CSTValidationError("Cannot have parens around names in NameItem.") 

2511 

2512 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "NameItem": 

2513 return NameItem( 

2514 name=visit_required(self, "name", self.name, visitor), 

2515 comma=visit_sentinel(self, "comma", self.comma, visitor), 

2516 ) 

2517 

2518 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None: 

2519 with state.record_syntactic_position(self): 

2520 self.name._codegen(state) 

2521 

2522 comma = self.comma 

2523 if comma is MaybeSentinel.DEFAULT and default_comma: 

2524 state.add_token(", ") 

2525 elif isinstance(comma, Comma): 

2526 comma._codegen(state) 

2527 

2528 

2529@add_slots 

2530@dataclass(frozen=True) 

2531class Global(BaseSmallStatement): 

2532 """ 

2533 A ``global`` statement. 

2534 """ 

2535 

2536 #: A list of one or more names. 

2537 names: Sequence[NameItem] 

2538 

2539 #: Whitespace appearing after the ``global`` keyword and before the first name. 

2540 whitespace_after_global: SimpleWhitespace = SimpleWhitespace.field(" ") 

2541 

2542 #: Optional semicolon when this is used in a statement line. This semicolon 

2543 #: owns the whitespace on both sides of it when it is used. 

2544 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

2545 

2546 def _validate(self) -> None: 

2547 if len(self.names) == 0: 

2548 raise CSTValidationError( 

2549 "A Global statement must have at least one NameItem." 

2550 ) 

2551 if self.names[-1].comma != MaybeSentinel.DEFAULT: 

2552 raise CSTValidationError( 

2553 "The last NameItem in a Global cannot have a trailing comma." 

2554 ) 

2555 if self.whitespace_after_global.empty: 

2556 raise CSTValidationError( 

2557 "Must have at least one space after 'global' keyword." 

2558 ) 

2559 

2560 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Global": 

2561 return Global( 

2562 whitespace_after_global=visit_required( 

2563 self, "whitespace_after_global", self.whitespace_after_global, visitor 

2564 ), 

2565 names=visit_sequence(self, "names", self.names, visitor), 

2566 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

2567 ) 

2568 

2569 def _codegen_impl( 

2570 self, state: CodegenState, default_semicolon: bool = False 

2571 ) -> None: 

2572 with state.record_syntactic_position(self): 

2573 state.add_token("global") 

2574 self.whitespace_after_global._codegen(state) 

2575 last_name = len(self.names) - 1 

2576 for i, name in enumerate(self.names): 

2577 name._codegen(state, default_comma=(i != last_name)) 

2578 

2579 semicolon = self.semicolon 

2580 if isinstance(semicolon, MaybeSentinel): 

2581 if default_semicolon: 

2582 state.add_token("; ") 

2583 elif isinstance(semicolon, Semicolon): 

2584 semicolon._codegen(state) 

2585 

2586 

2587@add_slots 

2588@dataclass(frozen=True) 

2589class Nonlocal(BaseSmallStatement): 

2590 """ 

2591 A ``nonlocal`` statement. 

2592 """ 

2593 

2594 #: A list of one or more names. 

2595 names: Sequence[NameItem] 

2596 

2597 #: Whitespace appearing after the ``global`` keyword and before the first name. 

2598 whitespace_after_nonlocal: SimpleWhitespace = SimpleWhitespace.field(" ") 

2599 

2600 #: Optional semicolon when this is used in a statement line. This semicolon 

2601 #: owns the whitespace on both sides of it when it is used. 

2602 semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT 

2603 

2604 def _validate(self) -> None: 

2605 if len(self.names) == 0: 

2606 raise CSTValidationError( 

2607 "A Nonlocal statement must have at least one NameItem." 

2608 ) 

2609 if self.names[-1].comma != MaybeSentinel.DEFAULT: 

2610 raise CSTValidationError( 

2611 "The last NameItem in a Nonlocal cannot have a trailing comma." 

2612 ) 

2613 if self.whitespace_after_nonlocal.empty: 

2614 raise CSTValidationError( 

2615 "Must have at least one space after 'nonlocal' keyword." 

2616 ) 

2617 

2618 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Nonlocal": 

2619 return Nonlocal( 

2620 whitespace_after_nonlocal=visit_required( 

2621 self, 

2622 "whitespace_after_nonlocal", 

2623 self.whitespace_after_nonlocal, 

2624 visitor, 

2625 ), 

2626 names=visit_sequence(self, "names", self.names, visitor), 

2627 semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), 

2628 ) 

2629 

2630 def _codegen_impl( 

2631 self, state: CodegenState, default_semicolon: bool = False 

2632 ) -> None: 

2633 with state.record_syntactic_position(self): 

2634 state.add_token("nonlocal") 

2635 self.whitespace_after_nonlocal._codegen(state) 

2636 last_name = len(self.names) - 1 

2637 for i, name in enumerate(self.names): 

2638 name._codegen(state, default_comma=(i != last_name)) 

2639 

2640 semicolon = self.semicolon 

2641 if isinstance(semicolon, MaybeSentinel): 

2642 if default_semicolon: 

2643 state.add_token("; ") 

2644 elif isinstance(semicolon, Semicolon): 

2645 semicolon._codegen(state) 

2646 

2647 

2648class MatchPattern(_BaseParenthesizedNode, ABC): 

2649 """ 

2650 A base class for anything that can appear as a pattern in a :class:`Match` 

2651 statement. 

2652 """ 

2653 

2654 __slots__ = () 

2655 

2656 

2657@add_slots 

2658@dataclass(frozen=True) 

2659# pyre-fixme[13]: Attribute `body` is never initialized. 

2660class Match(BaseCompoundStatement): 

2661 """ 

2662 A ``match`` statement. 

2663 """ 

2664 

2665 #: The subject of the match. 

2666 subject: BaseExpression 

2667 

2668 #: A non-empty list of match cases. 

2669 cases: Sequence["MatchCase"] 

2670 

2671 #: Sequence of empty lines appearing before this compound statement line. 

2672 leading_lines: Sequence[EmptyLine] = () 

2673 

2674 #: Whitespace between the ``match`` keyword and the subject. 

2675 whitespace_after_match: SimpleWhitespace = SimpleWhitespace.field(" ") 

2676 

2677 #: Whitespace after the subject but before the colon. 

2678 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

2679 

2680 #: Any optional trailing comment and the final ``NEWLINE`` at the end of the line. 

2681 whitespace_after_colon: TrailingWhitespace = TrailingWhitespace.field() 

2682 

2683 #: A string represents a specific indentation. A ``None`` value uses the modules's 

2684 #: default indentation. This is included because indentation is allowed to be 

2685 #: inconsistent across a file, just not ambiguously. 

2686 indent: Optional[str] = None 

2687 

2688 #: Any trailing comments or lines after the dedent that are owned by this match 

2689 #: block. Statements own preceeding and same-line trailing comments, but not 

2690 #: trailing lines, so it falls on :class:`Match` to own it. In the case 

2691 #: that a statement follows a :class:`Match` block, that statement will own the 

2692 #: comments and lines that are at the same indent as the statement, and this 

2693 #: :class:`Match` will own the comments and lines that are indented further. 

2694 footer: Sequence[EmptyLine] = () 

2695 

2696 def _validate(self) -> None: 

2697 if len(self.cases) == 0: 

2698 raise CSTValidationError("A match statement must have at least one case.") 

2699 

2700 indent = self.indent 

2701 if indent is not None: 

2702 if len(indent) == 0: 

2703 raise CSTValidationError( 

2704 "A match statement must have a non-zero width indent." 

2705 ) 

2706 if _INDENT_WHITESPACE_RE.fullmatch(indent) is None: 

2707 raise CSTValidationError( 

2708 "An indent must be composed of only whitespace characters." 

2709 ) 

2710 

2711 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Match": 

2712 return Match( 

2713 leading_lines=visit_sequence( 

2714 self, "leading_lines", self.leading_lines, visitor 

2715 ), 

2716 whitespace_after_match=visit_required( 

2717 self, "whitespace_after_match", self.whitespace_after_match, visitor 

2718 ), 

2719 subject=visit_required(self, "subject", self.subject, visitor), 

2720 whitespace_before_colon=visit_required( 

2721 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

2722 ), 

2723 whitespace_after_colon=visit_required( 

2724 self, "whitespace_after_colon", self.whitespace_after_colon, visitor 

2725 ), 

2726 indent=self.indent, 

2727 cases=visit_sequence(self, "cases", self.cases, visitor), 

2728 footer=visit_sequence(self, "footer", self.footer, visitor), 

2729 ) 

2730 

2731 def _codegen_impl(self, state: CodegenState) -> None: 

2732 for ll in self.leading_lines: 

2733 ll._codegen(state) 

2734 state.add_indent_tokens() 

2735 

2736 with state.record_syntactic_position(self, end_node=self.cases[-1]): 

2737 state.add_token("match") 

2738 self.whitespace_after_match._codegen(state) 

2739 self.subject._codegen(state) 

2740 self.whitespace_before_colon._codegen(state) 

2741 state.add_token(":") 

2742 self.whitespace_after_colon._codegen(state) 

2743 

2744 indent = self.indent 

2745 state.increase_indent(state.default_indent if indent is None else indent) 

2746 for c in self.cases: 

2747 c._codegen(state) 

2748 

2749 for f in self.footer: 

2750 f._codegen(state) 

2751 

2752 state.decrease_indent() 

2753 

2754 

2755@add_slots 

2756@dataclass(frozen=True) 

2757class MatchCase(CSTNode): 

2758 """ 

2759 A single ``case`` block of a :class:`Match` statement. 

2760 """ 

2761 

2762 #: The pattern that ``subject`` will be matched against. 

2763 pattern: MatchPattern 

2764 

2765 #: The body of this case block, to be evaluated if ``pattern`` matches ``subject`` 

2766 #: and ``guard`` evaluates to a truthy value. 

2767 body: BaseSuite 

2768 

2769 #: Optional expression that will be evaluated if ``pattern`` matches ``subject``. 

2770 guard: Optional[BaseExpression] = None 

2771 

2772 #: Sequence of empty lines appearing before this case block. 

2773 leading_lines: Sequence[EmptyLine] = () 

2774 

2775 #: Whitespace directly after the ``case`` keyword. 

2776 whitespace_after_case: SimpleWhitespace = SimpleWhitespace.field(" ") 

2777 

2778 #: Whitespace before the ``if`` keyword in case there's a guard expression. 

2779 whitespace_before_if: SimpleWhitespace = SimpleWhitespace.field("") 

2780 

2781 #: Whitespace after the ``if`` keyword in case there's a guard expression. 

2782 whitespace_after_if: SimpleWhitespace = SimpleWhitespace.field("") 

2783 

2784 #: Whitespace before the colon. 

2785 whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") 

2786 

2787 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode": 

2788 return MatchCase( 

2789 leading_lines=visit_sequence( 

2790 self, "leading_lines", self.leading_lines, visitor 

2791 ), 

2792 whitespace_after_case=visit_required( 

2793 self, "whitespace_after_case", self.whitespace_after_case, visitor 

2794 ), 

2795 pattern=visit_required(self, "pattern", self.pattern, visitor), 

2796 # pyre-fixme[6]: Expected `SimpleWhitespace` for 4th param but got 

2797 # `Optional[SimpleWhitespace]`. 

2798 whitespace_before_if=visit_optional( 

2799 self, "whitespace_before_if", self.whitespace_before_if, visitor 

2800 ), 

2801 # pyre-fixme[6]: Expected `SimpleWhitespace` for 5th param but got 

2802 # `Optional[SimpleWhitespace]`. 

2803 whitespace_after_if=visit_optional( 

2804 self, "whitespace_after_if", self.whitespace_after_if, visitor 

2805 ), 

2806 guard=visit_optional(self, "guard", self.guard, visitor), 

2807 body=visit_required(self, "body", self.body, visitor), 

2808 ) 

2809 

2810 def _codegen_impl(self, state: CodegenState) -> None: 

2811 for ll in self.leading_lines: 

2812 ll._codegen(state) 

2813 state.add_indent_tokens() 

2814 with state.record_syntactic_position(self, end_node=self.body): 

2815 state.add_token("case") 

2816 self.whitespace_after_case._codegen(state) 

2817 self.pattern._codegen(state) 

2818 

2819 guard = self.guard 

2820 if guard is not None: 

2821 self.whitespace_before_if._codegen(state) 

2822 state.add_token("if") 

2823 self.whitespace_after_if._codegen(state) 

2824 guard._codegen(state) 

2825 

2826 self.whitespace_before_colon._codegen(state) 

2827 state.add_token(":") 

2828 self.body._codegen(state) 

2829 

2830 

2831@add_slots 

2832@dataclass(frozen=True) 

2833class MatchValue(MatchPattern): 

2834 """ 

2835 A match literal or value pattern that compares by equality. 

2836 """ 

2837 

2838 #: an expression to compare to 

2839 value: BaseExpression 

2840 

2841 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode": 

2842 return MatchValue(value=visit_required(self, "value", self.value, visitor)) 

2843 

2844 def _codegen_impl(self, state: CodegenState) -> None: 

2845 with state.record_syntactic_position(self): 

2846 self.value._codegen(state) 

2847 

2848 @property 

2849 def lpar(self) -> Sequence[LeftParen]: 

2850 return self.value.lpar 

2851 

2852 @lpar.setter 

2853 def lpar(self, value: Sequence[LeftParen]) -> None: 

2854 self.value.lpar = value 

2855 

2856 @property 

2857 def rpar(self) -> Sequence[RightParen]: 

2858 return self.value.rpar 

2859 

2860 @rpar.setter 

2861 def rpar(self, value: Sequence[RightParen]) -> None: 

2862 self.value.rpar = value 

2863 

2864 

2865@add_slots 

2866@dataclass(frozen=True) 

2867class MatchSingleton(MatchPattern): 

2868 """ 

2869 A match literal pattern that compares by identity. 

2870 """ 

2871 

2872 #: a literal to compare to 

2873 value: Name 

2874 

2875 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode": 

2876 return MatchSingleton(value=visit_required(self, "value", self.value, visitor)) 

2877 

2878 def _validate(self) -> None: 

2879 if self.value.value not in {"True", "False", "None"}: 

2880 raise CSTValidationError( 

2881 "A match singleton can only be True, False, or None" 

2882 ) 

2883 

2884 def _codegen_impl(self, state: CodegenState) -> None: 

2885 with state.record_syntactic_position(self): 

2886 self.value._codegen(state) 

2887 

2888 @property 

2889 def lpar(self) -> Sequence[LeftParen]: 

2890 return self.value.lpar 

2891 

2892 @lpar.setter 

2893 def lpar(self, value: Sequence[LeftParen]) -> None: 

2894 # pyre-fixme[41]: Cannot reassign final attribute `lpar`. 

2895 self.value.lpar = value 

2896 

2897 @property 

2898 def rpar(self) -> Sequence[RightParen]: 

2899 return self.value.rpar 

2900 

2901 @rpar.setter 

2902 def rpar(self, value: Sequence[RightParen]) -> None: 

2903 # pyre-fixme[41]: Cannot reassign final attribute `rpar`. 

2904 self.value.rpar = value 

2905 

2906 

2907@add_slots 

2908@dataclass(frozen=True) 

2909class MatchSequenceElement(CSTNode): 

2910 """ 

2911 An element in a sequence match pattern. 

2912 """ 

2913 

2914 value: MatchPattern 

2915 

2916 #: An optional trailing comma. 

2917 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT 

2918 

2919 def _visit_and_replace_children( 

2920 self, visitor: CSTVisitorT 

2921 ) -> "MatchSequenceElement": 

2922 return MatchSequenceElement( 

2923 value=visit_required(self, "value", self.value, visitor), 

2924 comma=visit_sentinel(self, "comma", self.comma, visitor), 

2925 ) 

2926 

2927 def _codegen_impl( 

2928 self, 

2929 state: CodegenState, 

2930 default_comma: bool = False, 

2931 default_comma_whitespace: bool = True, 

2932 ) -> None: 

2933 with state.record_syntactic_position(self): 

2934 self.value._codegen(state) 

2935 comma = self.comma 

2936 if comma is MaybeSentinel.DEFAULT and default_comma: 

2937 state.add_token(", " if default_comma_whitespace else ",") 

2938 elif isinstance(comma, Comma): 

2939 comma._codegen(state) 

2940 

2941 

2942@add_slots 

2943@dataclass(frozen=True) 

2944class MatchStar(CSTNode): 

2945 """ 

2946 A starred element in a sequence match pattern. Matches the rest of the sequence. 

2947 """ 

2948 

2949 #: The name of the pattern binding. A ``None`` value represents ``*_``. 

2950 name: Optional[Name] = None 

2951 

2952 #: An optional trailing comma. 

2953 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT 

2954 

2955 #: Optional whitespace between the star and the name. 

2956 whitespace_before_name: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

2957 

2958 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchStar": 

2959 return MatchStar( 

2960 whitespace_before_name=visit_required( 

2961 self, "whitespace_before_name", self.whitespace_before_name, visitor 

2962 ), 

2963 name=visit_optional(self, "name", self.name, visitor), 

2964 comma=visit_sentinel(self, "comma", self.comma, visitor), 

2965 ) 

2966 

2967 def _codegen_impl( 

2968 self, 

2969 state: CodegenState, 

2970 default_comma: bool = False, 

2971 default_comma_whitespace: bool = True, 

2972 ) -> None: 

2973 with state.record_syntactic_position(self): 

2974 state.add_token("*") 

2975 self.whitespace_before_name._codegen(state) 

2976 name = self.name 

2977 if name is None: 

2978 state.add_token("_") 

2979 else: 

2980 name._codegen(state) 

2981 comma = self.comma 

2982 if comma is MaybeSentinel.DEFAULT and default_comma: 

2983 state.add_token(", " if default_comma_whitespace else ",") 

2984 elif isinstance(comma, Comma): 

2985 comma._codegen(state) 

2986 

2987 

2988class MatchSequence(MatchPattern, ABC): 

2989 """ 

2990 A match sequence pattern. It's either a :class:`MatchList` or a :class:`MatchTuple`. 

2991 Matches a variable length sequence if one of the patterns is a :class:`MatchStar`, 

2992 otherwise matches a fixed length sequence. 

2993 """ 

2994 

2995 __slots__ = () 

2996 

2997 #: Patterns to be matched against the subject elements if it is a sequence. 

2998 patterns: Sequence[Union[MatchSequenceElement, MatchStar]] 

2999 

3000 

3001@add_slots 

3002@dataclass(frozen=True) 

3003class MatchList(MatchSequence): 

3004 """ 

3005 A list match pattern. It's either an "open sequence pattern" (without brackets) or a 

3006 regular list literal (with brackets). 

3007 """ 

3008 

3009 #: Patterns to be matched against the subject elements if it is a sequence. 

3010 patterns: Sequence[Union[MatchSequenceElement, MatchStar]] 

3011 

3012 #: An optional left bracket. If missing, this is an open sequence pattern. 

3013 lbracket: Optional[LeftSquareBracket] = LeftSquareBracket.field() 

3014 

3015 #: An optional left bracket. If missing, this is an open sequence pattern. 

3016 rbracket: Optional[RightSquareBracket] = RightSquareBracket.field() 

3017 

3018 #: Parenthesis at the beginning of the node 

3019 lpar: Sequence[LeftParen] = () 

3020 #: Parentheses after the pattern, but before a comma (if there is one). 

3021 rpar: Sequence[RightParen] = () 

3022 

3023 def _validate(self) -> None: 

3024 if self.lbracket and not self.rbracket: 

3025 raise CSTValidationError("Cannot have left bracket without right bracket") 

3026 if self.rbracket and not self.lbracket: 

3027 raise CSTValidationError("Cannot have right bracket without left bracket") 

3028 

3029 if not self.patterns and not self.lbracket: 

3030 raise CSTValidationError( 

3031 "Must have brackets if matching against empty list" 

3032 ) 

3033 

3034 super(MatchList, self)._validate() 

3035 

3036 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchList": 

3037 return MatchList( 

3038 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3039 lbracket=visit_optional(self, "lbracket", self.lbracket, visitor), 

3040 patterns=visit_sequence(self, "patterns", self.patterns, visitor), 

3041 rbracket=visit_optional(self, "rbracket", self.rbracket, visitor), 

3042 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3043 ) 

3044 

3045 def _codegen_impl(self, state: CodegenState) -> None: 

3046 with self._parenthesize(state): 

3047 lbracket = self.lbracket 

3048 if lbracket is not None: 

3049 lbracket._codegen(state) 

3050 pats = self.patterns 

3051 for idx, pat in enumerate(pats): 

3052 pat._codegen(state, default_comma=(idx < len(pats) - 1)) 

3053 rbracket = self.rbracket 

3054 if rbracket is not None: 

3055 rbracket._codegen(state) 

3056 

3057 

3058@add_slots 

3059@dataclass(frozen=True) 

3060class MatchTuple(MatchSequence): 

3061 """ 

3062 A tuple match pattern. 

3063 """ 

3064 

3065 #: Patterns to be matched against the subject elements if it is a sequence. 

3066 patterns: Sequence[Union[MatchSequenceElement, MatchStar]] 

3067 

3068 #: Parenthesis at the beginning of the node 

3069 lpar: Sequence[LeftParen] = field(default_factory=lambda: (LeftParen(),)) 

3070 #: Parentheses after the pattern, but before a comma (if there is one). 

3071 rpar: Sequence[RightParen] = field(default_factory=lambda: (RightParen(),)) 

3072 

3073 def _validate(self) -> None: 

3074 if len(self.lpar) < 1: 

3075 raise CSTValidationError( 

3076 "Tuple patterns must have at least pair of parenthesis" 

3077 ) 

3078 

3079 super(MatchTuple, self)._validate() 

3080 

3081 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchTuple": 

3082 return MatchTuple( 

3083 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3084 patterns=visit_sequence(self, "patterns", self.patterns, visitor), 

3085 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3086 ) 

3087 

3088 def _codegen_impl(self, state: CodegenState) -> None: 

3089 with self._parenthesize(state): 

3090 pats = self.patterns 

3091 patlen = len(pats) 

3092 for idx, pat in enumerate(pats): 

3093 pat._codegen( 

3094 state, 

3095 default_comma=patlen == 1 or (idx < patlen - 1), 

3096 default_comma_whitespace=patlen != 1, 

3097 ) 

3098 

3099 

3100@add_slots 

3101@dataclass(frozen=True) 

3102class MatchMappingElement(CSTNode): 

3103 """ 

3104 A ``key: value`` pair in a match mapping pattern. 

3105 """ 

3106 

3107 key: BaseExpression 

3108 

3109 #: The pattern to be matched corresponding to ``key``. 

3110 pattern: MatchPattern 

3111 

3112 #: An optional trailing comma. 

3113 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT 

3114 

3115 #: Whitespace between ``key`` and the colon. 

3116 whitespace_before_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

3117 

3118 #: Whitespace between the colon and ``pattern``. 

3119 whitespace_after_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3120 

3121 def _visit_and_replace_children( 

3122 self, visitor: CSTVisitorT 

3123 ) -> "MatchMappingElement": 

3124 return MatchMappingElement( 

3125 key=visit_required(self, "key", self.key, visitor), 

3126 whitespace_before_colon=visit_required( 

3127 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

3128 ), 

3129 whitespace_after_colon=visit_required( 

3130 self, "whitespace_after_colon", self.whitespace_after_colon, visitor 

3131 ), 

3132 pattern=visit_required(self, "pattern", self.pattern, visitor), 

3133 comma=visit_sentinel(self, "comma", self.comma, visitor), 

3134 ) 

3135 

3136 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None: 

3137 with state.record_syntactic_position(self): 

3138 self.key._codegen(state) 

3139 self.whitespace_before_colon._codegen(state) 

3140 state.add_token(":") 

3141 self.whitespace_after_colon._codegen(state) 

3142 self.pattern._codegen(state) 

3143 comma = self.comma 

3144 if comma is MaybeSentinel.DEFAULT and default_comma: 

3145 state.add_token(", ") 

3146 elif isinstance(comma, Comma): 

3147 comma._codegen(state) 

3148 

3149 

3150@add_slots 

3151@dataclass(frozen=True) 

3152class MatchMapping(MatchPattern): 

3153 """ 

3154 A match mapping pattern. 

3155 """ 

3156 

3157 #: A sequence of mapping elements. 

3158 elements: Sequence[MatchMappingElement] = () 

3159 

3160 #: Left curly brace at the beginning of the pattern. 

3161 lbrace: LeftCurlyBrace = LeftCurlyBrace.field() 

3162 

3163 #: Right curly brace at the end of the pattern. 

3164 rbrace: RightCurlyBrace = RightCurlyBrace.field() 

3165 

3166 #: An optional name to capture the remaining elements of the mapping. 

3167 rest: Optional[Name] = None 

3168 

3169 #: Optional whitespace between stars and ``rest``. 

3170 whitespace_before_rest: SimpleWhitespace = SimpleWhitespace.field("") 

3171 

3172 #: An optional trailing comma attached to ``rest``. 

3173 trailing_comma: Optional[Comma] = None 

3174 

3175 #: Parenthesis at the beginning of the node 

3176 lpar: Sequence[LeftParen] = () 

3177 #: Parentheses after the pattern 

3178 rpar: Sequence[RightParen] = () 

3179 

3180 def _validate(self) -> None: 

3181 if isinstance(self.trailing_comma, Comma) and self.rest is not None: 

3182 raise CSTValidationError("Cannot have a trailing comma without **rest") 

3183 super(MatchMapping, self)._validate() 

3184 

3185 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchMapping": 

3186 return MatchMapping( 

3187 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3188 lbrace=visit_required(self, "lbrace", self.lbrace, visitor), 

3189 elements=visit_sequence(self, "elements", self.elements, visitor), 

3190 whitespace_before_rest=visit_required( 

3191 self, "whitespace_before_rest", self.whitespace_before_rest, visitor 

3192 ), 

3193 rest=visit_optional(self, "rest", self.rest, visitor), 

3194 trailing_comma=visit_optional( 

3195 self, "trailing_comma", self.trailing_comma, visitor 

3196 ), 

3197 rbrace=visit_required(self, "rbrace", self.rbrace, visitor), 

3198 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3199 ) 

3200 

3201 def _codegen_impl(self, state: CodegenState) -> None: 

3202 with self._parenthesize(state): 

3203 self.lbrace._codegen(state) 

3204 elems = self.elements 

3205 rest = self.rest 

3206 for idx, el in enumerate(elems): 

3207 el._codegen( 

3208 state, default_comma=rest is not None or idx < len(elems) - 1 

3209 ) 

3210 

3211 if rest is not None: 

3212 state.add_token("**") 

3213 self.whitespace_before_rest._codegen(state) 

3214 rest._codegen(state) 

3215 comma = self.trailing_comma 

3216 if comma is not None: 

3217 comma._codegen(state) 

3218 

3219 self.rbrace._codegen(state) 

3220 

3221 

3222@add_slots 

3223@dataclass(frozen=True) 

3224class MatchKeywordElement(CSTNode): 

3225 """ 

3226 A key=value pair in a :class:`MatchClass`. 

3227 """ 

3228 

3229 key: Name 

3230 

3231 #: The pattern to be matched against the attribute named ``key``. 

3232 pattern: MatchPattern 

3233 

3234 #: An optional trailing comma. 

3235 comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT 

3236 

3237 #: Whitespace between ``key`` and the equals sign. 

3238 whitespace_before_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

3239 

3240 #: Whitespace between the equals sign and ``pattern``. 

3241 whitespace_after_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

3242 

3243 def _visit_and_replace_children( 

3244 self, visitor: CSTVisitorT 

3245 ) -> "MatchKeywordElement": 

3246 return MatchKeywordElement( 

3247 key=visit_required(self, "key", self.key, visitor), 

3248 whitespace_before_equal=visit_required( 

3249 self, "whitespace_before_equal", self.whitespace_before_equal, visitor 

3250 ), 

3251 whitespace_after_equal=visit_required( 

3252 self, "whitespace_after_equal", self.whitespace_after_equal, visitor 

3253 ), 

3254 pattern=visit_required(self, "pattern", self.pattern, visitor), 

3255 comma=visit_sentinel(self, "comma", self.comma, visitor), 

3256 ) 

3257 

3258 def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None: 

3259 with state.record_syntactic_position(self): 

3260 self.key._codegen(state) 

3261 self.whitespace_before_equal._codegen(state) 

3262 state.add_token("=") 

3263 self.whitespace_after_equal._codegen(state) 

3264 self.pattern._codegen(state) 

3265 comma = self.comma 

3266 if comma is MaybeSentinel.DEFAULT and default_comma: 

3267 state.add_token(", ") 

3268 elif isinstance(comma, Comma): 

3269 comma._codegen(state) 

3270 

3271 

3272@add_slots 

3273@dataclass(frozen=True) 

3274class MatchClass(MatchPattern): 

3275 """ 

3276 A match class pattern. 

3277 """ 

3278 

3279 #: An expression giving the nominal class to be matched. 

3280 cls: BaseExpression 

3281 

3282 #: A sequence of patterns to be matched against the class defined sequence of 

3283 #: pattern matching attributes. 

3284 patterns: Sequence[MatchSequenceElement] = () 

3285 

3286 #: A sequence of additional attribute names and corresponding patterns to be 

3287 #: matched. 

3288 kwds: Sequence[MatchKeywordElement] = () 

3289 

3290 #: Whitespace between the class name and the left parenthesis. 

3291 whitespace_after_cls: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

3292 

3293 #: Whitespace between the left parenthesis and the first pattern. 

3294 whitespace_before_patterns: BaseParenthesizableWhitespace = SimpleWhitespace.field( 

3295 "" 

3296 ) 

3297 

3298 #: Whitespace between the last pattern and the right parenthesis. 

3299 whitespace_after_kwds: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

3300 

3301 #: Parenthesis at the beginning of the node 

3302 lpar: Sequence[LeftParen] = () 

3303 #: Parentheses after the pattern 

3304 rpar: Sequence[RightParen] = () 

3305 

3306 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchClass": 

3307 return MatchClass( 

3308 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3309 cls=visit_required(self, "cls", self.cls, visitor), 

3310 whitespace_after_cls=visit_required( 

3311 self, "whitespace_after_cls", self.whitespace_after_cls, visitor 

3312 ), 

3313 whitespace_before_patterns=visit_required( 

3314 self, 

3315 "whitespace_before_patterns", 

3316 self.whitespace_before_patterns, 

3317 visitor, 

3318 ), 

3319 patterns=visit_sequence(self, "patterns", self.patterns, visitor), 

3320 kwds=visit_sequence(self, "kwds", self.kwds, visitor), 

3321 whitespace_after_kwds=visit_required( 

3322 self, "whitespace_after_kwds", self.whitespace_after_kwds, visitor 

3323 ), 

3324 ) 

3325 

3326 def _codegen_impl(self, state: CodegenState) -> None: 

3327 with self._parenthesize(state): 

3328 self.cls._codegen(state) 

3329 self.whitespace_after_cls._codegen(state) 

3330 state.add_token("(") 

3331 self.whitespace_before_patterns._codegen(state) 

3332 pats = self.patterns 

3333 kwds = self.kwds 

3334 for idx, pat in enumerate(pats): 

3335 pat._codegen(state, default_comma=idx + 1 < len(pats) + len(kwds)) 

3336 for idx, kwd in enumerate(kwds): 

3337 kwd._codegen(state, default_comma=idx + 1 < len(kwds)) 

3338 self.whitespace_after_kwds._codegen(state) 

3339 state.add_token(")") 

3340 

3341 

3342@add_slots 

3343@dataclass(frozen=True) 

3344class MatchAs(MatchPattern): 

3345 """ 

3346 A match "as-pattern", capture pattern, or wildcard pattern. 

3347 """ 

3348 

3349 #: The match pattern that the subject will be matched against. If this is ``None``, 

3350 #: the node represents a capture pattern (i.e. a bare name) and will always succeed. 

3351 pattern: Optional[MatchPattern] = None 

3352 

3353 #: The name that will be bound if the pattern is successful. If this is ``None``, 

3354 #: ``pattern`` must also be ``None`` and the node represents the wildcard pattern 

3355 #: (i.e. ``_``). 

3356 name: Optional[Name] = None 

3357 

3358 #: Whitespace between ``pattern`` and the ``as`` keyword (if ``pattern`` is not 

3359 #: ``None``) 

3360 whitespace_before_as: Union[ 

3361 BaseParenthesizableWhitespace, MaybeSentinel 

3362 ] = MaybeSentinel.DEFAULT 

3363 

3364 #: Whitespace between the ``as`` keyword and ``name`` (if ``pattern`` is not 

3365 #: ``None``) 

3366 whitespace_after_as: Union[ 

3367 BaseParenthesizableWhitespace, MaybeSentinel 

3368 ] = MaybeSentinel.DEFAULT 

3369 

3370 #: Parenthesis at the beginning of the node 

3371 lpar: Sequence[LeftParen] = () 

3372 #: Parentheses after the pattern 

3373 rpar: Sequence[RightParen] = () 

3374 

3375 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchAs": 

3376 return MatchAs( 

3377 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3378 pattern=visit_optional(self, "pattern", self.pattern, visitor), 

3379 whitespace_before_as=visit_sentinel( 

3380 self, "whitespace_before_as", self.whitespace_before_as, visitor 

3381 ), 

3382 whitespace_after_as=visit_sentinel( 

3383 self, "whitespace_after_as", self.whitespace_after_as, visitor 

3384 ), 

3385 name=visit_optional(self, "name", self.name, visitor), 

3386 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3387 ) 

3388 

3389 def _validate(self) -> None: 

3390 if self.name is None and self.pattern is not None: 

3391 raise CSTValidationError("Pattern must be None if name is None") 

3392 super(MatchAs, self)._validate() 

3393 

3394 def _codegen_impl(self, state: CodegenState) -> None: 

3395 with self._parenthesize(state): 

3396 pat = self.pattern 

3397 name = self.name 

3398 if pat is not None: 

3399 pat._codegen(state) 

3400 ws_before = self.whitespace_before_as 

3401 if ws_before is MaybeSentinel.DEFAULT: 

3402 state.add_token(" ") 

3403 elif isinstance(ws_before, BaseParenthesizableWhitespace): 

3404 ws_before._codegen(state) 

3405 state.add_token("as") 

3406 ws_after = self.whitespace_after_as 

3407 if ws_after is MaybeSentinel.DEFAULT: 

3408 state.add_token(" ") 

3409 elif isinstance(ws_after, BaseParenthesizableWhitespace): 

3410 ws_after._codegen(state) 

3411 if name is None: 

3412 state.add_token("_") 

3413 else: 

3414 name._codegen(state) 

3415 

3416 

3417@add_slots 

3418@dataclass(frozen=True) 

3419class MatchOrElement(CSTNode): 

3420 """ 

3421 An element in a :class:`MatchOr` node. 

3422 """ 

3423 

3424 pattern: MatchPattern 

3425 

3426 #: An optional ``|`` separator. 

3427 separator: Union[BitOr, MaybeSentinel] = MaybeSentinel.DEFAULT 

3428 

3429 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOrElement": 

3430 return MatchOrElement( 

3431 pattern=visit_required(self, "pattern", self.pattern, visitor), 

3432 separator=visit_sentinel(self, "separator", self.separator, visitor), 

3433 ) 

3434 

3435 def _codegen_impl( 

3436 self, state: CodegenState, default_separator: bool = False 

3437 ) -> None: 

3438 with state.record_syntactic_position(self): 

3439 self.pattern._codegen(state) 

3440 sep = self.separator 

3441 if sep is MaybeSentinel.DEFAULT and default_separator: 

3442 state.add_token(" | ") 

3443 elif isinstance(sep, BitOr): 

3444 sep._codegen(state) 

3445 

3446 

3447@add_slots 

3448@dataclass(frozen=True) 

3449class MatchOr(MatchPattern): 

3450 """ 

3451 A match "or-pattern". It matches each of its subpatterns in turn to the subject, 

3452 until one succeeds. The or-pattern is then deemed to succeed. If none of the 

3453 subpatterns succeed the or-pattern fails. 

3454 """ 

3455 

3456 #: The subpatterns to be tried in turn. 

3457 patterns: Sequence[MatchOrElement] 

3458 

3459 #: Parenthesis at the beginning of the node 

3460 lpar: Sequence[LeftParen] = () 

3461 #: Parentheses after the pattern 

3462 rpar: Sequence[RightParen] = () 

3463 

3464 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOr": 

3465 return MatchOr( 

3466 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3467 patterns=visit_sequence(self, "patterns", self.patterns, visitor), 

3468 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3469 ) 

3470 

3471 def _codegen_impl(self, state: CodegenState) -> None: 

3472 with self._parenthesize(state): 

3473 pats = self.patterns 

3474 for idx, pat in enumerate(pats): 

3475 pat._codegen(state, default_separator=idx + 1 < len(pats))