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

1414 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 

6 

7import re 

8from abc import ABC, abstractmethod 

9from ast import literal_eval 

10from contextlib import contextmanager 

11from dataclasses import dataclass, field 

12from enum import auto, Enum 

13from tokenize import ( 

14 Floatnumber as FLOATNUMBER_RE, 

15 Imagnumber as IMAGNUMBER_RE, 

16 Intnumber as INTNUMBER_RE, 

17) 

18from typing import Callable, Generator, Optional, Sequence, Union 

19 

20from typing_extensions import Literal 

21 

22from libcst._add_slots import add_slots 

23from libcst._maybe_sentinel import MaybeSentinel 

24from libcst._nodes.base import CSTCodegenError, CSTNode, CSTValidationError 

25from libcst._nodes.internal import ( 

26 CodegenState, 

27 visit_optional, 

28 visit_required, 

29 visit_sentinel, 

30 visit_sequence, 

31) 

32from libcst._nodes.op import ( 

33 AssignEqual, 

34 BaseBinaryOp, 

35 BaseBooleanOp, 

36 BaseCompOp, 

37 BaseUnaryOp, 

38 Colon, 

39 Comma, 

40 Dot, 

41 In, 

42 Is, 

43 IsNot, 

44 Not, 

45 NotIn, 

46) 

47from libcst._nodes.whitespace import BaseParenthesizableWhitespace, SimpleWhitespace 

48from libcst._visitors import CSTVisitorT 

49 

50 

51@add_slots 

52@dataclass(frozen=True) 

53class LeftSquareBracket(CSTNode): 

54 """ 

55 Used by various nodes to denote a subscript or list section. This doesn't own 

56 the whitespace to the left of it since this is owned by the parent node. 

57 """ 

58 

59 #: Any space that appears directly after this left square bracket. 

60 whitespace_after: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

61 

62 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "LeftSquareBracket": 

63 return LeftSquareBracket( 

64 whitespace_after=visit_required( 

65 self, "whitespace_after", self.whitespace_after, visitor 

66 ) 

67 ) 

68 

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

70 state.add_token("[") 

71 self.whitespace_after._codegen(state) 

72 

73 

74@add_slots 

75@dataclass(frozen=True) 

76class RightSquareBracket(CSTNode): 

77 """ 

78 Used by various nodes to denote a subscript or list section. This doesn't own 

79 the whitespace to the right of it since this is owned by the parent node. 

80 """ 

81 

82 #: Any space that appears directly before this right square bracket. 

83 whitespace_before: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

84 

85 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "RightSquareBracket": 

86 return RightSquareBracket( 

87 whitespace_before=visit_required( 

88 self, "whitespace_before", self.whitespace_before, visitor 

89 ) 

90 ) 

91 

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

93 self.whitespace_before._codegen(state) 

94 state.add_token("]") 

95 

96 

97@add_slots 

98@dataclass(frozen=True) 

99class LeftCurlyBrace(CSTNode): 

100 """ 

101 Used by various nodes to denote a dict or set. This doesn't own the whitespace to 

102 the left of it since this is owned by the parent node. 

103 """ 

104 

105 #: Any space that appears directly after this left curly brace. 

106 whitespace_after: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

107 

108 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "LeftCurlyBrace": 

109 return LeftCurlyBrace( 

110 whitespace_after=visit_required( 

111 self, "whitespace_after", self.whitespace_after, visitor 

112 ) 

113 ) 

114 

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

116 state.add_token("{") 

117 self.whitespace_after._codegen(state) 

118 

119 

120@add_slots 

121@dataclass(frozen=True) 

122class RightCurlyBrace(CSTNode): 

123 """ 

124 Used by various nodes to denote a dict or set. This doesn't own the whitespace to 

125 the right of it since this is owned by the parent node. 

126 """ 

127 

128 #: Any space that appears directly before this right curly brace. 

129 whitespace_before: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

130 

131 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "RightCurlyBrace": 

132 return RightCurlyBrace( 

133 whitespace_before=visit_required( 

134 self, "whitespace_before", self.whitespace_before, visitor 

135 ) 

136 ) 

137 

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

139 self.whitespace_before._codegen(state) 

140 state.add_token("}") 

141 

142 

143@add_slots 

144@dataclass(frozen=True) 

145class LeftParen(CSTNode): 

146 """ 

147 Used by various nodes to denote a parenthesized section. This doesn't own 

148 the whitespace to the left of it since this is owned by the parent node. 

149 """ 

150 

151 #: Any space that appears directly after this left parenthesis. 

152 whitespace_after: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

153 

154 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "LeftParen": 

155 return LeftParen( 

156 whitespace_after=visit_required( 

157 self, "whitespace_after", self.whitespace_after, visitor 

158 ) 

159 ) 

160 

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

162 state.add_token("(") 

163 self.whitespace_after._codegen(state) 

164 

165 

166@add_slots 

167@dataclass(frozen=True) 

168class RightParen(CSTNode): 

169 """ 

170 Used by various nodes to denote a parenthesized section. This doesn't own 

171 the whitespace to the right of it since this is owned by the parent node. 

172 """ 

173 

174 #: Any space that appears directly after this left parenthesis. 

175 whitespace_before: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

176 

177 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "RightParen": 

178 return RightParen( 

179 whitespace_before=visit_required( 

180 self, "whitespace_before", self.whitespace_before, visitor 

181 ) 

182 ) 

183 

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

185 self.whitespace_before._codegen(state) 

186 state.add_token(")") 

187 

188 

189@add_slots 

190@dataclass(frozen=True) 

191class Asynchronous(CSTNode): 

192 """ 

193 Used by asynchronous function definitions, as well as ``async for`` and 

194 ``async with``. 

195 """ 

196 

197 #: Any space that appears directly after this async keyword. 

198 whitespace_after: SimpleWhitespace = SimpleWhitespace.field(" ") 

199 

200 def _validate(self) -> None: 

201 if len(self.whitespace_after.value) < 1: 

202 raise CSTValidationError("Must have at least one space after Asynchronous.") 

203 

204 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Asynchronous": 

205 return Asynchronous( 

206 whitespace_after=visit_required( 

207 self, "whitespace_after", self.whitespace_after, visitor 

208 ) 

209 ) 

210 

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

212 with state.record_syntactic_position(self): 

213 state.add_token("async") 

214 self.whitespace_after._codegen(state) 

215 

216 

217class _BaseParenthesizedNode(CSTNode, ABC): 

218 """ 

219 We don't want to have another level of indirection for parenthesis in 

220 our tree, since that makes us more of a CST than an AST. So, all the 

221 expressions or atoms that can be wrapped in parenthesis will subclass 

222 this to get that functionality. 

223 """ 

224 

225 __slots__ = () 

226 

227 lpar: Sequence[LeftParen] = () 

228 # Sequence of parenthesis for precedence dictation. 

229 rpar: Sequence[RightParen] = () 

230 

231 def _validate(self) -> None: 

232 if self.lpar and not self.rpar: 

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

234 if not self.lpar and self.rpar: 

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

236 if len(self.lpar) != len(self.rpar): 

237 raise CSTValidationError("Cannot have unbalanced parens.") 

238 

239 @contextmanager 

240 def _parenthesize(self, state: CodegenState) -> Generator[None, None, None]: 

241 for lpar in self.lpar: 

242 lpar._codegen(state) 

243 with state.record_syntactic_position(self): 

244 yield 

245 for rpar in self.rpar: 

246 rpar._codegen(state) 

247 

248 

249class ExpressionPosition(Enum): 

250 LEFT = auto() 

251 RIGHT = auto() 

252 

253 

254class BaseExpression(_BaseParenthesizedNode, ABC): 

255 """ 

256 An base class for all expressions. :class:`BaseExpression` contains no fields. 

257 """ 

258 

259 __slots__ = () 

260 

261 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

262 """ 

263 Returns true if this expression is safe to be use with a word operator 

264 such as "not" without space between the operator an ourselves. Examples 

265 where this is true are "not(True)", "(1)in[1,2,3]", etc. This base 

266 function handles parenthesized nodes, but certain nodes such as tuples, 

267 dictionaries and lists will override this to signifiy that they're always 

268 safe. 

269 """ 

270 

271 return len(self.lpar) > 0 and len(self.rpar) > 0 

272 

273 def _check_left_right_word_concatenation_safety( 

274 self, 

275 position: ExpressionPosition, 

276 left: "BaseExpression", 

277 right: "BaseExpression", 

278 ) -> bool: 

279 if position == ExpressionPosition.RIGHT: 

280 return left._safe_to_use_with_word_operator(ExpressionPosition.RIGHT) 

281 if position == ExpressionPosition.LEFT: 

282 return right._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

283 return False 

284 

285 

286class BaseAssignTargetExpression(BaseExpression, ABC): 

287 """ 

288 An expression that's valid on the left side of an assignment. That assignment may 

289 be part an :class:`Assign` node, or it may be part of a number of other control 

290 structures that perform an assignment, such as a :class:`For` loop. 

291 

292 Python's grammar defines all expression as valid in this position, but the AST 

293 compiler further restricts the allowed types, which is what this type attempts to 

294 express. 

295 

296 This is similar to a :class:`BaseDelTargetExpression`, but it also includes 

297 :class:`StarredElement` as a valid node. 

298 

299 The set of valid nodes are defined as part of `CPython's AST context computation 

300 <https://github.com/python/cpython/blob/v3.8.0a4/Python/ast.c#L1120>`_. 

301 """ 

302 

303 __slots__ = () 

304 

305 

306class BaseDelTargetExpression(BaseExpression, ABC): 

307 """ 

308 An expression that's valid on the right side of a :class:`Del` statement. 

309 

310 Python's grammar defines all expression as valid in this position, but the AST 

311 compiler further restricts the allowed types, which is what this type attempts to 

312 express. 

313 

314 This is similar to a :class:`BaseAssignTargetExpression`, but it excludes 

315 :class:`StarredElement`. 

316 

317 The set of valid nodes are defined as part of `CPython's AST context computation 

318 <https://github.com/python/cpython/blob/v3.8.0a4/Python/ast.c#L1120>`_ and as part 

319 of `CPython's bytecode compiler 

320 <https://github.com/python/cpython/blob/v3.8.0a4/Python/compile.c#L4854>`_. 

321 """ 

322 

323 __slots__ = () 

324 

325 

326@add_slots 

327@dataclass(frozen=True) 

328class Name(BaseAssignTargetExpression, BaseDelTargetExpression): 

329 """ 

330 A simple variable name. Names are typically used in the context of a variable 

331 access, an assignment, or a deletion. 

332 

333 Dotted variable names (``a.b.c``) are represented with :class:`Attribute` nodes, 

334 and subscripted variable names (``a[b]``) are represented with :class:`Subscript` 

335 nodes. 

336 """ 

337 

338 #: The variable's name (or "identifier") as a string. 

339 value: str 

340 

341 lpar: Sequence[LeftParen] = () 

342 #: Sequence of parenthesis for precedence dictation. 

343 rpar: Sequence[RightParen] = () 

344 

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

346 return Name( 

347 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

348 value=self.value, 

349 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

350 ) 

351 

352 def _validate(self) -> None: 

353 super(Name, self)._validate() 

354 if len(self.value) == 0: 

355 raise CSTValidationError("Cannot have empty name identifier.") 

356 if not self.value.isidentifier(): 

357 raise CSTValidationError(f"Name {self.value!r} is not a valid identifier.") 

358 

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

360 with self._parenthesize(state): 

361 state.add_token(self.value) 

362 

363 

364@add_slots 

365@dataclass(frozen=True) 

366class Ellipsis(BaseExpression): 

367 """ 

368 An ellipsis ``...``. When used as an expression, it evaluates to the 

369 `Ellipsis constant`_. Ellipsis are often used as placeholders in code or in 

370 conjunction with :class:`SubscriptElement`. 

371 

372 .. _Ellipsis constant: https://docs.python.org/3/library/constants.html#Ellipsis 

373 """ 

374 

375 lpar: Sequence[LeftParen] = () 

376 

377 #: Sequence of parenthesis for precedence dictation. 

378 rpar: Sequence[RightParen] = () 

379 

380 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Ellipsis": 

381 return Ellipsis( 

382 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

383 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

384 ) 

385 

386 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

387 return True 

388 

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

390 with self._parenthesize(state): 

391 state.add_token("...") 

392 

393 

394class BaseNumber(BaseExpression, ABC): 

395 """ 

396 A type such as :class:`Integer`, :class:`Float`, or :class:`Imaginary` that can be 

397 used anywhere that you need to explicitly take any number type. 

398 """ 

399 

400 __slots__ = () 

401 

402 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

403 """ 

404 Numbers are funny. The expression "5in [1,2,3,4,5]" is a valid expression 

405 which evaluates to "True". So, encapsulate that here by allowing zero spacing 

406 with the left hand side of an expression with a comparison operator. 

407 """ 

408 if position == ExpressionPosition.LEFT: 

409 return True 

410 return super(BaseNumber, self)._safe_to_use_with_word_operator(position) 

411 

412 

413@add_slots 

414@dataclass(frozen=True) 

415class Integer(BaseNumber): 

416 #: A string representation of the integer, such as ``"100000"`` or ``100_000``. 

417 #: 

418 #: To convert this string representation to an ``int``, use the calculated 

419 #: property :attr:`~Integer.evaluated_value`. 

420 value: str 

421 

422 lpar: Sequence[LeftParen] = () 

423 #: Sequence of parenthesis for precedence dictation. 

424 rpar: Sequence[RightParen] = () 

425 

426 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Integer": 

427 return Integer( 

428 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

429 value=self.value, 

430 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

431 ) 

432 

433 def _validate(self) -> None: 

434 super(Integer, self)._validate() 

435 if not re.fullmatch(INTNUMBER_RE, self.value): 

436 raise CSTValidationError("Number is not a valid integer.") 

437 

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

439 with self._parenthesize(state): 

440 state.add_token(self.value) 

441 

442 @property 

443 def evaluated_value(self) -> int: 

444 """ 

445 Return an :func:`ast.literal_eval` evaluated int of :py:attr:`value`. 

446 """ 

447 return literal_eval(self.value) 

448 

449 

450@add_slots 

451@dataclass(frozen=True) 

452class Float(BaseNumber): 

453 #: A string representation of the floating point number, such as ``"0.05"``, 

454 #: ``".050"``, or ``"5e-2"``. 

455 #: 

456 #: To convert this string representation to an ``float``, use the calculated 

457 #: property :attr:`~Float.evaluated_value`. 

458 value: str 

459 

460 lpar: Sequence[LeftParen] = () 

461 #: Sequence of parenthesis for precedence dictation. 

462 rpar: Sequence[RightParen] = () 

463 

464 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Float": 

465 return Float( 

466 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

467 value=self.value, 

468 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

469 ) 

470 

471 def _validate(self) -> None: 

472 super(Float, self)._validate() 

473 if not re.fullmatch(FLOATNUMBER_RE, self.value): 

474 raise CSTValidationError("Number is not a valid float.") 

475 

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

477 with self._parenthesize(state): 

478 state.add_token(self.value) 

479 

480 @property 

481 def evaluated_value(self) -> float: 

482 """ 

483 Return an :func:`ast.literal_eval` evaluated float of :py:attr:`value`. 

484 """ 

485 return literal_eval(self.value) 

486 

487 

488@add_slots 

489@dataclass(frozen=True) 

490class Imaginary(BaseNumber): 

491 #: A string representation of the imaginary (complex) number, such as ``"2j"``. 

492 #: 

493 #: To convert this string representation to an ``complex``, use the calculated 

494 #: property :attr:`~Imaginary.evaluated_value`. 

495 value: str 

496 

497 lpar: Sequence[LeftParen] = () 

498 #: Sequence of parenthesis for precedence dictation. 

499 rpar: Sequence[RightParen] = () 

500 

501 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Imaginary": 

502 return Imaginary( 

503 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

504 value=self.value, 

505 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

506 ) 

507 

508 def _validate(self) -> None: 

509 super(Imaginary, self)._validate() 

510 if not re.fullmatch(IMAGNUMBER_RE, self.value): 

511 raise CSTValidationError("Number is not a valid imaginary.") 

512 

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

514 with self._parenthesize(state): 

515 state.add_token(self.value) 

516 

517 @property 

518 def evaluated_value(self) -> complex: 

519 """ 

520 Return an :func:`ast.literal_eval` evaluated complex of :py:attr:`value`. 

521 """ 

522 return literal_eval(self.value) 

523 

524 

525class BaseString(BaseExpression, ABC): 

526 """ 

527 A type that can be used anywhere that you need to take any string. This includes 

528 :class:`SimpleString`, :class:`ConcatenatedString`, and :class:`FormattedString`. 

529 """ 

530 

531 __slots__ = () 

532 

533 

534StringQuoteLiteral = Literal['"', "'", '"""', "'''"] 

535 

536 

537class _BasePrefixedString(BaseString, ABC): 

538 __slots__ = () 

539 

540 @property 

541 def prefix(self) -> str: 

542 """ 

543 Returns the string's prefix, if any exists. 

544 

545 See `String and Bytes literals 

546 <https://docs.python.org/3.7/reference/lexical_analysis.html#string-and-bytes-literals>`_ 

547 for more information. 

548 """ 

549 ... 

550 

551 @property 

552 def quote(self) -> StringQuoteLiteral: 

553 """ 

554 Returns the quotation used to denote the string. Can be either ``'``, 

555 ``"``, ``'''`` or ``\"\"\"``. 

556 """ 

557 ... 

558 

559 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

560 """ 

561 ``"a"in"abc`` is okay, but if you add a prefix, (e.g. ``b"a"inb"abc"``), the string 

562 is no longer valid on the RHS of the word operator, because it's not clear where 

563 the keyword ends and the prefix begins, unless it's parenthesized. 

564 """ 

565 if position == ExpressionPosition.LEFT: 

566 return True 

567 elif self.prefix == "": # and position == ExpressionPosition.RIGHT 

568 return True 

569 else: 

570 return super(_BasePrefixedString, self)._safe_to_use_with_word_operator( 

571 position 

572 ) 

573 

574 

575@add_slots 

576@dataclass(frozen=True) 

577class SimpleString(_BasePrefixedString): 

578 """ 

579 Any sort of literal string expression that is not a :class:`FormattedString` 

580 (f-string), including triple-quoted multi-line strings. 

581 """ 

582 

583 #: The texual representation of the string, including quotes, prefix characters, and 

584 #: any escape characters present in the original source code , such as 

585 #: ``r"my string\n"``. To remove the quotes and interpret any escape characters, 

586 #: use the calculated property :attr:`~SimpleString.evaluated_value`. 

587 value: str 

588 

589 lpar: Sequence[LeftParen] = () 

590 #: Sequence of parenthesis for precidence dictation. 

591 rpar: Sequence[RightParen] = () 

592 

593 def _validate(self) -> None: 

594 super(SimpleString, self)._validate() 

595 

596 # Validate any prefix 

597 prefix = self.prefix 

598 if prefix not in ("", "r", "u", "b", "br", "rb"): 

599 raise CSTValidationError("Invalid string prefix.") 

600 prefixlen = len(prefix) 

601 # Validate wrapping quotes 

602 if len(self.value) < (prefixlen + 2): 

603 raise CSTValidationError("String must have enclosing quotes.") 

604 if ( 

605 self.value[prefixlen] not in ['"', "'"] 

606 or self.value[prefixlen] != self.value[-1] 

607 ): 

608 raise CSTValidationError("String must have matching enclosing quotes.") 

609 # Check validity of triple-quoted strings 

610 if len(self.value) >= (prefixlen + 6): 

611 if self.value[prefixlen] == self.value[prefixlen + 1]: 

612 # We know this isn't an empty string, so there needs to be a third 

613 # identical enclosing token. 

614 if ( 

615 self.value[prefixlen] != self.value[prefixlen + 2] 

616 or self.value[prefixlen] != self.value[-2] 

617 or self.value[prefixlen] != self.value[-3] 

618 ): 

619 raise CSTValidationError( 

620 "String must have matching enclosing quotes." 

621 ) 

622 # We should check the contents as well, but this is pretty complicated, 

623 # partially due to triple-quoted strings. 

624 

625 @property 

626 def prefix(self) -> str: 

627 """ 

628 Returns the string's prefix, if any exists. The prefix can be ``r``, 

629 ``u``, ``b``, ``br`` or ``rb``. 

630 """ 

631 

632 prefix: str = "" 

633 for c in self.value: 

634 if c in ['"', "'"]: 

635 break 

636 prefix += c 

637 return prefix.lower() 

638 

639 @property 

640 def quote(self) -> StringQuoteLiteral: 

641 """ 

642 Returns the quotation used to denote the string. Can be either ``'``, 

643 ``"``, ``'''`` or ``\"\"\"``. 

644 """ 

645 

646 quote: str = "" 

647 for char in self.value[len(self.prefix) :]: 

648 if char not in {"'", '"'}: 

649 break 

650 if quote and char != quote[0]: 

651 # This is no longer the same string quote 

652 break 

653 quote += char 

654 

655 if len(quote) == 2: 

656 # Let's assume this is an empty string. 

657 quote = quote[:1] 

658 elif 3 < len(quote) <= 6: 

659 # Let's assume this can be one of the following: 

660 # >>> """"foo""" 

661 # '"foo' 

662 # >>> """""bar""" 

663 # '""bar' 

664 # >>> """""" 

665 # '' 

666 quote = quote[:3] 

667 

668 if len(quote) not in {1, 3}: 

669 # We shouldn't get here due to construction validation logic, 

670 # but handle the case anyway. 

671 raise Exception(f"Invalid string {self.value}") 

672 

673 # pyre-ignore We know via the above validation that we will only 

674 # ever return one of the four string literals. 

675 return quote 

676 

677 @property 

678 def raw_value(self) -> str: 

679 """ 

680 Returns the raw value of the string as it appears in source, without 

681 the beginning or end quotes and without the prefix. This is often 

682 useful when constructing transforms which need to manipulate strings 

683 in source code. 

684 """ 

685 

686 prefix_len = len(self.prefix) 

687 quote_len = len(self.quote) 

688 return self.value[(prefix_len + quote_len) : (-quote_len)] 

689 

690 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "SimpleString": 

691 return SimpleString( 

692 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

693 value=self.value, 

694 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

695 ) 

696 

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

698 with self._parenthesize(state): 

699 state.add_token(self.value) 

700 

701 @property 

702 def evaluated_value(self) -> Union[str, bytes]: 

703 """ 

704 Return an :func:`ast.literal_eval` evaluated str of :py:attr:`value`. 

705 """ 

706 return literal_eval(self.value) 

707 

708 

709class BaseFormattedStringContent(CSTNode, ABC): 

710 """ 

711 The base type for :class:`FormattedStringText` and 

712 :class:`FormattedStringExpression`. A :class:`FormattedString` is composed of a 

713 sequence of :class:`BaseFormattedStringContent` parts. 

714 """ 

715 

716 __slots__ = () 

717 

718 

719@add_slots 

720@dataclass(frozen=True) 

721class FormattedStringText(BaseFormattedStringContent): 

722 """ 

723 Part of a :class:`FormattedString` that is not inside curly braces (``{`` or ``}``). 

724 For example, in:: 

725 

726 f"ab{cd}ef" 

727 

728 ``ab`` and ``ef`` are :class:`FormattedStringText` nodes, but ``{cd}`` is a 

729 :class:`FormattedStringExpression`. 

730 """ 

731 

732 #: The raw string value, including any escape characters present in the source 

733 #: code, not including any enclosing quotes. 

734 value: str 

735 

736 def _visit_and_replace_children( 

737 self, visitor: CSTVisitorT 

738 ) -> "FormattedStringText": 

739 return FormattedStringText(value=self.value) 

740 

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

742 state.add_token(self.value) 

743 

744 

745@add_slots 

746@dataclass(frozen=True) 

747class FormattedStringExpression(BaseFormattedStringContent): 

748 """ 

749 Part of a :class:`FormattedString` that is inside curly braces (``{`` or ``}``), 

750 including the surrounding curly braces. For example, in:: 

751 

752 f"ab{cd}ef" 

753 

754 ``{cd}`` is a :class:`FormattedStringExpression`, but ``ab`` and ``ef`` are 

755 :class:`FormattedStringText` nodes. 

756 

757 An f-string expression may contain ``conversion`` and ``format_spec`` suffixes that 

758 control how the expression is converted to a string. See `Python's language 

759 reference 

760 <https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals>`__ 

761 for details. 

762 """ 

763 

764 #: The expression we will evaluate and render when generating the string. 

765 expression: BaseExpression 

766 

767 #: An optional conversion specifier, such as ``!s``, ``!r`` or ``!a``. 

768 conversion: Optional[str] = None 

769 

770 #: An optional format specifier following the `format specification mini-language 

771 #: <https://docs.python.org/3/library/string.html#formatspec>`_. 

772 format_spec: Optional[Sequence[BaseFormattedStringContent]] = None 

773 

774 #: Whitespace after the opening curly brace (``{``), but before the ``expression``. 

775 whitespace_before_expression: BaseParenthesizableWhitespace = ( 

776 SimpleWhitespace.field("") 

777 ) 

778 

779 #: Whitespace after the ``expression``, but before the ``conversion``, 

780 #: ``format_spec`` and the closing curly brace (``}``). Python does not 

781 #: allow whitespace inside or after a ``conversion`` or ``format_spec``. 

782 whitespace_after_expression: BaseParenthesizableWhitespace = SimpleWhitespace.field( 

783 "" 

784 ) 

785 

786 #: Equal sign for formatted string expression uses self-documenting expressions, 

787 #: such as ``f"{x=}"``. See the `Python 3.8 release notes 

788 #: <https://docs.python.org/3/whatsnew/3.8.html#f-strings-support-for-self-documenting-expressions-and-debugging>`_. 

789 equal: Optional[AssignEqual] = None 

790 

791 def _validate(self) -> None: 

792 if self.conversion is not None and self.conversion not in ("s", "r", "a"): 

793 raise CSTValidationError("Invalid f-string conversion.") 

794 

795 def _visit_and_replace_children( 

796 self, visitor: CSTVisitorT 

797 ) -> "FormattedStringExpression": 

798 format_spec = self.format_spec 

799 return FormattedStringExpression( 

800 whitespace_before_expression=visit_required( 

801 self, 

802 "whitespace_before_expression", 

803 self.whitespace_before_expression, 

804 visitor, 

805 ), 

806 expression=visit_required(self, "expression", self.expression, visitor), 

807 equal=visit_optional(self, "equal", self.equal, visitor), 

808 whitespace_after_expression=visit_required( 

809 self, 

810 "whitespace_after_expression", 

811 self.whitespace_after_expression, 

812 visitor, 

813 ), 

814 conversion=self.conversion, 

815 format_spec=( 

816 visit_sequence(self, "format_spec", format_spec, visitor) 

817 if format_spec is not None 

818 else None 

819 ), 

820 ) 

821 

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

823 state.add_token("{") 

824 self.whitespace_before_expression._codegen(state) 

825 self.expression._codegen(state) 

826 equal = self.equal 

827 if equal is not None: 

828 equal._codegen(state) 

829 self.whitespace_after_expression._codegen(state) 

830 conversion = self.conversion 

831 if conversion is not None: 

832 state.add_token("!") 

833 state.add_token(conversion) 

834 format_spec = self.format_spec 

835 if format_spec is not None: 

836 state.add_token(":") 

837 for spec in format_spec: 

838 spec._codegen(state) 

839 state.add_token("}") 

840 

841 

842@add_slots 

843@dataclass(frozen=True) 

844class FormattedString(_BasePrefixedString): 

845 """ 

846 An "f-string". These formatted strings are string literals prefixed by the letter 

847 "f". An f-string may contain interpolated expressions inside curly braces (``{`` and 

848 ``}``). 

849 

850 F-strings are defined in `PEP 498`_ and documented in `Python's language 

851 reference 

852 <https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals>`__. 

853 

854 >>> import libcst as cst 

855 >>> cst.parse_expression('f"ab{cd}ef"') 

856 FormattedString( 

857 parts=[ 

858 FormattedStringText( 

859 value='ab', 

860 ), 

861 FormattedStringExpression( 

862 expression=Name( 

863 value='cd', 

864 lpar=[], 

865 rpar=[], 

866 ), 

867 conversion=None, 

868 format_spec=None, 

869 whitespace_before_expression=SimpleWhitespace( 

870 value='', 

871 ), 

872 whitespace_after_expression=SimpleWhitespace( 

873 value='', 

874 ), 

875 ), 

876 FormattedStringText( 

877 value='ef', 

878 ), 

879 ], 

880 start='f"', 

881 end='"', 

882 lpar=[], 

883 rpar=[], 

884 ) 

885 

886 .. _PEP 498: https://www.python.org/dev/peps/pep-0498/#specification 

887 """ 

888 

889 #: A formatted string is composed as a series of :class:`FormattedStringText` and 

890 #: :class:`FormattedStringExpression` parts. 

891 parts: Sequence[BaseFormattedStringContent] 

892 

893 #: The string prefix and the leading quote, such as ``f"``, ``F'``, ``fr"``, or 

894 #: ``f"""``. 

895 start: str = 'f"' 

896 

897 #: The trailing quote. This must match the type of quote used in ``start``. 

898 end: Literal['"', "'", '"""', "'''"] = '"' 

899 

900 lpar: Sequence[LeftParen] = () 

901 #: Sequence of parenthesis for precidence dictation. 

902 rpar: Sequence[RightParen] = () 

903 

904 def _validate(self) -> None: 

905 super(FormattedString, self)._validate() 

906 

907 # Validate any prefix 

908 prefix = self.prefix 

909 if prefix not in ("f", "fr", "rf"): 

910 raise CSTValidationError("Invalid f-string prefix.") 

911 

912 # Validate wrapping quotes 

913 starttoken = self.start[len(prefix) :] 

914 if starttoken != self.end: 

915 raise CSTValidationError("f-string must have matching enclosing quotes.") 

916 

917 # Validate valid wrapping quote usage 

918 if starttoken not in ('"', "'", '"""', "'''"): 

919 raise CSTValidationError("Invalid f-string enclosing quotes.") 

920 

921 @property 

922 def prefix(self) -> str: 

923 """ 

924 Returns the string's prefix, if any exists. The prefix can be ``f``, 

925 ``fr``, or ``rf``. 

926 """ 

927 

928 prefix = "" 

929 for c in self.start: 

930 if c in ['"', "'"]: 

931 break 

932 prefix += c 

933 return prefix.lower() 

934 

935 @property 

936 def quote(self) -> StringQuoteLiteral: 

937 """ 

938 Returns the quotation used to denote the string. Can be either ``'``, 

939 ``"``, ``'''`` or ``\"\"\"``. 

940 """ 

941 

942 return self.end 

943 

944 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "FormattedString": 

945 return FormattedString( 

946 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

947 start=self.start, 

948 parts=visit_sequence(self, "parts", self.parts, visitor), 

949 end=self.end, 

950 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

951 ) 

952 

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

954 with self._parenthesize(state): 

955 state.add_token(self.start) 

956 for part in self.parts: 

957 part._codegen(state) 

958 state.add_token(self.end) 

959 

960 

961@add_slots 

962@dataclass(frozen=True) 

963class ConcatenatedString(BaseString): 

964 """ 

965 Represents an implicitly concatenated string, such as:: 

966 

967 "abc" "def" == "abcdef" 

968 

969 .. warning:: 

970 This is different from two strings joined in a :class:`BinaryOperation` with an 

971 :class:`Add` operator, and is `sometimes viewed as an antifeature of Python 

972 <https://lwn.net/Articles/551426/>`_. 

973 """ 

974 

975 #: String on the left of the concatenation. 

976 left: Union[SimpleString, FormattedString] 

977 

978 #: String on the right of the concatenation. 

979 right: Union[SimpleString, FormattedString, "ConcatenatedString"] 

980 

981 lpar: Sequence[LeftParen] = () 

982 #: Sequence of parenthesis for precidence dictation. 

983 rpar: Sequence[RightParen] = () 

984 

985 #: Whitespace between the ``left`` and ``right`` substrings. 

986 whitespace_between: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

987 

988 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

989 if super(ConcatenatedString, self)._safe_to_use_with_word_operator(position): 

990 # if we have parenthesis, we're safe. 

991 return True 

992 return self._check_left_right_word_concatenation_safety( 

993 position, self.left, self.right 

994 ) 

995 

996 def _validate(self) -> None: 

997 super(ConcatenatedString, self)._validate() 

998 

999 # Strings that are concatenated cannot have parens. 

1000 if bool(self.left.lpar) or bool(self.left.rpar): 

1001 raise CSTValidationError("Cannot concatenate parenthesized strings.") 

1002 if bool(self.right.lpar) or bool(self.right.rpar): 

1003 raise CSTValidationError("Cannot concatenate parenthesized strings.") 

1004 

1005 # Cannot concatenate str and bytes 

1006 leftbytes = "b" in self.left.prefix 

1007 right = self.right 

1008 if isinstance(right, ConcatenatedString): 

1009 rightbytes = "b" in right.left.prefix 

1010 elif isinstance(right, SimpleString): 

1011 rightbytes = "b" in right.prefix 

1012 elif isinstance(right, FormattedString): 

1013 rightbytes = "b" in right.prefix 

1014 else: 

1015 raise Exception("Logic error!") 

1016 if leftbytes != rightbytes: 

1017 raise CSTValidationError("Cannot concatenate string and bytes.") 

1018 

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

1020 return ConcatenatedString( 

1021 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

1022 left=visit_required(self, "left", self.left, visitor), 

1023 whitespace_between=visit_required( 

1024 self, "whitespace_between", self.whitespace_between, visitor 

1025 ), 

1026 right=visit_required(self, "right", self.right, visitor), 

1027 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

1028 ) 

1029 

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

1031 with self._parenthesize(state): 

1032 self.left._codegen(state) 

1033 self.whitespace_between._codegen(state) 

1034 self.right._codegen(state) 

1035 

1036 @property 

1037 def evaluated_value(self) -> Union[str, bytes, None]: 

1038 """ 

1039 Return an :func:`ast.literal_eval` evaluated str of recursively concatenated :py:attr:`left` and :py:attr:`right` 

1040 if and only if both :py:attr:`left` and :py:attr:`right` are composed by :class:`SimpleString` or :class:`ConcatenatedString` 

1041 (:class:`FormattedString` cannot be evaluated). 

1042 """ 

1043 left = self.left 

1044 right = self.right 

1045 if isinstance(left, FormattedString) or isinstance(right, FormattedString): 

1046 return None 

1047 left_val = left.evaluated_value 

1048 right_val = right.evaluated_value 

1049 if right_val is None: 

1050 return None 

1051 if isinstance(left_val, bytes) and isinstance(right_val, bytes): 

1052 return left_val + right_val 

1053 if isinstance(left_val, str) and isinstance(right_val, str): 

1054 return left_val + right_val 

1055 return None 

1056 

1057 

1058@add_slots 

1059@dataclass(frozen=True) 

1060class ComparisonTarget(CSTNode): 

1061 """ 

1062 A target for a :class:`Comparison`. Owns the comparison operator and the value to 

1063 the right of the operator. 

1064 """ 

1065 

1066 #: A comparison operator such as ``<``, ``>=``, ``==``, ``is``, or ``in``. 

1067 operator: BaseCompOp 

1068 

1069 #: The right hand side of the comparison operation. 

1070 comparator: BaseExpression 

1071 

1072 def _validate(self) -> None: 

1073 # Validate operator spacing rules 

1074 operator = self.operator 

1075 if ( 

1076 isinstance(operator, (In, NotIn, Is, IsNot)) 

1077 and operator.whitespace_after.empty 

1078 and not self.comparator._safe_to_use_with_word_operator( 

1079 ExpressionPosition.RIGHT 

1080 ) 

1081 ): 

1082 raise CSTValidationError( 

1083 "Must have at least one space around comparison operator." 

1084 ) 

1085 

1086 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ComparisonTarget": 

1087 return ComparisonTarget( 

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

1089 comparator=visit_required(self, "comparator", self.comparator, visitor), 

1090 ) 

1091 

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

1093 self.operator._codegen(state) 

1094 self.comparator._codegen(state) 

1095 

1096 

1097@add_slots 

1098@dataclass(frozen=True) 

1099class Comparison(BaseExpression): 

1100 """ 

1101 A comparison between multiple values such as ``x < y``, ``x < y < z``, or 

1102 ``x in [y, z]``. These comparisions typically result in boolean values. 

1103 

1104 Unlike :class:`BinaryOperation` and :class:`BooleanOperation`, comparisons are not 

1105 restricted to a left and right child. Instead they can contain an arbitrary number 

1106 of :class:`ComparisonTarget` children. 

1107 

1108 ``x < y < z`` is not equivalent to ``(x < y) < z`` or ``x < (y < z)``. Instead, 

1109 it's roughly equivalent to ``x < y and y < z``. 

1110 

1111 For more details, see `Python's documentation on comparisons 

1112 <https://docs.python.org/3/reference/expressions.html#comparisons>`_. 

1113 

1114 :: 

1115 

1116 # x < y < z 

1117 

1118 Comparison( 

1119 Name("x"), 

1120 [ 

1121 ComparisonTarget(LessThan(), Name("y")), 

1122 ComparisonTarget(LessThan(), Name("z")), 

1123 ], 

1124 ) 

1125 """ 

1126 

1127 #: The first value in the full sequence of values to compare. This value will be 

1128 #: compared against the first value in ``comparisions``. 

1129 left: BaseExpression 

1130 

1131 #: Pairs of :class:`BaseCompOp` operators and expression values to compare. These 

1132 #: come after ``left``. Each value is compared against the value before and after 

1133 #: itself in the sequence. 

1134 comparisons: Sequence[ComparisonTarget] 

1135 

1136 lpar: Sequence[LeftParen] = () 

1137 #: Sequence of parenthesis for precedence dictation. 

1138 rpar: Sequence[RightParen] = () 

1139 

1140 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

1141 if super(Comparison, self)._safe_to_use_with_word_operator(position): 

1142 # we have parenthesis 

1143 return True 

1144 return self._check_left_right_word_concatenation_safety( 

1145 position, self.left, self.comparisons[-1].comparator 

1146 ) 

1147 

1148 def _validate(self) -> None: 

1149 # Perform any validation on base type 

1150 super(Comparison, self)._validate() 

1151 

1152 if len(self.comparisons) == 0: 

1153 raise CSTValidationError("Must have at least one ComparisonTarget.") 

1154 

1155 # Validate operator spacing rules 

1156 previous_comparator = self.left 

1157 for target in self.comparisons: 

1158 operator = target.operator 

1159 if ( 

1160 isinstance(operator, (In, NotIn, Is, IsNot)) 

1161 and operator.whitespace_before.empty 

1162 and not previous_comparator._safe_to_use_with_word_operator( 

1163 ExpressionPosition.LEFT 

1164 ) 

1165 ): 

1166 raise CSTValidationError( 

1167 "Must have at least one space around comparison operator." 

1168 ) 

1169 previous_comparator = target.comparator 

1170 

1171 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Comparison": 

1172 return Comparison( 

1173 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

1174 left=visit_required(self, "left", self.left, visitor), 

1175 comparisons=visit_sequence(self, "comparisons", self.comparisons, visitor), 

1176 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

1177 ) 

1178 

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

1180 with self._parenthesize(state): 

1181 self.left._codegen(state) 

1182 for comp in self.comparisons: 

1183 comp._codegen(state) 

1184 

1185 

1186@add_slots 

1187@dataclass(frozen=True) 

1188class UnaryOperation(BaseExpression): 

1189 """ 

1190 Any generic unary expression, such as ``not x`` or ``-x``. :class:`UnaryOperation` 

1191 nodes apply a :class:`BaseUnaryOp` to an expression. 

1192 """ 

1193 

1194 #: The unary operator that applies some operation (e.g. negation) to the 

1195 #: ``expression``. 

1196 operator: BaseUnaryOp 

1197 

1198 #: The expression that should be transformed (e.g. negated) by the operator to 

1199 #: create a new value. 

1200 expression: BaseExpression 

1201 

1202 lpar: Sequence[LeftParen] = () 

1203 #: Sequence of parenthesis for precedence dictation. 

1204 rpar: Sequence[RightParen] = () 

1205 

1206 def _validate(self) -> None: 

1207 # Perform any validation on base type 

1208 super(UnaryOperation, self)._validate() 

1209 

1210 if ( 

1211 isinstance(self.operator, Not) 

1212 and self.operator.whitespace_after.empty 

1213 and not self.expression._safe_to_use_with_word_operator( 

1214 ExpressionPosition.RIGHT 

1215 ) 

1216 ): 

1217 raise CSTValidationError("Must have at least one space after not operator.") 

1218 

1219 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "UnaryOperation": 

1220 return UnaryOperation( 

1221 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

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

1223 expression=visit_required(self, "expression", self.expression, visitor), 

1224 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

1225 ) 

1226 

1227 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

1228 """ 

1229 As long as we aren't comprised of the Not unary operator, we are safe to use 

1230 without space. 

1231 """ 

1232 if super(UnaryOperation, self)._safe_to_use_with_word_operator(position): 

1233 return True 

1234 if position == ExpressionPosition.RIGHT: 

1235 return not isinstance(self.operator, Not) 

1236 if position == ExpressionPosition.LEFT: 

1237 return self.expression._safe_to_use_with_word_operator( 

1238 ExpressionPosition.LEFT 

1239 ) 

1240 return False 

1241 

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

1243 with self._parenthesize(state): 

1244 self.operator._codegen(state) 

1245 self.expression._codegen(state) 

1246 

1247 

1248@add_slots 

1249@dataclass(frozen=True) 

1250class BinaryOperation(BaseExpression): 

1251 """ 

1252 An operation that combines two expression such as ``x << y`` or ``y + z``. 

1253 :class:`BinaryOperation` nodes apply a :class:`BaseBinaryOp` to an expression. 

1254 

1255 Binary operations do not include operations performed with :class:`BaseBooleanOp` 

1256 nodes, such as ``and`` or ``or``. Instead, those operations are provided by 

1257 :class:`BooleanOperation`. 

1258 

1259 It also does not include support for comparision operators performed with 

1260 :class:`BaseCompOp`, such as ``<``, ``>=``, ``==``, ``is``, or ``in``. Instead, 

1261 those operations are provided by :class:`Comparison`. 

1262 """ 

1263 

1264 #: The left hand side of the operation. 

1265 left: BaseExpression 

1266 

1267 #: The actual operator such as ``<<`` or ``+`` that combines the ``left`` and 

1268 #: ``right`` expressions. 

1269 operator: BaseBinaryOp 

1270 

1271 #: The right hand side of the operation. 

1272 right: BaseExpression 

1273 

1274 lpar: Sequence[LeftParen] = () 

1275 #: Sequence of parenthesis for precedence dictation. 

1276 rpar: Sequence[RightParen] = () 

1277 

1278 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "BinaryOperation": 

1279 return BinaryOperation( 

1280 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

1281 left=visit_required(self, "left", self.left, visitor), 

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

1283 right=visit_required(self, "right", self.right, visitor), 

1284 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

1285 ) 

1286 

1287 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

1288 if super(BinaryOperation, self)._safe_to_use_with_word_operator(position): 

1289 return True 

1290 return self._check_left_right_word_concatenation_safety( 

1291 position, self.left, self.right 

1292 ) 

1293 

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

1295 with self._parenthesize(state): 

1296 self.left._codegen(state) 

1297 self.operator._codegen(state) 

1298 self.right._codegen(state) 

1299 

1300 

1301@add_slots 

1302@dataclass(frozen=True) 

1303class BooleanOperation(BaseExpression): 

1304 """ 

1305 An operation that combines two booleans such as ``x or y`` or ``z and w`` 

1306 :class:`BooleanOperation` nodes apply a :class:`BaseBooleanOp` to an expression. 

1307 

1308 Boolean operations do not include operations performed with :class:`BaseBinaryOp` 

1309 nodes, such as ``+`` or ``<<``. Instead, those operations are provided by 

1310 :class:`BinaryOperation`. 

1311 

1312 It also does not include support for comparision operators performed with 

1313 :class:`BaseCompOp`, such as ``<``, ``>=``, ``==``, ``is``, or ``in``. Instead, 

1314 those operations are provided by :class:`Comparison`. 

1315 """ 

1316 

1317 #: The left hand side of the operation. 

1318 left: BaseExpression 

1319 

1320 #: The actual operator such as ``and`` or ``or`` that combines the ``left`` and 

1321 #: ``right`` expressions. 

1322 operator: BaseBooleanOp 

1323 

1324 #: The right hand side of the operation. 

1325 right: BaseExpression 

1326 

1327 lpar: Sequence[LeftParen] = () 

1328 #: Sequence of parenthesis for precedence dictation. 

1329 rpar: Sequence[RightParen] = () 

1330 

1331 def _validate(self) -> None: 

1332 # Paren validation and such 

1333 super(BooleanOperation, self)._validate() 

1334 # Validate spacing rules 

1335 if ( 

1336 self.operator.whitespace_before.empty 

1337 and not self.left._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

1338 ): 

1339 raise CSTValidationError( 

1340 "Must have at least one space around boolean operator." 

1341 ) 

1342 if ( 

1343 self.operator.whitespace_after.empty 

1344 and not self.right._safe_to_use_with_word_operator(ExpressionPosition.RIGHT) 

1345 ): 

1346 raise CSTValidationError( 

1347 "Must have at least one space around boolean operator." 

1348 ) 

1349 

1350 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "BooleanOperation": 

1351 return BooleanOperation( 

1352 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

1353 left=visit_required(self, "left", self.left, visitor), 

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

1355 right=visit_required(self, "right", self.right, visitor), 

1356 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

1357 ) 

1358 

1359 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

1360 if super(BooleanOperation, self)._safe_to_use_with_word_operator(position): 

1361 return True 

1362 return self._check_left_right_word_concatenation_safety( 

1363 position, self.left, self.right 

1364 ) 

1365 

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

1367 with self._parenthesize(state): 

1368 self.left._codegen(state) 

1369 self.operator._codegen(state) 

1370 self.right._codegen(state) 

1371 

1372 

1373@add_slots 

1374@dataclass(frozen=True) 

1375class Attribute(BaseAssignTargetExpression, BaseDelTargetExpression): 

1376 """ 

1377 An attribute reference, such as ``x.y``. 

1378 

1379 Note that in the case of ``x.y.z``, the outer attribute will have an attr of ``z`` 

1380 and the value will be another :class:`Attribute` referencing the ``y`` attribute on 

1381 ``x``:: 

1382 

1383 Attribute( 

1384 value=Attribute( 

1385 value=Name("x") 

1386 attr=Name("y") 

1387 ), 

1388 attr=Name("z"), 

1389 ) 

1390 """ 

1391 

1392 #: An expression which, when evaluated, will produce an object with ``attr`` as an 

1393 #: attribute. 

1394 value: BaseExpression 

1395 

1396 #: The name of the attribute being accessed on the ``value`` object. 

1397 attr: Name 

1398 

1399 #: A separating dot. If there's whitespace between the ``value`` and ``attr``, this 

1400 #: dot owns it. 

1401 dot: Dot = Dot() 

1402 

1403 lpar: Sequence[LeftParen] = () 

1404 #: Sequence of parenthesis for precedence dictation. 

1405 rpar: Sequence[RightParen] = () 

1406 

1407 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Attribute": 

1408 return Attribute( 

1409 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

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

1411 dot=visit_required(self, "dot", self.dot, visitor), 

1412 attr=visit_required(self, "attr", self.attr, visitor), 

1413 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

1414 ) 

1415 

1416 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

1417 if super(Attribute, self)._safe_to_use_with_word_operator(position): 

1418 return True 

1419 return self._check_left_right_word_concatenation_safety( 

1420 position, self.value, self.attr 

1421 ) 

1422 

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

1424 with self._parenthesize(state): 

1425 self.value._codegen(state) 

1426 self.dot._codegen(state) 

1427 self.attr._codegen(state) 

1428 

1429 

1430class BaseSlice(CSTNode, ABC): 

1431 """ 

1432 Any slice type that can slot into a :class:`SubscriptElement`. 

1433 This node is purely for typing. 

1434 """ 

1435 

1436 __slots__ = () 

1437 

1438 

1439@add_slots 

1440@dataclass(frozen=True) 

1441class Index(BaseSlice): 

1442 """ 

1443 Any index as passed to a :class:`Subscript`. In ``x[2]``, this would be the ``2`` 

1444 value. 

1445 """ 

1446 

1447 #: The index value itself. 

1448 value: BaseExpression 

1449 

1450 #: An optional string with an asterisk appearing before the name. This is 

1451 #: expanded into variable number of positional arguments. See PEP-646 

1452 star: Optional[Literal["*"]] = None 

1453 

1454 #: Whitespace after the ``star`` (if it exists), but before the ``value``. 

1455 whitespace_after_star: Optional[BaseParenthesizableWhitespace] = None 

1456 

1457 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Index": 

1458 return Index( 

1459 star=self.star, 

1460 whitespace_after_star=visit_optional( 

1461 self, "whitespace_after_star", self.whitespace_after_star, visitor 

1462 ), 

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

1464 ) 

1465 

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

1467 star = self.star 

1468 if star is not None: 

1469 state.add_token(star) 

1470 ws = self.whitespace_after_star 

1471 if ws is not None: 

1472 ws._codegen(state) 

1473 self.value._codegen(state) 

1474 

1475 

1476@add_slots 

1477@dataclass(frozen=True) 

1478class Slice(BaseSlice): 

1479 """ 

1480 Any slice operation in a :class:`Subscript`, such as ``1:``, ``2:3:4``, etc. 

1481 

1482 Note that the grammar does NOT allow parenthesis around a slice so they are not 

1483 supported here. 

1484 """ 

1485 

1486 #: The lower bound in the slice, if present 

1487 lower: Optional[BaseExpression] 

1488 

1489 #: The upper bound in the slice, if present 

1490 upper: Optional[BaseExpression] 

1491 

1492 #: The step in the slice, if present 

1493 step: Optional[BaseExpression] = None 

1494 

1495 #: The first slice operator 

1496 first_colon: Colon = Colon.field() 

1497 

1498 #: The second slice operator, usually omitted 

1499 second_colon: Union[Colon, MaybeSentinel] = MaybeSentinel.DEFAULT 

1500 

1501 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Slice": 

1502 return Slice( 

1503 lower=visit_optional(self, "lower", self.lower, visitor), 

1504 first_colon=visit_required(self, "first_colon", self.first_colon, visitor), 

1505 upper=visit_optional(self, "upper", self.upper, visitor), 

1506 second_colon=visit_sentinel( 

1507 self, "second_colon", self.second_colon, visitor 

1508 ), 

1509 step=visit_optional(self, "step", self.step, visitor), 

1510 ) 

1511 

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

1513 lower = self.lower 

1514 if lower is not None: 

1515 lower._codegen(state) 

1516 self.first_colon._codegen(state) 

1517 upper = self.upper 

1518 if upper is not None: 

1519 upper._codegen(state) 

1520 second_colon = self.second_colon 

1521 if second_colon is MaybeSentinel.DEFAULT and self.step is not None: 

1522 state.add_token(":") 

1523 elif isinstance(second_colon, Colon): 

1524 second_colon._codegen(state) 

1525 step = self.step 

1526 if step is not None: 

1527 step._codegen(state) 

1528 

1529 

1530@add_slots 

1531@dataclass(frozen=True) 

1532class SubscriptElement(CSTNode): 

1533 """ 

1534 Part of a sequence of slices in a :class:`Subscript`, such as ``1:2, 3``. This is 

1535 not used in Python's standard library, but it is used in some third-party 

1536 libraries. For example, `NumPy uses it to select values and ranges from 

1537 multi-dimensional arrays 

1538 <https://docs.scipy.org/doc/numpy-1.10.1/user/basics.indexing.html>`_. 

1539 """ 

1540 

1541 #: A slice or index that is part of a subscript. 

1542 slice: BaseSlice 

1543 

1544 #: A separating comma, with any whitespace it owns. 

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

1546 

1547 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "SubscriptElement": 

1548 return SubscriptElement( 

1549 slice=visit_required(self, "slice", self.slice, visitor), 

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

1551 ) 

1552 

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

1554 with state.record_syntactic_position(self): 

1555 self.slice._codegen(state) 

1556 

1557 comma = self.comma 

1558 if comma is MaybeSentinel.DEFAULT and default_comma: 

1559 state.add_token(", ") 

1560 elif isinstance(comma, Comma): 

1561 comma._codegen(state) 

1562 

1563 

1564@add_slots 

1565@dataclass(frozen=True) 

1566class Subscript(BaseAssignTargetExpression, BaseDelTargetExpression): 

1567 """ 

1568 A indexed subscript reference (:class:`Index`) such as ``x[2]``, a :class:`Slice` 

1569 such as ``x[1:-1]``, or an extended slice (:class:`SubscriptElement`) such as ``x[1:2, 3]``. 

1570 """ 

1571 

1572 #: The left-hand expression which, when evaluated, will be subscripted, such as 

1573 #: ``x`` in ``x[2]``. 

1574 value: BaseExpression 

1575 

1576 #: The :class:`SubscriptElement` to extract from the ``value``. 

1577 slice: Sequence[SubscriptElement] 

1578 

1579 lbracket: LeftSquareBracket = LeftSquareBracket.field() 

1580 #: Brackets after the ``value`` surrounding the ``slice``. 

1581 rbracket: RightSquareBracket = RightSquareBracket.field() 

1582 

1583 lpar: Sequence[LeftParen] = () 

1584 #: Sequence of parenthesis for precedence dictation. 

1585 rpar: Sequence[RightParen] = () 

1586 

1587 #: Whitespace after the ``value``, but before the ``lbracket``. 

1588 whitespace_after_value: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

1589 

1590 def _validate(self) -> None: 

1591 super(Subscript, self)._validate() 

1592 # Validate valid commas 

1593 if len(self.slice) < 1: 

1594 raise CSTValidationError("Cannot have empty SubscriptElement.") 

1595 

1596 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Subscript": 

1597 return Subscript( 

1598 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

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

1600 whitespace_after_value=visit_required( 

1601 self, "whitespace_after_value", self.whitespace_after_value, visitor 

1602 ), 

1603 lbracket=visit_required(self, "lbracket", self.lbracket, visitor), 

1604 slice=visit_sequence(self, "slice", self.slice, visitor), 

1605 rbracket=visit_required(self, "rbracket", self.rbracket, visitor), 

1606 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

1607 ) 

1608 

1609 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

1610 if position == ExpressionPosition.LEFT: 

1611 return True 

1612 if super(Subscript, self)._safe_to_use_with_word_operator(position): 

1613 return True 

1614 if position == ExpressionPosition.RIGHT: 

1615 return self.value._safe_to_use_with_word_operator(ExpressionPosition.RIGHT) 

1616 return False 

1617 

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

1619 with self._parenthesize(state): 

1620 self.value._codegen(state) 

1621 self.whitespace_after_value._codegen(state) 

1622 self.lbracket._codegen(state) 

1623 lastslice = len(self.slice) - 1 

1624 for i, slice in enumerate(self.slice): 

1625 slice._codegen(state, default_comma=(i != lastslice)) 

1626 self.rbracket._codegen(state) 

1627 

1628 

1629@add_slots 

1630@dataclass(frozen=True) 

1631class Annotation(CSTNode): 

1632 """ 

1633 An annotation for a function (`PEP 3107`_) or on a variable (`PEP 526`_). Typically 

1634 these are used in the context of type hints (`PEP 484`_), such as:: 

1635 

1636 # a variable with a type 

1637 good_ideas: List[str] = [] 

1638 

1639 # a function with type annotations 

1640 def concat(substrings: Sequence[str]) -> str: 

1641 ... 

1642 

1643 .. _PEP 3107: https://www.python.org/dev/peps/pep-3107/ 

1644 .. _PEP 526: https://www.python.org/dev/peps/pep-0526/ 

1645 .. _PEP 484: https://www.python.org/dev/peps/pep-0484/ 

1646 """ 

1647 

1648 #: The annotation's value itself. This is the part of the annotation after the 

1649 #: colon or arrow. 

1650 annotation: BaseExpression 

1651 

1652 whitespace_before_indicator: Union[ 

1653 BaseParenthesizableWhitespace, MaybeSentinel 

1654 ] = MaybeSentinel.DEFAULT 

1655 whitespace_after_indicator: BaseParenthesizableWhitespace = SimpleWhitespace.field( 

1656 " " 

1657 ) 

1658 

1659 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Annotation": 

1660 return Annotation( 

1661 whitespace_before_indicator=visit_sentinel( 

1662 self, 

1663 "whitespace_before_indicator", 

1664 self.whitespace_before_indicator, 

1665 visitor, 

1666 ), 

1667 whitespace_after_indicator=visit_required( 

1668 self, 

1669 "whitespace_after_indicator", 

1670 self.whitespace_after_indicator, 

1671 visitor, 

1672 ), 

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

1674 ) 

1675 

1676 def _codegen_impl( 

1677 self, state: CodegenState, default_indicator: Optional[str] = None 

1678 ) -> None: 

1679 # First, figure out the indicator which tells us default whitespace. 

1680 if default_indicator is None: 

1681 raise CSTCodegenError( 

1682 "Must specify a concrete default_indicator if default used on indicator." 

1683 ) 

1684 

1685 # Now, output the whitespace 

1686 whitespace_before_indicator = self.whitespace_before_indicator 

1687 if isinstance(whitespace_before_indicator, BaseParenthesizableWhitespace): 

1688 whitespace_before_indicator._codegen(state) 

1689 elif isinstance(whitespace_before_indicator, MaybeSentinel): 

1690 if default_indicator == "->": 

1691 state.add_token(" ") 

1692 else: 

1693 raise Exception("Logic error!") 

1694 

1695 # Now, output the indicator and the rest of the annotation 

1696 state.add_token(default_indicator) 

1697 self.whitespace_after_indicator._codegen(state) 

1698 

1699 with state.record_syntactic_position(self): 

1700 self.annotation._codegen(state) 

1701 

1702 

1703@add_slots 

1704@dataclass(frozen=True) 

1705class ParamStar(CSTNode): 

1706 """ 

1707 A sentinel indicator on a :class:`Parameters` list to denote that the subsequent 

1708 params are keyword-only args. 

1709 

1710 This syntax is described in `PEP 3102`_. 

1711 

1712 .. _PEP 3102: https://www.python.org/dev/peps/pep-3102/#specification 

1713 """ 

1714 

1715 # Comma that comes after the star. 

1716 comma: Comma = Comma.field(whitespace_after=SimpleWhitespace(" ")) 

1717 

1718 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ParamStar": 

1719 return ParamStar(comma=visit_required(self, "comma", self.comma, visitor)) 

1720 

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

1722 state.add_token("*") 

1723 self.comma._codegen(state) 

1724 

1725 

1726@add_slots 

1727@dataclass(frozen=True) 

1728class ParamSlash(CSTNode): 

1729 """ 

1730 A sentinel indicator on a :class:`Parameters` list to denote that the previous 

1731 params are positional-only args. 

1732 

1733 This syntax is described in `PEP 570`_. 

1734 

1735 .. _PEP 570: https://www.python.org/dev/peps/pep-0570/#specification 

1736 """ 

1737 

1738 #: Optional comma that comes after the slash. This comma doesn't own the whitespace 

1739 #: between ``/`` and ``,``. 

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

1741 

1742 #: Whitespace after the ``/`` character. This is captured here in case there is a 

1743 #: comma. 

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

1745 

1746 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ParamSlash": 

1747 return ParamSlash( 

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

1749 whitespace_after=visit_required( 

1750 self, "whitespace_after", self.whitespace_after, visitor 

1751 ), 

1752 ) 

1753 

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

1755 state.add_token("/") 

1756 

1757 self.whitespace_after._codegen(state) 

1758 comma = self.comma 

1759 if comma is MaybeSentinel.DEFAULT and default_comma: 

1760 state.add_token(", ") 

1761 elif isinstance(comma, Comma): 

1762 comma._codegen(state) 

1763 

1764 

1765@add_slots 

1766@dataclass(frozen=True) 

1767class Param(CSTNode): 

1768 """ 

1769 A positional or keyword argument in a :class:`Parameters` list. May contain an 

1770 :class:`Annotation` and, in some cases, a ``default``. 

1771 """ 

1772 

1773 #: The parameter name itself. 

1774 name: Name 

1775 

1776 #: Any optional :class:`Annotation`. These annotations are usually used as type 

1777 #: hints. 

1778 annotation: Optional[Annotation] = None 

1779 

1780 #: The equal sign used to denote assignment if there is a default. 

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

1782 

1783 #: Any optional default value, used when the argument is not supplied. 

1784 default: Optional[BaseExpression] = None 

1785 

1786 #: A trailing comma. If one is not provided, :class:`MaybeSentinel` will be 

1787 #: replaced with a comma only if a comma is required. 

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

1789 

1790 #: Zero, one, or two asterisks appearing before name for :class:`Param`'s 

1791 #: ``star_arg`` and ``star_kwarg``. 

1792 star: Union[str, MaybeSentinel] = MaybeSentinel.DEFAULT 

1793 

1794 #: The whitespace before ``name``. It will appear after ``star`` when a star 

1795 #: exists. 

1796 whitespace_after_star: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

1797 

1798 #: The whitespace after this entire node. 

1799 whitespace_after_param: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

1800 

1801 def _validate(self) -> None: 

1802 if self.default is None and isinstance(self.equal, AssignEqual): 

1803 raise CSTValidationError( 

1804 "Must have a default when specifying an AssignEqual." 

1805 ) 

1806 if isinstance(self.star, str) and self.star not in ("", "*", "**"): 

1807 raise CSTValidationError("Must specify either '', '*' or '**' for star.") 

1808 

1809 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Param": 

1810 return Param( 

1811 star=self.star, 

1812 whitespace_after_star=visit_required( 

1813 self, "whitespace_after_star", self.whitespace_after_star, visitor 

1814 ), 

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

1816 annotation=visit_optional(self, "annotation", self.annotation, visitor), 

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

1818 default=visit_optional(self, "default", self.default, visitor), 

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

1820 whitespace_after_param=visit_required( 

1821 self, "whitespace_after_param", self.whitespace_after_param, visitor 

1822 ), 

1823 ) 

1824 

1825 def _codegen_impl( 

1826 self, 

1827 state: CodegenState, 

1828 default_star: Optional[str] = None, 

1829 default_comma: bool = False, 

1830 ) -> None: 

1831 with state.record_syntactic_position(self): 

1832 star = self.star 

1833 if isinstance(star, MaybeSentinel): 

1834 if default_star is None: 

1835 raise CSTCodegenError( 

1836 "Must specify a concrete default_star if default used on star." 

1837 ) 

1838 star = default_star 

1839 if isinstance(star, str): 

1840 state.add_token(star) 

1841 self.whitespace_after_star._codegen(state) 

1842 self.name._codegen(state) 

1843 

1844 annotation = self.annotation 

1845 if annotation is not None: 

1846 annotation._codegen(state, default_indicator=":") 

1847 equal = self.equal 

1848 if equal is MaybeSentinel.DEFAULT and self.default is not None: 

1849 state.add_token(" = ") 

1850 elif isinstance(equal, AssignEqual): 

1851 equal._codegen(state) 

1852 default = self.default 

1853 if default is not None: 

1854 default._codegen(state) 

1855 comma = self.comma 

1856 if comma is MaybeSentinel.DEFAULT and default_comma: 

1857 state.add_token(", ") 

1858 elif isinstance(comma, Comma): 

1859 comma._codegen(state) 

1860 

1861 self.whitespace_after_param._codegen(state) 

1862 

1863 

1864@add_slots 

1865@dataclass(frozen=True) 

1866class Parameters(CSTNode): 

1867 """ 

1868 A function or lambda parameter list. 

1869 """ 

1870 

1871 #: Positional parameters, with or without defaults. Positional parameters 

1872 #: with defaults must all be after those without defaults. 

1873 params: Sequence[Param] = () 

1874 

1875 # Optional parameter that captures unspecified positional arguments or a sentinel 

1876 # star that dictates parameters following are kwonly args. 

1877 star_arg: Union[Param, ParamStar, MaybeSentinel] = MaybeSentinel.DEFAULT 

1878 

1879 #: Keyword-only params that may or may not have defaults. 

1880 kwonly_params: Sequence[Param] = () 

1881 

1882 #: Optional parameter that captures unspecified kwargs. 

1883 star_kwarg: Optional[Param] = None 

1884 

1885 #: Positional-only parameters, with or without defaults. Positional-only 

1886 #: parameters with defaults must all be after those without defaults. 

1887 posonly_params: Sequence[Param] = () 

1888 

1889 #: Optional sentinel that dictates parameters preceeding are positional-only 

1890 #: args. 

1891 posonly_ind: Union[ParamSlash, MaybeSentinel] = MaybeSentinel.DEFAULT 

1892 

1893 def _validate_stars_sequence(self, vals: Sequence[Param], *, section: str) -> None: 

1894 if len(vals) == 0: 

1895 return 

1896 for val in vals: 

1897 if isinstance(val.star, str) and val.star != "": 

1898 raise CSTValidationError( 

1899 f"Expecting a star prefix of '' for {section} Param." 

1900 ) 

1901 

1902 def _validate_posonly_ind(self) -> None: 

1903 if isinstance(self.posonly_ind, ParamSlash) and len(self.posonly_params) == 0: 

1904 raise CSTValidationError( 

1905 "Must have at least one posonly param if ParamSlash is used." 

1906 ) 

1907 

1908 def _validate_kwonly_star(self) -> None: 

1909 if isinstance(self.star_arg, ParamStar) and len(self.kwonly_params) == 0: 

1910 raise CSTValidationError( 

1911 "Must have at least one kwonly param if ParamStar is used." 

1912 ) 

1913 

1914 def _validate_defaults(self) -> None: 

1915 seen_default = False 

1916 # pyre-fixme[60]: Concatenation not yet support for multiple variadic 

1917 # tuples: `*self.posonly_params, *self.params`. 

1918 for param in (*self.posonly_params, *self.params): 

1919 if param.default: 

1920 # Mark that we've moved onto defaults 

1921 if not seen_default: 

1922 seen_default = True 

1923 else: 

1924 if seen_default: 

1925 # We accidentally included a non-default after a default arg! 

1926 raise CSTValidationError( 

1927 "Cannot have param without defaults following a param with defaults." 

1928 ) 

1929 star_arg = self.star_arg 

1930 if isinstance(star_arg, Param) and star_arg.default is not None: 

1931 raise CSTValidationError("Cannot have default for star_arg.") 

1932 star_kwarg = self.star_kwarg 

1933 if star_kwarg is not None and star_kwarg.default is not None: 

1934 raise CSTValidationError("Cannot have default for star_kwarg.") 

1935 

1936 def _validate_stars(self) -> None: 

1937 if len(self.params) > 0: 

1938 self._validate_stars_sequence(self.params, section="params") 

1939 if len(self.posonly_params) > 0: 

1940 self._validate_stars_sequence(self.posonly_params, section="posonly_params") 

1941 star_arg = self.star_arg 

1942 if ( 

1943 isinstance(star_arg, Param) 

1944 and isinstance(star_arg.star, str) 

1945 and star_arg.star != "*" 

1946 ): 

1947 raise CSTValidationError( 

1948 "Expecting a star prefix of '*' for star_arg Param." 

1949 ) 

1950 if len(self.kwonly_params) > 0: 

1951 self._validate_stars_sequence(self.kwonly_params, section="kwonly_params") 

1952 star_kwarg = self.star_kwarg 

1953 if ( 

1954 star_kwarg is not None 

1955 and isinstance(star_kwarg.star, str) 

1956 and star_kwarg.star != "**" 

1957 ): 

1958 raise CSTValidationError( 

1959 "Expecting a star prefix of '**' for star_kwarg Param." 

1960 ) 

1961 

1962 def _validate(self) -> None: 

1963 # Validate posonly_params slash placement semantics. 

1964 self._validate_posonly_ind() 

1965 # Validate kwonly_param star placement semantics. 

1966 self._validate_kwonly_star() 

1967 # Validate defaults semantics for params and star_arg/star_kwarg. 

1968 self._validate_defaults() 

1969 # Validate that we don't have random stars on non star_kwarg. 

1970 self._validate_stars() 

1971 

1972 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Parameters": 

1973 return Parameters( 

1974 posonly_params=visit_sequence( 

1975 self, "posonly_params", self.posonly_params, visitor 

1976 ), 

1977 posonly_ind=visit_sentinel(self, "posonly_ind", self.posonly_ind, visitor), 

1978 params=visit_sequence(self, "params", self.params, visitor), 

1979 star_arg=visit_sentinel(self, "star_arg", self.star_arg, visitor), 

1980 kwonly_params=visit_sequence( 

1981 self, "kwonly_params", self.kwonly_params, visitor 

1982 ), 

1983 star_kwarg=visit_optional(self, "star_kwarg", self.star_kwarg, visitor), 

1984 ) 

1985 

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

1987 # Compute the star existence first so we can ask about whether 

1988 # each element is the last in the list or not. 

1989 star_arg = self.star_arg 

1990 if isinstance(star_arg, MaybeSentinel): 

1991 starincluded = len(self.kwonly_params) > 0 

1992 elif isinstance(star_arg, (Param, ParamStar)): 

1993 starincluded = True 

1994 else: 

1995 starincluded = False 

1996 # Render out the positional-only params first. They will always have trailing 

1997 # commas because in order to have positional-only params, there must be a 

1998 # slash afterwards. 

1999 for i, param in enumerate(self.posonly_params): 

2000 param._codegen(state, default_star="", default_comma=True) 

2001 # Render out the positional-only indicator if necessary. 

2002 more_values = ( 

2003 starincluded 

2004 or len(self.params) > 0 

2005 or len(self.kwonly_params) > 0 

2006 or self.star_kwarg is not None 

2007 ) 

2008 posonly_ind = self.posonly_ind 

2009 if isinstance(posonly_ind, ParamSlash): 

2010 # Its explicitly included, so render the version we have here which 

2011 # might have spacing applied to its comma. 

2012 posonly_ind._codegen(state, default_comma=more_values) 

2013 elif len(self.posonly_params) > 0: 

2014 if more_values: 

2015 state.add_token("/, ") 

2016 else: 

2017 state.add_token("/") 

2018 # Render out the params next, computing necessary trailing commas. 

2019 lastparam = len(self.params) - 1 

2020 more_values = ( 

2021 starincluded or len(self.kwonly_params) > 0 or self.star_kwarg is not None 

2022 ) 

2023 for i, param in enumerate(self.params): 

2024 param._codegen( 

2025 state, default_star="", default_comma=(i < lastparam or more_values) 

2026 ) 

2027 # Render out optional star sentinel if its explicitly included or 

2028 # if we are inferring it from kwonly_params. Otherwise, render out the 

2029 # optional star_arg. 

2030 if isinstance(star_arg, MaybeSentinel): 

2031 if starincluded: 

2032 state.add_token("*, ") 

2033 elif isinstance(star_arg, Param): 

2034 more_values = len(self.kwonly_params) > 0 or self.star_kwarg is not None 

2035 star_arg._codegen(state, default_star="*", default_comma=more_values) 

2036 elif isinstance(star_arg, ParamStar): 

2037 star_arg._codegen(state) 

2038 # Render out the kwonly_args next, computing necessary trailing commas. 

2039 lastparam = len(self.kwonly_params) - 1 

2040 more_values = self.star_kwarg is not None 

2041 for i, param in enumerate(self.kwonly_params): 

2042 param._codegen( 

2043 state, default_star="", default_comma=(i < lastparam or more_values) 

2044 ) 

2045 # Finally, render out any optional star_kwarg 

2046 star_kwarg = self.star_kwarg 

2047 if star_kwarg is not None: 

2048 star_kwarg._codegen(state, default_star="**", default_comma=False) 

2049 

2050 

2051@add_slots 

2052@dataclass(frozen=True) 

2053class Lambda(BaseExpression): 

2054 """ 

2055 A lambda expression that creates an anonymous function. 

2056 

2057 :: 

2058 

2059 Lambda( 

2060 params=Parameters([Param(Name("arg"))]), 

2061 body=Ellipsis(), 

2062 ) 

2063 

2064 Represents the following code:: 

2065 

2066 lambda arg: ... 

2067 

2068 Named functions statements are provided by :class:`FunctionDef`. 

2069 """ 

2070 

2071 #: The arguments to the lambda. This is similar to the arguments on a 

2072 #: :class:`FunctionDef`, however lambda arguments are not allowed to have an 

2073 #: :class:`Annotation`. 

2074 params: Parameters 

2075 

2076 #: The value that the lambda computes and returns when called. 

2077 body: BaseExpression 

2078 

2079 #: The colon separating the parameters from the body. 

2080 colon: Colon = Colon.field(whitespace_after=SimpleWhitespace(" ")) 

2081 

2082 lpar: Sequence[LeftParen] = () 

2083 #: Sequence of parenthesis for precedence dictation. 

2084 rpar: Sequence[RightParen] = () 

2085 

2086 #: Whitespace after the lambda keyword, but before any argument or the colon. 

2087 whitespace_after_lambda: Union[ 

2088 BaseParenthesizableWhitespace, MaybeSentinel 

2089 ] = MaybeSentinel.DEFAULT 

2090 

2091 def _validate(self) -> None: 

2092 # Validate parents 

2093 super(Lambda, self)._validate() 

2094 # Sum up all parameters 

2095 all_params = [ 

2096 *self.params.posonly_params, 

2097 *self.params.params, 

2098 *self.params.kwonly_params, 

2099 ] 

2100 star_arg = self.params.star_arg 

2101 if isinstance(star_arg, Param): 

2102 all_params.append(star_arg) 

2103 star_kwarg = self.params.star_kwarg 

2104 if star_kwarg is not None: 

2105 all_params.append(star_kwarg) 

2106 # Check for nonzero parameters because several checks care 

2107 # about this. 

2108 if len(all_params) > 0: 

2109 for param in all_params: 

2110 if param.annotation is not None: 

2111 raise CSTValidationError( 

2112 "Lambda params cannot have type annotations." 

2113 ) 

2114 whitespace_after_lambda = self.whitespace_after_lambda 

2115 if ( 

2116 isinstance(whitespace_after_lambda, BaseParenthesizableWhitespace) 

2117 and whitespace_after_lambda.empty 

2118 ): 

2119 raise CSTValidationError( 

2120 "Must have at least one space after lambda when specifying params" 

2121 ) 

2122 

2123 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Lambda": 

2124 return Lambda( 

2125 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

2126 whitespace_after_lambda=visit_sentinel( 

2127 self, "whitespace_after_lambda", self.whitespace_after_lambda, visitor 

2128 ), 

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

2130 colon=visit_required(self, "colon", self.colon, visitor), 

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

2132 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

2133 ) 

2134 

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

2136 with self._parenthesize(state): 

2137 state.add_token("lambda") 

2138 whitespace_after_lambda = self.whitespace_after_lambda 

2139 if isinstance(whitespace_after_lambda, MaybeSentinel): 

2140 if not ( 

2141 len(self.params.posonly_params) == 0 

2142 and len(self.params.params) == 0 

2143 and not isinstance(self.params.star_arg, Param) 

2144 and len(self.params.kwonly_params) == 0 

2145 and self.params.star_kwarg is None 

2146 ): 

2147 # We have one or more params, provide a space 

2148 state.add_token(" ") 

2149 elif isinstance(whitespace_after_lambda, BaseParenthesizableWhitespace): 

2150 whitespace_after_lambda._codegen(state) 

2151 self.params._codegen(state) 

2152 self.colon._codegen(state) 

2153 self.body._codegen(state) 

2154 

2155 

2156@add_slots 

2157@dataclass(frozen=True) 

2158class Arg(CSTNode): 

2159 """ 

2160 A single argument to a :class:`Call`. 

2161 

2162 This supports named keyword arguments in the form of ``keyword=value`` and variable 

2163 argument expansion using ``*args`` or ``**kwargs`` syntax. 

2164 """ 

2165 

2166 #: The argument expression itself, not including a preceding keyword, or any of 

2167 #: the surrounding the value, like a comma or asterisks. 

2168 value: BaseExpression 

2169 

2170 #: Optional keyword for the argument. 

2171 keyword: Optional[Name] = None 

2172 

2173 #: The equal sign used to denote assignment if there is a keyword. 

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

2175 

2176 #: Any trailing comma. 

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

2178 

2179 #: A string with zero, one, or two asterisks appearing before the name. These are 

2180 #: expanded into variable number of positional or keyword arguments. 

2181 star: Literal["", "*", "**"] = "" 

2182 

2183 #: Whitespace after the ``star`` (if it exists), but before the ``keyword`` or 

2184 #: ``value`` (if no keyword is provided). 

2185 whitespace_after_star: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

2186 #: Whitespace after this entire node. The :class:`Comma` node (if it exists) may 

2187 #: also store some trailing whitespace. 

2188 whitespace_after_arg: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

2189 

2190 def _validate(self) -> None: 

2191 if self.keyword is None and isinstance(self.equal, AssignEqual): 

2192 raise CSTValidationError( 

2193 "Must have a keyword when specifying an AssignEqual." 

2194 ) 

2195 if self.star not in ("", "*", "**"): 

2196 raise CSTValidationError("Must specify either '', '*' or '**' for star.") 

2197 if self.star in ("*", "**") and self.keyword is not None: 

2198 raise CSTValidationError("Cannot specify a star and a keyword together.") 

2199 

2200 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Arg": 

2201 return Arg( 

2202 star=self.star, 

2203 whitespace_after_star=visit_required( 

2204 self, "whitespace_after_star", self.whitespace_after_star, visitor 

2205 ), 

2206 keyword=visit_optional(self, "keyword", self.keyword, visitor), 

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

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

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

2210 whitespace_after_arg=visit_required( 

2211 self, "whitespace_after_arg", self.whitespace_after_arg, visitor 

2212 ), 

2213 ) 

2214 

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

2216 with state.record_syntactic_position(self): 

2217 state.add_token(self.star) 

2218 self.whitespace_after_star._codegen(state) 

2219 keyword = self.keyword 

2220 if keyword is not None: 

2221 keyword._codegen(state) 

2222 equal = self.equal 

2223 if equal is MaybeSentinel.DEFAULT and self.keyword is not None: 

2224 state.add_token(" = ") 

2225 elif isinstance(equal, AssignEqual): 

2226 equal._codegen(state) 

2227 self.value._codegen(state) 

2228 

2229 comma = self.comma 

2230 if comma is MaybeSentinel.DEFAULT and default_comma: 

2231 state.add_token(", ") 

2232 elif isinstance(comma, Comma): 

2233 comma._codegen(state) 

2234 self.whitespace_after_arg._codegen(state) 

2235 

2236 

2237class _BaseExpressionWithArgs(BaseExpression, ABC): 

2238 """ 

2239 Arguments are complicated enough that we can't represent them easily 

2240 in typing. So, we have common validation functions here. 

2241 """ 

2242 

2243 __slots__ = () 

2244 

2245 #: Sequence of arguments that will be passed to the function call. 

2246 args: Sequence[Arg] = () 

2247 

2248 def _check_kwargs_or_keywords(self, arg: Arg) -> None: 

2249 """ 

2250 Validates that we only have a mix of "keyword=arg" and "**arg" expansion. 

2251 """ 

2252 

2253 if arg.keyword is not None: 

2254 # Valid, keyword argument 

2255 return None 

2256 elif arg.star == "**": 

2257 # Valid, kwargs 

2258 return None 

2259 elif arg.star == "*": 

2260 # Invalid, cannot have "*" follow "**" 

2261 raise CSTValidationError( 

2262 "Cannot have iterable argument unpacking after keyword argument unpacking." 

2263 ) 

2264 else: 

2265 # Invalid, cannot have positional argument follow **/keyword 

2266 raise CSTValidationError( 

2267 "Cannot have positional argument after keyword argument unpacking." 

2268 ) 

2269 

2270 def _check_starred_or_keywords( 

2271 self, arg: Arg 

2272 ) -> Optional[Callable[[Arg], Callable[[Arg], None]]]: 

2273 """ 

2274 Validates that we only have a mix of "*arg" expansion and "keyword=arg". 

2275 """ 

2276 

2277 if arg.keyword is not None: 

2278 # Valid, keyword argument 

2279 return None 

2280 elif arg.star == "**": 

2281 # Valid, but we now no longer allow "*" args 

2282 # pyre-fixme[7]: Expected `Optional[Callable[[Arg], Callable[..., 

2283 # Any]]]` but got `Callable[[Arg], Optional[Callable[[Arg], Callable[..., 

2284 # Any]]]]`. 

2285 return self._check_kwargs_or_keywords 

2286 elif arg.star == "*": 

2287 # Valid, iterable unpacking 

2288 return None 

2289 else: 

2290 # Invalid, cannot have positional argument follow **/keyword 

2291 raise CSTValidationError( 

2292 "Cannot have positional argument after keyword argument." 

2293 ) 

2294 

2295 def _check_positional( 

2296 self, arg: Arg 

2297 ) -> Optional[Callable[[Arg], Callable[[Arg], Callable[[Arg], None]]]]: 

2298 """ 

2299 Validates that we only have a mix of positional args and "*arg" expansion. 

2300 """ 

2301 

2302 if arg.keyword is not None: 

2303 # Valid, but this puts us into starred/keyword state 

2304 # pyre-fixme[7]: Expected `Optional[Callable[[Arg], Callable[..., 

2305 # Any]]]` but got `Callable[[Arg], Optional[Callable[[Arg], Callable[..., 

2306 # Any]]]]`. 

2307 return self._check_starred_or_keywords 

2308 elif arg.star == "**": 

2309 # Valid, but we skip states to kwargs/keywords 

2310 # pyre-fixme[7]: Expected `Optional[Callable[[Arg], Callable[..., 

2311 # Any]]]` but got `Callable[[Arg], Optional[Callable[[Arg], Callable[..., 

2312 # Any]]]]`. 

2313 return self._check_kwargs_or_keywords 

2314 elif arg.star == "*": 

2315 # Valid, iterator expansion 

2316 return None 

2317 else: 

2318 # Valid, allowed to have positional arguments here 

2319 return None 

2320 

2321 # pyre-fixme[30]: Pyre gave up inferring some types - function `_validate` was 

2322 # too complex. 

2323 def _validate(self) -> None: 

2324 # Validate any super-class stuff, whatever it may be. 

2325 super()._validate() 

2326 # Now, validate the weird intermingling rules for arguments by running 

2327 # a small validator state machine. This works by passing each argument 

2328 # to a validator function which can either raise an exception if it 

2329 # detects an invalid sequence, return a new validator to be used for the 

2330 # next arg, or return None to use the same validator. We could enforce 

2331 # always returning ourselves instead of None but it ends up making the 

2332 # functions themselves less readable. In this way, the current validator 

2333 # function encodes the state we're in (positional state, iterable 

2334 # expansion state, or dictionary expansion state). 

2335 validator = self._check_positional 

2336 for arg in self.args: 

2337 validator = validator(arg) or validator 

2338 

2339 

2340@add_slots 

2341@dataclass(frozen=True) 

2342class Call(_BaseExpressionWithArgs): 

2343 """ 

2344 An expression representing a function call, such as ``do_math(1, 2)`` or 

2345 ``picture.post_on_instagram()``. 

2346 

2347 Function calls consist of a function name and a sequence of arguments wrapped in 

2348 :class:`Arg` nodes. 

2349 """ 

2350 

2351 #: The expression resulting in a callable that we are to call. Often a :class:`Name` 

2352 #: or :class:`Attribute`. 

2353 func: BaseExpression 

2354 

2355 #: The arguments to pass to the resulting callable. These may be a mix of 

2356 #: positional arguments, keyword arguments, or "starred" arguments. 

2357 args: Sequence[Arg] = () 

2358 

2359 lpar: Sequence[LeftParen] = () 

2360 #: Sequence of parenthesis for precedence dictation. These are not the parenthesis 

2361 #: before and after the list of ``args``, but rather arguments around the entire 

2362 #: call expression, such as ``(( do_math(1, 2) ))``. 

2363 rpar: Sequence[RightParen] = () 

2364 

2365 #: Whitespace after the ``func`` name, but before the opening parenthesis. 

2366 whitespace_after_func: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

2367 #: Whitespace after the opening parenthesis but before the first argument (if there 

2368 #: are any). Whitespace after the last argument but before the closing parenthesis 

2369 #: is owned by the last :class:`Arg` if it exists. 

2370 whitespace_before_args: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

2371 

2372 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

2373 """ 

2374 Calls have a close paren on the right side regardless of whether they're 

2375 parenthesized as a whole. As a result, they are safe to use directly against 

2376 an adjacent node to the right. 

2377 """ 

2378 if position == ExpressionPosition.LEFT: 

2379 return True 

2380 if super(Call, self)._safe_to_use_with_word_operator(position): 

2381 return True 

2382 if position == ExpressionPosition.RIGHT: 

2383 return self.func._safe_to_use_with_word_operator(ExpressionPosition.RIGHT) 

2384 return False 

2385 

2386 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Call": 

2387 return Call( 

2388 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

2389 func=visit_required(self, "func", self.func, visitor), 

2390 whitespace_after_func=visit_required( 

2391 self, "whitespace_after_func", self.whitespace_after_func, visitor 

2392 ), 

2393 whitespace_before_args=visit_required( 

2394 self, "whitespace_before_args", self.whitespace_before_args, visitor 

2395 ), 

2396 args=visit_sequence(self, "args", self.args, visitor), 

2397 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

2398 ) 

2399 

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

2401 with self._parenthesize(state): 

2402 self.func._codegen(state) 

2403 self.whitespace_after_func._codegen(state) 

2404 state.add_token("(") 

2405 self.whitespace_before_args._codegen(state) 

2406 lastarg = len(self.args) - 1 

2407 for i, arg in enumerate(self.args): 

2408 arg._codegen(state, default_comma=(i != lastarg)) 

2409 state.add_token(")") 

2410 

2411 

2412@add_slots 

2413@dataclass(frozen=True) 

2414class Await(BaseExpression): 

2415 """ 

2416 An await expression. Await expressions are only valid inside the body of an 

2417 asynchronous :class:`FunctionDef` or (as of Python 3.7) inside of an asynchronous 

2418 :class:`GeneratorExp` nodes. 

2419 """ 

2420 

2421 #: The actual expression we need to wait for. 

2422 expression: BaseExpression 

2423 

2424 lpar: Sequence[LeftParen] = () 

2425 #: Sequence of parenthesis for precedence dictation. 

2426 rpar: Sequence[RightParen] = () 

2427 

2428 #: Whitespace that appears after the ``async`` keyword, but before the inner 

2429 #: ``expression``. 

2430 whitespace_after_await: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

2431 

2432 def _validate(self) -> None: 

2433 # Validate any super-class stuff, whatever it may be. 

2434 super(Await, self)._validate() 

2435 # Make sure we don't run identifiers together. 

2436 if ( 

2437 self.whitespace_after_await.empty 

2438 and not self.expression._safe_to_use_with_word_operator( 

2439 ExpressionPosition.RIGHT 

2440 ) 

2441 ): 

2442 raise CSTValidationError("Must have at least one space after await") 

2443 

2444 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Await": 

2445 return Await( 

2446 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

2447 whitespace_after_await=visit_required( 

2448 self, "whitespace_after_await", self.whitespace_after_await, visitor 

2449 ), 

2450 expression=visit_required(self, "expression", self.expression, visitor), 

2451 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

2452 ) 

2453 

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

2455 with self._parenthesize(state): 

2456 state.add_token("await") 

2457 self.whitespace_after_await._codegen(state) 

2458 self.expression._codegen(state) 

2459 

2460 

2461@add_slots 

2462@dataclass(frozen=True) 

2463class IfExp(BaseExpression): 

2464 """ 

2465 An if expression of the form ``body if test else orelse``. 

2466 

2467 If statements are provided by :class:`If` and :class:`Else` nodes. 

2468 """ 

2469 

2470 #: The test to perform. 

2471 test: BaseExpression 

2472 

2473 #: The expression to evaluate when the test is true. 

2474 body: BaseExpression 

2475 

2476 #: The expression to evaluate when the test is false. 

2477 orelse: BaseExpression 

2478 

2479 lpar: Sequence[LeftParen] = () 

2480 #: Sequence of parenthesis for precedence dictation. 

2481 rpar: Sequence[RightParen] = () 

2482 

2483 #: Whitespace after the ``body`` expression, but before the ``if`` keyword. 

2484 whitespace_before_if: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

2485 

2486 #: Whitespace after the ``if`` keyword, but before the ``test`` clause. 

2487 whitespace_after_if: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

2488 

2489 #: Whitespace after the ``test`` expression, but before the ``else`` keyword. 

2490 whitespace_before_else: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

2491 

2492 #: Whitespace after the ``else`` keyword, but before the ``orelse`` expression. 

2493 whitespace_after_else: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

2494 

2495 def _validate(self) -> None: 

2496 # Paren validation and such 

2497 super(IfExp, self)._validate() 

2498 # Validate spacing rules 

2499 if ( 

2500 self.whitespace_before_if.empty 

2501 and not self.body._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

2502 ): 

2503 raise CSTValidationError( 

2504 "Must have at least one space before 'if' keyword." 

2505 ) 

2506 if ( 

2507 self.whitespace_after_if.empty 

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

2509 ): 

2510 raise CSTValidationError("Must have at least one space after 'if' keyword.") 

2511 if ( 

2512 self.whitespace_before_else.empty 

2513 and not self.test._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

2514 ): 

2515 raise CSTValidationError( 

2516 "Must have at least one space before 'else' keyword." 

2517 ) 

2518 if ( 

2519 self.whitespace_after_else.empty 

2520 and not self.orelse._safe_to_use_with_word_operator( 

2521 ExpressionPosition.RIGHT 

2522 ) 

2523 ): 

2524 raise CSTValidationError( 

2525 "Must have at least one space after 'else' keyword." 

2526 ) 

2527 

2528 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "IfExp": 

2529 return IfExp( 

2530 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

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

2532 whitespace_before_if=visit_required( 

2533 self, "whitespace_before_if", self.whitespace_before_if, visitor 

2534 ), 

2535 whitespace_after_if=visit_required( 

2536 self, "whitespace_after_if", self.whitespace_after_if, visitor 

2537 ), 

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

2539 whitespace_before_else=visit_required( 

2540 self, "whitespace_before_else", self.whitespace_before_else, visitor 

2541 ), 

2542 whitespace_after_else=visit_required( 

2543 self, "whitespace_after_else", self.whitespace_after_else, visitor 

2544 ), 

2545 orelse=visit_required(self, "orelse", self.orelse, visitor), 

2546 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

2547 ) 

2548 

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

2550 with self._parenthesize(state): 

2551 self.body._codegen(state) 

2552 self.whitespace_before_if._codegen(state) 

2553 state.add_token("if") 

2554 self.whitespace_after_if._codegen(state) 

2555 self.test._codegen(state) 

2556 self.whitespace_before_else._codegen(state) 

2557 state.add_token("else") 

2558 self.whitespace_after_else._codegen(state) 

2559 self.orelse._codegen(state) 

2560 

2561 

2562@add_slots 

2563@dataclass(frozen=True) 

2564class From(CSTNode): 

2565 """ 

2566 A ``from x`` stanza in a :class:`Yield` or :class:`Raise`. 

2567 """ 

2568 

2569 #: The expression that we are yielding/raising from. 

2570 item: BaseExpression 

2571 

2572 #: The whitespace at the very start of this node. 

2573 whitespace_before_from: Union[ 

2574 BaseParenthesizableWhitespace, MaybeSentinel 

2575 ] = MaybeSentinel.DEFAULT 

2576 

2577 #: The whitespace after the ``from`` keyword, but before the ``item``. 

2578 whitespace_after_from: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

2579 

2580 def _validate(self) -> None: 

2581 if ( 

2582 isinstance(self.whitespace_after_from, BaseParenthesizableWhitespace) 

2583 and self.whitespace_after_from.empty 

2584 and not self.item._safe_to_use_with_word_operator(ExpressionPosition.RIGHT) 

2585 ): 

2586 raise CSTValidationError( 

2587 "Must have at least one space after 'from' keyword." 

2588 ) 

2589 

2590 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "From": 

2591 return From( 

2592 whitespace_before_from=visit_sentinel( 

2593 self, "whitespace_before_from", self.whitespace_before_from, visitor 

2594 ), 

2595 whitespace_after_from=visit_required( 

2596 self, "whitespace_after_from", self.whitespace_after_from, visitor 

2597 ), 

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

2599 ) 

2600 

2601 def _codegen_impl(self, state: CodegenState, default_space: str = "") -> None: 

2602 whitespace_before_from = self.whitespace_before_from 

2603 if isinstance(whitespace_before_from, BaseParenthesizableWhitespace): 

2604 whitespace_before_from._codegen(state) 

2605 else: 

2606 state.add_token(default_space) 

2607 

2608 with state.record_syntactic_position(self): 

2609 state.add_token("from") 

2610 self.whitespace_after_from._codegen(state) 

2611 self.item._codegen(state) 

2612 

2613 

2614@add_slots 

2615@dataclass(frozen=True) 

2616class Yield(BaseExpression): 

2617 """ 

2618 A yield expression similar to ``yield x`` or ``yield from fun()``. 

2619 

2620 To learn more about the ways that yield can be used in generators, refer to 

2621 `Python's language reference 

2622 <https://docs.python.org/3/reference/expressions.html#yieldexpr>`__. 

2623 """ 

2624 

2625 #: The value yielded from the generator, in the case of a :class:`From` clause, a 

2626 #: sub-generator to iterate over. 

2627 value: Optional[Union[BaseExpression, From]] = None 

2628 

2629 lpar: Sequence[LeftParen] = () 

2630 #: Sequence of parenthesis for precedence dictation. 

2631 rpar: Sequence[RightParen] = () 

2632 

2633 #: Whitespace after the ``yield`` keyword, but before the ``value``. 

2634 whitespace_after_yield: Union[ 

2635 BaseParenthesizableWhitespace, MaybeSentinel 

2636 ] = MaybeSentinel.DEFAULT 

2637 

2638 def _validate(self) -> None: 

2639 # Paren rules and such 

2640 super(Yield, self)._validate() 

2641 # Our own rules 

2642 whitespace_after_yield = self.whitespace_after_yield 

2643 if ( 

2644 isinstance(whitespace_after_yield, BaseParenthesizableWhitespace) 

2645 and whitespace_after_yield.empty 

2646 ): 

2647 value = self.value 

2648 if isinstance(value, From): 

2649 raise CSTValidationError( 

2650 "Must have at least one space after 'yield' keyword." 

2651 ) 

2652 if isinstance( 

2653 value, BaseExpression 

2654 ) and not value._safe_to_use_with_word_operator(ExpressionPosition.RIGHT): 

2655 raise CSTValidationError( 

2656 "Must have at least one space after 'yield' keyword." 

2657 ) 

2658 

2659 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Yield": 

2660 return Yield( 

2661 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

2662 whitespace_after_yield=visit_sentinel( 

2663 self, "whitespace_after_yield", self.whitespace_after_yield, visitor 

2664 ), 

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

2666 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

2667 ) 

2668 

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

2670 with self._parenthesize(state): 

2671 state.add_token("yield") 

2672 whitespace_after_yield = self.whitespace_after_yield 

2673 if isinstance(whitespace_after_yield, BaseParenthesizableWhitespace): 

2674 whitespace_after_yield._codegen(state) 

2675 else: 

2676 # Only need a space after yield if there is a value to yield. 

2677 if self.value is not None: 

2678 state.add_token(" ") 

2679 value = self.value 

2680 if isinstance(value, From): 

2681 value._codegen(state, default_space="") 

2682 elif value is not None: 

2683 value._codegen(state) 

2684 

2685 

2686class _BaseElementImpl(CSTNode, ABC): 

2687 """ 

2688 An internal base class for :class:`Element` and :class:`DictElement`. 

2689 """ 

2690 

2691 __slots__ = () 

2692 

2693 value: BaseExpression 

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

2695 

2696 def _codegen_comma( 

2697 self, 

2698 state: CodegenState, 

2699 default_comma: bool = False, 

2700 default_comma_whitespace: bool = False, # False for a single-item collection 

2701 ) -> None: 

2702 """ 

2703 Called by `_codegen_impl` in subclasses to generate the comma. 

2704 """ 

2705 comma = self.comma 

2706 if comma is MaybeSentinel.DEFAULT and default_comma: 

2707 if default_comma_whitespace: 

2708 state.add_token(", ") 

2709 else: 

2710 state.add_token(",") 

2711 elif isinstance(comma, Comma): 

2712 comma._codegen(state) 

2713 

2714 @abstractmethod 

2715 def _codegen_impl( 

2716 self, 

2717 state: CodegenState, 

2718 default_comma: bool = False, 

2719 default_comma_whitespace: bool = False, # False for a single-item collection 

2720 ) -> None: 

2721 ... 

2722 

2723 

2724class BaseElement(_BaseElementImpl, ABC): 

2725 """ 

2726 An element of a literal list, tuple, or set. For elements of a literal dict, see 

2727 BaseDictElement. 

2728 """ 

2729 

2730 __slots__ = () 

2731 

2732 

2733class BaseDictElement(_BaseElementImpl, ABC): 

2734 """ 

2735 An element of a literal dict. For elements of a list, tuple, or set, see 

2736 BaseElement. 

2737 """ 

2738 

2739 __slots__ = () 

2740 

2741 

2742@add_slots 

2743@dataclass(frozen=True) 

2744class Element(BaseElement): 

2745 """ 

2746 A simple value in a literal :class:`List`, :class:`Tuple`, or :class:`Set`. 

2747 These a literal collection may also contain a :class:`StarredElement`. 

2748 

2749 If you're using a literal :class:`Dict`, see :class:`DictElement` instead. 

2750 """ 

2751 

2752 value: BaseExpression 

2753 

2754 #: A trailing comma. By default, we'll only insert a comma if one is required. 

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

2756 

2757 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Element": 

2758 return Element( 

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

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

2761 ) 

2762 

2763 def _codegen_impl( 

2764 self, 

2765 state: CodegenState, 

2766 default_comma: bool = False, 

2767 default_comma_whitespace: bool = False, 

2768 ) -> None: 

2769 with state.record_syntactic_position(self): 

2770 self.value._codegen(state) 

2771 self._codegen_comma(state, default_comma, default_comma_whitespace) 

2772 

2773 

2774@add_slots 

2775@dataclass(frozen=True) 

2776class DictElement(BaseDictElement): 

2777 """ 

2778 A simple ``key: value`` pair that represents a single entry in a literal 

2779 :class:`Dict`. :class:`Dict` nodes may also contain a 

2780 :class:`StarredDictElement`. 

2781 

2782 If you're using a literal :class:`List`, :class:`Tuple`, or :class:`Set`, 

2783 see :class:`Element` instead. 

2784 """ 

2785 

2786 key: BaseExpression 

2787 value: BaseExpression 

2788 

2789 #: A trailing comma. By default, we'll only insert a comma if one is required. 

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

2791 

2792 #: Whitespace after the key, but before the colon in ``key : value``. 

2793 whitespace_before_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

2794 #: Whitespace after the colon, but before the value in ``key : value``. 

2795 whitespace_after_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

2796 

2797 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "DictElement": 

2798 return DictElement( 

2799 key=visit_required(self, "key", self.key, visitor), 

2800 whitespace_before_colon=visit_required( 

2801 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

2802 ), 

2803 whitespace_after_colon=visit_required( 

2804 self, "whitespace_after_colon", self.whitespace_after_colon, visitor 

2805 ), 

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

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

2808 ) 

2809 

2810 def _codegen_impl( 

2811 self, 

2812 state: CodegenState, 

2813 default_comma: bool = False, 

2814 default_comma_whitespace: bool = False, 

2815 ) -> None: 

2816 with state.record_syntactic_position(self): 

2817 self.key._codegen(state) 

2818 self.whitespace_before_colon._codegen(state) 

2819 state.add_token(":") 

2820 self.whitespace_after_colon._codegen(state) 

2821 self.value._codegen(state) 

2822 self._codegen_comma(state, default_comma, default_comma_whitespace) 

2823 

2824 

2825@add_slots 

2826@dataclass(frozen=True) 

2827class StarredElement(BaseElement, BaseExpression, _BaseParenthesizedNode): 

2828 """ 

2829 A starred ``*value`` element that expands to represent multiple values in a literal 

2830 :class:`List`, :class:`Tuple`, or :class:`Set`. 

2831 

2832 If you're using a literal :class:`Dict`, see :class:`StarredDictElement` instead. 

2833 

2834 If this node owns parenthesis, those parenthesis wrap the leading asterisk, but not 

2835 the trailing comma. For example:: 

2836 

2837 StarredElement( 

2838 cst.Name("el"), 

2839 comma=cst.Comma(), 

2840 lpar=[cst.LeftParen()], 

2841 rpar=[cst.RightParen()], 

2842 ) 

2843 

2844 will generate:: 

2845 

2846 (*el), 

2847 """ 

2848 

2849 value: BaseExpression 

2850 

2851 #: A trailing comma. By default, we'll only insert a comma if one is required. 

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

2853 

2854 #: Parenthesis at the beginning of the node, before the leading asterisk. 

2855 lpar: Sequence[LeftParen] = () 

2856 #: Parentheses after the value, but before a comma (if there is one). 

2857 rpar: Sequence[RightParen] = () 

2858 

2859 #: Whitespace between the leading asterisk and the value expression. 

2860 whitespace_before_value: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

2861 

2862 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "StarredElement": 

2863 return StarredElement( 

2864 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

2865 whitespace_before_value=visit_required( 

2866 self, "whitespace_before_value", self.whitespace_before_value, visitor 

2867 ), 

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

2869 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

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

2871 ) 

2872 

2873 def _codegen_impl( 

2874 self, 

2875 state: CodegenState, 

2876 default_comma: bool = False, 

2877 default_comma_whitespace: bool = False, 

2878 ) -> None: 

2879 with self._parenthesize(state): 

2880 state.add_token("*") 

2881 self.whitespace_before_value._codegen(state) 

2882 self.value._codegen(state) 

2883 self._codegen_comma(state, default_comma, default_comma_whitespace) 

2884 

2885 

2886@add_slots 

2887@dataclass(frozen=True) 

2888class StarredDictElement(BaseDictElement): 

2889 """ 

2890 A starred ``**value`` element that expands to represent multiple values in a literal 

2891 :class:`Dict`. 

2892 

2893 If you're using a literal :class:`List`, :class:`Tuple`, or :class:`Set`, 

2894 see :class:`StarredElement` instead. 

2895 

2896 Unlike :class:`StarredElement`, this node does not own left or right parenthesis, 

2897 but the ``value`` field may still contain parenthesis. This is due to some 

2898 asymmetry in Python's grammar. 

2899 """ 

2900 

2901 value: BaseExpression 

2902 

2903 #: A trailing comma. By default, we'll only insert a comma if one is required. 

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

2905 

2906 #: Whitespace between the leading asterisks and the value expression. 

2907 whitespace_before_value: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

2908 

2909 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "StarredDictElement": 

2910 return StarredDictElement( 

2911 whitespace_before_value=visit_required( 

2912 self, "whitespace_before_value", self.whitespace_before_value, visitor 

2913 ), 

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

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

2916 ) 

2917 

2918 def _codegen_impl( 

2919 self, 

2920 state: CodegenState, 

2921 default_comma: bool = False, 

2922 default_comma_whitespace: bool = False, 

2923 ) -> None: 

2924 with state.record_syntactic_position(self): 

2925 state.add_token("**") 

2926 self.whitespace_before_value._codegen(state) 

2927 self.value._codegen(state) 

2928 self._codegen_comma(state, default_comma, default_comma_whitespace) 

2929 

2930 

2931@add_slots 

2932@dataclass(frozen=True) 

2933class Tuple(BaseAssignTargetExpression, BaseDelTargetExpression): 

2934 """ 

2935 An immutable literal tuple. Tuples are often (but not always) parenthesized. 

2936 

2937 :: 

2938 

2939 Tuple([ 

2940 Element(Integer("1")), 

2941 Element(Integer("2")), 

2942 StarredElement(Name("others")), 

2943 ]) 

2944 

2945 generates the following code:: 

2946 

2947 (1, 2, *others) 

2948 """ 

2949 

2950 #: A sequence containing all the :class:`Element` and :class:`StarredElement` nodes 

2951 #: in the tuple. 

2952 elements: Sequence[BaseElement] 

2953 

2954 lpar: Sequence[LeftParen] = field(default_factory=lambda: (LeftParen(),)) 

2955 #: Sequence of parenthesis for precedence dictation. 

2956 rpar: Sequence[RightParen] = field(default_factory=lambda: (RightParen(),)) 

2957 

2958 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

2959 if super(Tuple, self)._safe_to_use_with_word_operator(position): 

2960 # if we have parenthesis, we're safe. 

2961 return True 

2962 # elements[-1] and elements[0] must exist past this point, because 

2963 # we're not parenthesized, meaning we must have at least one element. 

2964 elements = self.elements 

2965 if position == ExpressionPosition.LEFT: 

2966 last_element = elements[-1] 

2967 return ( 

2968 isinstance(last_element.comma, Comma) 

2969 or ( 

2970 isinstance(last_element, StarredElement) 

2971 and len(last_element.rpar) > 0 

2972 ) 

2973 or last_element.value._safe_to_use_with_word_operator(position) 

2974 ) 

2975 else: # ExpressionPosition.RIGHT 

2976 first_element = elements[0] 

2977 # starred elements are always safe because they begin with ( or * 

2978 return isinstance( 

2979 first_element, StarredElement 

2980 ) or first_element.value._safe_to_use_with_word_operator(position) 

2981 

2982 def _validate(self) -> None: 

2983 # Paren validation and such 

2984 super(Tuple, self)._validate() 

2985 

2986 if len(self.elements) == 0: 

2987 if len(self.lpar) == 0: # assumes len(lpar) == len(rpar), via superclass 

2988 raise CSTValidationError( 

2989 "A zero-length tuple must be wrapped in parentheses." 

2990 ) 

2991 # Invalid commas aren't possible, because MaybeSentinel will ensure that there 

2992 # is a comma where required. 

2993 

2994 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Tuple": 

2995 return Tuple( 

2996 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

2997 elements=visit_sequence(self, "elements", self.elements, visitor), 

2998 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

2999 ) 

3000 

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

3002 with self._parenthesize(state): 

3003 elements = self.elements 

3004 if len(elements) == 1: 

3005 elements[0]._codegen( 

3006 state, default_comma=True, default_comma_whitespace=False 

3007 ) 

3008 else: 

3009 for idx, el in enumerate(elements): 

3010 el._codegen( 

3011 state, 

3012 default_comma=(idx < len(elements) - 1), 

3013 default_comma_whitespace=True, 

3014 ) 

3015 

3016 

3017class BaseList(BaseExpression, ABC): 

3018 """ 

3019 A base class for :class:`List` and :class:`ListComp`, which both result in a list 

3020 object when evaluated. 

3021 """ 

3022 

3023 __slots__ = () 

3024 

3025 lbracket: LeftSquareBracket = LeftSquareBracket.field() 

3026 #: Brackets surrounding the list. 

3027 rbracket: RightSquareBracket = RightSquareBracket.field() 

3028 

3029 lpar: Sequence[LeftParen] = () 

3030 #: Sequence of parenthesis for precedence dictation. 

3031 rpar: Sequence[RightParen] = () 

3032 

3033 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

3034 return True 

3035 

3036 @contextmanager 

3037 def _bracketize(self, state: CodegenState) -> Generator[None, None, None]: 

3038 self.lbracket._codegen(state) 

3039 yield 

3040 self.rbracket._codegen(state) 

3041 

3042 

3043@add_slots 

3044@dataclass(frozen=True) 

3045class List(BaseList, BaseAssignTargetExpression, BaseDelTargetExpression): 

3046 """ 

3047 A mutable literal list. 

3048 

3049 :: 

3050 

3051 List([ 

3052 Element(Integer("1")), 

3053 Element(Integer("2")), 

3054 StarredElement(Name("others")), 

3055 ]) 

3056 

3057 generates the following code:: 

3058 

3059 [1, 2, *others] 

3060 

3061 List comprehensions are represented with a :class:`ListComp` node. 

3062 """ 

3063 

3064 #: A sequence containing all the :class:`Element` and :class:`StarredElement` nodes 

3065 #: in the list. 

3066 elements: Sequence[BaseElement] 

3067 

3068 lbracket: LeftSquareBracket = LeftSquareBracket.field() 

3069 #: Brackets surrounding the list. 

3070 rbracket: RightSquareBracket = RightSquareBracket.field() 

3071 

3072 lpar: Sequence[LeftParen] = () 

3073 #: Sequence of parenthesis for precedence dictation. 

3074 rpar: Sequence[RightParen] = () 

3075 

3076 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "List": 

3077 return List( 

3078 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3079 lbracket=visit_required(self, "lbracket", self.lbracket, visitor), 

3080 elements=visit_sequence(self, "elements", self.elements, visitor), 

3081 rbracket=visit_required(self, "rbracket", self.rbracket, visitor), 

3082 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3083 ) 

3084 

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

3086 with self._parenthesize(state), self._bracketize(state): 

3087 elements = self.elements 

3088 for idx, el in enumerate(elements): 

3089 el._codegen( 

3090 state, 

3091 default_comma=(idx < len(elements) - 1), 

3092 default_comma_whitespace=True, 

3093 ) 

3094 

3095 

3096class _BaseSetOrDict(BaseExpression, ABC): 

3097 """ 

3098 An abstract base class for :class:`BaseSet` and :class:`BaseDict`. 

3099 

3100 Literal sets and dicts are syntactically similar (hence this shared base class), but 

3101 are semantically different. This base class is an implementation detail and 

3102 shouldn't be exported. 

3103 """ 

3104 

3105 __slots__ = () 

3106 

3107 lbrace: LeftCurlyBrace = LeftCurlyBrace.field() 

3108 #: Braces surrounding the set or dict. 

3109 rbrace: RightCurlyBrace = RightCurlyBrace.field() 

3110 

3111 lpar: Sequence[LeftParen] = () 

3112 #: Sequence of parenthesis for precedence dictation. 

3113 rpar: Sequence[RightParen] = () 

3114 

3115 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

3116 return True 

3117 

3118 # brace-ize seems like a very made-up word. And it is! 

3119 @contextmanager 

3120 def _braceize(self, state: CodegenState) -> Generator[None, None, None]: 

3121 self.lbrace._codegen(state) 

3122 yield 

3123 self.rbrace._codegen(state) 

3124 

3125 

3126class BaseSet(_BaseSetOrDict, ABC): 

3127 """ 

3128 An abstract base class for :class:`Set` and :class:`SetComp`, which both result in 

3129 a set object when evaluated. 

3130 """ 

3131 

3132 __slots__ = () 

3133 

3134 

3135@add_slots 

3136@dataclass(frozen=True) 

3137class Set(BaseSet): 

3138 """ 

3139 A mutable literal set. 

3140 

3141 :: 

3142 

3143 Set([ 

3144 Element(Integer("1")), 

3145 Element(Integer("2")), 

3146 StarredElement(Name("others")), 

3147 ]) 

3148 

3149 generates the following code:: 

3150 

3151 {1, 2, *others} 

3152 

3153 Set comprehensions are represented with a :class:`SetComp` node. 

3154 """ 

3155 

3156 #: A sequence containing all the :class:`Element` and :class:`StarredElement` nodes 

3157 #: in the set. 

3158 elements: Sequence[BaseElement] 

3159 

3160 lbrace: LeftCurlyBrace = LeftCurlyBrace.field() 

3161 #: Braces surrounding the set. 

3162 rbrace: RightCurlyBrace = RightCurlyBrace.field() 

3163 

3164 lpar: Sequence[LeftParen] = () 

3165 #: Sequence of parenthesis for precedence dictation. 

3166 rpar: Sequence[RightParen] = () 

3167 

3168 def _validate(self) -> None: 

3169 super(Set, self)._validate() 

3170 

3171 if len(self.elements) == 0: 

3172 raise CSTValidationError( 

3173 "A literal set must have at least one element. A zero-element set " 

3174 + "would be syntatically ambiguous with an empty dict, `{}`." 

3175 ) 

3176 

3177 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Set": 

3178 return Set( 

3179 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3180 lbrace=visit_required(self, "lbrace", self.lbrace, visitor), 

3181 elements=visit_sequence(self, "elements", self.elements, visitor), 

3182 rbrace=visit_required(self, "rbrace", self.rbrace, visitor), 

3183 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3184 ) 

3185 

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

3187 with self._parenthesize(state), self._braceize(state): 

3188 elements = self.elements 

3189 for idx, el in enumerate(elements): 

3190 el._codegen( 

3191 state, 

3192 default_comma=(idx < len(elements) - 1), 

3193 default_comma_whitespace=True, 

3194 ) 

3195 

3196 

3197class BaseDict(_BaseSetOrDict, ABC): 

3198 """ 

3199 An abstract base class for :class:`Dict` and :class:`DictComp`, which both result in 

3200 a dict object when evaluated. 

3201 """ 

3202 

3203 __slots__ = () 

3204 

3205 

3206@add_slots 

3207@dataclass(frozen=True) 

3208class Dict(BaseDict): 

3209 """ 

3210 A literal dictionary. Key-value pairs are stored in ``elements`` using 

3211 :class:`DictElement` nodes. 

3212 

3213 It's possible to expand one dictionary into another, as in ``{k: v, **expanded}``. 

3214 Expanded elements are stored as :class:`StarredDictElement` nodes. 

3215 

3216 :: 

3217 

3218 Dict([ 

3219 DictElement(Name("k1"), Name("v1")), 

3220 DictElement(Name("k2"), Name("v2")), 

3221 StarredDictElement(Name("expanded")), 

3222 ]) 

3223 

3224 generates the following code:: 

3225 

3226 {k1: v1, k2: v2, **expanded} 

3227 """ 

3228 

3229 elements: Sequence[BaseDictElement] 

3230 lbrace: LeftCurlyBrace = LeftCurlyBrace.field() 

3231 rbrace: RightCurlyBrace = RightCurlyBrace.field() 

3232 lpar: Sequence[LeftParen] = () 

3233 rpar: Sequence[RightParen] = () 

3234 

3235 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Dict": 

3236 return Dict( 

3237 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3238 lbrace=visit_required(self, "lbrace", self.lbrace, visitor), 

3239 elements=visit_sequence(self, "elements", self.elements, visitor), 

3240 rbrace=visit_required(self, "rbrace", self.rbrace, visitor), 

3241 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3242 ) 

3243 

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

3245 with self._parenthesize(state), self._braceize(state): 

3246 elements = self.elements 

3247 for idx, el in enumerate(elements): 

3248 el._codegen( 

3249 state, 

3250 default_comma=(idx < len(elements) - 1), 

3251 default_comma_whitespace=True, 

3252 ) 

3253 

3254 

3255@add_slots 

3256@dataclass(frozen=True) 

3257class CompFor(CSTNode): 

3258 """ 

3259 One ``for`` clause in a :class:`BaseComp`, or a nested hierarchy of 

3260 ``for`` clauses. 

3261 

3262 Nested loops in comprehensions are difficult to get right, but they can be thought 

3263 of as a flat representation of nested clauses. 

3264 

3265 ``elt for a in b for c in d if e`` can be thought of as:: 

3266 

3267 for a in b: 

3268 for c in d: 

3269 if e: 

3270 yield elt 

3271 

3272 And that would form the following CST:: 

3273 

3274 ListComp( 

3275 elt=Name("elt"), 

3276 for_in=CompFor( 

3277 target=Name("a"), 

3278 iter=Name("b"), 

3279 ifs=[], 

3280 inner_comp_for=CompFor( 

3281 target=Name("c"), 

3282 iter=Name("d"), 

3283 ifs=[ 

3284 CompIf( 

3285 test=Name("e"), 

3286 ), 

3287 ], 

3288 ), 

3289 ), 

3290 ) 

3291 

3292 Normal ``for`` statements are provided by :class:`For`. 

3293 """ 

3294 

3295 #: The target to assign a value to in each iteration of the loop. This is different 

3296 #: from :attr:`GeneratorExp.elt`, :attr:`ListComp.elt`, :attr:`SetComp.elt`, and 

3297 #: ``key`` and ``value`` in :class:`DictComp`, because it doesn't directly effect 

3298 #: the value of resulting generator, list, set, or dict. 

3299 target: BaseAssignTargetExpression 

3300 

3301 #: The value to iterate over. Every value in ``iter`` is stored in ``target``. 

3302 iter: BaseExpression 

3303 

3304 #: Zero or more conditional clauses that control this loop. If any of these tests 

3305 #: fail, the ``target`` item is skipped. 

3306 #: 

3307 #: :: 

3308 #: 

3309 #: if a if b if c 

3310 #: 

3311 #: has similar semantics to:: 

3312 #: 

3313 #: if a and b and c 

3314 ifs: Sequence["CompIf"] = () 

3315 

3316 #: Another :class:`CompFor` node used to form nested loops. Nested comprehensions 

3317 #: can be useful, but they tend to be difficult to read and write. As a result they 

3318 #: are uncommon. 

3319 inner_for_in: Optional["CompFor"] = None 

3320 

3321 #: An optional async modifier that appears before the ``for`` keyword. 

3322 asynchronous: Optional[Asynchronous] = None 

3323 

3324 #: Whitespace that appears at the beginning of this node, before the ``for`` and 

3325 #: ``async`` keywords. 

3326 whitespace_before: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3327 

3328 #: Whitespace appearing after the ``for`` keyword, but before the ``target``. 

3329 whitespace_after_for: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3330 

3331 #: Whitespace appearing after the ``target``, but before the ``in`` keyword. 

3332 whitespace_before_in: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3333 

3334 #: Whitespace appearing after the ``in`` keyword, but before the ``iter``. 

3335 whitespace_after_in: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3336 

3337 def _validate(self) -> None: 

3338 if ( 

3339 self.whitespace_after_for.empty 

3340 and not self.target._safe_to_use_with_word_operator( 

3341 ExpressionPosition.RIGHT 

3342 ) 

3343 ): 

3344 raise CSTValidationError( 

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

3346 ) 

3347 

3348 if ( 

3349 self.whitespace_before_in.empty 

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

3351 ): 

3352 raise CSTValidationError( 

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

3354 ) 

3355 

3356 if ( 

3357 self.whitespace_after_in.empty 

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

3359 ): 

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

3361 

3362 prev_expr = self.iter 

3363 for if_clause in self.ifs: 

3364 if ( 

3365 if_clause.whitespace_before.empty 

3366 and not prev_expr._safe_to_use_with_word_operator( 

3367 ExpressionPosition.LEFT 

3368 ) 

3369 ): 

3370 raise CSTValidationError( 

3371 "Must have at least one space before 'if' keyword." 

3372 ) 

3373 prev_expr = if_clause.test 

3374 

3375 inner_for_in = self.inner_for_in 

3376 if ( 

3377 inner_for_in is not None 

3378 and inner_for_in.whitespace_before.empty 

3379 and not prev_expr._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

3380 ): 

3381 keyword = "async" if inner_for_in.asynchronous else "for" 

3382 raise CSTValidationError( 

3383 f"Must have at least one space before '{keyword}' keyword." 

3384 ) 

3385 

3386 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CompFor": 

3387 return CompFor( 

3388 whitespace_before=visit_required( 

3389 self, "whitespace_before", self.whitespace_before, visitor 

3390 ), 

3391 asynchronous=visit_optional( 

3392 self, "asynchronous", self.asynchronous, visitor 

3393 ), 

3394 whitespace_after_for=visit_required( 

3395 self, "whitespace_after_for", self.whitespace_after_for, visitor 

3396 ), 

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

3398 whitespace_before_in=visit_required( 

3399 self, "whitespace_before_in", self.whitespace_before_in, visitor 

3400 ), 

3401 whitespace_after_in=visit_required( 

3402 self, "whitespace_after_in", self.whitespace_after_in, visitor 

3403 ), 

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

3405 ifs=visit_sequence(self, "ifs", self.ifs, visitor), 

3406 inner_for_in=visit_optional( 

3407 self, "inner_for_in", self.inner_for_in, visitor 

3408 ), 

3409 ) 

3410 

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

3412 self.whitespace_before._codegen(state) 

3413 asynchronous = self.asynchronous 

3414 if asynchronous is not None: 

3415 asynchronous._codegen(state) 

3416 state.add_token("for") 

3417 self.whitespace_after_for._codegen(state) 

3418 self.target._codegen(state) 

3419 self.whitespace_before_in._codegen(state) 

3420 state.add_token("in") 

3421 self.whitespace_after_in._codegen(state) 

3422 self.iter._codegen(state) 

3423 ifs = self.ifs 

3424 for if_clause in ifs: 

3425 if_clause._codegen(state) 

3426 inner_for_in = self.inner_for_in 

3427 if inner_for_in is not None: 

3428 inner_for_in._codegen(state) 

3429 

3430 

3431@add_slots 

3432@dataclass(frozen=True) 

3433class CompIf(CSTNode): 

3434 """ 

3435 A conditional clause in a :class:`CompFor`, used as part of a generator or 

3436 comprehension expression. 

3437 

3438 If the ``test`` fails, the current element in the :class:`CompFor` will be skipped. 

3439 """ 

3440 

3441 #: An expression to evaluate. When interpreted, Python will coerce it to a boolean. 

3442 test: BaseExpression 

3443 

3444 #: Whitespace before the ``if`` keyword. 

3445 whitespace_before: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3446 

3447 #: Whitespace after the ``if`` keyword, but before the ``test`` expression. 

3448 whitespace_before_test: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3449 

3450 def _validate(self) -> None: 

3451 if ( 

3452 self.whitespace_before_test.empty 

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

3454 ): 

3455 raise CSTValidationError("Must have at least one space after 'if' keyword.") 

3456 

3457 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CompIf": 

3458 return CompIf( 

3459 whitespace_before=visit_required( 

3460 self, "whitespace_before", self.whitespace_before, visitor 

3461 ), 

3462 whitespace_before_test=visit_required( 

3463 self, "whitespace_before_test", self.whitespace_before_test, visitor 

3464 ), 

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

3466 ) 

3467 

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

3469 self.whitespace_before._codegen(state) 

3470 state.add_token("if") 

3471 self.whitespace_before_test._codegen(state) 

3472 self.test._codegen(state) 

3473 

3474 

3475class BaseComp(BaseExpression, ABC): 

3476 """ 

3477 A base class for all comprehension and generator expressions, including 

3478 :class:`GeneratorExp`, :class:`ListComp`, :class:`SetComp`, and :class:`DictComp`. 

3479 """ 

3480 

3481 __slots__ = () 

3482 

3483 for_in: CompFor 

3484 

3485 

3486class BaseSimpleComp(BaseComp, ABC): 

3487 """ 

3488 The base class for :class:`ListComp`, :class:`SetComp`, and :class:`GeneratorExp`. 

3489 :class:`DictComp` is not a :class:`BaseSimpleComp`, because it uses ``key`` and 

3490 ``value``. 

3491 """ 

3492 

3493 __slots__ = () 

3494 

3495 #: The expression evaluated during each iteration of the comprehension. This 

3496 #: lexically comes before the ``for_in`` clause, but it is semantically the 

3497 #: inner-most element, evaluated inside the ``for_in`` clause. 

3498 elt: BaseAssignTargetExpression 

3499 

3500 #: The ``for ... in ... if ...`` clause that lexically comes after ``elt``. This may 

3501 #: be a nested structure for nested comprehensions. See :class:`CompFor` for 

3502 #: details. 

3503 for_in: CompFor 

3504 

3505 def _validate(self) -> None: 

3506 super(BaseSimpleComp, self)._validate() 

3507 

3508 for_in = self.for_in 

3509 if ( 

3510 for_in.whitespace_before.empty 

3511 and not self.elt._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

3512 ): 

3513 keyword = "async" if for_in.asynchronous else "for" 

3514 raise CSTValidationError( 

3515 f"Must have at least one space before '{keyword}' keyword." 

3516 ) 

3517 

3518 

3519@add_slots 

3520@dataclass(frozen=True) 

3521class GeneratorExp(BaseSimpleComp): 

3522 """ 

3523 A generator expression. ``elt`` represents the value yielded for each item in 

3524 :attr:`CompFor.iter`. 

3525 

3526 All ``for ... in ...`` and ``if ...`` clauses are stored as a recursive 

3527 :class:`CompFor` data structure inside ``for_in``. 

3528 """ 

3529 

3530 #: The expression evaluated and yielded during each iteration of the generator. 

3531 elt: BaseAssignTargetExpression 

3532 

3533 #: The ``for ... in ... if ...`` clause that comes after ``elt``. This may be a 

3534 #: nested structure for nested comprehensions. See :class:`CompFor` for details. 

3535 for_in: CompFor 

3536 

3537 lpar: Sequence[LeftParen] = field(default_factory=lambda: (LeftParen(),)) 

3538 #: Sequence of parentheses for precedence dictation. Generator expressions must 

3539 #: always be parenthesized. However, if a generator expression is the only argument 

3540 #: inside a function call, the enclosing :class:`Call` node may own the parentheses 

3541 #: instead. 

3542 rpar: Sequence[RightParen] = field(default_factory=lambda: (RightParen(),)) 

3543 

3544 def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool: 

3545 # Generators are always parenthesized 

3546 return True 

3547 

3548 # A note about validation: Generators must always be parenthesized, but it's 

3549 # possible that this Generator node doesn't own those parenthesis (in the case of a 

3550 # function call with a single generator argument). 

3551 # 

3552 # Therefore, there's no useful validation we can do here. In theory, our parent 

3553 # could do the validation, but there's a ton of potential parents to a Generator, so 

3554 # it's not worth the effort. 

3555 

3556 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "GeneratorExp": 

3557 return GeneratorExp( 

3558 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3559 elt=visit_required(self, "elt", self.elt, visitor), 

3560 for_in=visit_required(self, "for_in", self.for_in, visitor), 

3561 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3562 ) 

3563 

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

3565 with self._parenthesize(state): 

3566 self.elt._codegen(state) 

3567 self.for_in._codegen(state) 

3568 

3569 

3570@add_slots 

3571@dataclass(frozen=True) 

3572class ListComp(BaseList, BaseSimpleComp): 

3573 """ 

3574 A list comprehension. ``elt`` represents the value stored for each item in 

3575 :attr:`CompFor.iter`. 

3576 

3577 All ``for ... in ...`` and ``if ...`` clauses are stored as a recursive 

3578 :class:`CompFor` data structure inside ``for_in``. 

3579 """ 

3580 

3581 #: The expression evaluated and stored during each iteration of the comprehension. 

3582 elt: BaseAssignTargetExpression 

3583 

3584 #: The ``for ... in ... if ...`` clause that comes after ``elt``. This may be a 

3585 #: nested structure for nested comprehensions. See :class:`CompFor` for details. 

3586 for_in: CompFor 

3587 

3588 lbracket: LeftSquareBracket = LeftSquareBracket.field() 

3589 #: Brackets surrounding the list comprehension. 

3590 rbracket: RightSquareBracket = RightSquareBracket.field() 

3591 

3592 lpar: Sequence[LeftParen] = () 

3593 #: Sequence of parenthesis for precedence dictation. 

3594 rpar: Sequence[RightParen] = () 

3595 

3596 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ListComp": 

3597 return ListComp( 

3598 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3599 lbracket=visit_required(self, "lbracket", self.lbracket, visitor), 

3600 elt=visit_required(self, "elt", self.elt, visitor), 

3601 for_in=visit_required(self, "for_in", self.for_in, visitor), 

3602 rbracket=visit_required(self, "rbracket", self.rbracket, visitor), 

3603 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3604 ) 

3605 

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

3607 with self._parenthesize(state), self._bracketize(state): 

3608 self.elt._codegen(state) 

3609 self.for_in._codegen(state) 

3610 

3611 

3612@add_slots 

3613@dataclass(frozen=True) 

3614class SetComp(BaseSet, BaseSimpleComp): 

3615 """ 

3616 A set comprehension. ``elt`` represents the value stored for each item in 

3617 :attr:`CompFor.iter`. 

3618 

3619 All ``for ... in ...`` and ``if ...`` clauses are stored as a recursive 

3620 :class:`CompFor` data structure inside ``for_in``. 

3621 """ 

3622 

3623 #: The expression evaluated and stored during each iteration of the comprehension. 

3624 elt: BaseAssignTargetExpression 

3625 

3626 #: The ``for ... in ... if ...`` clause that comes after ``elt``. This may be a 

3627 #: nested structure for nested comprehensions. See :class:`CompFor` for details. 

3628 for_in: CompFor 

3629 

3630 lbrace: LeftCurlyBrace = LeftCurlyBrace.field() 

3631 #: Braces surrounding the set comprehension. 

3632 rbrace: RightCurlyBrace = RightCurlyBrace.field() 

3633 

3634 lpar: Sequence[LeftParen] = () 

3635 #: Sequence of parenthesis for precedence dictation. 

3636 rpar: Sequence[RightParen] = () 

3637 

3638 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "SetComp": 

3639 return SetComp( 

3640 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3641 lbrace=visit_required(self, "lbrace", self.lbrace, visitor), 

3642 elt=visit_required(self, "elt", self.elt, visitor), 

3643 for_in=visit_required(self, "for_in", self.for_in, visitor), 

3644 rbrace=visit_required(self, "rbrace", self.rbrace, visitor), 

3645 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3646 ) 

3647 

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

3649 with self._parenthesize(state), self._braceize(state): 

3650 self.elt._codegen(state) 

3651 self.for_in._codegen(state) 

3652 

3653 

3654@add_slots 

3655@dataclass(frozen=True) 

3656class DictComp(BaseDict, BaseComp): 

3657 """ 

3658 A dictionary comprehension. ``key`` and ``value`` represent the dictionary entry 

3659 evaluated for each item. 

3660 

3661 All ``for ... in ...`` and ``if ...`` clauses are stored as a recursive 

3662 :class:`CompFor` data structure inside ``for_in``. 

3663 """ 

3664 

3665 #: The key inserted into the dictionary during each iteration of the comprehension. 

3666 key: BaseAssignTargetExpression 

3667 #: The value associated with the ``key`` inserted into the dictionary during each 

3668 #: iteration of the comprehension. 

3669 value: BaseAssignTargetExpression 

3670 

3671 #: The ``for ... in ... if ...`` clause that lexically comes after ``key`` and 

3672 #: ``value``. This may be a nested structure for nested comprehensions. See 

3673 #: :class:`CompFor` for details. 

3674 for_in: CompFor 

3675 

3676 lbrace: LeftCurlyBrace = LeftCurlyBrace.field() 

3677 #: Braces surrounding the dict comprehension. 

3678 rbrace: RightCurlyBrace = RightCurlyBrace.field() 

3679 

3680 lpar: Sequence[LeftParen] = () 

3681 #: Sequence of parenthesis for precedence dictation. 

3682 rpar: Sequence[RightParen] = () 

3683 

3684 #: Whitespace after the key, but before the colon in ``key : value``. 

3685 whitespace_before_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

3686 #: Whitespace after the colon, but before the value in ``key : value``. 

3687 whitespace_after_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3688 

3689 def _validate(self) -> None: 

3690 super(DictComp, self)._validate() 

3691 

3692 for_in = self.for_in 

3693 if ( 

3694 for_in.whitespace_before.empty 

3695 and not self.value._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

3696 ): 

3697 keyword = "async" if for_in.asynchronous else "for" 

3698 raise CSTValidationError( 

3699 f"Must have at least one space before '{keyword}' keyword." 

3700 ) 

3701 

3702 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "DictComp": 

3703 return DictComp( 

3704 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

3705 lbrace=visit_required(self, "lbrace", self.lbrace, visitor), 

3706 key=visit_required(self, "key", self.key, visitor), 

3707 whitespace_before_colon=visit_required( 

3708 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

3709 ), 

3710 whitespace_after_colon=visit_required( 

3711 self, "whitespace_after_colon", self.whitespace_after_colon, visitor 

3712 ), 

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

3714 for_in=visit_required(self, "for_in", self.for_in, visitor), 

3715 rbrace=visit_required(self, "rbrace", self.rbrace, visitor), 

3716 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3717 ) 

3718 

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

3720 with self._parenthesize(state), self._braceize(state): 

3721 self.key._codegen(state) 

3722 self.whitespace_before_colon._codegen(state) 

3723 state.add_token(":") 

3724 self.whitespace_after_colon._codegen(state) 

3725 self.value._codegen(state) 

3726 self.for_in._codegen(state) 

3727 

3728 

3729@add_slots 

3730@dataclass(frozen=True) 

3731class NamedExpr(BaseExpression): 

3732 """ 

3733 An expression that is also an assignment, such as ``x := y + z``. Affectionately 

3734 known as the walrus operator, this expression allows you to make an assignment 

3735 inside an expression. This greatly simplifies loops:: 

3736 

3737 while line := read_some_line_or_none(): 

3738 do_thing_with_line(line) 

3739 """ 

3740 

3741 #: The target that is being assigned to. 

3742 target: BaseExpression 

3743 

3744 #: The expression being assigned to the target. 

3745 value: BaseExpression 

3746 

3747 #: Sequence of parenthesis for precedence dictation. 

3748 lpar: Sequence[LeftParen] = () 

3749 #: Sequence of parenthesis for precedence dictation. 

3750 rpar: Sequence[RightParen] = () 

3751 

3752 #: Whitespace after the target, but before the walrus operator. 

3753 whitespace_before_walrus: BaseParenthesizableWhitespace = SimpleWhitespace.field( 

3754 " " 

3755 ) 

3756 #: Whitespace after the walrus operator, but before the value. 

3757 whitespace_after_walrus: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3758 

3759 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "NamedExpr": 

3760 return NamedExpr( 

3761 lpar=visit_sequence(self, "lpar", self.lpar, visitor), 

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

3763 whitespace_before_walrus=visit_required( 

3764 self, "whitespace_before_walrus", self.whitespace_before_walrus, visitor 

3765 ), 

3766 whitespace_after_walrus=visit_required( 

3767 self, "whitespace_after_walrus", self.whitespace_after_walrus, visitor 

3768 ), 

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

3770 rpar=visit_sequence(self, "rpar", self.rpar, visitor), 

3771 ) 

3772 

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

3774 with self._parenthesize(state): 

3775 self.target._codegen(state) 

3776 self.whitespace_before_walrus._codegen(state) 

3777 state.add_token(":=") 

3778 self.whitespace_after_walrus._codegen(state) 

3779 self.value._codegen(state)