Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/black/nodes.py: 18%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

541 statements  

1""" 

2blib2to3 Node/Leaf transformation-related utility functions. 

3""" 

4 

5from collections.abc import Iterator 

6from typing import Final, Generic, Literal, TypeGuard, TypeVar, Union 

7 

8from mypy_extensions import mypyc_attr 

9 

10from black.mode import Mode, Preview 

11from black.strings import get_string_prefix, has_triple_quotes 

12from blib2to3 import pygram 

13from blib2to3.pgen2 import token 

14from blib2to3.pytree import NL, Leaf, Node, type_repr 

15 

16# The CLI cache directory is not known until Click parses the command line. 

17pygram.initialize(save=False) 

18syms: Final = pygram.python_symbols 

19 

20 

21# types 

22T = TypeVar("T") 

23LN = Union[Leaf, Node] 

24LeafID = int 

25NodeType = int 

26 

27 

28WHITESPACE: Final = {token.DEDENT, token.INDENT, token.NEWLINE} 

29STATEMENT: Final = { 

30 syms.if_stmt, 

31 syms.while_stmt, 

32 syms.for_stmt, 

33 syms.try_stmt, 

34 syms.except_clause, 

35 syms.with_stmt, 

36 syms.funcdef, 

37 syms.classdef, 

38 syms.match_stmt, 

39 syms.case_block, 

40} 

41STANDALONE_COMMENT: Final = 153 

42token.tok_name[STANDALONE_COMMENT] = "STANDALONE_COMMENT" 

43LOGIC_OPERATORS: Final = {"and", "or"} 

44COMPARATORS: Final = { 

45 token.LESS, 

46 token.GREATER, 

47 token.EQEQUAL, 

48 token.NOTEQUAL, 

49 token.LESSEQUAL, 

50 token.GREATEREQUAL, 

51} 

52MATH_OPERATORS: Final = { 

53 token.VBAR, 

54 token.CIRCUMFLEX, 

55 token.AMPER, 

56 token.LEFTSHIFT, 

57 token.RIGHTSHIFT, 

58 token.PLUS, 

59 token.MINUS, 

60 token.STAR, 

61 token.SLASH, 

62 token.DOUBLESLASH, 

63 token.PERCENT, 

64 token.AT, 

65 token.TILDE, 

66 token.DOUBLESTAR, 

67} 

68STARS: Final = {token.STAR, token.DOUBLESTAR} 

69VARARGS_SPECIALS: Final = STARS | {token.SLASH} 

70VARARGS_PARENTS: Final = { 

71 syms.arglist, 

72 syms.argument, # double star in arglist 

73 syms.trailer, # single argument to call 

74 syms.typedargslist, 

75 syms.varargslist, # lambdas 

76 syms.typevartuple, # star in a PEP 695 type parameter list 

77 syms.paramspec, # double star in a PEP 695 type parameter list 

78} 

79UNPACKING_PARENTS: Final = { 

80 syms.atom, # single element of a list or set literal 

81 syms.dictsetmaker, 

82 syms.listmaker, 

83 syms.testlist_gexp, 

84 syms.testlist_star_expr, 

85 syms.subject_expr, 

86 syms.pattern, 

87} 

88TEST_DESCENDANTS: Final = { 

89 syms.test, 

90 syms.lambdef, 

91 syms.or_test, 

92 syms.and_test, 

93 syms.not_test, 

94 syms.comparison, 

95 syms.star_expr, 

96 syms.expr, 

97 syms.xor_expr, 

98 syms.and_expr, 

99 syms.shift_expr, 

100 syms.arith_expr, 

101 syms.trailer, 

102 syms.term, 

103 syms.power, 

104 syms.namedexpr_test, 

105} 

106TYPED_NAMES: Final = {syms.tname, syms.tname_star} 

107ASSIGNMENTS: Final = { 

108 "=", 

109 "+=", 

110 "-=", 

111 "*=", 

112 "@=", 

113 "/=", 

114 "%=", 

115 "&=", 

116 "|=", 

117 "^=", 

118 "<<=", 

119 ">>=", 

120 "**=", 

121 "//=", 

122 ":", 

123} 

124 

125IMPLICIT_TUPLE: Final = {syms.testlist, syms.testlist_star_expr, syms.exprlist} 

126BRACKET: Final = { 

127 token.LPAR: token.RPAR, 

128 token.LSQB: token.RSQB, 

129 token.LBRACE: token.RBRACE, 

130} 

131OPENING_BRACKETS: Final = set(BRACKET.keys()) 

132CLOSING_BRACKETS: Final = set(BRACKET.values()) 

133BRACKETS: Final = OPENING_BRACKETS | CLOSING_BRACKETS 

134ALWAYS_NO_SPACE: Final = CLOSING_BRACKETS | { 

135 token.COMMA, 

136 STANDALONE_COMMENT, 

137 token.FSTRING_MIDDLE, 

138 token.FSTRING_END, 

139 token.TSTRING_MIDDLE, 

140 token.TSTRING_END, 

141 token.BANG, 

142} 

143 

144 

145@mypyc_attr(allow_interpreted_subclasses=True) 

146class Visitor(Generic[T]): 

147 """Basic lib2to3 visitor that yields things of type `T` on `visit()`.""" 

148 

149 def visit(self, node: LN) -> Iterator[T]: 

150 """Main method to visit `node` and its children. 

151 

152 It tries to find a `visit_*()` method for the given `node.type`, like 

153 `visit_simple_stmt` for Node objects or `visit_INDENT` for Leaf objects. 

154 If no dedicated `visit_*()` method is found, chooses `visit_default()` 

155 instead. 

156 

157 Then yields objects of type `T` from the selected visitor. 

158 """ 

159 if node.type < 256: 

160 name = token.tok_name[node.type] 

161 else: 

162 name = str(type_repr(node.type)) 

163 # We explicitly branch on whether a visitor exists (instead of 

164 # using self.visit_default as the default arg to getattr) in order 

165 # to save needing to create a bound method object and so mypyc can 

166 # generate a native call to visit_default. 

167 visitf = getattr(self, f"visit_{name}", None) 

168 if visitf: 

169 yield from visitf(node) 

170 else: 

171 yield from self.visit_default(node) 

172 

173 def visit_default(self, node: LN) -> Iterator[T]: 

174 """Default `visit_*()` implementation. Recurses to children of `node`.""" 

175 if isinstance(node, Node): 

176 for child in node.children: 

177 yield from self.visit(child) 

178 

179 

180def whitespace(leaf: Leaf, *, complex_subscript: bool, mode: Mode) -> str: 

181 """Return whitespace prefix if needed for the given `leaf`. 

182 

183 `complex_subscript` signals whether the given leaf is part of a subscription 

184 which has non-trivial arguments, like arithmetic expressions or function calls. 

185 """ 

186 NO: Final[str] = "" 

187 SPACE: Final[str] = " " 

188 DOUBLESPACE: Final[str] = " " 

189 t = leaf.type 

190 p = leaf.parent 

191 v = leaf.value 

192 if t in ALWAYS_NO_SPACE: 

193 return NO 

194 

195 if t == token.COMMENT: 

196 return DOUBLESPACE 

197 

198 assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" 

199 if t == token.COLON and p.type not in { 

200 syms.subscript, 

201 syms.subscriptlist, 

202 syms.sliceop, 

203 }: 

204 return NO 

205 

206 if t == token.LBRACE and p.type in ( 

207 syms.fstring_replacement_field, 

208 syms.tstring_replacement_field, 

209 ): 

210 return NO 

211 

212 prev = leaf.prev_sibling 

213 if not prev: 

214 prevp = preceding_leaf(p) 

215 if not prevp or prevp.type in OPENING_BRACKETS: 

216 return NO 

217 

218 if t == token.COLON: 

219 if prevp.type == token.COLON: 

220 return NO 

221 

222 elif prevp.type != token.COMMA and not complex_subscript: 

223 return NO 

224 

225 return SPACE 

226 

227 if prevp.type == token.EQUAL: 

228 if prevp.parent: 

229 if prevp.parent.type in { 

230 syms.arglist, 

231 syms.argument, 

232 syms.parameters, 

233 syms.varargslist, 

234 }: 

235 return NO 

236 

237 elif prevp.parent.type == syms.typedargslist: 

238 # A bit hacky: if the equal sign has whitespace, it means we 

239 # previously found it's a typed argument. So, we're using 

240 # that, too. 

241 return prevp.prefix 

242 

243 elif ( 

244 prevp.type == token.STAR 

245 and parent_type(prevp) == syms.star_expr 

246 and parent_type(prevp.parent) in (syms.subscriptlist, syms.tname_star) 

247 ): 

248 # No space between typevar tuples or unpacking them. 

249 return NO 

250 

251 elif prevp.type in VARARGS_SPECIALS: 

252 if is_vararg(prevp, within=VARARGS_PARENTS | UNPACKING_PARENTS): 

253 return NO 

254 

255 elif prevp.type == token.COLON: 

256 if prevp.parent and prevp.parent.type in {syms.subscript, syms.sliceop}: 

257 return SPACE if complex_subscript else NO 

258 

259 elif ( 

260 prevp.parent 

261 and prevp.parent.type == syms.factor 

262 and prevp.type in MATH_OPERATORS 

263 ): 

264 return NO 

265 

266 elif prevp.type == token.AT and p.parent and p.parent.type == syms.decorator: 

267 # no space in decorators 

268 return NO 

269 

270 elif prev.type in OPENING_BRACKETS: 

271 return NO 

272 

273 elif prev.type == token.BANG: 

274 return NO 

275 

276 if p.type in {syms.parameters, syms.arglist}: 

277 # untyped function signatures or calls 

278 if not prev or prev.type != token.COMMA: 

279 return NO 

280 

281 elif p.type == syms.varargslist: 

282 # lambdas 

283 if prev and prev.type != token.COMMA: 

284 return NO 

285 

286 elif p.type == syms.typedargslist: 

287 # typed function signatures 

288 if not prev: 

289 return NO 

290 

291 if t == token.EQUAL: 

292 if prev.type not in TYPED_NAMES: 

293 return NO 

294 

295 elif prev.type == token.EQUAL: 

296 # A bit hacky: if the equal sign has whitespace, it means we 

297 # previously found it's a typed argument. So, we're using that, too. 

298 return prev.prefix 

299 

300 elif prev.type != token.COMMA: 

301 return NO 

302 

303 elif p.type in TYPED_NAMES: 

304 # type names 

305 if not prev: 

306 prevp = preceding_leaf(p) 

307 if not prevp or prevp.type != token.COMMA: 

308 return NO 

309 

310 elif p.type == syms.trailer: 

311 # attributes and calls 

312 if t == token.LPAR or t == token.RPAR: 

313 return NO 

314 

315 if not prev: 

316 if t == token.DOT or t == token.LSQB: 

317 return NO 

318 

319 elif prev.type != token.COMMA: 

320 return NO 

321 

322 elif p.type == syms.argument: 

323 # single argument 

324 if t == token.EQUAL: 

325 return NO 

326 

327 if not prev: 

328 prevp = preceding_leaf(p) 

329 if not prevp or prevp.type == token.LPAR: 

330 return NO 

331 

332 elif prev.type in {token.EQUAL} | VARARGS_SPECIALS: 

333 return NO 

334 

335 elif p.type == syms.decorator: 

336 # decorators 

337 return NO 

338 

339 elif p.type == syms.dotted_name: 

340 if prev: 

341 return NO 

342 

343 prevp = preceding_leaf(p) 

344 if not prevp or prevp.type == token.AT or prevp.type == token.DOT: 

345 return NO 

346 

347 elif p.type == syms.classdef: 

348 if t == token.LPAR: 

349 return NO 

350 

351 if prev and prev.type == token.LPAR: 

352 return NO 

353 

354 elif p.type in {syms.subscript, syms.sliceop}: 

355 # indexing 

356 if not prev: 

357 assert p.parent is not None, "subscripts are always parented" 

358 if p.parent.type == syms.subscriptlist: 

359 return SPACE 

360 

361 return NO 

362 

363 elif t == token.COLONEQUAL or prev.type == token.COLONEQUAL: 

364 return SPACE 

365 

366 elif not complex_subscript: 

367 return NO 

368 

369 elif p.type == syms.atom: 

370 if prev and t == token.DOT: 

371 # dots, but not the first one. 

372 return NO 

373 

374 elif p.type == syms.dictsetmaker: 

375 # dict unpacking 

376 if prev and prev.type == token.DOUBLESTAR: 

377 return NO 

378 

379 elif p.type in {syms.factor, syms.star_expr}: 

380 # unary ops 

381 if not prev: 

382 prevp = preceding_leaf(p) 

383 if not prevp or prevp.type in OPENING_BRACKETS: 

384 return NO 

385 

386 prevp_parent = prevp.parent 

387 assert prevp_parent is not None 

388 if prevp.type == token.COLON and prevp_parent.type in { 

389 syms.subscript, 

390 syms.sliceop, 

391 }: 

392 return NO 

393 

394 elif prevp.type == token.EQUAL and prevp_parent.type == syms.argument: 

395 return NO 

396 

397 elif t in {token.NAME, token.NUMBER, token.STRING}: 

398 return NO 

399 

400 elif p.type == syms.import_from: 

401 if t == token.DOT: 

402 if prev and prev.type == token.DOT: 

403 return NO 

404 

405 elif t == token.NAME: 

406 if v == "import": 

407 return SPACE 

408 

409 if prev and prev.type == token.DOT: 

410 return NO 

411 

412 elif p.type == syms.sliceop: 

413 return NO 

414 

415 elif p.type == syms.except_clause: 

416 if t == token.STAR: 

417 return NO 

418 

419 elif p.type == syms.typeparams: 

420 # PEP 695 type parameter lists hug the preceding name 

421 if t == token.LSQB: 

422 return NO 

423 

424 elif p.type in {syms.typevartuple, syms.paramspec}: 

425 # PEP 695 `*Ts` and `**P` 

426 if prev and prev.type in STARS: 

427 return NO 

428 

429 if Preview.simplify_power_operator_hugging in mode: 

430 # Power operator hugging 

431 if t == token.DOUBLESTAR and is_simple_exponentiation(p): 

432 return NO 

433 prevp = preceding_leaf(leaf) 

434 if prevp and prevp.type == token.DOUBLESTAR: 

435 if prevp.parent and is_simple_exponentiation(prevp.parent): 

436 return NO 

437 

438 return SPACE 

439 

440 

441def make_simple_prefix(nl_count: int, form_feed: bool, empty_line: str = "\n") -> str: 

442 """Generate a normalized prefix string.""" 

443 if form_feed: 

444 return (empty_line * (nl_count - 1)) + "\f" + empty_line 

445 return empty_line * nl_count 

446 

447 

448def preceding_leaf(node: LN | None) -> Leaf | None: 

449 """Return the first leaf that precedes `node`, if any.""" 

450 while node: 

451 res = node.prev_sibling 

452 if res: 

453 if isinstance(res, Leaf): 

454 return res 

455 

456 try: 

457 return list(res.leaves())[-1] 

458 

459 except IndexError: 

460 return None 

461 

462 node = node.parent 

463 return None 

464 

465 

466def prev_siblings_are(node: LN | None, tokens: list[NodeType | None]) -> bool: 

467 """Return if the `node` and its previous siblings match types against the provided 

468 list of tokens; the provided `node` has its type matched against the last element in 

469 the list. `None` can be used as the first element to declare that the start of the 

470 list is anchored at the start of its parent's children.""" 

471 if not tokens: 

472 return True 

473 if tokens[-1] is None: 

474 return node is None 

475 if not node: 

476 return False 

477 if node.type != tokens[-1]: 

478 return False 

479 return prev_siblings_are(node.prev_sibling, tokens[:-1]) 

480 

481 

482def parent_type(node: LN | None) -> NodeType | None: 

483 """ 

484 Returns: 

485 @node.parent.type, if @node is not None and has a parent. 

486 OR 

487 None, otherwise. 

488 """ 

489 if node is None or node.parent is None: 

490 return None 

491 

492 return node.parent.type 

493 

494 

495def child_towards(ancestor: Node, descendant: LN) -> LN | None: 

496 """Return the child of `ancestor` that contains `descendant`.""" 

497 node: LN | None = descendant 

498 while node and node.parent != ancestor: 

499 node = node.parent 

500 return node 

501 

502 

503def replace_child(old_child: LN, new_child: LN) -> None: 

504 """ 

505 Side Effects: 

506 * If @old_child.parent is set, replace @old_child with @new_child in 

507 @old_child's underlying Node structure. 

508 OR 

509 * Otherwise, this function does nothing. 

510 """ 

511 parent = old_child.parent 

512 if not parent: 

513 return 

514 

515 child_idx = old_child.remove() 

516 if child_idx is not None: 

517 parent.insert_child(child_idx, new_child) 

518 

519 

520def container_of(leaf: Leaf) -> LN: 

521 """Return `leaf` or one of its ancestors that is the topmost container of it. 

522 

523 By "container" we mean a node where `leaf` is the very first child. 

524 """ 

525 same_prefix = leaf.prefix 

526 container: LN = leaf 

527 while container: 

528 parent = container.parent 

529 if parent is None: 

530 break 

531 

532 if parent.children[0].prefix != same_prefix: 

533 break 

534 

535 if parent.type == syms.file_input: 

536 break 

537 

538 if parent.prev_sibling is not None and parent.prev_sibling.type in BRACKETS: 

539 break 

540 

541 container = parent 

542 return container 

543 

544 

545def first_leaf_of(node: LN) -> Leaf | None: 

546 """Returns the first leaf of the node tree.""" 

547 if isinstance(node, Leaf): 

548 return node 

549 if node.children: 

550 return first_leaf_of(node.children[0]) 

551 else: 

552 return None 

553 

554 

555def is_arith_like(node: LN) -> bool: 

556 """Whether node is an arithmetic or a binary arithmetic expression""" 

557 return node.type in { 

558 syms.arith_expr, 

559 syms.shift_expr, 

560 syms.xor_expr, 

561 syms.and_expr, 

562 } 

563 

564 

565def is_simple_exponentiation(node: LN) -> bool: 

566 """Whether whitespace around `**` should be removed.""" 

567 

568 def is_simple(node: LN) -> bool: 

569 if isinstance(node, Leaf): 

570 return node.type in (token.NAME, token.NUMBER, token.DOT, token.DOUBLESTAR) 

571 elif node.type == syms.factor: # unary operators 

572 return is_simple(node.children[1]) 

573 else: 

574 return all(is_simple(child) for child in node.children) 

575 

576 return ( 

577 node.type == syms.power 

578 and len(node.children) >= 3 

579 and node.children[-2].type == token.DOUBLESTAR 

580 and is_simple(node) 

581 ) 

582 

583 

584def is_docstring(node: NL) -> bool: 

585 if isinstance(node, Leaf): 

586 if node.type != token.STRING: 

587 return False 

588 

589 prefix = get_string_prefix(node.value) 

590 # Bytes, f-strings and t-strings never evaluate to str, so they are not 

591 # docstrings even in docstring position. 

592 if set(prefix).intersection("bBfFtT"): 

593 return False 

594 

595 if ( 

596 node.parent 

597 and node.parent.type == syms.simple_stmt 

598 and not node.parent.prev_sibling 

599 and node.parent.parent 

600 and node.parent.parent.type == syms.file_input 

601 ): 

602 return True 

603 

604 if prev_siblings_are( 

605 node.parent, [None, token.NEWLINE, token.INDENT, syms.simple_stmt] 

606 ): 

607 return True 

608 

609 # Multiline docstring on the same line as the `def`. 

610 if prev_siblings_are(node.parent, [syms.parameters, token.COLON, syms.simple_stmt]): 

611 # `syms.parameters` is only used in funcdefs and async_funcdefs in the Python 

612 # grammar. We're safe to return True without further checks. 

613 return True 

614 

615 return False 

616 

617 

618def is_empty_tuple(node: LN) -> bool: 

619 """Return True if `node` holds an empty tuple.""" 

620 return ( 

621 node.type == syms.atom 

622 and len(node.children) == 2 

623 and node.children[0].type == token.LPAR 

624 and node.children[1].type == token.RPAR 

625 ) 

626 

627 

628def is_one_tuple(node: LN) -> bool: 

629 """Return True if `node` holds a tuple with one element, with or without parens.""" 

630 if node.type == syms.atom: 

631 gexp = unwrap_singleton_parenthesis(node) 

632 if gexp is None or gexp.type != syms.testlist_gexp: 

633 return False 

634 

635 return len(gexp.children) == 2 and gexp.children[1].type == token.COMMA 

636 

637 return ( 

638 node.type in IMPLICIT_TUPLE 

639 and len(node.children) == 2 

640 and node.children[1].type == token.COMMA 

641 ) 

642 

643 

644def is_one_element_subscript(node: LN) -> bool: 

645 """Return True if `node` is a subscript trailer with one element, like `[x,]`.""" 

646 return ( 

647 node.type == syms.trailer 

648 and node.children[1].type == syms.subscriptlist 

649 and len(node.children[1].children) == 2 

650 and node.children[1].children[1].type == token.COMMA 

651 ) 

652 

653 

654def is_tuple(node: LN) -> bool: 

655 """Return True if `node` holds a tuple.""" 

656 if node.type != syms.atom: 

657 return False 

658 gexp = unwrap_singleton_parenthesis(node) 

659 if gexp is None or gexp.type != syms.testlist_gexp: 

660 return False 

661 

662 return True 

663 

664 

665def is_list(node: LN) -> bool: 

666 """Return True if `node` holds a list or list comprehension.""" 

667 return ( 

668 node.type == syms.atom 

669 and len(node.children) >= 2 

670 and node.children[0].type == token.LSQB 

671 and node.children[-1].type == token.RSQB 

672 ) 

673 

674 

675def is_tuple_containing_walrus(node: LN) -> bool: 

676 """Return True if `node` holds a tuple that contains a walrus operator.""" 

677 if node.type != syms.atom: 

678 return False 

679 gexp = unwrap_singleton_parenthesis(node) 

680 if gexp is None or gexp.type != syms.testlist_gexp: 

681 return False 

682 

683 return any(child.type == syms.namedexpr_test for child in gexp.children) 

684 

685 

686def is_tuple_containing_star(node: LN) -> bool: 

687 """Return True if `node` holds a tuple that contains a star operator.""" 

688 if node.type != syms.atom: 

689 return False 

690 gexp = unwrap_singleton_parenthesis(node) 

691 if gexp is None or gexp.type != syms.testlist_gexp: 

692 return False 

693 

694 return any(child.type == syms.star_expr for child in gexp.children) 

695 

696 

697def is_generator(node: LN) -> bool: 

698 """Return True if `node` holds a generator.""" 

699 if node.type != syms.atom: 

700 return False 

701 gexp = unwrap_singleton_parenthesis(node) 

702 if gexp is None or gexp.type != syms.testlist_gexp: 

703 return False 

704 

705 return any(child.type == syms.old_comp_for for child in gexp.children) 

706 

707 

708def is_one_sequence_between( 

709 opening: Leaf, 

710 closing: Leaf, 

711 leaves: list[Leaf], 

712 brackets: tuple[int, int] = (token.LPAR, token.RPAR), 

713) -> bool: 

714 """Return True if content between `opening` and `closing` is a one-sequence.""" 

715 if (opening.type, closing.type) != brackets: 

716 return False 

717 

718 depth = closing.bracket_depth + 1 

719 # Locate `opening` by scanning inward from both ends at once. Callers pass the 

720 # whole line's leaf list and this runs once per bracket, so a plain forward scan 

721 # from the start costs O(index) and turns quadratic on a long line; meeting in 

722 # the middle bounds each lookup to the nearer end. 

723 _opening_index = -1 

724 left = 0 

725 right = len(leaves) - 1 

726 while left <= right: 

727 if leaves[left] is opening: 

728 _opening_index = left 

729 break 

730 if leaves[right] is opening: 

731 _opening_index = right 

732 break 

733 left += 1 

734 right -= 1 

735 

736 if _opening_index == -1: 

737 return False 

738 

739 commas = 0 

740 _opening_index += 1 

741 for leaf in leaves[_opening_index:]: 

742 if leaf is closing: 

743 break 

744 

745 bracket_depth = leaf.bracket_depth 

746 if bracket_depth == depth and leaf.type == token.COMMA: 

747 commas += 1 

748 if leaf.parent and leaf.parent.type in { 

749 syms.arglist, 

750 syms.typedargslist, 

751 }: 

752 commas += 1 

753 break 

754 

755 return commas < 2 

756 

757 

758def is_walrus_assignment(node: LN) -> bool: 

759 """Return True iff `node` is of the shape ( test := test )""" 

760 inner = unwrap_singleton_parenthesis(node) 

761 return inner is not None and inner.type == syms.namedexpr_test 

762 

763 

764def is_simple_decorator_trailer(node: LN, last: bool = False) -> bool: 

765 """Return True iff `node` is a trailer valid in a simple decorator""" 

766 return node.type == syms.trailer and ( 

767 ( 

768 len(node.children) == 2 

769 and node.children[0].type == token.DOT 

770 and node.children[1].type == token.NAME 

771 ) 

772 # last trailer can be an argument-less parentheses pair 

773 or ( 

774 last 

775 and len(node.children) == 2 

776 and node.children[0].type == token.LPAR 

777 and node.children[1].type == token.RPAR 

778 ) 

779 # last trailer can be arguments 

780 or ( 

781 last 

782 and len(node.children) == 3 

783 and node.children[0].type == token.LPAR 

784 # and node.children[1].type == syms.argument 

785 and node.children[2].type == token.RPAR 

786 ) 

787 ) 

788 

789 

790def is_simple_decorator_expression(node: LN) -> bool: 

791 """Return True iff `node` could be a 'dotted name' decorator 

792 

793 This function takes the node of the 'namedexpr_test' of the new decorator 

794 grammar and test if it would be valid under the old decorator grammar. 

795 

796 The old grammar was: decorator: @ dotted_name [arguments] NEWLINE 

797 The new grammar is : decorator: @ namedexpr_test NEWLINE 

798 """ 

799 if node.type == token.NAME: 

800 return True 

801 if node.type == syms.power: 

802 if node.children: 

803 return ( 

804 node.children[0].type == token.NAME 

805 and all(map(is_simple_decorator_trailer, node.children[1:-1])) 

806 and ( 

807 len(node.children) < 2 

808 or is_simple_decorator_trailer(node.children[-1], last=True) 

809 ) 

810 ) 

811 return False 

812 

813 

814def is_yield(node: LN) -> bool: 

815 """Return True if `node` holds a `yield` or `yield from` expression.""" 

816 if node.type == syms.yield_expr: 

817 return True 

818 

819 if is_name_token(node) and node.value == "yield": 

820 return True 

821 

822 if node.type != syms.atom: 

823 return False 

824 

825 if len(node.children) != 3: 

826 return False 

827 

828 lpar, expr, rpar = node.children 

829 if lpar.type == token.LPAR and rpar.type == token.RPAR: 

830 return is_yield(expr) 

831 

832 return False 

833 

834 

835def is_vararg(leaf: Leaf, within: set[NodeType]) -> bool: 

836 """Return True if `leaf` is a star or double star in a vararg or kwarg. 

837 

838 If `within` includes VARARGS_PARENTS, this applies to function signatures. 

839 If `within` includes UNPACKING_PARENTS, it applies to right hand-side 

840 extended iterable unpacking (PEP 3132) and additional unpacking 

841 generalizations (PEP 448). 

842 """ 

843 if leaf.type not in VARARGS_SPECIALS or not leaf.parent: 

844 return False 

845 

846 p = leaf.parent 

847 if p.type == syms.star_expr: 

848 # Star expressions are also used as assignment targets in extended 

849 # iterable unpacking (PEP 3132). See what its parent is instead. 

850 if not p.parent: 

851 return False 

852 

853 p = p.parent 

854 

855 return p.type in within 

856 

857 

858def is_fstring(node: Node) -> bool: 

859 """Return True if the node is an f-string""" 

860 return node.type == syms.fstring 

861 

862 

863def fstring_tstring_to_string(node: Node) -> Leaf: 

864 """Converts an fstring or tstring node back to a string node.""" 

865 string_without_prefix = str(node)[len(node.prefix) :] 

866 string_leaf = Leaf(token.STRING, string_without_prefix, prefix=node.prefix) 

867 string_leaf.lineno = node.get_lineno() or 0 

868 return string_leaf 

869 

870 

871def is_multiline_string(node: LN) -> bool: 

872 """Return True if `leaf` is a multiline string that actually spans many lines.""" 

873 if isinstance(node, Node) and is_fstring(node): 

874 leaf = fstring_tstring_to_string(node) 

875 elif isinstance(node, Leaf): 

876 leaf = node 

877 else: 

878 return False 

879 

880 return has_triple_quotes(leaf.value) and "\n" in leaf.value 

881 

882 

883def is_parent_function_or_class(node: Node) -> bool: 

884 assert node.type in {syms.suite, syms.simple_stmt} 

885 assert node.parent is not None 

886 # Note this works for suites / simple_stmts in async def as well 

887 return node.parent.type in {syms.funcdef, syms.classdef} 

888 

889 

890def is_stub_suite(node: Node) -> bool: 

891 """Return True if `node` is a suite with a stub body.""" 

892 if node.parent is not None and not is_parent_function_or_class(node): 

893 return False 

894 

895 # If there is a comment, we want to keep it. 

896 if node.prefix.strip(): 

897 return False 

898 

899 if ( 

900 len(node.children) != 4 

901 or node.children[0].type != token.NEWLINE 

902 or node.children[1].type != token.INDENT 

903 or node.children[3].type != token.DEDENT 

904 ): 

905 return False 

906 

907 if node.children[3].prefix.strip(): 

908 return False 

909 

910 return is_stub_body(node.children[2]) 

911 

912 

913def is_stub_body(node: LN) -> bool: 

914 """Return True if `node` is a simple statement containing an ellipsis.""" 

915 if not isinstance(node, Node) or node.type != syms.simple_stmt: 

916 return False 

917 

918 if len(node.children) != 2: 

919 return False 

920 

921 child = node.children[0] 

922 return ( 

923 not child.prefix.strip() 

924 and child.type == syms.atom 

925 and len(child.children) == 3 

926 and all(leaf == Leaf(token.DOT, ".") for leaf in child.children) 

927 ) 

928 

929 

930def is_atom_with_invisible_parens(node: LN) -> bool: 

931 """Given a `LN`, determines whether it's an atom `node` with invisible 

932 parens. Useful in dedupe-ing and normalizing parens. 

933 """ 

934 if isinstance(node, Leaf) or node.type != syms.atom: 

935 return False 

936 

937 first, last = node.children[0], node.children[-1] 

938 return ( 

939 isinstance(first, Leaf) 

940 and first.type == token.LPAR 

941 and first.value == "" 

942 and isinstance(last, Leaf) 

943 and last.type == token.RPAR 

944 and last.value == "" 

945 ) 

946 

947 

948def is_empty_par(leaf: Leaf) -> bool: 

949 return is_empty_lpar(leaf) or is_empty_rpar(leaf) 

950 

951 

952def is_empty_lpar(leaf: Leaf) -> bool: 

953 return leaf.type == token.LPAR and leaf.value == "" 

954 

955 

956def is_empty_rpar(leaf: Leaf) -> bool: 

957 return leaf.type == token.RPAR and leaf.value == "" 

958 

959 

960def is_import(leaf: Leaf) -> bool: 

961 """Return True if the given leaf starts an import statement.""" 

962 p = leaf.parent 

963 t = leaf.type 

964 v = leaf.value 

965 return bool( 

966 (t == token.LAZY and p and p.type == syms.lazy_import) 

967 or ( 

968 t == token.NAME 

969 and ( 

970 (v == "import" and p and p.type == syms.import_name) 

971 or (v == "from" and p and p.type == syms.import_from) 

972 ) 

973 ) 

974 ) 

975 

976 

977def is_with_or_async_with_stmt(leaf: Leaf) -> bool: 

978 """Return True if the given leaf starts a with or async with statement.""" 

979 return bool( 

980 leaf.type == token.NAME 

981 and leaf.value == "with" 

982 and leaf.parent 

983 and leaf.parent.type == syms.with_stmt 

984 ) or bool( 

985 leaf.type == token.ASYNC 

986 and leaf.next_sibling 

987 and leaf.next_sibling.type == syms.with_stmt 

988 ) 

989 

990 

991def is_async_stmt_or_funcdef(leaf: Leaf) -> bool: 

992 """Return True if the given leaf starts an async def/for/with statement. 

993 

994 Note that `async def` can be either an `async_stmt` or `async_funcdef`, 

995 the latter is used when it has decorators. 

996 """ 

997 return bool( 

998 leaf.type == token.ASYNC 

999 and leaf.parent 

1000 and leaf.parent.type in {syms.async_stmt, syms.async_funcdef} 

1001 ) 

1002 

1003 

1004def is_type_comment(leaf: Leaf, mode: Mode) -> bool: 

1005 """Return True if the given leaf is a type comment. This function should only 

1006 be used for general type comments (excluding ignore annotations, which should 

1007 use `is_type_ignore_comment`). Note that general type comments are no longer 

1008 used in modern version of Python, this function may be deprecated in the future.""" 

1009 t = leaf.type 

1010 v = leaf.value 

1011 return t in {token.COMMENT, STANDALONE_COMMENT} and is_type_comment_string(v, mode) 

1012 

1013 

1014def is_type_comment_string(value: str, mode: Mode) -> bool: 

1015 return value.startswith("#") and value[1:].lstrip().startswith("type:") 

1016 

1017 

1018def is_type_ignore_comment(leaf: Leaf, mode: Mode) -> bool: 

1019 """Return True if the given leaf is a type comment with ignore annotation.""" 

1020 t = leaf.type 

1021 v = leaf.value 

1022 return t in {token.COMMENT, STANDALONE_COMMENT} and is_type_ignore_comment_string( 

1023 v, mode 

1024 ) 

1025 

1026 

1027def is_type_ignore_comment_string(value: str, mode: Mode) -> bool: 

1028 """Return True if the given string match with type comment with 

1029 ignore annotation.""" 

1030 return is_type_comment_string(value, mode) and value.split(":", 1)[ 

1031 1 

1032 ].lstrip().startswith("ignore") 

1033 

1034 

1035def wrap_in_parentheses( 

1036 parent: Node, child: LN, *, visible: bool = True, index: int | None = None 

1037) -> None: 

1038 """Wrap `child` in parentheses. 

1039 

1040 This replaces `child` with an atom holding the parentheses and the old 

1041 child. That requires moving the prefix. 

1042 

1043 If `visible` is False, the leaves will be valueless (and thus invisible). 

1044 

1045 When the caller already knows `child`'s position in `parent.children` it 

1046 can pass `index` so the child is swapped in place. The default path locates 

1047 `child` with `Base.remove`, which scans and rewrites the whole child list; 

1048 that is O(len(parent.children)) per call and turns quadratic when a caller 

1049 wraps every child of a large node (e.g. each value of a big dict literal). 

1050 """ 

1051 lpar = Leaf(token.LPAR, "(" if visible else "") 

1052 rpar = Leaf(token.RPAR, ")" if visible else "") 

1053 prefix = child.prefix 

1054 child.prefix = "" 

1055 if index is None: 

1056 index = child.remove() or 0 

1057 new_child = Node(syms.atom, [lpar, child, rpar]) 

1058 new_child.prefix = prefix 

1059 parent.insert_child(index, new_child) 

1060 else: 

1061 # Detach the child pointer first so the atom can adopt it, then swap it 

1062 # in place. set_child resets the old child's parent, so reattach it to 

1063 # the new atom afterwards. 

1064 child.parent = None 

1065 new_child = Node(syms.atom, [lpar, child, rpar]) 

1066 new_child.prefix = prefix 

1067 parent.set_child(index, new_child) 

1068 child.parent = new_child 

1069 

1070 

1071def unwrap_singleton_parenthesis(node: LN) -> LN | None: 

1072 """Returns `wrapped` if `node` is of the shape ( wrapped ). 

1073 

1074 Parenthesis can be optional. Returns None otherwise""" 

1075 if len(node.children) != 3: 

1076 return None 

1077 

1078 lpar, wrapped, rpar = node.children 

1079 if not (lpar.type == token.LPAR and rpar.type == token.RPAR): 

1080 return None 

1081 

1082 return wrapped 

1083 

1084 

1085def ensure_visible(leaf: Leaf) -> None: 

1086 """Make sure parentheses are visible. 

1087 

1088 They could be invisible as part of some statements (see 

1089 :func:`normalize_invisible_parens` and :func:`visit_import_from`). 

1090 """ 

1091 if leaf.type == token.LPAR: 

1092 leaf.value = "(" 

1093 elif leaf.type == token.RPAR: 

1094 leaf.value = ")" 

1095 

1096 

1097def is_name_token(nl: NL) -> TypeGuard[Leaf]: 

1098 return nl.type == token.NAME 

1099 

1100 

1101def is_lpar_token(nl: NL) -> TypeGuard[Leaf]: 

1102 return nl.type == token.LPAR 

1103 

1104 

1105def is_rpar_token(nl: NL) -> TypeGuard[Leaf]: 

1106 return nl.type == token.RPAR 

1107 

1108 

1109def is_number_token(nl: NL) -> TypeGuard[Leaf]: 

1110 return nl.type == token.NUMBER 

1111 

1112 

1113def get_annotation_type(leaf: Leaf) -> Literal["return", "param", None]: 

1114 """Returns the type of annotation this leaf is part of, if any.""" 

1115 ancestor = leaf.parent 

1116 while ancestor is not None: 

1117 if ancestor.prev_sibling and ancestor.prev_sibling.type == token.RARROW: 

1118 return "return" 

1119 if ancestor.parent and ancestor.parent.type == syms.tname: 

1120 return "param" 

1121 ancestor = ancestor.parent 

1122 return None 

1123 

1124 

1125def is_part_of_annotation(leaf: Leaf) -> bool: 

1126 """Returns whether this leaf is part of a type annotation.""" 

1127 assert leaf.parent is not None 

1128 return get_annotation_type(leaf) is not None 

1129 

1130 

1131def first_leaf(node: LN) -> Leaf | None: 

1132 """Returns the first leaf of the ancestor node.""" 

1133 if isinstance(node, Leaf): 

1134 return node 

1135 elif not node.children: 

1136 return None 

1137 else: 

1138 return first_leaf(node.children[0]) 

1139 

1140 

1141def last_leaf(node: LN) -> Leaf | None: 

1142 """Returns the last leaf of the ancestor node.""" 

1143 if isinstance(node, Leaf): 

1144 return node 

1145 elif not node.children: 

1146 return None 

1147 else: 

1148 return last_leaf(node.children[-1]) 

1149 

1150 

1151def furthest_ancestor_with_last_leaf(leaf: Leaf) -> LN: 

1152 """Returns the furthest ancestor that has this leaf node as the last leaf.""" 

1153 node: LN = leaf 

1154 while node.parent and node.parent.children and node is node.parent.children[-1]: 

1155 node = node.parent 

1156 return node 

1157 

1158 

1159def has_sibling_with_type(node: LN, type: int) -> bool: 

1160 # Check previous siblings 

1161 sibling = node.prev_sibling 

1162 while sibling is not None: 

1163 if sibling.type == type: 

1164 return True 

1165 sibling = sibling.prev_sibling 

1166 

1167 # Check next siblings 

1168 sibling = node.next_sibling 

1169 while sibling is not None: 

1170 if sibling.type == type: 

1171 return True 

1172 sibling = sibling.next_sibling 

1173 

1174 return False