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

1432 statements  

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

1# Copyright (c) Meta Platforms, Inc. and affiliates. 

2# 

3# This source code is licensed under the MIT license found in the 

4# LICENSE file in the root directory of this source tree. 

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 _safe_to_join_with_lambda(self) -> bool: 

1987 """ 

1988 Determine if Parameters need a space after the `lambda` keyword. Returns True 

1989 iff it's safe to omit the space between `lambda` and these Parameters. 

1990 

1991 See also `BaseExpression._safe_to_use_with_word_operator`. 

1992 

1993 For example: `lambda*_: pass` 

1994 """ 

1995 if len(self.posonly_params) != 0: 

1996 return False 

1997 

1998 # posonly_ind can't appear if above condition is false 

1999 

2000 if len(self.params) > 0 and self.params[0].star not in {"*", "**"}: 

2001 return False 

2002 

2003 return True 

2004 

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

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

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

2008 star_arg = self.star_arg 

2009 if isinstance(star_arg, MaybeSentinel): 

2010 starincluded = len(self.kwonly_params) > 0 

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

2012 starincluded = True 

2013 else: 

2014 starincluded = False 

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

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

2017 # slash afterwards. 

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

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

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

2021 more_values = ( 

2022 starincluded 

2023 or len(self.params) > 0 

2024 or len(self.kwonly_params) > 0 

2025 or self.star_kwarg is not None 

2026 ) 

2027 posonly_ind = self.posonly_ind 

2028 if isinstance(posonly_ind, ParamSlash): 

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

2030 # might have spacing applied to its comma. 

2031 posonly_ind._codegen(state, default_comma=more_values) 

2032 elif len(self.posonly_params) > 0: 

2033 if more_values: 

2034 state.add_token("/, ") 

2035 else: 

2036 state.add_token("/") 

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

2038 lastparam = len(self.params) - 1 

2039 more_values = ( 

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

2041 ) 

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

2043 param._codegen( 

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

2045 ) 

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

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

2048 # optional star_arg. 

2049 if isinstance(star_arg, MaybeSentinel): 

2050 if starincluded: 

2051 state.add_token("*, ") 

2052 elif isinstance(star_arg, Param): 

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

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

2055 elif isinstance(star_arg, ParamStar): 

2056 star_arg._codegen(state) 

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

2058 lastparam = len(self.kwonly_params) - 1 

2059 more_values = self.star_kwarg is not None 

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

2061 param._codegen( 

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

2063 ) 

2064 # Finally, render out any optional star_kwarg 

2065 star_kwarg = self.star_kwarg 

2066 if star_kwarg is not None: 

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

2068 

2069 

2070@add_slots 

2071@dataclass(frozen=True) 

2072class Lambda(BaseExpression): 

2073 """ 

2074 A lambda expression that creates an anonymous function. 

2075 

2076 :: 

2077 

2078 Lambda( 

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

2080 body=Ellipsis(), 

2081 ) 

2082 

2083 Represents the following code:: 

2084 

2085 lambda arg: ... 

2086 

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

2088 """ 

2089 

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

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

2092 #: :class:`Annotation`. 

2093 params: Parameters 

2094 

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

2096 body: BaseExpression 

2097 

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

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

2100 

2101 lpar: Sequence[LeftParen] = () 

2102 #: Sequence of parenthesis for precedence dictation. 

2103 rpar: Sequence[RightParen] = () 

2104 

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

2106 whitespace_after_lambda: Union[ 

2107 BaseParenthesizableWhitespace, MaybeSentinel 

2108 ] = MaybeSentinel.DEFAULT 

2109 

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

2111 if position == ExpressionPosition.LEFT: 

2112 return len(self.rpar) > 0 or self.body._safe_to_use_with_word_operator( 

2113 position 

2114 ) 

2115 return super()._safe_to_use_with_word_operator(position) 

2116 

2117 def _validate(self) -> None: 

2118 # Validate parents 

2119 super(Lambda, self)._validate() 

2120 # Sum up all parameters 

2121 all_params = [ 

2122 *self.params.posonly_params, 

2123 *self.params.params, 

2124 *self.params.kwonly_params, 

2125 ] 

2126 star_arg = self.params.star_arg 

2127 if isinstance(star_arg, Param): 

2128 all_params.append(star_arg) 

2129 star_kwarg = self.params.star_kwarg 

2130 if star_kwarg is not None: 

2131 all_params.append(star_kwarg) 

2132 # Check for nonzero parameters because several checks care 

2133 # about this. 

2134 if len(all_params) > 0: 

2135 for param in all_params: 

2136 if param.annotation is not None: 

2137 raise CSTValidationError( 

2138 "Lambda params cannot have type annotations." 

2139 ) 

2140 whitespace_after_lambda = self.whitespace_after_lambda 

2141 if ( 

2142 isinstance(whitespace_after_lambda, BaseParenthesizableWhitespace) 

2143 and whitespace_after_lambda.empty 

2144 and not self.params._safe_to_join_with_lambda() 

2145 ): 

2146 raise CSTValidationError( 

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

2148 ) 

2149 

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

2151 return Lambda( 

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

2153 whitespace_after_lambda=visit_sentinel( 

2154 self, "whitespace_after_lambda", self.whitespace_after_lambda, visitor 

2155 ), 

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

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

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

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

2160 ) 

2161 

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

2163 with self._parenthesize(state): 

2164 state.add_token("lambda") 

2165 whitespace_after_lambda = self.whitespace_after_lambda 

2166 if isinstance(whitespace_after_lambda, MaybeSentinel): 

2167 if not ( 

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

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

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

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

2172 and self.params.star_kwarg is None 

2173 ): 

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

2175 state.add_token(" ") 

2176 elif isinstance(whitespace_after_lambda, BaseParenthesizableWhitespace): 

2177 whitespace_after_lambda._codegen(state) 

2178 self.params._codegen(state) 

2179 self.colon._codegen(state) 

2180 self.body._codegen(state) 

2181 

2182 

2183@add_slots 

2184@dataclass(frozen=True) 

2185class Arg(CSTNode): 

2186 """ 

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

2188 

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

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

2191 """ 

2192 

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

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

2195 value: BaseExpression 

2196 

2197 #: Optional keyword for the argument. 

2198 keyword: Optional[Name] = None 

2199 

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

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

2202 

2203 #: Any trailing comma. 

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

2205 

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

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

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

2209 

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

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

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

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

2214 #: also store some trailing whitespace. 

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

2216 

2217 def _validate(self) -> None: 

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

2219 raise CSTValidationError( 

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

2221 ) 

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

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

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

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

2226 

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

2228 return Arg( 

2229 star=self.star, 

2230 whitespace_after_star=visit_required( 

2231 self, "whitespace_after_star", self.whitespace_after_star, visitor 

2232 ), 

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

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

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

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

2237 whitespace_after_arg=visit_required( 

2238 self, "whitespace_after_arg", self.whitespace_after_arg, visitor 

2239 ), 

2240 ) 

2241 

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

2243 with state.record_syntactic_position(self): 

2244 state.add_token(self.star) 

2245 self.whitespace_after_star._codegen(state) 

2246 keyword = self.keyword 

2247 if keyword is not None: 

2248 keyword._codegen(state) 

2249 equal = self.equal 

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

2251 state.add_token(" = ") 

2252 elif isinstance(equal, AssignEqual): 

2253 equal._codegen(state) 

2254 self.value._codegen(state) 

2255 

2256 comma = self.comma 

2257 if comma is MaybeSentinel.DEFAULT and default_comma: 

2258 state.add_token(", ") 

2259 elif isinstance(comma, Comma): 

2260 comma._codegen(state) 

2261 self.whitespace_after_arg._codegen(state) 

2262 

2263 

2264class _BaseExpressionWithArgs(BaseExpression, ABC): 

2265 """ 

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

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

2268 """ 

2269 

2270 __slots__ = () 

2271 

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

2273 args: Sequence[Arg] = () 

2274 

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

2276 """ 

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

2278 """ 

2279 

2280 if arg.keyword is not None: 

2281 # Valid, keyword argument 

2282 return None 

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

2284 # Valid, kwargs 

2285 return None 

2286 elif arg.star == "*": 

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

2288 raise CSTValidationError( 

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

2290 ) 

2291 else: 

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

2293 raise CSTValidationError( 

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

2295 ) 

2296 

2297 def _check_starred_or_keywords( 

2298 self, arg: Arg 

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

2300 """ 

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

2302 """ 

2303 

2304 if arg.keyword is not None: 

2305 # Valid, keyword argument 

2306 return None 

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

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

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

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

2311 # Any]]]]`. 

2312 return self._check_kwargs_or_keywords 

2313 elif arg.star == "*": 

2314 # Valid, iterable unpacking 

2315 return None 

2316 else: 

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

2318 raise CSTValidationError( 

2319 "Cannot have positional argument after keyword argument." 

2320 ) 

2321 

2322 def _check_positional( 

2323 self, arg: Arg 

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

2325 """ 

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

2327 """ 

2328 

2329 if arg.keyword is not None: 

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

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

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

2333 # Any]]]]`. 

2334 return self._check_starred_or_keywords 

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

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

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

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

2339 # Any]]]]`. 

2340 return self._check_kwargs_or_keywords 

2341 elif arg.star == "*": 

2342 # Valid, iterator expansion 

2343 return None 

2344 else: 

2345 # Valid, allowed to have positional arguments here 

2346 return None 

2347 

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

2349 # too complex. 

2350 def _validate(self) -> None: 

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

2352 super()._validate() 

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

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

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

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

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

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

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

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

2361 # expansion state, or dictionary expansion state). 

2362 validator = self._check_positional 

2363 for arg in self.args: 

2364 validator = validator(arg) or validator 

2365 

2366 

2367@add_slots 

2368@dataclass(frozen=True) 

2369class Call(_BaseExpressionWithArgs): 

2370 """ 

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

2372 ``picture.post_on_instagram()``. 

2373 

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

2375 :class:`Arg` nodes. 

2376 """ 

2377 

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

2379 #: or :class:`Attribute`. 

2380 func: BaseExpression 

2381 

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

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

2384 args: Sequence[Arg] = () 

2385 

2386 lpar: Sequence[LeftParen] = () 

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

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

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

2390 rpar: Sequence[RightParen] = () 

2391 

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

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

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

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

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

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

2398 

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

2400 """ 

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

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

2403 an adjacent node to the right. 

2404 """ 

2405 if position == ExpressionPosition.LEFT: 

2406 return True 

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

2408 return True 

2409 if position == ExpressionPosition.RIGHT: 

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

2411 return False 

2412 

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

2414 return Call( 

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

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

2417 whitespace_after_func=visit_required( 

2418 self, "whitespace_after_func", self.whitespace_after_func, visitor 

2419 ), 

2420 whitespace_before_args=visit_required( 

2421 self, "whitespace_before_args", self.whitespace_before_args, visitor 

2422 ), 

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

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

2425 ) 

2426 

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

2428 with self._parenthesize(state): 

2429 self.func._codegen(state) 

2430 self.whitespace_after_func._codegen(state) 

2431 state.add_token("(") 

2432 self.whitespace_before_args._codegen(state) 

2433 lastarg = len(self.args) - 1 

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

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

2436 state.add_token(")") 

2437 

2438 

2439@add_slots 

2440@dataclass(frozen=True) 

2441class Await(BaseExpression): 

2442 """ 

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

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

2445 :class:`GeneratorExp` nodes. 

2446 """ 

2447 

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

2449 expression: BaseExpression 

2450 

2451 lpar: Sequence[LeftParen] = () 

2452 #: Sequence of parenthesis for precedence dictation. 

2453 rpar: Sequence[RightParen] = () 

2454 

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

2456 #: ``expression``. 

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

2458 

2459 def _validate(self) -> None: 

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

2461 super(Await, self)._validate() 

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

2463 if ( 

2464 self.whitespace_after_await.empty 

2465 and not self.expression._safe_to_use_with_word_operator( 

2466 ExpressionPosition.RIGHT 

2467 ) 

2468 ): 

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

2470 

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

2472 return Await( 

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

2474 whitespace_after_await=visit_required( 

2475 self, "whitespace_after_await", self.whitespace_after_await, visitor 

2476 ), 

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

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

2479 ) 

2480 

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

2482 with self._parenthesize(state): 

2483 state.add_token("await") 

2484 self.whitespace_after_await._codegen(state) 

2485 self.expression._codegen(state) 

2486 

2487 

2488@add_slots 

2489@dataclass(frozen=True) 

2490class IfExp(BaseExpression): 

2491 """ 

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

2493 

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

2495 """ 

2496 

2497 #: The test to perform. 

2498 test: BaseExpression 

2499 

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

2501 body: BaseExpression 

2502 

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

2504 orelse: BaseExpression 

2505 

2506 lpar: Sequence[LeftParen] = () 

2507 #: Sequence of parenthesis for precedence dictation. 

2508 rpar: Sequence[RightParen] = () 

2509 

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

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

2512 

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

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

2515 

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

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

2518 

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

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

2521 

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

2523 if position == ExpressionPosition.RIGHT: 

2524 return self.body._safe_to_use_with_word_operator(position) 

2525 else: 

2526 return self.orelse._safe_to_use_with_word_operator(position) 

2527 

2528 def _validate(self) -> None: 

2529 # Paren validation and such 

2530 super(IfExp, self)._validate() 

2531 # Validate spacing rules 

2532 if ( 

2533 self.whitespace_before_if.empty 

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

2535 ): 

2536 raise CSTValidationError( 

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

2538 ) 

2539 if ( 

2540 self.whitespace_after_if.empty 

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

2542 ): 

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

2544 if ( 

2545 self.whitespace_before_else.empty 

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

2547 ): 

2548 raise CSTValidationError( 

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

2550 ) 

2551 if ( 

2552 self.whitespace_after_else.empty 

2553 and not self.orelse._safe_to_use_with_word_operator( 

2554 ExpressionPosition.RIGHT 

2555 ) 

2556 ): 

2557 raise CSTValidationError( 

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

2559 ) 

2560 

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

2562 return IfExp( 

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

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

2565 whitespace_before_if=visit_required( 

2566 self, "whitespace_before_if", self.whitespace_before_if, visitor 

2567 ), 

2568 whitespace_after_if=visit_required( 

2569 self, "whitespace_after_if", self.whitespace_after_if, visitor 

2570 ), 

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

2572 whitespace_before_else=visit_required( 

2573 self, "whitespace_before_else", self.whitespace_before_else, visitor 

2574 ), 

2575 whitespace_after_else=visit_required( 

2576 self, "whitespace_after_else", self.whitespace_after_else, visitor 

2577 ), 

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

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

2580 ) 

2581 

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

2583 with self._parenthesize(state): 

2584 self.body._codegen(state) 

2585 self.whitespace_before_if._codegen(state) 

2586 state.add_token("if") 

2587 self.whitespace_after_if._codegen(state) 

2588 self.test._codegen(state) 

2589 self.whitespace_before_else._codegen(state) 

2590 state.add_token("else") 

2591 self.whitespace_after_else._codegen(state) 

2592 self.orelse._codegen(state) 

2593 

2594 

2595@add_slots 

2596@dataclass(frozen=True) 

2597class From(CSTNode): 

2598 """ 

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

2600 """ 

2601 

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

2603 item: BaseExpression 

2604 

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

2606 whitespace_before_from: Union[ 

2607 BaseParenthesizableWhitespace, MaybeSentinel 

2608 ] = MaybeSentinel.DEFAULT 

2609 

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

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

2612 

2613 def _validate(self) -> None: 

2614 if ( 

2615 isinstance(self.whitespace_after_from, BaseParenthesizableWhitespace) 

2616 and self.whitespace_after_from.empty 

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

2618 ): 

2619 raise CSTValidationError( 

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

2621 ) 

2622 

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

2624 return From( 

2625 whitespace_before_from=visit_sentinel( 

2626 self, "whitespace_before_from", self.whitespace_before_from, visitor 

2627 ), 

2628 whitespace_after_from=visit_required( 

2629 self, "whitespace_after_from", self.whitespace_after_from, visitor 

2630 ), 

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

2632 ) 

2633 

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

2635 whitespace_before_from = self.whitespace_before_from 

2636 if isinstance(whitespace_before_from, BaseParenthesizableWhitespace): 

2637 whitespace_before_from._codegen(state) 

2638 else: 

2639 state.add_token(default_space) 

2640 

2641 with state.record_syntactic_position(self): 

2642 state.add_token("from") 

2643 self.whitespace_after_from._codegen(state) 

2644 self.item._codegen(state) 

2645 

2646 

2647@add_slots 

2648@dataclass(frozen=True) 

2649class Yield(BaseExpression): 

2650 """ 

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

2652 

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

2654 `Python's language reference 

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

2656 """ 

2657 

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

2659 #: sub-generator to iterate over. 

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

2661 

2662 lpar: Sequence[LeftParen] = () 

2663 #: Sequence of parenthesis for precedence dictation. 

2664 rpar: Sequence[RightParen] = () 

2665 

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

2667 whitespace_after_yield: Union[ 

2668 BaseParenthesizableWhitespace, MaybeSentinel 

2669 ] = MaybeSentinel.DEFAULT 

2670 

2671 def _validate(self) -> None: 

2672 # Paren rules and such 

2673 super(Yield, self)._validate() 

2674 # Our own rules 

2675 whitespace_after_yield = self.whitespace_after_yield 

2676 if ( 

2677 isinstance(whitespace_after_yield, BaseParenthesizableWhitespace) 

2678 and whitespace_after_yield.empty 

2679 ): 

2680 value = self.value 

2681 if isinstance(value, From): 

2682 raise CSTValidationError( 

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

2684 ) 

2685 if isinstance( 

2686 value, BaseExpression 

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

2688 raise CSTValidationError( 

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

2690 ) 

2691 

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

2693 return Yield( 

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

2695 whitespace_after_yield=visit_sentinel( 

2696 self, "whitespace_after_yield", self.whitespace_after_yield, visitor 

2697 ), 

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

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

2700 ) 

2701 

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

2703 with self._parenthesize(state): 

2704 state.add_token("yield") 

2705 whitespace_after_yield = self.whitespace_after_yield 

2706 if isinstance(whitespace_after_yield, BaseParenthesizableWhitespace): 

2707 whitespace_after_yield._codegen(state) 

2708 else: 

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

2710 if self.value is not None: 

2711 state.add_token(" ") 

2712 value = self.value 

2713 if isinstance(value, From): 

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

2715 elif value is not None: 

2716 value._codegen(state) 

2717 

2718 

2719class _BaseElementImpl(CSTNode, ABC): 

2720 """ 

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

2722 """ 

2723 

2724 __slots__ = () 

2725 

2726 value: BaseExpression 

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

2728 

2729 def _codegen_comma( 

2730 self, 

2731 state: CodegenState, 

2732 default_comma: bool = False, 

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

2734 ) -> None: 

2735 """ 

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

2737 """ 

2738 comma = self.comma 

2739 if comma is MaybeSentinel.DEFAULT and default_comma: 

2740 if default_comma_whitespace: 

2741 state.add_token(", ") 

2742 else: 

2743 state.add_token(",") 

2744 elif isinstance(comma, Comma): 

2745 comma._codegen(state) 

2746 

2747 @abstractmethod 

2748 def _codegen_impl( 

2749 self, 

2750 state: CodegenState, 

2751 default_comma: bool = False, 

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

2753 ) -> None: 

2754 ... 

2755 

2756 

2757class BaseElement(_BaseElementImpl, ABC): 

2758 """ 

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

2760 BaseDictElement. 

2761 """ 

2762 

2763 __slots__ = () 

2764 

2765 

2766class BaseDictElement(_BaseElementImpl, ABC): 

2767 """ 

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

2769 BaseElement. 

2770 """ 

2771 

2772 __slots__ = () 

2773 

2774 

2775@add_slots 

2776@dataclass(frozen=True) 

2777class Element(BaseElement): 

2778 """ 

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

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

2781 

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

2783 """ 

2784 

2785 value: BaseExpression 

2786 

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

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

2789 

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

2791 return Element( 

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

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

2794 ) 

2795 

2796 def _codegen_impl( 

2797 self, 

2798 state: CodegenState, 

2799 default_comma: bool = False, 

2800 default_comma_whitespace: bool = False, 

2801 ) -> None: 

2802 with state.record_syntactic_position(self): 

2803 self.value._codegen(state) 

2804 self._codegen_comma(state, default_comma, default_comma_whitespace) 

2805 

2806 

2807@add_slots 

2808@dataclass(frozen=True) 

2809class DictElement(BaseDictElement): 

2810 """ 

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

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

2813 :class:`StarredDictElement`. 

2814 

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

2816 see :class:`Element` instead. 

2817 """ 

2818 

2819 key: BaseExpression 

2820 value: BaseExpression 

2821 

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

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

2824 

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

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

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

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

2829 

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

2831 return DictElement( 

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

2833 whitespace_before_colon=visit_required( 

2834 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

2835 ), 

2836 whitespace_after_colon=visit_required( 

2837 self, "whitespace_after_colon", self.whitespace_after_colon, visitor 

2838 ), 

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

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

2841 ) 

2842 

2843 def _codegen_impl( 

2844 self, 

2845 state: CodegenState, 

2846 default_comma: bool = False, 

2847 default_comma_whitespace: bool = False, 

2848 ) -> None: 

2849 with state.record_syntactic_position(self): 

2850 self.key._codegen(state) 

2851 self.whitespace_before_colon._codegen(state) 

2852 state.add_token(":") 

2853 self.whitespace_after_colon._codegen(state) 

2854 self.value._codegen(state) 

2855 self._codegen_comma(state, default_comma, default_comma_whitespace) 

2856 

2857 

2858@add_slots 

2859@dataclass(frozen=True) 

2860class StarredElement(BaseElement, BaseExpression, _BaseParenthesizedNode): 

2861 """ 

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

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

2864 

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

2866 

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

2868 the trailing comma. For example:: 

2869 

2870 StarredElement( 

2871 cst.Name("el"), 

2872 comma=cst.Comma(), 

2873 lpar=[cst.LeftParen()], 

2874 rpar=[cst.RightParen()], 

2875 ) 

2876 

2877 will generate:: 

2878 

2879 (*el), 

2880 """ 

2881 

2882 value: BaseExpression 

2883 

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

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

2886 

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

2888 lpar: Sequence[LeftParen] = () 

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

2890 rpar: Sequence[RightParen] = () 

2891 

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

2893 whitespace_before_value: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

2894 

2895 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "StarredElement": 

2896 return StarredElement( 

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

2898 whitespace_before_value=visit_required( 

2899 self, "whitespace_before_value", self.whitespace_before_value, visitor 

2900 ), 

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

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

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

2904 ) 

2905 

2906 def _codegen_impl( 

2907 self, 

2908 state: CodegenState, 

2909 default_comma: bool = False, 

2910 default_comma_whitespace: bool = False, 

2911 ) -> None: 

2912 with self._parenthesize(state): 

2913 state.add_token("*") 

2914 self.whitespace_before_value._codegen(state) 

2915 self.value._codegen(state) 

2916 self._codegen_comma(state, default_comma, default_comma_whitespace) 

2917 

2918 

2919@add_slots 

2920@dataclass(frozen=True) 

2921class StarredDictElement(BaseDictElement): 

2922 """ 

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

2924 :class:`Dict`. 

2925 

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

2927 see :class:`StarredElement` instead. 

2928 

2929 Unlike :class:`StarredElement`, this node does not own left or right parenthesis, 

2930 but the ``value`` field may still contain parenthesis. This is due to some 

2931 asymmetry in Python's grammar. 

2932 """ 

2933 

2934 value: BaseExpression 

2935 

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

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

2938 

2939 #: Whitespace between the leading asterisks and the value expression. 

2940 whitespace_before_value: BaseParenthesizableWhitespace = SimpleWhitespace.field("") 

2941 

2942 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "StarredDictElement": 

2943 return StarredDictElement( 

2944 whitespace_before_value=visit_required( 

2945 self, "whitespace_before_value", self.whitespace_before_value, visitor 

2946 ), 

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

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

2949 ) 

2950 

2951 def _codegen_impl( 

2952 self, 

2953 state: CodegenState, 

2954 default_comma: bool = False, 

2955 default_comma_whitespace: bool = False, 

2956 ) -> None: 

2957 with state.record_syntactic_position(self): 

2958 state.add_token("**") 

2959 self.whitespace_before_value._codegen(state) 

2960 self.value._codegen(state) 

2961 self._codegen_comma(state, default_comma, default_comma_whitespace) 

2962 

2963 

2964@add_slots 

2965@dataclass(frozen=True) 

2966class Tuple(BaseAssignTargetExpression, BaseDelTargetExpression): 

2967 """ 

2968 An immutable literal tuple. Tuples are often (but not always) parenthesized. 

2969 

2970 :: 

2971 

2972 Tuple([ 

2973 Element(Integer("1")), 

2974 Element(Integer("2")), 

2975 StarredElement(Name("others")), 

2976 ]) 

2977 

2978 generates the following code:: 

2979 

2980 (1, 2, *others) 

2981 """ 

2982 

2983 #: A sequence containing all the :class:`Element` and :class:`StarredElement` nodes 

2984 #: in the tuple. 

2985 elements: Sequence[BaseElement] 

2986 

2987 lpar: Sequence[LeftParen] = field(default_factory=lambda: (LeftParen(),)) 

2988 #: Sequence of parenthesis for precedence dictation. 

2989 rpar: Sequence[RightParen] = field(default_factory=lambda: (RightParen(),)) 

2990 

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

2992 if super(Tuple, self)._safe_to_use_with_word_operator(position): 

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

2994 return True 

2995 # elements[-1] and elements[0] must exist past this point, because 

2996 # we're not parenthesized, meaning we must have at least one element. 

2997 elements = self.elements 

2998 if position == ExpressionPosition.LEFT: 

2999 last_element = elements[-1] 

3000 return ( 

3001 isinstance(last_element.comma, Comma) 

3002 or ( 

3003 isinstance(last_element, StarredElement) 

3004 and len(last_element.rpar) > 0 

3005 ) 

3006 or last_element.value._safe_to_use_with_word_operator(position) 

3007 ) 

3008 else: # ExpressionPosition.RIGHT 

3009 first_element = elements[0] 

3010 # starred elements are always safe because they begin with ( or * 

3011 return isinstance( 

3012 first_element, StarredElement 

3013 ) or first_element.value._safe_to_use_with_word_operator(position) 

3014 

3015 def _validate(self) -> None: 

3016 # Paren validation and such 

3017 super(Tuple, self)._validate() 

3018 

3019 if len(self.elements) == 0: 

3020 if len(self.lpar) == 0: # assumes len(lpar) == len(rpar), via superclass 

3021 raise CSTValidationError( 

3022 "A zero-length tuple must be wrapped in parentheses." 

3023 ) 

3024 # Invalid commas aren't possible, because MaybeSentinel will ensure that there 

3025 # is a comma where required. 

3026 

3027 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Tuple": 

3028 return Tuple( 

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

3030 elements=visit_sequence(self, "elements", self.elements, visitor), 

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

3032 ) 

3033 

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

3035 with self._parenthesize(state): 

3036 elements = self.elements 

3037 if len(elements) == 1: 

3038 elements[0]._codegen( 

3039 state, default_comma=True, default_comma_whitespace=False 

3040 ) 

3041 else: 

3042 for idx, el in enumerate(elements): 

3043 el._codegen( 

3044 state, 

3045 default_comma=(idx < len(elements) - 1), 

3046 default_comma_whitespace=True, 

3047 ) 

3048 

3049 

3050class BaseList(BaseExpression, ABC): 

3051 """ 

3052 A base class for :class:`List` and :class:`ListComp`, which both result in a list 

3053 object when evaluated. 

3054 """ 

3055 

3056 __slots__ = () 

3057 

3058 lbracket: LeftSquareBracket = LeftSquareBracket.field() 

3059 #: Brackets surrounding the list. 

3060 rbracket: RightSquareBracket = RightSquareBracket.field() 

3061 

3062 lpar: Sequence[LeftParen] = () 

3063 #: Sequence of parenthesis for precedence dictation. 

3064 rpar: Sequence[RightParen] = () 

3065 

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

3067 return True 

3068 

3069 @contextmanager 

3070 def _bracketize(self, state: CodegenState) -> Generator[None, None, None]: 

3071 self.lbracket._codegen(state) 

3072 yield 

3073 self.rbracket._codegen(state) 

3074 

3075 

3076@add_slots 

3077@dataclass(frozen=True) 

3078class List(BaseList, BaseAssignTargetExpression, BaseDelTargetExpression): 

3079 """ 

3080 A mutable literal list. 

3081 

3082 :: 

3083 

3084 List([ 

3085 Element(Integer("1")), 

3086 Element(Integer("2")), 

3087 StarredElement(Name("others")), 

3088 ]) 

3089 

3090 generates the following code:: 

3091 

3092 [1, 2, *others] 

3093 

3094 List comprehensions are represented with a :class:`ListComp` node. 

3095 """ 

3096 

3097 #: A sequence containing all the :class:`Element` and :class:`StarredElement` nodes 

3098 #: in the list. 

3099 elements: Sequence[BaseElement] 

3100 

3101 lbracket: LeftSquareBracket = LeftSquareBracket.field() 

3102 #: Brackets surrounding the list. 

3103 rbracket: RightSquareBracket = RightSquareBracket.field() 

3104 

3105 lpar: Sequence[LeftParen] = () 

3106 #: Sequence of parenthesis for precedence dictation. 

3107 rpar: Sequence[RightParen] = () 

3108 

3109 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "List": 

3110 return List( 

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

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

3113 elements=visit_sequence(self, "elements", self.elements, visitor), 

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

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

3116 ) 

3117 

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

3119 with self._parenthesize(state), self._bracketize(state): 

3120 elements = self.elements 

3121 for idx, el in enumerate(elements): 

3122 el._codegen( 

3123 state, 

3124 default_comma=(idx < len(elements) - 1), 

3125 default_comma_whitespace=True, 

3126 ) 

3127 

3128 

3129class _BaseSetOrDict(BaseExpression, ABC): 

3130 """ 

3131 An abstract base class for :class:`BaseSet` and :class:`BaseDict`. 

3132 

3133 Literal sets and dicts are syntactically similar (hence this shared base class), but 

3134 are semantically different. This base class is an implementation detail and 

3135 shouldn't be exported. 

3136 """ 

3137 

3138 __slots__ = () 

3139 

3140 lbrace: LeftCurlyBrace = LeftCurlyBrace.field() 

3141 #: Braces surrounding the set or dict. 

3142 rbrace: RightCurlyBrace = RightCurlyBrace.field() 

3143 

3144 lpar: Sequence[LeftParen] = () 

3145 #: Sequence of parenthesis for precedence dictation. 

3146 rpar: Sequence[RightParen] = () 

3147 

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

3149 return True 

3150 

3151 # brace-ize seems like a very made-up word. And it is! 

3152 @contextmanager 

3153 def _braceize(self, state: CodegenState) -> Generator[None, None, None]: 

3154 self.lbrace._codegen(state) 

3155 yield 

3156 self.rbrace._codegen(state) 

3157 

3158 

3159class BaseSet(_BaseSetOrDict, ABC): 

3160 """ 

3161 An abstract base class for :class:`Set` and :class:`SetComp`, which both result in 

3162 a set object when evaluated. 

3163 """ 

3164 

3165 __slots__ = () 

3166 

3167 

3168@add_slots 

3169@dataclass(frozen=True) 

3170class Set(BaseSet): 

3171 """ 

3172 A mutable literal set. 

3173 

3174 :: 

3175 

3176 Set([ 

3177 Element(Integer("1")), 

3178 Element(Integer("2")), 

3179 StarredElement(Name("others")), 

3180 ]) 

3181 

3182 generates the following code:: 

3183 

3184 {1, 2, *others} 

3185 

3186 Set comprehensions are represented with a :class:`SetComp` node. 

3187 """ 

3188 

3189 #: A sequence containing all the :class:`Element` and :class:`StarredElement` nodes 

3190 #: in the set. 

3191 elements: Sequence[BaseElement] 

3192 

3193 lbrace: LeftCurlyBrace = LeftCurlyBrace.field() 

3194 #: Braces surrounding the set. 

3195 rbrace: RightCurlyBrace = RightCurlyBrace.field() 

3196 

3197 lpar: Sequence[LeftParen] = () 

3198 #: Sequence of parenthesis for precedence dictation. 

3199 rpar: Sequence[RightParen] = () 

3200 

3201 def _validate(self) -> None: 

3202 super(Set, self)._validate() 

3203 

3204 if len(self.elements) == 0: 

3205 raise CSTValidationError( 

3206 "A literal set must have at least one element. A zero-element set " 

3207 + "would be syntatically ambiguous with an empty dict, `{}`." 

3208 ) 

3209 

3210 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Set": 

3211 return Set( 

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

3213 lbrace=visit_required(self, "lbrace", self.lbrace, visitor), 

3214 elements=visit_sequence(self, "elements", self.elements, visitor), 

3215 rbrace=visit_required(self, "rbrace", self.rbrace, visitor), 

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

3217 ) 

3218 

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

3220 with self._parenthesize(state), self._braceize(state): 

3221 elements = self.elements 

3222 for idx, el in enumerate(elements): 

3223 el._codegen( 

3224 state, 

3225 default_comma=(idx < len(elements) - 1), 

3226 default_comma_whitespace=True, 

3227 ) 

3228 

3229 

3230class BaseDict(_BaseSetOrDict, ABC): 

3231 """ 

3232 An abstract base class for :class:`Dict` and :class:`DictComp`, which both result in 

3233 a dict object when evaluated. 

3234 """ 

3235 

3236 __slots__ = () 

3237 

3238 

3239@add_slots 

3240@dataclass(frozen=True) 

3241class Dict(BaseDict): 

3242 """ 

3243 A literal dictionary. Key-value pairs are stored in ``elements`` using 

3244 :class:`DictElement` nodes. 

3245 

3246 It's possible to expand one dictionary into another, as in ``{k: v, **expanded}``. 

3247 Expanded elements are stored as :class:`StarredDictElement` nodes. 

3248 

3249 :: 

3250 

3251 Dict([ 

3252 DictElement(Name("k1"), Name("v1")), 

3253 DictElement(Name("k2"), Name("v2")), 

3254 StarredDictElement(Name("expanded")), 

3255 ]) 

3256 

3257 generates the following code:: 

3258 

3259 {k1: v1, k2: v2, **expanded} 

3260 """ 

3261 

3262 elements: Sequence[BaseDictElement] 

3263 lbrace: LeftCurlyBrace = LeftCurlyBrace.field() 

3264 rbrace: RightCurlyBrace = RightCurlyBrace.field() 

3265 lpar: Sequence[LeftParen] = () 

3266 rpar: Sequence[RightParen] = () 

3267 

3268 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Dict": 

3269 return Dict( 

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

3271 lbrace=visit_required(self, "lbrace", self.lbrace, visitor), 

3272 elements=visit_sequence(self, "elements", self.elements, visitor), 

3273 rbrace=visit_required(self, "rbrace", self.rbrace, visitor), 

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

3275 ) 

3276 

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

3278 with self._parenthesize(state), self._braceize(state): 

3279 elements = self.elements 

3280 for idx, el in enumerate(elements): 

3281 el._codegen( 

3282 state, 

3283 default_comma=(idx < len(elements) - 1), 

3284 default_comma_whitespace=True, 

3285 ) 

3286 

3287 

3288@add_slots 

3289@dataclass(frozen=True) 

3290class CompFor(CSTNode): 

3291 """ 

3292 One ``for`` clause in a :class:`BaseComp`, or a nested hierarchy of 

3293 ``for`` clauses. 

3294 

3295 Nested loops in comprehensions are difficult to get right, but they can be thought 

3296 of as a flat representation of nested clauses. 

3297 

3298 ``elt for a in b for c in d if e`` can be thought of as:: 

3299 

3300 for a in b: 

3301 for c in d: 

3302 if e: 

3303 yield elt 

3304 

3305 And that would form the following CST:: 

3306 

3307 ListComp( 

3308 elt=Name("elt"), 

3309 for_in=CompFor( 

3310 target=Name("a"), 

3311 iter=Name("b"), 

3312 ifs=[], 

3313 inner_comp_for=CompFor( 

3314 target=Name("c"), 

3315 iter=Name("d"), 

3316 ifs=[ 

3317 CompIf( 

3318 test=Name("e"), 

3319 ), 

3320 ], 

3321 ), 

3322 ), 

3323 ) 

3324 

3325 Normal ``for`` statements are provided by :class:`For`. 

3326 """ 

3327 

3328 #: The target to assign a value to in each iteration of the loop. This is different 

3329 #: from :attr:`GeneratorExp.elt`, :attr:`ListComp.elt`, :attr:`SetComp.elt`, and 

3330 #: ``key`` and ``value`` in :class:`DictComp`, because it doesn't directly effect 

3331 #: the value of resulting generator, list, set, or dict. 

3332 target: BaseAssignTargetExpression 

3333 

3334 #: The value to iterate over. Every value in ``iter`` is stored in ``target``. 

3335 iter: BaseExpression 

3336 

3337 #: Zero or more conditional clauses that control this loop. If any of these tests 

3338 #: fail, the ``target`` item is skipped. 

3339 #: 

3340 #: :: 

3341 #: 

3342 #: if a if b if c 

3343 #: 

3344 #: has similar semantics to:: 

3345 #: 

3346 #: if a and b and c 

3347 ifs: Sequence["CompIf"] = () 

3348 

3349 #: Another :class:`CompFor` node used to form nested loops. Nested comprehensions 

3350 #: can be useful, but they tend to be difficult to read and write. As a result they 

3351 #: are uncommon. 

3352 inner_for_in: Optional["CompFor"] = None 

3353 

3354 #: An optional async modifier that appears before the ``for`` keyword. 

3355 asynchronous: Optional[Asynchronous] = None 

3356 

3357 #: Whitespace that appears at the beginning of this node, before the ``for`` and 

3358 #: ``async`` keywords. 

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

3360 

3361 #: Whitespace appearing after the ``for`` keyword, but before the ``target``. 

3362 whitespace_after_for: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3363 

3364 #: Whitespace appearing after the ``target``, but before the ``in`` keyword. 

3365 whitespace_before_in: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3366 

3367 #: Whitespace appearing after the ``in`` keyword, but before the ``iter``. 

3368 whitespace_after_in: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3369 

3370 def _validate(self) -> None: 

3371 if ( 

3372 self.whitespace_after_for.empty 

3373 and not self.target._safe_to_use_with_word_operator( 

3374 ExpressionPosition.RIGHT 

3375 ) 

3376 ): 

3377 raise CSTValidationError( 

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

3379 ) 

3380 

3381 if ( 

3382 self.whitespace_before_in.empty 

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

3384 ): 

3385 raise CSTValidationError( 

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

3387 ) 

3388 

3389 if ( 

3390 self.whitespace_after_in.empty 

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

3392 ): 

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

3394 

3395 prev_expr = self.iter 

3396 for if_clause in self.ifs: 

3397 if ( 

3398 if_clause.whitespace_before.empty 

3399 and not prev_expr._safe_to_use_with_word_operator( 

3400 ExpressionPosition.LEFT 

3401 ) 

3402 ): 

3403 raise CSTValidationError( 

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

3405 ) 

3406 prev_expr = if_clause.test 

3407 

3408 inner_for_in = self.inner_for_in 

3409 if ( 

3410 inner_for_in is not None 

3411 and inner_for_in.whitespace_before.empty 

3412 and not prev_expr._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

3413 ): 

3414 keyword = "async" if inner_for_in.asynchronous else "for" 

3415 raise CSTValidationError( 

3416 f"Must have at least one space before '{keyword}' keyword." 

3417 ) 

3418 

3419 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CompFor": 

3420 return CompFor( 

3421 whitespace_before=visit_required( 

3422 self, "whitespace_before", self.whitespace_before, visitor 

3423 ), 

3424 asynchronous=visit_optional( 

3425 self, "asynchronous", self.asynchronous, visitor 

3426 ), 

3427 whitespace_after_for=visit_required( 

3428 self, "whitespace_after_for", self.whitespace_after_for, visitor 

3429 ), 

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

3431 whitespace_before_in=visit_required( 

3432 self, "whitespace_before_in", self.whitespace_before_in, visitor 

3433 ), 

3434 whitespace_after_in=visit_required( 

3435 self, "whitespace_after_in", self.whitespace_after_in, visitor 

3436 ), 

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

3438 ifs=visit_sequence(self, "ifs", self.ifs, visitor), 

3439 inner_for_in=visit_optional( 

3440 self, "inner_for_in", self.inner_for_in, visitor 

3441 ), 

3442 ) 

3443 

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

3445 self.whitespace_before._codegen(state) 

3446 asynchronous = self.asynchronous 

3447 if asynchronous is not None: 

3448 asynchronous._codegen(state) 

3449 state.add_token("for") 

3450 self.whitespace_after_for._codegen(state) 

3451 self.target._codegen(state) 

3452 self.whitespace_before_in._codegen(state) 

3453 state.add_token("in") 

3454 self.whitespace_after_in._codegen(state) 

3455 self.iter._codegen(state) 

3456 ifs = self.ifs 

3457 for if_clause in ifs: 

3458 if_clause._codegen(state) 

3459 inner_for_in = self.inner_for_in 

3460 if inner_for_in is not None: 

3461 inner_for_in._codegen(state) 

3462 

3463 

3464@add_slots 

3465@dataclass(frozen=True) 

3466class CompIf(CSTNode): 

3467 """ 

3468 A conditional clause in a :class:`CompFor`, used as part of a generator or 

3469 comprehension expression. 

3470 

3471 If the ``test`` fails, the current element in the :class:`CompFor` will be skipped. 

3472 """ 

3473 

3474 #: An expression to evaluate. When interpreted, Python will coerce it to a boolean. 

3475 test: BaseExpression 

3476 

3477 #: Whitespace before the ``if`` keyword. 

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

3479 

3480 #: Whitespace after the ``if`` keyword, but before the ``test`` expression. 

3481 whitespace_before_test: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3482 

3483 def _validate(self) -> None: 

3484 if ( 

3485 self.whitespace_before_test.empty 

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

3487 ): 

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

3489 

3490 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CompIf": 

3491 return CompIf( 

3492 whitespace_before=visit_required( 

3493 self, "whitespace_before", self.whitespace_before, visitor 

3494 ), 

3495 whitespace_before_test=visit_required( 

3496 self, "whitespace_before_test", self.whitespace_before_test, visitor 

3497 ), 

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

3499 ) 

3500 

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

3502 self.whitespace_before._codegen(state) 

3503 state.add_token("if") 

3504 self.whitespace_before_test._codegen(state) 

3505 self.test._codegen(state) 

3506 

3507 

3508class BaseComp(BaseExpression, ABC): 

3509 """ 

3510 A base class for all comprehension and generator expressions, including 

3511 :class:`GeneratorExp`, :class:`ListComp`, :class:`SetComp`, and :class:`DictComp`. 

3512 """ 

3513 

3514 __slots__ = () 

3515 

3516 for_in: CompFor 

3517 

3518 

3519class BaseSimpleComp(BaseComp, ABC): 

3520 """ 

3521 The base class for :class:`ListComp`, :class:`SetComp`, and :class:`GeneratorExp`. 

3522 :class:`DictComp` is not a :class:`BaseSimpleComp`, because it uses ``key`` and 

3523 ``value``. 

3524 """ 

3525 

3526 __slots__ = () 

3527 

3528 #: The expression evaluated during each iteration of the comprehension. This 

3529 #: lexically comes before the ``for_in`` clause, but it is semantically the 

3530 #: inner-most element, evaluated inside the ``for_in`` clause. 

3531 elt: BaseExpression 

3532 

3533 #: The ``for ... in ... if ...`` clause that lexically comes after ``elt``. This may 

3534 #: be a nested structure for nested comprehensions. See :class:`CompFor` for 

3535 #: details. 

3536 for_in: CompFor 

3537 

3538 def _validate(self) -> None: 

3539 super(BaseSimpleComp, self)._validate() 

3540 

3541 for_in = self.for_in 

3542 if ( 

3543 for_in.whitespace_before.empty 

3544 and not self.elt._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

3545 ): 

3546 keyword = "async" if for_in.asynchronous else "for" 

3547 raise CSTValidationError( 

3548 f"Must have at least one space before '{keyword}' keyword." 

3549 ) 

3550 

3551 

3552@add_slots 

3553@dataclass(frozen=True) 

3554class GeneratorExp(BaseSimpleComp): 

3555 """ 

3556 A generator expression. ``elt`` represents the value yielded for each item in 

3557 :attr:`CompFor.iter`. 

3558 

3559 All ``for ... in ...`` and ``if ...`` clauses are stored as a recursive 

3560 :class:`CompFor` data structure inside ``for_in``. 

3561 """ 

3562 

3563 #: The expression evaluated and yielded during each iteration of the generator. 

3564 elt: BaseExpression 

3565 

3566 #: The ``for ... in ... if ...`` clause that comes after ``elt``. This may be a 

3567 #: nested structure for nested comprehensions. See :class:`CompFor` for details. 

3568 for_in: CompFor 

3569 

3570 lpar: Sequence[LeftParen] = field(default_factory=lambda: (LeftParen(),)) 

3571 #: Sequence of parentheses for precedence dictation. Generator expressions must 

3572 #: always be parenthesized. However, if a generator expression is the only argument 

3573 #: inside a function call, the enclosing :class:`Call` node may own the parentheses 

3574 #: instead. 

3575 rpar: Sequence[RightParen] = field(default_factory=lambda: (RightParen(),)) 

3576 

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

3578 # Generators are always parenthesized 

3579 return True 

3580 

3581 # A note about validation: Generators must always be parenthesized, but it's 

3582 # possible that this Generator node doesn't own those parenthesis (in the case of a 

3583 # function call with a single generator argument). 

3584 # 

3585 # Therefore, there's no useful validation we can do here. In theory, our parent 

3586 # could do the validation, but there's a ton of potential parents to a Generator, so 

3587 # it's not worth the effort. 

3588 

3589 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "GeneratorExp": 

3590 return GeneratorExp( 

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

3592 elt=visit_required(self, "elt", self.elt, visitor), 

3593 for_in=visit_required(self, "for_in", self.for_in, visitor), 

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

3595 ) 

3596 

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

3598 with self._parenthesize(state): 

3599 self.elt._codegen(state) 

3600 self.for_in._codegen(state) 

3601 

3602 

3603@add_slots 

3604@dataclass(frozen=True) 

3605class ListComp(BaseList, BaseSimpleComp): 

3606 """ 

3607 A list comprehension. ``elt`` represents the value stored for each item in 

3608 :attr:`CompFor.iter`. 

3609 

3610 All ``for ... in ...`` and ``if ...`` clauses are stored as a recursive 

3611 :class:`CompFor` data structure inside ``for_in``. 

3612 """ 

3613 

3614 #: The expression evaluated and stored during each iteration of the comprehension. 

3615 elt: BaseExpression 

3616 

3617 #: The ``for ... in ... if ...`` clause that comes after ``elt``. This may be a 

3618 #: nested structure for nested comprehensions. See :class:`CompFor` for details. 

3619 for_in: CompFor 

3620 

3621 lbracket: LeftSquareBracket = LeftSquareBracket.field() 

3622 #: Brackets surrounding the list comprehension. 

3623 rbracket: RightSquareBracket = RightSquareBracket.field() 

3624 

3625 lpar: Sequence[LeftParen] = () 

3626 #: Sequence of parenthesis for precedence dictation. 

3627 rpar: Sequence[RightParen] = () 

3628 

3629 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ListComp": 

3630 return ListComp( 

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

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

3633 elt=visit_required(self, "elt", self.elt, visitor), 

3634 for_in=visit_required(self, "for_in", self.for_in, visitor), 

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

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

3637 ) 

3638 

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

3640 with self._parenthesize(state), self._bracketize(state): 

3641 self.elt._codegen(state) 

3642 self.for_in._codegen(state) 

3643 

3644 

3645@add_slots 

3646@dataclass(frozen=True) 

3647class SetComp(BaseSet, BaseSimpleComp): 

3648 """ 

3649 A set comprehension. ``elt`` represents the value stored for each item in 

3650 :attr:`CompFor.iter`. 

3651 

3652 All ``for ... in ...`` and ``if ...`` clauses are stored as a recursive 

3653 :class:`CompFor` data structure inside ``for_in``. 

3654 """ 

3655 

3656 #: The expression evaluated and stored during each iteration of the comprehension. 

3657 elt: BaseExpression 

3658 

3659 #: The ``for ... in ... if ...`` clause that comes after ``elt``. This may be a 

3660 #: nested structure for nested comprehensions. See :class:`CompFor` for details. 

3661 for_in: CompFor 

3662 

3663 lbrace: LeftCurlyBrace = LeftCurlyBrace.field() 

3664 #: Braces surrounding the set comprehension. 

3665 rbrace: RightCurlyBrace = RightCurlyBrace.field() 

3666 

3667 lpar: Sequence[LeftParen] = () 

3668 #: Sequence of parenthesis for precedence dictation. 

3669 rpar: Sequence[RightParen] = () 

3670 

3671 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "SetComp": 

3672 return SetComp( 

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

3674 lbrace=visit_required(self, "lbrace", self.lbrace, visitor), 

3675 elt=visit_required(self, "elt", self.elt, visitor), 

3676 for_in=visit_required(self, "for_in", self.for_in, visitor), 

3677 rbrace=visit_required(self, "rbrace", self.rbrace, visitor), 

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

3679 ) 

3680 

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

3682 with self._parenthesize(state), self._braceize(state): 

3683 self.elt._codegen(state) 

3684 self.for_in._codegen(state) 

3685 

3686 

3687@add_slots 

3688@dataclass(frozen=True) 

3689class DictComp(BaseDict, BaseComp): 

3690 """ 

3691 A dictionary comprehension. ``key`` and ``value`` represent the dictionary entry 

3692 evaluated for each item. 

3693 

3694 All ``for ... in ...`` and ``if ...`` clauses are stored as a recursive 

3695 :class:`CompFor` data structure inside ``for_in``. 

3696 """ 

3697 

3698 #: The key inserted into the dictionary during each iteration of the comprehension. 

3699 key: BaseExpression 

3700 #: The value associated with the ``key`` inserted into the dictionary during each 

3701 #: iteration of the comprehension. 

3702 value: BaseExpression 

3703 

3704 #: The ``for ... in ... if ...`` clause that lexically comes after ``key`` and 

3705 #: ``value``. This may be a nested structure for nested comprehensions. See 

3706 #: :class:`CompFor` for details. 

3707 for_in: CompFor 

3708 

3709 lbrace: LeftCurlyBrace = LeftCurlyBrace.field() 

3710 #: Braces surrounding the dict comprehension. 

3711 rbrace: RightCurlyBrace = RightCurlyBrace.field() 

3712 

3713 lpar: Sequence[LeftParen] = () 

3714 #: Sequence of parenthesis for precedence dictation. 

3715 rpar: Sequence[RightParen] = () 

3716 

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

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

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

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

3721 

3722 def _validate(self) -> None: 

3723 super(DictComp, self)._validate() 

3724 

3725 for_in = self.for_in 

3726 if ( 

3727 for_in.whitespace_before.empty 

3728 and not self.value._safe_to_use_with_word_operator(ExpressionPosition.LEFT) 

3729 ): 

3730 keyword = "async" if for_in.asynchronous else "for" 

3731 raise CSTValidationError( 

3732 f"Must have at least one space before '{keyword}' keyword." 

3733 ) 

3734 

3735 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "DictComp": 

3736 return DictComp( 

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

3738 lbrace=visit_required(self, "lbrace", self.lbrace, visitor), 

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

3740 whitespace_before_colon=visit_required( 

3741 self, "whitespace_before_colon", self.whitespace_before_colon, visitor 

3742 ), 

3743 whitespace_after_colon=visit_required( 

3744 self, "whitespace_after_colon", self.whitespace_after_colon, visitor 

3745 ), 

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

3747 for_in=visit_required(self, "for_in", self.for_in, visitor), 

3748 rbrace=visit_required(self, "rbrace", self.rbrace, visitor), 

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

3750 ) 

3751 

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

3753 with self._parenthesize(state), self._braceize(state): 

3754 self.key._codegen(state) 

3755 self.whitespace_before_colon._codegen(state) 

3756 state.add_token(":") 

3757 self.whitespace_after_colon._codegen(state) 

3758 self.value._codegen(state) 

3759 self.for_in._codegen(state) 

3760 

3761 

3762@add_slots 

3763@dataclass(frozen=True) 

3764class NamedExpr(BaseExpression): 

3765 """ 

3766 An expression that is also an assignment, such as ``x := y + z``. Affectionately 

3767 known as the walrus operator, this expression allows you to make an assignment 

3768 inside an expression. This greatly simplifies loops:: 

3769 

3770 while line := read_some_line_or_none(): 

3771 do_thing_with_line(line) 

3772 """ 

3773 

3774 #: The target that is being assigned to. 

3775 target: BaseExpression 

3776 

3777 #: The expression being assigned to the target. 

3778 value: BaseExpression 

3779 

3780 #: Sequence of parenthesis for precedence dictation. 

3781 lpar: Sequence[LeftParen] = () 

3782 #: Sequence of parenthesis for precedence dictation. 

3783 rpar: Sequence[RightParen] = () 

3784 

3785 #: Whitespace after the target, but before the walrus operator. 

3786 whitespace_before_walrus: BaseParenthesizableWhitespace = SimpleWhitespace.field( 

3787 " " 

3788 ) 

3789 #: Whitespace after the walrus operator, but before the value. 

3790 whitespace_after_walrus: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ") 

3791 

3792 def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "NamedExpr": 

3793 return NamedExpr( 

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

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

3796 whitespace_before_walrus=visit_required( 

3797 self, "whitespace_before_walrus", self.whitespace_before_walrus, visitor 

3798 ), 

3799 whitespace_after_walrus=visit_required( 

3800 self, "whitespace_after_walrus", self.whitespace_after_walrus, visitor 

3801 ), 

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

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

3804 ) 

3805 

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

3807 if position == ExpressionPosition.LEFT: 

3808 return len(self.rpar) > 0 or self.value._safe_to_use_with_word_operator( 

3809 position 

3810 ) 

3811 return len(self.lpar) > 0 or self.target._safe_to_use_with_word_operator( 

3812 position 

3813 ) 

3814 

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

3816 with self._parenthesize(state): 

3817 self.target._codegen(state) 

3818 self.whitespace_before_walrus._codegen(state) 

3819 state.add_token(":=") 

3820 self.whitespace_after_walrus._codegen(state) 

3821 self.value._codegen(state)