Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/black/lines.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

816 statements  

1import itertools 

2import math 

3from collections.abc import Callable, Iterator, Sequence 

4from dataclasses import dataclass, field 

5from typing import NamedTuple, Optional, TypeVar, Union, cast 

6 

7from black.brackets import COMMA_PRIORITY, DOT_PRIORITY, BracketTracker 

8from black.mode import Mode, Preview 

9from black.nodes import ( 

10 BRACKETS, 

11 CLOSING_BRACKETS, 

12 OPENING_BRACKETS, 

13 STANDALONE_COMMENT, 

14 TEST_DESCENDANTS, 

15 child_towards, 

16 first_leaf, 

17 is_docstring, 

18 is_import, 

19 is_multiline_string, 

20 is_one_sequence_between, 

21 is_one_tuple, 

22 is_type_comment, 

23 is_type_ignore_comment, 

24 is_with_or_async_with_stmt, 

25 last_leaf, 

26 make_simple_prefix, 

27 replace_child, 

28 syms, 

29 whitespace, 

30) 

31from black.strings import str_width 

32from blib2to3.pgen2 import token 

33from blib2to3.pytree import Leaf, Node 

34 

35# types 

36T = TypeVar("T") 

37Index = int 

38LeafID = int 

39LN = Union[Leaf, Node] 

40 

41 

42@dataclass 

43class Line: 

44 """Holds leaves and comments. Can be printed with `str(line)`.""" 

45 

46 mode: Mode = field(repr=False) 

47 depth: int = 0 

48 leaves: list[Leaf] = field(default_factory=list) 

49 # keys ordered like `leaves` 

50 comments: dict[LeafID, list[Leaf]] = field(default_factory=dict) 

51 bracket_tracker: BracketTracker = field(default_factory=BracketTracker) 

52 inside_brackets: bool = False 

53 should_split_rhs: bool = False 

54 magic_trailing_comma: Leaf | None = None 

55 _complex_subscript_cache: dict[LeafID, bool] = field( 

56 default_factory=dict, repr=False 

57 ) 

58 

59 def append( 

60 self, leaf: Leaf, preformatted: bool = False, track_bracket: bool = False 

61 ) -> None: 

62 """Add a new `leaf` to the end of the line. 

63 

64 Unless `preformatted` is True, the `leaf` will receive a new consistent 

65 whitespace prefix and metadata applied by :class:`BracketTracker`. 

66 Trailing commas are maybe removed, unpacked for loop variables are 

67 demoted from being delimiters. 

68 

69 Inline comments are put aside. 

70 """ 

71 has_value = ( 

72 leaf.type in BRACKETS 

73 # empty fstring and tstring middles must not be truncated 

74 or leaf.type in (token.FSTRING_MIDDLE, token.TSTRING_MIDDLE) 

75 or bool(leaf.value.strip()) 

76 ) 

77 if not has_value: 

78 return 

79 

80 if leaf.type == token.COLON and self.is_class_paren_empty: 

81 del self.leaves[-2:] 

82 if self.leaves and not preformatted: 

83 # Note: at this point leaf.prefix should be empty except for 

84 # imports, for which we only preserve newlines. 

85 leaf.prefix += whitespace( 

86 leaf, 

87 complex_subscript=self.is_complex_subscript(leaf), 

88 mode=self.mode, 

89 ) 

90 if self.inside_brackets or not preformatted or track_bracket: 

91 self.bracket_tracker.mark(leaf) 

92 if self.mode.magic_trailing_comma: 

93 if self.has_magic_trailing_comma(leaf): 

94 self.magic_trailing_comma = leaf 

95 elif self.has_magic_trailing_comma(leaf) and not ( 

96 # A one-element tuple's trailing comma is syntactically required, 

97 # not magic, so it must never be removed. This is normally caught 

98 # by has_magic_trailing_comma, but that check misses the tuple 

99 # when its opening bracket was split onto an earlier line (for 

100 # example by a standalone comment inside the tuple), so verify 

101 # against the tree here before dropping the comma. 

102 leaf.parent is not None 

103 and is_one_tuple(leaf.parent) 

104 ): 

105 self.remove_trailing_comma() 

106 if not self.append_comment(leaf): 

107 self.leaves.append(leaf) 

108 

109 def append_safe(self, leaf: Leaf, preformatted: bool = False) -> None: 

110 """Like :func:`append()` but disallow invalid standalone comment structure. 

111 

112 Raises ValueError when any `leaf` is appended after a standalone comment 

113 or when a standalone comment is not the first leaf on the line. 

114 """ 

115 if ( 

116 self.bracket_tracker.depth == 0 

117 or self.bracket_tracker.any_open_for_or_lambda() 

118 ): 

119 if self.is_comment: 

120 raise ValueError("cannot append to standalone comments") 

121 

122 if self.leaves and leaf.type == STANDALONE_COMMENT: 

123 raise ValueError( 

124 "cannot append standalone comments to a populated line" 

125 ) 

126 

127 self.append(leaf, preformatted=preformatted) 

128 

129 @property 

130 def is_comment(self) -> bool: 

131 """Is this line a standalone comment?""" 

132 return len(self.leaves) == 1 and self.leaves[0].type == STANDALONE_COMMENT 

133 

134 @property 

135 def is_decorator(self) -> bool: 

136 """Is this line a decorator?""" 

137 return bool(self) and self.leaves[0].type == token.AT 

138 

139 @property 

140 def is_import(self) -> bool: 

141 """Is this an import line?""" 

142 return bool(self) and is_import(self.leaves[0]) 

143 

144 @property 

145 def is_with_or_async_with_stmt(self) -> bool: 

146 """Is this a with_stmt line?""" 

147 return bool(self) and is_with_or_async_with_stmt(self.leaves[0]) 

148 

149 @property 

150 def is_class(self) -> bool: 

151 """Is this line a class definition?""" 

152 return ( 

153 bool(self) 

154 and self.leaves[0].type == token.NAME 

155 and self.leaves[0].value == "class" 

156 ) 

157 

158 @property 

159 def is_stub_class(self) -> bool: 

160 """Is this line a class definition with a body consisting only of "..."?""" 

161 return self.is_class and self.leaves[-3:] == [ 

162 Leaf(token.DOT, ".") for _ in range(3) 

163 ] 

164 

165 @property 

166 def is_def(self) -> bool: 

167 """Is this a function definition? (Also returns True for async defs.)""" 

168 try: 

169 first_leaf = self.leaves[0] 

170 except IndexError: 

171 return False 

172 

173 try: 

174 second_leaf: Leaf | None = self.leaves[1] 

175 except IndexError: 

176 second_leaf = None 

177 return (first_leaf.type == token.NAME and first_leaf.value == "def") or ( 

178 first_leaf.type == token.ASYNC 

179 and second_leaf is not None 

180 and second_leaf.type == token.NAME 

181 and second_leaf.value == "def" 

182 ) 

183 

184 @property 

185 def is_stub_def(self) -> bool: 

186 """Is this line a function definition with a body consisting only of "..."?""" 

187 return self.is_def and self.leaves[-4:] == [Leaf(token.COLON, ":")] + [ 

188 Leaf(token.DOT, ".") for _ in range(3) 

189 ] 

190 

191 @property 

192 def is_class_paren_empty(self) -> bool: 

193 """Is this a class with no base classes but using parentheses? 

194 

195 Those are unnecessary and should be removed. 

196 """ 

197 return ( 

198 bool(self) 

199 and len(self.leaves) == 4 

200 and self.is_class 

201 and self.leaves[2].type == token.LPAR 

202 and self.leaves[2].value == "(" 

203 and self.leaves[3].type == token.RPAR 

204 and self.leaves[3].value == ")" 

205 ) 

206 

207 @property 

208 def _is_triple_quoted_string(self) -> bool: 

209 """Is the line a triple quoted string?""" 

210 if not self or self.leaves[0].type != token.STRING: 

211 return False 

212 value = self.leaves[0].value 

213 if value.startswith(('"""', "'''")): 

214 return True 

215 if value.startswith(("r'''", 'r"""', "R'''", 'R"""')): 

216 return True 

217 return False 

218 

219 @property 

220 def is_docstring(self) -> bool: 

221 """Is the line a docstring?""" 

222 return bool(self) and is_docstring(self.leaves[0]) 

223 

224 @property 

225 def is_chained_assignment(self) -> bool: 

226 """Is the line a chained assignment""" 

227 return [leaf.type for leaf in self.leaves].count(token.EQUAL) > 1 

228 

229 @property 

230 def opens_block(self) -> bool: 

231 """Does this line open a new level of indentation.""" 

232 if len(self.leaves) == 0: 

233 return False 

234 return self.leaves[-1].type == token.COLON 

235 

236 def is_fmt_pass_converted( 

237 self, *, first_leaf_matches: Callable[[Leaf], bool] | None = None 

238 ) -> bool: 

239 """Is this line converted from fmt off/skip code? 

240 

241 If first_leaf_matches is not None, it only returns True if the first 

242 leaf of converted code matches. 

243 """ 

244 if len(self.leaves) != 1: 

245 return False 

246 leaf = self.leaves[0] 

247 if ( 

248 leaf.type != STANDALONE_COMMENT 

249 or leaf.fmt_pass_converted_first_leaf is None 

250 ): 

251 return False 

252 return first_leaf_matches is None or first_leaf_matches( 

253 leaf.fmt_pass_converted_first_leaf 

254 ) 

255 

256 def contains_standalone_comments(self) -> bool: 

257 """If so, needs to be split before emitting.""" 

258 for leaf in self.leaves: 

259 if leaf.type == STANDALONE_COMMENT: 

260 return True 

261 

262 return False 

263 

264 def contains_implicit_multiline_string_with_comments(self) -> bool: 

265 """Check if we have an implicit multiline string with comments on the line""" 

266 for leaf_type, leaf_group_iterator in itertools.groupby( 

267 self.leaves, lambda leaf: leaf.type 

268 ): 

269 if leaf_type != token.STRING: 

270 continue 

271 leaf_list = list(leaf_group_iterator) 

272 if len(leaf_list) == 1: 

273 continue 

274 for leaf in leaf_list: 

275 if self.comments_after(leaf): 

276 return True 

277 return False 

278 

279 def contains_uncollapsable_type_comments(self) -> bool: 

280 ignored_ids = set() 

281 try: 

282 last_leaf = self.leaves[-1] 

283 ignored_ids.add(id(last_leaf)) 

284 if last_leaf.type == token.COMMA or ( 

285 last_leaf.type == token.RPAR and not last_leaf.value 

286 ): 

287 # When trailing commas or optional parens are inserted by Black for 

288 # consistency, comments after the previous last element are not moved 

289 # (they don't have to, rendering will still be correct). So we ignore 

290 # trailing commas and invisible. 

291 last_leaf = self.leaves[-2] 

292 ignored_ids.add(id(last_leaf)) 

293 except IndexError: 

294 return False 

295 

296 # A type comment is uncollapsable if it is attached to a leaf 

297 # that isn't at the end of the line (since that could cause it 

298 # to get associated to a different argument) or if there are 

299 # comments before it (since that could cause it to get hidden 

300 # behind a comment. 

301 comment_seen = False 

302 for leaf_id, comments in self.comments.items(): 

303 for comment in comments: 

304 if is_type_comment(comment, mode=self.mode): 

305 if comment_seen or ( 

306 not is_type_ignore_comment(comment, mode=self.mode) 

307 and leaf_id not in ignored_ids 

308 ): 

309 return True 

310 

311 comment_seen = True 

312 

313 return False 

314 

315 def contains_unsplittable_type_ignore(self) -> bool: 

316 if not self.leaves: 

317 return False 

318 

319 # If a 'type: ignore' is attached to the end of a line, we 

320 # can't split the line, because we can't know which of the 

321 # subexpressions the ignore was meant to apply to. 

322 # 

323 # We only want this to apply to actual physical lines from the 

324 # original source, though: we don't want the presence of a 

325 # 'type: ignore' at the end of a multiline expression to 

326 # justify pushing it all onto one line. Thus we 

327 # (unfortunately) need to check the actual source lines and 

328 # only report an unsplittable 'type: ignore' if this line was 

329 # one line in the original code. 

330 

331 # Grab the first and last line numbers, skipping generated leaves 

332 first_line = next((leaf.lineno for leaf in self.leaves if leaf.lineno != 0), 0) 

333 last_line = next( 

334 (leaf.lineno for leaf in reversed(self.leaves) if leaf.lineno != 0), 0 

335 ) 

336 

337 if first_line == last_line: 

338 # We look at the last two leaves since a comma or an 

339 # invisible paren could have been added at the end of the 

340 # line. 

341 for node in self.leaves[-2:]: 

342 for comment in self.comments.get(id(node), []): 

343 if is_type_ignore_comment(comment, mode=self.mode): 

344 return True 

345 

346 return False 

347 

348 def contains_multiline_strings(self) -> bool: 

349 return any(is_multiline_string(leaf) for leaf in self.leaves) 

350 

351 def has_magic_trailing_comma(self, closing: Leaf) -> bool: 

352 """Return True if we have a magic trailing comma, that is when: 

353 - there's a trailing comma here 

354 - it's not from single-element square bracket indexing 

355 - it's not a one-tuple 

356 """ 

357 if not ( 

358 closing.type in CLOSING_BRACKETS 

359 and self.leaves 

360 and self.leaves[-1].type == token.COMMA 

361 ): 

362 return False 

363 

364 if closing.type == token.RBRACE: 

365 return True 

366 

367 if closing.type == token.RSQB: 

368 if ( 

369 closing.parent is not None 

370 and closing.parent.type == syms.trailer 

371 and closing.opening_bracket is not None 

372 and is_one_sequence_between( 

373 closing.opening_bracket, 

374 closing, 

375 self.leaves, 

376 brackets=(token.LSQB, token.RSQB), 

377 ) 

378 ): 

379 assert closing.prev_sibling is not None 

380 assert closing.prev_sibling.type == syms.subscriptlist 

381 return False 

382 

383 return True 

384 

385 if self.is_import: 

386 return True 

387 

388 if closing.opening_bracket is not None and not is_one_sequence_between( 

389 closing.opening_bracket, closing, self.leaves 

390 ): 

391 return True 

392 

393 return False 

394 

395 def append_comment(self, comment: Leaf) -> bool: 

396 """Add an inline or standalone comment to the line.""" 

397 if ( 

398 comment.type == STANDALONE_COMMENT 

399 and self.bracket_tracker.any_open_brackets() 

400 ): 

401 comment.prefix = "" 

402 return False 

403 

404 if comment.type != token.COMMENT: 

405 return False 

406 

407 if not self.leaves: 

408 comment.type = STANDALONE_COMMENT 

409 comment.prefix = "" 

410 return False 

411 

412 last_leaf = self.leaves[-1] 

413 if ( 

414 last_leaf.type == token.RPAR 

415 and not last_leaf.value 

416 and last_leaf.parent 

417 and len(list(last_leaf.parent.leaves())) <= 3 

418 and not is_type_comment(comment, mode=self.mode) 

419 ): 

420 # Comments on an optional parens wrapping a single leaf should belong to 

421 # the wrapped node except if it's a type comment. Pinning the comment like 

422 # this avoids unstable formatting caused by comment migration. 

423 if len(self.leaves) < 2: 

424 comment.type = STANDALONE_COMMENT 

425 comment.prefix = "" 

426 return False 

427 

428 last_leaf = self.leaves[-2] 

429 self.comments.setdefault(id(last_leaf), []).append(comment) 

430 return True 

431 

432 def comments_after(self, leaf: Leaf) -> list[Leaf]: 

433 """Generate comments that should appear directly after `leaf`.""" 

434 return self.comments.get(id(leaf), []) 

435 

436 def remove_trailing_comma(self) -> None: 

437 """Remove the trailing comma and moves the comments attached to it.""" 

438 trailing_comma = self.leaves.pop() 

439 trailing_comma_comments = self.comments.pop(id(trailing_comma), []) 

440 self.comments.setdefault(id(self.leaves[-1]), []).extend( 

441 trailing_comma_comments 

442 ) 

443 

444 def is_complex_subscript(self, leaf: Leaf) -> bool: 

445 """Return True iff `leaf` is part of a slice with non-trivial exprs.""" 

446 open_lsqb = self.bracket_tracker.get_open_lsqb() 

447 if open_lsqb is None: 

448 return False 

449 

450 subscript_start = open_lsqb.next_sibling 

451 

452 if isinstance(subscript_start, Node): 

453 if subscript_start.type == syms.listmaker: 

454 return False 

455 

456 if subscript_start.type == syms.subscriptlist: 

457 subscript_start = child_towards(subscript_start, leaf) 

458 

459 if subscript_start is None: 

460 return False 

461 

462 # The pre_order walk only depends on subscript_start, which is stable 

463 # while a line is built, so cache it per node. Without this, appending 

464 # every leaf of a large bracketed expression that holds no TEST_DESCENDANTS 

465 # node (e.g. a long run of implicitly concatenated strings) re-walks the 

466 # whole subtree each time, which is quadratic. 

467 key = id(subscript_start) 

468 cached = self._complex_subscript_cache.get(key) 

469 if cached is None: 

470 cached = any( 

471 n.type in TEST_DESCENDANTS for n in subscript_start.pre_order() 

472 ) 

473 self._complex_subscript_cache[key] = cached 

474 return cached 

475 

476 def enumerate_with_length( 

477 self, is_reversed: bool = False 

478 ) -> Iterator[tuple[Index, Leaf, int]]: 

479 """Return an enumeration of leaves with their length. 

480 

481 Stops prematurely on multiline strings and standalone comments. 

482 """ 

483 op = cast( 

484 Callable[[Sequence[Leaf]], Iterator[tuple[Index, Leaf]]], 

485 enumerate_reversed if is_reversed else enumerate, 

486 ) 

487 for index, leaf in op(self.leaves): 

488 length = len(leaf.prefix) + len(leaf.value) 

489 if "\n" in leaf.value: 

490 return # Multiline strings, we can't continue. 

491 

492 for comment in self.comments_after(leaf): 

493 length += len(comment.value) 

494 

495 yield index, leaf, length 

496 

497 def clone(self) -> "Line": 

498 return Line( 

499 mode=self.mode, 

500 depth=self.depth, 

501 inside_brackets=self.inside_brackets, 

502 should_split_rhs=self.should_split_rhs, 

503 magic_trailing_comma=self.magic_trailing_comma, 

504 ) 

505 

506 def __str__(self) -> str: 

507 """Render the line.""" 

508 if not self: 

509 return "\n" 

510 

511 indent = " " * self.depth 

512 leaves = iter(self.leaves) 

513 first = next(leaves) 

514 res = f"{first.prefix}{indent}{first.value}" 

515 res += "".join(str(leaf) for leaf in leaves) 

516 comments_iter = itertools.chain.from_iterable(self.comments.values()) 

517 comments = [str(comment) for comment in comments_iter] 

518 res += "".join(comments) 

519 

520 return res + "\n" 

521 

522 def __bool__(self) -> bool: 

523 """Return True if the line has leaves or comments.""" 

524 return bool(self.leaves or self.comments) 

525 

526 

527@dataclass 

528class RHSResult: 

529 """Intermediate split result from a right hand split.""" 

530 

531 head: Line 

532 body: Line 

533 tail: Line 

534 opening_bracket: Leaf 

535 closing_bracket: Leaf 

536 

537 

538@dataclass 

539class LinesBlock: 

540 """Class that holds information about a block of formatted lines. 

541 

542 This is introduced so that the EmptyLineTracker can look behind the standalone 

543 comments and adjust their empty lines for class or def lines. 

544 """ 

545 

546 mode: Mode 

547 previous_block: Optional["LinesBlock"] 

548 original_line: Line 

549 before: int = 0 

550 content_lines: list[str] = field(default_factory=list) 

551 after: int = 0 

552 form_feed: bool = False 

553 

554 def all_lines(self) -> list[str]: 

555 empty_line = str(Line(mode=self.mode)) 

556 prefix = make_simple_prefix(self.before, self.form_feed, empty_line) 

557 return [prefix] + self.content_lines + [empty_line * self.after] 

558 

559 

560_WHITESPACE_TOKENS = ( 

561 token.NEWLINE, 

562 token.NL, 

563 token.INDENT, 

564 token.DEDENT, 

565 token.COMMENT, 

566 token.ENDMARKER, 

567) 

568 

569 

570class _DecoratedFuncInfo(NamedTuple): 

571 """Tracks the most recently seen decorated function for overload grouping.""" 

572 

573 name: str 

574 depth: int 

575 is_multi: bool 

576 

577 

578@dataclass 

579class EmptyLineTracker: 

580 """Provides a stateful method that returns the number of potential extra 

581 empty lines needed before and after the currently processed line. 

582 

583 Note: this tracker works on lines that haven't been split yet. It assumes 

584 the prefix of the first leaf consists of optional newlines. Those newlines 

585 are consumed by `maybe_empty_lines()` and included in the computation. 

586 """ 

587 

588 mode: Mode 

589 previous_line: Line | None = None 

590 previous_block: LinesBlock | None = None 

591 previous_defs: list[Line] = field(default_factory=list) 

592 semantic_leading_comment: LinesBlock | None = None 

593 _pyi_previous_decorated_func: _DecoratedFuncInfo | None = None 

594 

595 @staticmethod 

596 def _get_funcdef_name(node: Node | Leaf) -> str | None: 

597 """Extract the function name from a funcdef or async_funcdef node.""" 

598 funcdef: Node | Leaf = node 

599 if isinstance(node, Node) and node.type == syms.async_funcdef: 

600 for sub in node.children: 

601 if sub.type == syms.funcdef: 

602 funcdef = sub 

603 break 

604 if not isinstance(funcdef, Node) or funcdef.type != syms.funcdef: 

605 return None 

606 # Grammar: funcdef = 'def' NAME parameters ':' ... 

607 name_node = funcdef.children[1] 

608 assert isinstance(name_node, Leaf) 

609 return name_node.value 

610 

611 @staticmethod 

612 def _decorated_node_has_func_named(node: Node, name: str) -> bool: 

613 """Check if a ``decorated`` node contains a function with the given name.""" 

614 return any( 

615 sub.type in (syms.funcdef, syms.async_funcdef) 

616 and EmptyLineTracker._get_funcdef_name(sub) == name 

617 for sub in node.children 

618 ) 

619 

620 @staticmethod 

621 def _find_adjacent_decorated( 

622 node: Node | Leaf, *, reverse: bool = False 

623 ) -> Node | None: 

624 """Walk siblings skipping whitespace tokens, returning the first 

625 ``decorated`` node found or ``None``.""" 

626 sibling = node.prev_sibling if reverse else node.next_sibling 

627 while sibling is not None: 

628 if sibling.type == syms.decorated: 

629 assert isinstance(sibling, Node) 

630 return sibling 

631 elif sibling.type in _WHITESPACE_TOKENS: 

632 sibling = sibling.prev_sibling if reverse else sibling.next_sibling 

633 else: 

634 return None 

635 return None 

636 

637 @staticmethod 

638 def _get_suite_first_decorated_funcname(suite: Node) -> str | None: 

639 """Return the function name of the first decorated function in a suite.""" 

640 for child in suite.children: 

641 if isinstance(child, Node) and child.type == syms.decorated: 

642 for sub in child.children: 

643 if sub.type in (syms.funcdef, syms.async_funcdef): 

644 return EmptyLineTracker._get_funcdef_name(sub) 

645 break 

646 if child.type not in (token.NEWLINE, token.NL, token.INDENT, token.DEDENT): 

647 break 

648 return None 

649 

650 @staticmethod 

651 def _if_stmt_branch_has_func_named( 

652 if_stmt: Node, exclude_suite: Node, name: str 

653 ) -> bool: 

654 """Check if any branch of an ``if_stmt`` (other than *exclude_suite*) 

655 contains a decorated function with the given *name*.""" 

656 for child in if_stmt.children: 

657 if not isinstance(child, Node): 

658 continue 

659 if child.type != syms.suite: 

660 continue 

661 if child is exclude_suite: 

662 continue 

663 for stmt in child.children: 

664 if not isinstance(stmt, Node): 

665 continue 

666 if stmt.type != syms.decorated: 

667 continue 

668 if EmptyLineTracker._decorated_node_has_func_named(stmt, name): 

669 return True 

670 break 

671 return False 

672 

673 @staticmethod 

674 def _get_def_name(line: Line) -> str | None: 

675 """Extract the function name from a line that is a function definition.""" 

676 if not line.is_def or not line.leaves: 

677 return None 

678 if line.leaves[0].value == "def": 

679 return line.leaves[1].value 

680 # async def: leaves = ['async', 'def', NAME, ...] 

681 return line.leaves[2].value 

682 

683 @staticmethod 

684 def _is_line_decorated(line: Line) -> bool: 

685 """Check if a def line is part of a decorated statement.""" 

686 if not line.is_def or not line.leaves: 

687 return False 

688 return EmptyLineTracker._find_decorated_node(line) is not None 

689 

690 @staticmethod 

691 def _find_decorated_node(line: Line) -> Node | None: 

692 """Walk up from the first leaf of a line to its ``decorated`` node.""" 

693 if not line.leaves: 

694 return None 

695 node = line.leaves[0].parent 

696 while node is not None: 

697 if node.type == syms.decorated: 

698 assert isinstance(node, Node) 

699 return node 

700 if node.type in (syms.suite, syms.file_input): 

701 return None 

702 node = node.parent 

703 return None 

704 

705 @staticmethod 

706 def _get_decorator_target_name(line: Line) -> str | None: 

707 """For a decorator line, extract the name of the function it decorates. 

708 

709 Only handles decorated functions, not decorated classes. 

710 """ 

711 if not line.is_decorator: 

712 return None 

713 decorated = EmptyLineTracker._find_decorated_node(line) 

714 if decorated is None: 

715 return None 

716 for child in decorated.children: 

717 if child.type in (syms.funcdef, syms.async_funcdef): 

718 return EmptyLineTracker._get_funcdef_name(child) 

719 return None 

720 

721 @staticmethod 

722 def _decorator_decorates_class(line: Line) -> bool: 

723 """Check if a decorator line decorates a class definition.""" 

724 if not line.is_decorator: 

725 return False 

726 decorated = EmptyLineTracker._find_decorated_node(line) 

727 if decorated is None: 

728 return False 

729 return any(child.type == syms.classdef for child in decorated.children) 

730 

731 @staticmethod 

732 def _def_is_followed_by_same_name_decorated_func( 

733 line: Line, *, include_conditional_blocks: bool = False 

734 ) -> bool: 

735 """Check if a decorated function is followed by a same-name decorated func. 

736 

737 If *include_conditional_blocks* is true, the next decorated function may 

738 be inside an adjacent conditional block. 

739 """ 

740 name = EmptyLineTracker._get_def_name(line) 

741 decorated = EmptyLineTracker._find_decorated_node(line) 

742 if name is None or decorated is None: 

743 return False 

744 

745 sibling = decorated.next_sibling 

746 while sibling is not None and sibling.type in _WHITESPACE_TOKENS: 

747 sibling = sibling.next_sibling 

748 

749 if sibling is None or not isinstance(sibling, Node): 

750 return False 

751 if sibling.type == syms.decorated: 

752 return EmptyLineTracker._decorated_node_has_func_named(sibling, name) 

753 if not include_conditional_blocks or sibling.type != syms.if_stmt: 

754 return False 

755 for child in sibling.children: 

756 if not isinstance(child, Node): 

757 continue 

758 if child.type != syms.suite: 

759 continue 

760 first_decorated_funcname = ( 

761 EmptyLineTracker._get_suite_first_decorated_funcname(child) 

762 ) 

763 return first_decorated_funcname == name 

764 return False 

765 

766 def _is_in_current_group(self, current_line: Line) -> bool: 

767 """Check if current_line belongs to the same overload group being tracked.""" 

768 prev = self._pyi_previous_decorated_func 

769 if prev is None: 

770 return False 

771 cur_name = ( 

772 self._get_decorator_target_name(current_line) 

773 if current_line.is_decorator 

774 else self._get_def_name(current_line) 

775 ) 

776 return ( 

777 cur_name is not None 

778 and cur_name == prev.name 

779 and prev.depth == current_line.depth 

780 and (current_line.is_decorator or self._is_line_decorated(current_line)) 

781 ) 

782 

783 @staticmethod 

784 def _decorated_node_starts_group(decorated_node: Node) -> bool: 

785 """Check if a `decorated` AST node is the first in a multi-function group. 

786 

787 Returns True when the next statement-level sibling is also a decorated 

788 function with the same name. 

789 """ 

790 name = None 

791 for child in decorated_node.children: 

792 if child.type in (syms.funcdef, syms.async_funcdef): 

793 name = EmptyLineTracker._get_funcdef_name(child) 

794 break 

795 if name is None: 

796 return False 

797 adjacent = EmptyLineTracker._find_adjacent_decorated(decorated_node) 

798 return adjacent is not None and EmptyLineTracker._decorated_node_has_func_named( 

799 adjacent, name 

800 ) 

801 

802 @staticmethod 

803 def _is_start_of_decorated_group(line: Line) -> bool: 

804 """Check if a decorator line starts a multi-function group. 

805 

806 A multi-function group is 2+ consecutive decorated functions sharing the 

807 same name (e.g. @overload groups, @property + setter pairs). 

808 """ 

809 if not line.is_decorator: 

810 return False 

811 decorated_node = EmptyLineTracker._find_decorated_node(line) 

812 if decorated_node is None or decorated_node.parent is None: 

813 return False 

814 return EmptyLineTracker._decorated_node_starts_group(decorated_node) 

815 

816 @staticmethod 

817 def _get_block_first_decorated_funcname(line: Line) -> str | None: 

818 """Return the function name of the first decorated function in a block. 

819 

820 *line* must be a block-opening line (ending with ``:``) such as 

821 ``if ...:``. Returns ``None`` when the block doesn't start with a 

822 decorated function. 

823 """ 

824 if not line.leaves or line.leaves[-1].type != token.COLON: 

825 return None 

826 suite = line.leaves[-1].next_sibling 

827 if suite is None or not isinstance(suite, Node) or suite.type != syms.suite: 

828 return None 

829 return EmptyLineTracker._get_suite_first_decorated_funcname(suite) 

830 

831 @staticmethod 

832 def _block_is_part_of_overload_group(line: Line) -> bool: 

833 """Check if a block-opening line contains a decorated function that is 

834 part of a larger overload group — either because the ``if_stmt``'s next 

835 sibling is a same-name decorated function, or because another branch of 

836 the same ``if_stmt`` has one. 

837 """ 

838 func_name = EmptyLineTracker._get_block_first_decorated_funcname(line) 

839 if func_name is None: 

840 return False 

841 

842 suite = line.leaves[-1].next_sibling 

843 if suite is None or not isinstance(suite, Node): 

844 return False 

845 if_stmt = suite.parent 

846 if if_stmt is None or not isinstance(if_stmt, Node): 

847 return False 

848 

849 # Check if the if_stmt's next sibling is a same-name decorated function. 

850 adjacent = EmptyLineTracker._find_adjacent_decorated(if_stmt) 

851 if adjacent is not None and EmptyLineTracker._decorated_node_has_func_named( 

852 adjacent, func_name 

853 ): 

854 return True 

855 

856 # Check other branches (elif/else) of the same if_stmt. 

857 return EmptyLineTracker._if_stmt_branch_has_func_named( 

858 if_stmt, suite, func_name 

859 ) 

860 

861 @staticmethod 

862 def _is_decorator_in_conditional_overload(line: Line) -> bool: 

863 """Check if a decorator is inside an if/else block that is part of a 

864 broader overload group (a sibling or another branch of the same 

865 ``if_stmt`` contains a same-name decorated function).""" 

866 name = EmptyLineTracker._get_decorator_target_name(line) 

867 if name is None: 

868 return False 

869 

870 decorated = EmptyLineTracker._find_decorated_node(line) 

871 if decorated is None: 

872 return False 

873 

874 suite = decorated.parent 

875 if suite is None or suite.type != syms.suite: 

876 return False 

877 

878 if_stmt = suite.parent 

879 if if_stmt is None or if_stmt.type != syms.if_stmt: 

880 return False 

881 

882 # Check if_stmt's adjacent siblings for same-name decorated function. 

883 for reverse in (True, False): 

884 adjacent = EmptyLineTracker._find_adjacent_decorated( 

885 if_stmt, reverse=reverse 

886 ) 

887 if ( 

888 adjacent is not None 

889 and EmptyLineTracker._decorated_node_has_func_named(adjacent, name) 

890 ): 

891 return True 

892 

893 # Check other branches of the same if_stmt. 

894 return EmptyLineTracker._if_stmt_branch_has_func_named(if_stmt, suite, name) 

895 

896 def maybe_empty_lines(self, current_line: Line) -> LinesBlock: 

897 """Return the number of extra empty lines before and after the `current_line`. 

898 

899 This is for separating `def`, `async def` and `class` with extra empty 

900 lines (two on module-level). 

901 """ 

902 form_feed = ( 

903 current_line.depth == 0 

904 and bool(current_line.leaves) 

905 and "\f\n" in current_line.leaves[0].prefix 

906 ) 

907 before, after = self._maybe_empty_lines(current_line) 

908 previous_after = self.previous_block.after if self.previous_block else 0 

909 before = max(0, before - previous_after) 

910 

911 # Always have one empty line after a module docstring 

912 if self._line_is_module_docstring(current_line): 

913 before = 1 

914 

915 block = LinesBlock( 

916 mode=self.mode, 

917 previous_block=self.previous_block, 

918 original_line=current_line, 

919 before=before, 

920 after=after, 

921 form_feed=form_feed, 

922 ) 

923 

924 # Maintain the semantic_leading_comment state. 

925 if current_line.is_comment: 

926 if self.previous_line is None or ( 

927 not self.previous_line.is_decorator 

928 # `or before` means this comment already has an empty line before 

929 and (not self.previous_line.is_comment or before) 

930 and (self.semantic_leading_comment is None or before) 

931 ): 

932 self.semantic_leading_comment = block 

933 # `or before` means this decorator already has an empty line before 

934 elif not current_line.is_decorator or before: 

935 self.semantic_leading_comment = None 

936 

937 # Maintain _pyi_previous_decorated_func state for overload groups. 

938 # The tuple is (name, depth, is_multi) where is_multi indicates 

939 # the group has 2+ same-name decorated functions. 

940 overload_groups = ( 

941 self.mode.is_pyi and Preview.pyi_overload_group_blank_lines in self.mode 

942 ) 

943 if overload_groups: 

944 if current_line.is_def and self._is_line_decorated(current_line): 

945 name = self._get_def_name(current_line) 

946 if name is not None: 

947 prev = self._pyi_previous_decorated_func 

948 is_multi = ( 

949 prev is not None 

950 and prev.name == name 

951 and prev.depth == current_line.depth 

952 ) 

953 self._pyi_previous_decorated_func = _DecoratedFuncInfo( 

954 name=name, 

955 depth=current_line.depth, 

956 is_multi=is_multi 

957 or (prev is not None and prev.name == name and prev.is_multi), 

958 ) 

959 elif ( 

960 not current_line.is_decorator 

961 and not current_line.is_comment 

962 and ( 

963 self._pyi_previous_decorated_func is None 

964 or ( 

965 current_line.depth <= self._pyi_previous_decorated_func.depth 

966 # Don't reset on else/elif — they continue an if/else 

967 # chain that may contain overloads at a deeper depth. 

968 and not ( 

969 current_line.leaves 

970 and current_line.leaves[0].value in ("else", "elif") 

971 ) 

972 ) 

973 ) 

974 ): 

975 # Only reset when we see a non-decorator line at the same or 

976 # lower depth. Body lines (docstrings, ...) at deeper depth 

977 # should not clear the state. 

978 self._pyi_previous_decorated_func = None 

979 

980 self.previous_line = current_line 

981 self.previous_block = block 

982 return block 

983 

984 def _line_is_module_docstring(self, current_line: Line) -> bool: 

985 previous_block = self.previous_block 

986 if not previous_block: 

987 return False 

988 if ( 

989 len(previous_block.original_line.leaves) != 1 

990 or not previous_block.original_line.is_docstring 

991 or current_line.is_class 

992 or current_line.is_def 

993 ): 

994 return False 

995 while previous_block := previous_block.previous_block: 

996 if not previous_block.original_line.is_comment: 

997 return False 

998 return True 

999 

1000 def _maybe_empty_lines(self, current_line: Line) -> tuple[int, int]: 

1001 max_allowed = 1 

1002 if current_line.depth == 0: 

1003 max_allowed = 1 if self.mode.is_pyi else 2 

1004 overload_groups = ( 

1005 self.mode.is_pyi and Preview.pyi_overload_group_blank_lines in self.mode 

1006 ) 

1007 

1008 if current_line.leaves: 

1009 # Consume the first leaf's extra newlines. 

1010 first_leaf = current_line.leaves[0] 

1011 before = first_leaf.prefix.count("\n") 

1012 before = min(before, max_allowed) 

1013 first_leaf.prefix = "" 

1014 else: 

1015 before = 0 

1016 

1017 user_had_newline = bool(before) 

1018 depth = current_line.depth 

1019 

1020 # Mutate self.previous_defs, remainder of this function should be pure 

1021 previous_def = None 

1022 while self.previous_defs and self.previous_defs[-1].depth >= depth: 

1023 previous_def = self.previous_defs.pop() 

1024 if current_line.is_def or current_line.is_class: 

1025 self.previous_defs.append(current_line) 

1026 

1027 if self.previous_line is None: 

1028 # Don't insert empty lines before the first line in the file. 

1029 return 0, 0 

1030 

1031 if current_line.is_docstring: 

1032 if self.previous_line.is_class: 

1033 return 0, 1 

1034 if self.previous_line.opens_block and self.previous_line.is_def: 

1035 return 0, 0 

1036 

1037 if previous_def is not None: 

1038 assert self.previous_line is not None 

1039 # Note: for decorator/def/class lines, `before` computed here is 

1040 # passed to _maybe_empty_lines_for_class_or_def which may override 

1041 # it. This block still matters for non-decorator/def/class lines 

1042 # (e.g. a `var: int` statement following an overload group). 

1043 if self.mode.is_pyi: 

1044 if previous_def.is_class and not previous_def.is_stub_class: 

1045 before = 1 

1046 elif ( 

1047 overload_groups 

1048 and self._pyi_previous_decorated_func is not None 

1049 and self._pyi_previous_decorated_func.is_multi 

1050 and not current_line.is_comment 

1051 and self.previous_line.depth >= current_line.depth 

1052 and not ( 

1053 current_line.leaves 

1054 and current_line.leaves[0].value in ("else", "elif") 

1055 ) 

1056 ): 

1057 if self._is_in_current_group(current_line): 

1058 before = 0 

1059 elif current_line.opens_block and ( 

1060 self._get_block_first_decorated_funcname(current_line) 

1061 == self._pyi_previous_decorated_func.name 

1062 ): 

1063 before = 0 

1064 else: 

1065 before = 1 

1066 elif ( 

1067 overload_groups 

1068 and current_line.opens_block 

1069 and current_line.leaves 

1070 and current_line.leaves[0].value in ("else", "elif") 

1071 and self._block_is_part_of_overload_group(current_line) 

1072 ): 

1073 # else/elif continuing a conditional overload group: 

1074 # don't insert a blank line above. 

1075 before = 0 

1076 elif ( 

1077 Preview.pyi_blank_line_after_function_docstring in self.mode 

1078 and previous_def.is_def 

1079 and self.previous_line.is_docstring 

1080 and self.previous_line.depth == previous_def.depth + 1 

1081 and not self._def_is_followed_by_same_name_decorated_func( 

1082 previous_def, 

1083 include_conditional_blocks=overload_groups, 

1084 ) 

1085 ): 

1086 before = 1 

1087 elif depth and not current_line.is_def and self.previous_line.is_def: 

1088 if ( 

1089 overload_groups 

1090 and current_line.opens_block 

1091 and self.previous_line.depth <= current_line.depth 

1092 and self._block_is_part_of_overload_group(current_line) 

1093 ): 

1094 before = 1 

1095 else: 

1096 # Empty lines between attributes and methods should 

1097 # be preserved. 

1098 before = 1 if user_had_newline else 0 

1099 elif ( 

1100 overload_groups 

1101 and current_line.is_comment 

1102 and self._pyi_previous_decorated_func is not None 

1103 and self._pyi_previous_decorated_func.depth == current_line.depth 

1104 ): 

1105 # Own-line comments after a decorated function in .pyi: 

1106 # preserve a single user blank line but never insert one. 

1107 # For non-overload cases the comment_to_add_newlines 

1108 # mechanism will retroactively add needed blank lines. 

1109 before = 1 if user_had_newline else 0 

1110 elif depth: 

1111 before = 0 

1112 else: 

1113 before = 1 

1114 else: 

1115 if depth: 

1116 before = 1 

1117 elif ( 

1118 not depth 

1119 and previous_def.depth 

1120 and current_line.leaves[-1].type == token.COLON 

1121 and ( 

1122 current_line.leaves[0].value 

1123 not in ("with", "try", "for", "while", "if", "match") 

1124 ) 

1125 ): 

1126 # We shouldn't add two newlines between an indented function and 

1127 # a dependent non-indented clause. This is to avoid issues with 

1128 # conditional function definitions that are technically top-level 

1129 # and therefore get two trailing newlines, but look weird and 

1130 # inconsistent when they're followed by elif, else, etc. This is 

1131 # worse because these functions only get *one* preceding newline 

1132 # already. 

1133 before = 1 

1134 else: 

1135 before = 2 

1136 

1137 if current_line.is_decorator or current_line.is_def or current_line.is_class: 

1138 return self._maybe_empty_lines_for_class_or_def( 

1139 current_line, before, user_had_newline 

1140 ) 

1141 

1142 if ( 

1143 Preview.fmt_off_class_blank_lines in self.mode 

1144 and self.previous_line.is_import 

1145 and self.previous_line.depth == 0 

1146 and current_line.depth == 0 

1147 and current_line.is_fmt_pass_converted( 

1148 first_leaf_matches=lambda leaf: leaf.value == "class" 

1149 ) 

1150 ): 

1151 return 2, 0 

1152 

1153 if ( 

1154 self.previous_line.is_import 

1155 and self.previous_line.depth == 0 

1156 and current_line.depth == 0 

1157 and not current_line.is_import 

1158 and not current_line.is_fmt_pass_converted(first_leaf_matches=is_import) 

1159 ): 

1160 return 1, 0 

1161 

1162 if ( 

1163 self.previous_line.is_import 

1164 and not current_line.is_import 

1165 and not current_line.is_fmt_pass_converted(first_leaf_matches=is_import) 

1166 and depth == self.previous_line.depth 

1167 ): 

1168 return (before or 1), 0 

1169 

1170 return before, 0 

1171 

1172 def _maybe_empty_lines_for_class_or_def( 

1173 self, current_line: Line, before: int, user_had_newline: bool 

1174 ) -> tuple[int, int]: 

1175 assert self.previous_line is not None 

1176 overload_groups = ( 

1177 self.mode.is_pyi and Preview.pyi_overload_group_blank_lines in self.mode 

1178 ) 

1179 

1180 if self.previous_line.is_decorator: 

1181 if self.mode.is_pyi and current_line.is_stub_class: 

1182 # Insert an empty line after a decorated stub class 

1183 return 0, 1 

1184 return 0, 0 

1185 

1186 if self.previous_line.depth < current_line.depth and ( 

1187 self.previous_line.is_class or self.previous_line.is_def 

1188 ): 

1189 if self.mode.is_pyi: 

1190 return 0, 0 

1191 return 1 if user_had_newline else 0, 0 

1192 

1193 comment_to_add_newlines: LinesBlock | None = None 

1194 if ( 

1195 self.previous_line.is_comment 

1196 and self.previous_line.depth == current_line.depth 

1197 and before == 0 

1198 ): 

1199 slc = self.semantic_leading_comment 

1200 if ( 

1201 slc is not None 

1202 and slc.previous_block is not None 

1203 and not slc.previous_block.original_line.is_class 

1204 and not slc.previous_block.original_line.opens_block 

1205 and slc.before <= 1 

1206 ): 

1207 comment_to_add_newlines = slc 

1208 else: 

1209 return 0, 0 

1210 

1211 if self.mode.is_pyi: 

1212 if current_line.is_class or self.previous_line.is_class: 

1213 if self.previous_line.depth < current_line.depth: 

1214 newlines = 0 

1215 elif self.previous_line.depth > current_line.depth: 

1216 newlines = 1 

1217 elif current_line.is_stub_class and self.previous_line.is_stub_class: 

1218 # No blank line between classes with an empty body 

1219 newlines = 0 

1220 else: 

1221 newlines = 1 

1222 # Don't inspect only the previous line if it's part of the body of the 

1223 # preceding statement. We always want a blank line after something with a 

1224 # body. 

1225 elif self.previous_line.depth > current_line.depth: 

1226 if overload_groups and self._is_in_current_group(current_line): 

1227 newlines = 0 

1228 else: 

1229 newlines = 1 

1230 elif ( 

1231 overload_groups 

1232 and self._pyi_previous_decorated_func is not None 

1233 and self._pyi_previous_decorated_func.is_multi 

1234 and self.previous_line.depth >= current_line.depth 

1235 ): 

1236 newlines = 0 if self._is_in_current_group(current_line) else 1 

1237 elif overload_groups and self._is_in_current_group(current_line): 

1238 # A comment between overloads may prevent is_multi from being 

1239 # set, but _is_in_current_group still detects name continuity. 

1240 newlines = 0 

1241 elif ( 

1242 overload_groups 

1243 and current_line.is_decorator 

1244 and self.previous_line.depth >= current_line.depth 

1245 and self._is_start_of_decorated_group(current_line) 

1246 and ( 

1247 self._pyi_previous_decorated_func is None 

1248 or ( 

1249 self._pyi_previous_decorated_func.name 

1250 != self._get_decorator_target_name(current_line) 

1251 ) 

1252 or self._pyi_previous_decorated_func.depth != current_line.depth 

1253 ) 

1254 ): 

1255 newlines = 1 

1256 elif ( 

1257 current_line.is_def or current_line.is_decorator 

1258 ) and not self.previous_line.is_def: 

1259 if ( 

1260 overload_groups 

1261 and current_line.is_decorator 

1262 and self.previous_line.is_comment 

1263 and self._is_decorator_in_conditional_overload(current_line) 

1264 ): 

1265 # Comment before an overload inside a conditional block: 

1266 # remove blank lines between the comment and decorator. 

1267 newlines = 0 

1268 elif current_line.depth: 

1269 # In classes empty lines between attributes and methods should 

1270 # be preserved. 

1271 newlines = min(1, before) 

1272 else: 

1273 # Blank line between a block of functions (maybe with preceding 

1274 # decorators) and a block of non-functions 

1275 newlines = 1 

1276 elif ( 

1277 Preview.pyi_blank_line_before_decorated_class in self.mode 

1278 and current_line.is_decorator 

1279 and self._decorator_decorates_class(current_line) 

1280 ): 

1281 newlines = 1 

1282 else: 

1283 newlines = 0 

1284 else: 

1285 newlines = 1 if current_line.depth else 2 

1286 # If a user has left no space after a dummy implementation, don't insert 

1287 # new lines. This is useful for instance for @overload or Protocols. 

1288 if self.previous_line.is_stub_def and not user_had_newline: 

1289 newlines = 0 

1290 if comment_to_add_newlines is not None: 

1291 previous_block = comment_to_add_newlines.previous_block 

1292 if previous_block is not None: 

1293 comment_to_add_newlines.before = ( 

1294 max(comment_to_add_newlines.before, newlines) - previous_block.after 

1295 ) 

1296 newlines = 0 

1297 return newlines, 0 

1298 

1299 

1300def enumerate_reversed(sequence: Sequence[T]) -> Iterator[tuple[Index, T]]: 

1301 """Like `reversed(enumerate(sequence))` if that were possible.""" 

1302 index = len(sequence) - 1 

1303 for element in reversed(sequence): 

1304 yield (index, element) 

1305 index -= 1 

1306 

1307 

1308def append_leaves( 

1309 new_line: Line, old_line: Line, leaves: list[Leaf], preformatted: bool = False 

1310) -> None: 

1311 """ 

1312 Append leaves (taken from @old_line) to @new_line, making sure to fix the 

1313 underlying Node structure where appropriate. 

1314 

1315 All of the leaves in @leaves are duplicated. The duplicates are then 

1316 appended to @new_line and used to replace their originals in the underlying 

1317 Node structure. Any comments attached to the old leaves are reattached to 

1318 the new leaves. 

1319 

1320 Pre-conditions: 

1321 set(@leaves) is a subset of set(@old_line.leaves). 

1322 """ 

1323 # @leaves is a slice of @old_line.leaves, so the leaves are in tree (DFS) 

1324 # order and the children replaced within any one parent are reached at 

1325 # strictly increasing positions. Remembering where the last child of each 

1326 # parent was found lets the lookup resume from there instead of rescanning the 

1327 # whole child list through Base.remove on every leaf, which is otherwise 

1328 # O(n^2) when a node has many children replaced (e.g. wrapping the operand 

1329 # tuple of "%s" % (a, b, c, ...) or copying a very long line for a second 

1330 # formatting pass). 

1331 search_start: dict[int, int] = {} 

1332 for old_leaf in leaves: 

1333 new_leaf = Leaf(old_leaf.type, old_leaf.value) 

1334 parent = old_leaf.parent 

1335 if parent is not None: 

1336 children = parent.children 

1337 index = search_start.get(id(parent), 0) 

1338 while index < len(children) and children[index] is not old_leaf: 

1339 index += 1 

1340 if index < len(children): 

1341 # set_child swaps the child in place (the old one keeps the same 

1342 # position), so the next sibling to replace is always further on. 

1343 parent.set_child(index, new_leaf) 

1344 search_start[id(parent)] = index + 1 

1345 else: 

1346 # The resume hint missed (unexpected ordering); fall back to the 

1347 # full scan so behaviour is unchanged. 

1348 replace_child(old_leaf, new_leaf) 

1349 new_line.append(new_leaf, preformatted=preformatted) 

1350 

1351 for comment_leaf in old_line.comments_after(old_leaf): 

1352 new_line.append(comment_leaf, preformatted=True) 

1353 

1354 

1355def is_line_short_enough(line: Line, *, mode: Mode, line_str: str = "") -> bool: 

1356 """For non-multiline strings, return True if `line` is no longer than `line_length`. 

1357 For multiline strings, looks at the context around `line` to determine 

1358 if it should be inlined or split up. 

1359 Uses the provided `line_str` rendering, if any, otherwise computes a new one. 

1360 """ 

1361 if not line_str: 

1362 line_str = line_to_string(line) 

1363 

1364 if line.contains_standalone_comments(): 

1365 return False 

1366 if "\n" not in line_str: 

1367 # No multiline strings (MLS) present 

1368 return str_width(line_str) <= mode.line_length 

1369 

1370 first, *_, last = line_str.split("\n") 

1371 if str_width(first) > mode.line_length or str_width(last) > mode.line_length: 

1372 return False 

1373 

1374 # Traverse the AST to examine the context of the multiline string (MLS), 

1375 # tracking aspects such as depth and comma existence, 

1376 # to determine whether to split the MLS or keep it together. 

1377 # Depth (which is based on the existing bracket_depth concept) 

1378 # is needed to determine nesting level of the MLS. 

1379 # Includes special case for trailing commas. 

1380 commas: list[int] = [] # tracks number of commas per depth level 

1381 multiline_string: Leaf | None = None 

1382 # store the leaves that contain parts of the MLS 

1383 multiline_string_contexts: list[LN] = [] 

1384 

1385 # `id()`s of the leaves on this line, used to find which ancestors of the 

1386 # multiline string are rendered in full on this line (see below). 

1387 line_leaf_ids = {id(leaf) for leaf in line.leaves} 

1388 

1389 max_level_to_update: int | float = math.inf # track the depth of the MLS 

1390 for i, leaf in enumerate(line.leaves): 

1391 if max_level_to_update == math.inf: 

1392 had_comma: int | None = None 

1393 if leaf.bracket_depth + 1 > len(commas): 

1394 commas.append(0) 

1395 elif leaf.bracket_depth + 1 < len(commas): 

1396 had_comma = commas.pop() 

1397 if ( 

1398 had_comma is not None 

1399 and multiline_string is not None 

1400 and multiline_string.bracket_depth == leaf.bracket_depth + 1 

1401 ): 

1402 # Have left the level with the MLS, stop tracking commas 

1403 max_level_to_update = leaf.bracket_depth 

1404 if had_comma > 0: 

1405 # MLS was in parens with at least one comma - force split 

1406 return False 

1407 

1408 if leaf.bracket_depth <= max_level_to_update and leaf.type == token.COMMA: 

1409 # Inside brackets, ignore trailing comma 

1410 # directly after MLS/MLS-containing expression 

1411 ignore_ctxs: list[LN | None] = [None] 

1412 ignore_ctxs += multiline_string_contexts 

1413 if (line.inside_brackets or leaf.bracket_depth > 0) and ( 

1414 i != len(line.leaves) - 1 or leaf.prev_sibling not in ignore_ctxs 

1415 ): 

1416 commas[leaf.bracket_depth] += 1 

1417 if max_level_to_update != math.inf: 

1418 max_level_to_update = min(max_level_to_update, leaf.bracket_depth) 

1419 

1420 if is_multiline_string(leaf): 

1421 if leaf.parent and ( 

1422 leaf.parent.type == syms.test 

1423 or (leaf.parent.parent and leaf.parent.parent.type == syms.dictsetmaker) 

1424 ): 

1425 # Keep ternary and dictionary values parenthesized 

1426 return False 

1427 if len(multiline_string_contexts) > 0: 

1428 # >1 multiline string cannot fit on a single line - force split 

1429 return False 

1430 multiline_string = leaf 

1431 ctx: LN = leaf 

1432 # fetch the leaf components of the MLS in the AST. An ancestor is 

1433 # part of the MLS context while it is rendered in full on this line, 

1434 # i.e. its first and last leaves both belong to the line (a line is 

1435 # a contiguous run of leaves). Walking the leaf ids instead of 

1436 # re-rendering `str(ctx)` and substring-searching `line_str` at every 

1437 # level keeps this linear; the old form was quadratic on lines with a 

1438 # large multiline-string-bearing collection (e.g. a dict literal with 

1439 # many triple-quoted values). 

1440 while ( 

1441 id(first_leaf(ctx)) in line_leaf_ids 

1442 and id(last_leaf(ctx)) in line_leaf_ids 

1443 ): 

1444 multiline_string_contexts.append(ctx) 

1445 if ctx.parent is None: 

1446 break 

1447 ctx = ctx.parent 

1448 

1449 # May not have a triple-quoted multiline string at all, 

1450 # in case of a regular string with embedded newlines and line continuations 

1451 if len(multiline_string_contexts) == 0: 

1452 return True 

1453 

1454 return all(val == 0 for val in commas) 

1455 

1456 

1457def can_be_split(line: Line) -> bool: 

1458 """Return False if the line cannot be split *for sure*. 

1459 

1460 This is not an exhaustive search but a cheap heuristic that we can use to 

1461 avoid some unfortunate formattings (mostly around wrapping unsplittable code 

1462 in unnecessary parentheses). 

1463 """ 

1464 leaves = line.leaves 

1465 if len(leaves) < 2: 

1466 return False 

1467 

1468 if leaves[0].type == token.STRING and leaves[1].type == token.DOT: 

1469 call_count = 0 

1470 dot_count = 0 

1471 next = leaves[-1] 

1472 for leaf in leaves[-2::-1]: 

1473 if leaf.type in OPENING_BRACKETS: 

1474 if next.type not in CLOSING_BRACKETS: 

1475 return False 

1476 

1477 call_count += 1 

1478 elif leaf.type == token.DOT: 

1479 dot_count += 1 

1480 elif leaf.type == token.NAME: 

1481 if not (next.type == token.DOT or next.type in OPENING_BRACKETS): 

1482 return False 

1483 

1484 elif leaf.type not in CLOSING_BRACKETS: 

1485 return False 

1486 

1487 if dot_count > 1 and call_count > 1: 

1488 return False 

1489 

1490 return True 

1491 

1492 

1493def _is_annotated_assignment(head: Line) -> bool: 

1494 """Does `head` (an assignment's leaves up to the `=`) contain an annotation? 

1495 

1496 Detects a `:` at the top bracket level, as in `x: dict[str, int] = ...`. 

1497 Colons nested inside brackets (e.g. slices like `x[a:b]`) don't count. 

1498 """ 

1499 depth = 0 

1500 for leaf in head.leaves: 

1501 if not leaf.value: 

1502 continue 

1503 if leaf.type in OPENING_BRACKETS: 

1504 depth += 1 

1505 elif leaf.type in CLOSING_BRACKETS: 

1506 depth -= 1 

1507 elif leaf.type == token.COLON and depth == 0: 

1508 return True 

1509 return False 

1510 

1511 

1512def can_omit_invisible_parens( 

1513 rhs: RHSResult, 

1514 line_length: int, 

1515 mode: Mode, 

1516) -> bool: 

1517 """Does `rhs.body` have a shape safe to reformat without optional parens around it? 

1518 

1519 Returns True for only a subset of potentially nice looking formattings but 

1520 the point is to not return false positives that end up producing lines that 

1521 are too long. 

1522 """ 

1523 line = rhs.body 

1524 

1525 # Don't omit optional parens when the opening paren carries an inline comment. 

1526 # Omitting them re-parents the comment onto a different leaf after the next 

1527 # parse, which can make the RHS splitter choose a different shape on each 

1528 # pass (unstable formatting / "different code on the second pass"). See 

1529 # issues #3701, #3706, and #4384. 

1530 if rhs.opening_bracket.type == token.LPAR and not rhs.opening_bracket.value: 

1531 if rhs.head.comments.get(id(rhs.opening_bracket)): 

1532 return False 

1533 

1534 # We can't omit parens if doing so would result in a type: ignore comment 

1535 # sharing a line with other comments, as that breaks type: ignore parsing. 

1536 # Check if the opening bracket (last leaf of head) has comments that would merge 

1537 # with comments from the first line of the body. 

1538 if rhs.head.leaves: 

1539 opening_bracket = rhs.head.leaves[-1] 

1540 head_comments = rhs.head.comments.get(id(opening_bracket), []) 

1541 

1542 # If there are comments on the opening bracket line, check if any would 

1543 # conflict with type: ignore comments in the body 

1544 if head_comments: 

1545 has_type_ignore_in_head = any( 

1546 is_type_ignore_comment(comment, mode=rhs.head.mode) 

1547 for comment in head_comments 

1548 ) 

1549 has_other_comment_in_head = any( 

1550 not is_type_ignore_comment(comment, mode=rhs.head.mode) 

1551 for comment in head_comments 

1552 ) 

1553 

1554 # Check for comments in the body that would potentially end up on the 

1555 # same line as the head comments when parens are removed 

1556 has_type_ignore_in_body = False 

1557 has_other_comment_in_body = False 

1558 for leaf in rhs.body.leaves: 

1559 for comment in rhs.body.comments.get(id(leaf), []): 

1560 if is_type_ignore_comment(comment, mode=rhs.body.mode): 

1561 has_type_ignore_in_body = True 

1562 else: 

1563 has_other_comment_in_body = True 

1564 

1565 # Preserve parens if we have both type: ignore and other comments that 

1566 # could end up on the same line 

1567 if (has_type_ignore_in_head and has_other_comment_in_body) or ( 

1568 has_other_comment_in_head and has_type_ignore_in_body 

1569 ): 

1570 return False 

1571 

1572 # We need optional parens in order to split standalone comments to their own lines 

1573 # if there are no nested parens around the standalone comments 

1574 closing_bracket: Leaf | None = None 

1575 for leaf in reversed(line.leaves): 

1576 if closing_bracket and leaf is closing_bracket.opening_bracket: 

1577 closing_bracket = None 

1578 if leaf.type == STANDALONE_COMMENT and not closing_bracket: 

1579 return False 

1580 if ( 

1581 not closing_bracket 

1582 and leaf.type in CLOSING_BRACKETS 

1583 and leaf.opening_bracket in line.leaves 

1584 and leaf.value 

1585 ): 

1586 closing_bracket = leaf 

1587 

1588 # For assignments to a subscripted target whose head is too long to fit 

1589 # (e.g. `x[long_key][another_long_key] = expr`), the target must be split 

1590 # at its subscript brackets no matter what, so optional parens around a 

1591 # RHS that fits on the resulting tail line (`] = expr`) are unnecessary. 

1592 # When the head fits on one line, paren-wrapping the RHS remains the 

1593 # preferred style and this check must not fire. It must also come before 

1594 # the delimiter_count > 1 early return below, which would otherwise 

1595 # reject bodies with multiple delimiters such as `10 - 5`. 

1596 if ( 

1597 Preview.fix_unnecessary_parens_in_indexed_assignment in mode 

1598 and len(rhs.head.leaves) >= 3 

1599 and rhs.head.leaves[-2].type == token.EQUAL 

1600 # The target must actually end with a subscript: a visible `]` 

1601 # immediately before the `=`. Invisible parens (e.g. around tuple 

1602 # targets like `(x,) = ...`) have an empty value and don't count. 

1603 and rhs.head.leaves[-3].type == token.RSQB 

1604 and rhs.head.leaves[-3].value 

1605 # In annotated assignments (`x: dict[str, int] = ...`) the `]` before 

1606 # the `=` belongs to the annotation; splitting on the annotation's 

1607 # brackets would be wrong, so leave the optional parens alone. 

1608 and not _is_annotated_assignment(rhs.head) 

1609 # The head must be too long to fit, forcing the subscript split. 

1610 and not is_line_short_enough(rhs.head, mode=mode) 

1611 ): 

1612 # 4 extra characters for the `] = ` prefix on the tail line. 

1613 tail_line_length = 4 * line.depth + 4 

1614 for _index, _leaf, leaf_length in line.enumerate_with_length(): 

1615 tail_line_length += leaf_length 

1616 if tail_line_length <= line_length: 

1617 return True 

1618 

1619 bt = line.bracket_tracker 

1620 if not bt.delimiters: 

1621 # Without delimiters the optional parentheses are useless. 

1622 return True 

1623 

1624 max_priority = bt.max_delimiter_priority() 

1625 delimiter_count = bt.delimiter_count_with_priority(max_priority) 

1626 if delimiter_count > 1: 

1627 # With more than one delimiter of a kind the optional parentheses read better. 

1628 return False 

1629 

1630 if delimiter_count == 1: 

1631 if max_priority == COMMA_PRIORITY and rhs.head.is_with_or_async_with_stmt: 

1632 # For two context manager with statements, the optional parentheses read 

1633 # better. In this case, `rhs.body` is the context managers part of 

1634 # the with statement. `rhs.head` is the `with (` part on the previous 

1635 # line. 

1636 return False 

1637 # Otherwise it may also read better, but we don't do it today and requires 

1638 # careful considerations for all possible cases. See 

1639 # https://github.com/psf/black/issues/2156. 

1640 

1641 if max_priority == DOT_PRIORITY: 

1642 # A single stranded method call doesn't require optional parentheses. 

1643 return True 

1644 

1645 assert len(line.leaves) >= 2, "Stranded delimiter" 

1646 

1647 # With a single delimiter, omit if the expression starts or ends with 

1648 # a bracket. 

1649 first = line.leaves[0] 

1650 second = line.leaves[1] 

1651 if first.type in OPENING_BRACKETS and second.type not in CLOSING_BRACKETS: 

1652 if _can_omit_opening_paren(line, first=first, line_length=line_length): 

1653 return True 

1654 

1655 # Note: we are not returning False here because a line might have *both* 

1656 # a leading opening bracket and a trailing closing bracket. If the 

1657 # opening bracket doesn't match our rule, maybe the closing will. 

1658 

1659 penultimate = line.leaves[-2] 

1660 last = line.leaves[-1] 

1661 

1662 if ( 

1663 last.type == token.RPAR 

1664 or last.type == token.RBRACE 

1665 or ( 

1666 # don't use indexing for omitting optional parentheses; 

1667 # it looks weird 

1668 last.type == token.RSQB 

1669 and last.parent 

1670 and last.parent.type != syms.trailer 

1671 ) 

1672 ): 

1673 if penultimate.type in OPENING_BRACKETS: 

1674 # Empty brackets don't help. 

1675 return False 

1676 

1677 if is_multiline_string(first): 

1678 # Additional wrapping of a multiline string in this situation is 

1679 # unnecessary. 

1680 return True 

1681 

1682 if _can_omit_closing_paren(line, last=last, line_length=line_length): 

1683 return True 

1684 

1685 return False 

1686 

1687 

1688def _can_omit_opening_paren(line: Line, *, first: Leaf, line_length: int) -> bool: 

1689 """See `can_omit_invisible_parens`.""" 

1690 remainder = False 

1691 length = 4 * line.depth 

1692 _index = -1 

1693 for _index, leaf, leaf_length in line.enumerate_with_length(): 

1694 if leaf.type in CLOSING_BRACKETS and leaf.opening_bracket is first: 

1695 remainder = True 

1696 if remainder: 

1697 length += leaf_length 

1698 if length > line_length: 

1699 break 

1700 

1701 if leaf.type in OPENING_BRACKETS: 

1702 # There are brackets we can further split on. 

1703 remainder = False 

1704 

1705 else: 

1706 # checked the entire string and line length wasn't exceeded 

1707 if len(line.leaves) == _index + 1: 

1708 return True 

1709 

1710 return False 

1711 

1712 

1713def _can_omit_closing_paren(line: Line, *, last: Leaf, line_length: int) -> bool: 

1714 """See `can_omit_invisible_parens`.""" 

1715 length = 4 * line.depth 

1716 seen_other_brackets = False 

1717 for _index, leaf, leaf_length in line.enumerate_with_length(): 

1718 length += leaf_length 

1719 if leaf is last.opening_bracket: 

1720 if seen_other_brackets or length <= line_length: 

1721 return True 

1722 

1723 elif leaf.type in OPENING_BRACKETS: 

1724 # There are brackets we can further split on. 

1725 seen_other_brackets = True 

1726 

1727 return False 

1728 

1729 

1730def line_to_string(line: Line) -> str: 

1731 """Returns the string representation of @line. 

1732 

1733 WARNING: This is known to be computationally expensive. 

1734 """ 

1735 return str(line).strip("\n")