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

818 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.comments import FMT_ON, contains_fmt_directive 

9from black.mode import Mode, Preview 

10from black.nodes import ( 

11 BRACKETS, 

12 CLOSING_BRACKETS, 

13 OPENING_BRACKETS, 

14 STANDALONE_COMMENT, 

15 TEST_DESCENDANTS, 

16 child_towards, 

17 first_leaf, 

18 is_docstring, 

19 is_import, 

20 is_multiline_string, 

21 is_one_sequence_between, 

22 is_one_tuple, 

23 is_type_comment, 

24 is_type_ignore_comment, 

25 is_with_or_async_with_stmt, 

26 last_leaf, 

27 make_simple_prefix, 

28 replace_child, 

29 syms, 

30 whitespace, 

31) 

32from black.strings import str_width 

33from blib2to3.pgen2 import token 

34from blib2to3.pytree import Leaf, Node 

35 

36# types 

37T = TypeVar("T") 

38Index = int 

39LeafID = int 

40LN = Union[Leaf, Node] 

41 

42 

43@dataclass 

44class Line: 

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

46 

47 mode: Mode = field(repr=False) 

48 depth: int = 0 

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

50 # keys ordered like `leaves` 

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

52 bracket_tracker: BracketTracker = field(default_factory=BracketTracker) 

53 inside_brackets: bool = False 

54 should_split_rhs: bool = False 

55 magic_trailing_comma: Leaf | None = None 

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

57 default_factory=dict, repr=False 

58 ) 

59 

60 def append( 

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

62 ) -> None: 

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

64 

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

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

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

68 demoted from being delimiters. 

69 

70 Inline comments are put aside. 

71 """ 

72 has_value = ( 

73 leaf.type in BRACKETS 

74 # empty fstring and tstring middles must not be truncated 

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

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

77 ) 

78 if not has_value: 

79 return 

80 

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

82 del self.leaves[-2:] 

83 if self.leaves and not preformatted: 

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

85 # imports, for which we only preserve newlines. 

86 leaf.prefix += whitespace( 

87 leaf, 

88 complex_subscript=self.is_complex_subscript(leaf), 

89 mode=self.mode, 

90 ) 

91 if self.inside_brackets or not preformatted or track_bracket: 

92 self.bracket_tracker.mark(leaf) 

93 if self.mode.magic_trailing_comma: 

94 if self.has_magic_trailing_comma(leaf): 

95 self.magic_trailing_comma = leaf 

96 elif self.has_magic_trailing_comma(leaf) and not ( 

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

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

99 # by has_magic_trailing_comma, but that check misses the tuple 

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

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

102 # against the tree here before dropping the comma. 

103 leaf.parent is not None 

104 and is_one_tuple(leaf.parent) 

105 ): 

106 self.remove_trailing_comma() 

107 if not self.append_comment(leaf): 

108 self.leaves.append(leaf) 

109 

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

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

112 

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

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

115 """ 

116 if ( 

117 self.bracket_tracker.depth == 0 

118 or self.bracket_tracker.any_open_for_or_lambda() 

119 ): 

120 if self.is_comment: 

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

122 

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

124 raise ValueError( 

125 "cannot append standalone comments to a populated line" 

126 ) 

127 

128 self.append(leaf, preformatted=preformatted) 

129 

130 @property 

131 def is_comment(self) -> bool: 

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

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

134 

135 @property 

136 def is_decorator(self) -> bool: 

137 """Is this line a decorator?""" 

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

139 

140 @property 

141 def is_import(self) -> bool: 

142 """Is this an import line?""" 

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

144 

145 @property 

146 def is_with_or_async_with_stmt(self) -> bool: 

147 """Is this a with_stmt line?""" 

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

149 

150 @property 

151 def is_class(self) -> bool: 

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

153 return ( 

154 bool(self) 

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

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

157 ) 

158 

159 @property 

160 def is_stub_class(self) -> bool: 

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

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

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

164 ] 

165 

166 @property 

167 def is_def(self) -> bool: 

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

169 try: 

170 first_leaf = self.leaves[0] 

171 except IndexError: 

172 return False 

173 

174 try: 

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

176 except IndexError: 

177 second_leaf = None 

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

179 first_leaf.type == token.ASYNC 

180 and second_leaf is not None 

181 and second_leaf.type == token.NAME 

182 and second_leaf.value == "def" 

183 ) 

184 

185 @property 

186 def is_stub_def(self) -> bool: 

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

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

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

190 ] 

191 

192 @property 

193 def is_class_paren_empty(self) -> bool: 

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

195 

196 Those are unnecessary and should be removed. 

197 """ 

198 return ( 

199 bool(self) 

200 and len(self.leaves) == 4 

201 and self.is_class 

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

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

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

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

206 ) 

207 

208 @property 

209 def _is_triple_quoted_string(self) -> bool: 

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

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

212 return False 

213 value = self.leaves[0].value 

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

215 return True 

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

217 return True 

218 return False 

219 

220 @property 

221 def is_docstring(self) -> bool: 

222 """Is the line a docstring?""" 

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

224 

225 @property 

226 def is_chained_assignment(self) -> bool: 

227 """Is the line a chained assignment""" 

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

229 

230 @property 

231 def opens_block(self) -> bool: 

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

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

234 return False 

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

236 

237 def is_fmt_pass_converted( 

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

239 ) -> bool: 

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

241 

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

243 leaf of converted code matches. 

244 """ 

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

246 return False 

247 leaf = self.leaves[0] 

248 if ( 

249 leaf.type != STANDALONE_COMMENT 

250 or leaf.fmt_pass_converted_first_leaf is None 

251 ): 

252 return False 

253 return first_leaf_matches is None or first_leaf_matches( 

254 leaf.fmt_pass_converted_first_leaf 

255 ) 

256 

257 def contains_standalone_comments(self) -> bool: 

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

259 for leaf in self.leaves: 

260 if leaf.type == STANDALONE_COMMENT: 

261 return True 

262 

263 return False 

264 

265 def contains_implicit_multiline_string_with_comments(self) -> bool: 

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

267 for leaf_type, leaf_group_iterator in itertools.groupby( 

268 self.leaves, lambda leaf: leaf.type 

269 ): 

270 if leaf_type != token.STRING: 

271 continue 

272 leaf_list = list(leaf_group_iterator) 

273 if len(leaf_list) == 1: 

274 continue 

275 for leaf in leaf_list: 

276 if self.comments_after(leaf): 

277 return True 

278 return False 

279 

280 def contains_uncollapsable_type_comments(self) -> bool: 

281 ignored_ids = set() 

282 try: 

283 last_leaf = self.leaves[-1] 

284 ignored_ids.add(id(last_leaf)) 

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

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

287 ): 

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

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

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

291 # trailing commas and invisible. 

292 last_leaf = self.leaves[-2] 

293 ignored_ids.add(id(last_leaf)) 

294 except IndexError: 

295 return False 

296 

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

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

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

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

301 # behind a comment. 

302 comment_seen = False 

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

304 for comment in comments: 

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

306 if comment_seen or ( 

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

308 and leaf_id not in ignored_ids 

309 ): 

310 return True 

311 

312 comment_seen = True 

313 

314 return False 

315 

316 def contains_unsplittable_type_ignore(self) -> bool: 

317 if not self.leaves: 

318 return False 

319 

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

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

322 # subexpressions the ignore was meant to apply to. 

323 # 

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

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

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

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

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

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

330 # one line in the original code. 

331 

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

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

334 last_line = next( 

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

336 ) 

337 

338 if first_line == last_line: 

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

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

341 # line. 

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

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

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

345 return True 

346 

347 return False 

348 

349 def contains_multiline_strings(self) -> bool: 

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

351 

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

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

354 - there's a trailing comma here 

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

356 - it's not a one-tuple 

357 """ 

358 if not ( 

359 closing.type in CLOSING_BRACKETS 

360 and self.leaves 

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

362 ): 

363 return False 

364 

365 if closing.type == token.RBRACE: 

366 return True 

367 

368 if closing.type == token.RSQB: 

369 if ( 

370 closing.parent is not None 

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

372 and closing.opening_bracket is not None 

373 and is_one_sequence_between( 

374 closing.opening_bracket, 

375 closing, 

376 self.leaves, 

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

378 ) 

379 ): 

380 assert closing.prev_sibling is not None 

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

382 return False 

383 

384 return True 

385 

386 if self.is_import: 

387 return True 

388 

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

390 closing.opening_bracket, closing, self.leaves 

391 ): 

392 return True 

393 

394 return False 

395 

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

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

398 if ( 

399 comment.type == STANDALONE_COMMENT 

400 and self.bracket_tracker.any_open_brackets() 

401 ): 

402 comment.prefix = "" 

403 return False 

404 

405 if comment.type != token.COMMENT: 

406 return False 

407 

408 if not self.leaves: 

409 comment.type = STANDALONE_COMMENT 

410 comment.prefix = "" 

411 return False 

412 

413 last_leaf = self.leaves[-1] 

414 if ( 

415 last_leaf.type == token.RPAR 

416 and not last_leaf.value 

417 and last_leaf.parent 

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

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

420 ): 

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

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

423 # this avoids unstable formatting caused by comment migration. 

424 if len(self.leaves) < 2: 

425 comment.type = STANDALONE_COMMENT 

426 comment.prefix = "" 

427 return False 

428 

429 last_leaf = self.leaves[-2] 

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

431 return True 

432 

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

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

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

436 

437 def remove_trailing_comma(self) -> None: 

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

439 trailing_comma = self.leaves.pop() 

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

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

442 trailing_comma_comments 

443 ) 

444 

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

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

447 open_lsqb = self.bracket_tracker.get_open_lsqb() 

448 if open_lsqb is None: 

449 return False 

450 

451 subscript_start = open_lsqb.next_sibling 

452 

453 if isinstance(subscript_start, Node): 

454 if subscript_start.type == syms.listmaker: 

455 return False 

456 

457 if subscript_start.type == syms.subscriptlist: 

458 subscript_start = child_towards(subscript_start, leaf) 

459 

460 if subscript_start is None: 

461 return False 

462 

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

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

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

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

467 # whole subtree each time, which is quadratic. 

468 key = id(subscript_start) 

469 cached = self._complex_subscript_cache.get(key) 

470 if cached is None: 

471 cached = any( 

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

473 ) 

474 self._complex_subscript_cache[key] = cached 

475 return cached 

476 

477 def enumerate_with_length( 

478 self, is_reversed: bool = False 

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

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

481 

482 Stops prematurely on multiline strings and standalone comments. 

483 """ 

484 op = cast( 

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

486 enumerate_reversed if is_reversed else enumerate, 

487 ) 

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

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

490 if "\n" in leaf.value: 

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

492 

493 for comment in self.comments_after(leaf): 

494 length += len(comment.value) 

495 

496 yield index, leaf, length 

497 

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

499 return Line( 

500 mode=self.mode, 

501 depth=self.depth, 

502 inside_brackets=self.inside_brackets, 

503 should_split_rhs=self.should_split_rhs, 

504 magic_trailing_comma=self.magic_trailing_comma, 

505 ) 

506 

507 def __str__(self) -> str: 

508 """Render the line.""" 

509 if not self: 

510 return "\n" 

511 

512 indent = " " * self.depth 

513 leaves = iter(self.leaves) 

514 first = next(leaves) 

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

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

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

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

519 res += "".join(comments) 

520 

521 return res + "\n" 

522 

523 def __bool__(self) -> bool: 

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

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

526 

527 

528@dataclass 

529class RHSResult: 

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

531 

532 head: Line 

533 body: Line 

534 tail: Line 

535 opening_bracket: Leaf 

536 closing_bracket: Leaf 

537 

538 

539@dataclass 

540class LinesBlock: 

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

542 

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

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

545 """ 

546 

547 mode: Mode 

548 previous_block: Optional["LinesBlock"] 

549 original_line: Line 

550 before: int = 0 

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

552 after: int = 0 

553 form_feed: bool = False 

554 

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

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

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

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

559 

560 

561_WHITESPACE_TOKENS = ( 

562 token.NEWLINE, 

563 token.NL, 

564 token.INDENT, 

565 token.DEDENT, 

566 token.COMMENT, 

567 token.ENDMARKER, 

568) 

569 

570 

571class _DecoratedFuncInfo(NamedTuple): 

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

573 

574 name: str 

575 depth: int 

576 is_multi: bool 

577 

578 

579@dataclass 

580class EmptyLineTracker: 

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

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

583 

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

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

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

587 """ 

588 

589 mode: Mode 

590 previous_line: Line | None = None 

591 previous_block: LinesBlock | None = None 

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

593 semantic_leading_comment: LinesBlock | None = None 

594 _pyi_previous_decorated_func: _DecoratedFuncInfo | None = None 

595 

596 @staticmethod 

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

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

599 funcdef: Node | Leaf = node 

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

601 for sub in node.children: 

602 if sub.type == syms.funcdef: 

603 funcdef = sub 

604 break 

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

606 return None 

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

608 name_node = funcdef.children[1] 

609 assert isinstance(name_node, Leaf) 

610 return name_node.value 

611 

612 @staticmethod 

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

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

615 return any( 

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

617 and EmptyLineTracker._get_funcdef_name(sub) == name 

618 for sub in node.children 

619 ) 

620 

621 @staticmethod 

622 def _find_adjacent_decorated( 

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

624 ) -> Node | None: 

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

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

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

628 while sibling is not None: 

629 if sibling.type == syms.decorated: 

630 assert isinstance(sibling, Node) 

631 return sibling 

632 elif sibling.type in _WHITESPACE_TOKENS: 

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

634 else: 

635 return None 

636 return None 

637 

638 @staticmethod 

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

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

641 for child in suite.children: 

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

643 for sub in child.children: 

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

645 return EmptyLineTracker._get_funcdef_name(sub) 

646 break 

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

648 break 

649 return None 

650 

651 @staticmethod 

652 def _if_stmt_branch_has_func_named( 

653 if_stmt: Node, exclude_suite: Node, name: str 

654 ) -> bool: 

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

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

657 for child in if_stmt.children: 

658 if not isinstance(child, Node): 

659 continue 

660 if child.type != syms.suite: 

661 continue 

662 if child is exclude_suite: 

663 continue 

664 for stmt in child.children: 

665 if not isinstance(stmt, Node): 

666 continue 

667 if stmt.type != syms.decorated: 

668 continue 

669 if EmptyLineTracker._decorated_node_has_func_named(stmt, name): 

670 return True 

671 break 

672 return False 

673 

674 @staticmethod 

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

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

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

678 return None 

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

680 return line.leaves[1].value 

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

682 return line.leaves[2].value 

683 

684 @staticmethod 

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

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

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

688 return False 

689 return EmptyLineTracker._find_decorated_node(line) is not None 

690 

691 @staticmethod 

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

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

694 if not line.leaves: 

695 return None 

696 node = line.leaves[0].parent 

697 while node is not None: 

698 if node.type == syms.decorated: 

699 assert isinstance(node, Node) 

700 return node 

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

702 return None 

703 node = node.parent 

704 return None 

705 

706 @staticmethod 

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

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

709 

710 Only handles decorated functions, not decorated classes. 

711 """ 

712 if not line.is_decorator: 

713 return None 

714 decorated = EmptyLineTracker._find_decorated_node(line) 

715 if decorated is None: 

716 return None 

717 for child in decorated.children: 

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

719 return EmptyLineTracker._get_funcdef_name(child) 

720 return None 

721 

722 @staticmethod 

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

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

725 if not line.is_decorator: 

726 return False 

727 decorated = EmptyLineTracker._find_decorated_node(line) 

728 if decorated is None: 

729 return False 

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

731 

732 @staticmethod 

733 def _def_is_followed_by_same_name_decorated_func( 

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

735 ) -> bool: 

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

737 

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

739 be inside an adjacent conditional block. 

740 """ 

741 name = EmptyLineTracker._get_def_name(line) 

742 decorated = EmptyLineTracker._find_decorated_node(line) 

743 if name is None or decorated is None: 

744 return False 

745 

746 sibling = decorated.next_sibling 

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

748 sibling = sibling.next_sibling 

749 

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

751 return False 

752 if sibling.type == syms.decorated: 

753 return EmptyLineTracker._decorated_node_has_func_named(sibling, name) 

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

755 return False 

756 for child in sibling.children: 

757 if not isinstance(child, Node): 

758 continue 

759 if child.type != syms.suite: 

760 continue 

761 first_decorated_funcname = ( 

762 EmptyLineTracker._get_suite_first_decorated_funcname(child) 

763 ) 

764 return first_decorated_funcname == name 

765 return False 

766 

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

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

769 prev = self._pyi_previous_decorated_func 

770 if prev is None: 

771 return False 

772 cur_name = ( 

773 self._get_decorator_target_name(current_line) 

774 if current_line.is_decorator 

775 else self._get_def_name(current_line) 

776 ) 

777 return ( 

778 cur_name is not None 

779 and cur_name == prev.name 

780 and prev.depth == current_line.depth 

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

782 ) 

783 

784 @staticmethod 

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

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

787 

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

789 function with the same name. 

790 """ 

791 name = None 

792 for child in decorated_node.children: 

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

794 name = EmptyLineTracker._get_funcdef_name(child) 

795 break 

796 if name is None: 

797 return False 

798 adjacent = EmptyLineTracker._find_adjacent_decorated(decorated_node) 

799 return adjacent is not None and EmptyLineTracker._decorated_node_has_func_named( 

800 adjacent, name 

801 ) 

802 

803 @staticmethod 

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

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

806 

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

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

809 """ 

810 if not line.is_decorator: 

811 return False 

812 decorated_node = EmptyLineTracker._find_decorated_node(line) 

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

814 return False 

815 return EmptyLineTracker._decorated_node_starts_group(decorated_node) 

816 

817 @staticmethod 

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

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

820 

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

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

823 decorated function. 

824 """ 

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

826 return None 

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

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

829 return None 

830 return EmptyLineTracker._get_suite_first_decorated_funcname(suite) 

831 

832 @staticmethod 

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

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

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

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

837 the same ``if_stmt`` has one. 

838 """ 

839 func_name = EmptyLineTracker._get_block_first_decorated_funcname(line) 

840 if func_name is None: 

841 return False 

842 

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

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

845 return False 

846 if_stmt = suite.parent 

847 if if_stmt is None: 

848 return False 

849 

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

851 adjacent = EmptyLineTracker._find_adjacent_decorated(if_stmt) 

852 if adjacent is not None and EmptyLineTracker._decorated_node_has_func_named( 

853 adjacent, func_name 

854 ): 

855 return True 

856 

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

858 return EmptyLineTracker._if_stmt_branch_has_func_named( 

859 if_stmt, suite, func_name 

860 ) 

861 

862 @staticmethod 

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

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

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

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

867 name = EmptyLineTracker._get_decorator_target_name(line) 

868 if name is None: 

869 return False 

870 

871 decorated = EmptyLineTracker._find_decorated_node(line) 

872 if decorated is None: 

873 return False 

874 

875 suite = decorated.parent 

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

877 return False 

878 

879 if_stmt = suite.parent 

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

881 return False 

882 

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

884 for reverse in (True, False): 

885 adjacent = EmptyLineTracker._find_adjacent_decorated( 

886 if_stmt, reverse=reverse 

887 ) 

888 if ( 

889 adjacent is not None 

890 and EmptyLineTracker._decorated_node_has_func_named(adjacent, name) 

891 ): 

892 return True 

893 

894 # Check other branches of the same if_stmt. 

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

896 

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

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

899 

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

901 lines (two on module-level). 

902 """ 

903 form_feed = ( 

904 current_line.depth == 0 

905 and bool(current_line.leaves) 

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

907 ) 

908 before, after = self._maybe_empty_lines(current_line) 

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

910 before = max(0, before - previous_after) 

911 

912 # Always have one empty line after a module docstring 

913 if self._line_is_module_docstring(current_line): 

914 before = 1 

915 

916 block = LinesBlock( 

917 mode=self.mode, 

918 previous_block=self.previous_block, 

919 original_line=current_line, 

920 before=before, 

921 after=after, 

922 form_feed=form_feed, 

923 ) 

924 

925 # Maintain the semantic_leading_comment state. 

926 if current_line.is_comment: 

927 if self.previous_line is None or ( 

928 not self.previous_line.is_decorator 

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

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

931 and (self.semantic_leading_comment is None or before) 

932 ): 

933 self.semantic_leading_comment = block 

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

935 elif not current_line.is_decorator or before: 

936 self.semantic_leading_comment = None 

937 

938 # Maintain _pyi_previous_decorated_func state for overload groups. 

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

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

941 overload_groups = ( 

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

943 ) 

944 if overload_groups: 

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

946 name = self._get_def_name(current_line) 

947 if name is not None: 

948 prev = self._pyi_previous_decorated_func 

949 is_multi = ( 

950 prev is not None 

951 and prev.name == name 

952 and prev.depth == current_line.depth 

953 ) 

954 self._pyi_previous_decorated_func = _DecoratedFuncInfo( 

955 name=name, 

956 depth=current_line.depth, 

957 is_multi=is_multi 

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

959 ) 

960 elif ( 

961 not current_line.is_decorator 

962 and not current_line.is_comment 

963 and ( 

964 self._pyi_previous_decorated_func is None 

965 or ( 

966 current_line.depth <= self._pyi_previous_decorated_func.depth 

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

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

969 and not ( 

970 current_line.leaves 

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

972 ) 

973 ) 

974 ) 

975 ): 

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

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

978 # should not clear the state. 

979 self._pyi_previous_decorated_func = None 

980 

981 self.previous_line = current_line 

982 self.previous_block = block 

983 return block 

984 

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

986 previous_block = self.previous_block 

987 if not previous_block: 

988 return False 

989 if ( 

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

991 or not previous_block.original_line.is_docstring 

992 or previous_block.original_line.depth != 0 

993 or current_line.is_class 

994 or current_line.is_def 

995 ): 

996 return False 

997 while previous_block := previous_block.previous_block: 

998 if not previous_block.original_line.is_comment: 

999 return False 

1000 return True 

1001 

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

1003 max_allowed = 1 

1004 if current_line.depth == 0: 

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

1006 overload_groups = ( 

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

1008 ) 

1009 

1010 if current_line.leaves: 

1011 # Consume the first leaf's extra newlines. 

1012 first_leaf = current_line.leaves[0] 

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

1014 # The blank lines that terminate a `# fmt: off` region live in the 

1015 # prefix of the `# fmt: on` comment, not in the verbatim block, so 

1016 # capping them here would edit formatting that was opted out of. 

1017 if not ( 

1018 first_leaf.type == STANDALONE_COMMENT 

1019 and contains_fmt_directive(first_leaf.value, FMT_ON) 

1020 and self.previous_line is not None 

1021 and self.previous_line.is_fmt_pass_converted() 

1022 ): 

1023 before = min(before, max_allowed) 

1024 first_leaf.prefix = "" 

1025 else: 

1026 before = 0 

1027 

1028 user_had_newline = bool(before) 

1029 depth = current_line.depth 

1030 

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

1032 previous_def = None 

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

1034 previous_def = self.previous_defs.pop() 

1035 if current_line.is_def or current_line.is_class: 

1036 self.previous_defs.append(current_line) 

1037 

1038 if self.previous_line is None: 

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

1040 return 0, 0 

1041 

1042 if current_line.is_docstring: 

1043 if self.previous_line.is_class: 

1044 return 0, 1 

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

1046 return 0, 0 

1047 

1048 if previous_def is not None: 

1049 assert self.previous_line is not None 

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

1051 # passed to _maybe_empty_lines_for_class_or_def which may override 

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

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

1054 if self.mode.is_pyi: 

1055 if previous_def.is_class and not previous_def.is_stub_class: 

1056 before = 1 

1057 elif ( 

1058 overload_groups 

1059 and self._pyi_previous_decorated_func is not None 

1060 and self._pyi_previous_decorated_func.is_multi 

1061 and not current_line.is_comment 

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

1063 and not ( 

1064 current_line.leaves 

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

1066 ) 

1067 ): 

1068 if self._is_in_current_group(current_line): 

1069 before = 0 

1070 elif current_line.opens_block and ( 

1071 self._get_block_first_decorated_funcname(current_line) 

1072 == self._pyi_previous_decorated_func.name 

1073 ): 

1074 before = 0 

1075 else: 

1076 before = 1 

1077 elif ( 

1078 overload_groups 

1079 and current_line.opens_block 

1080 and current_line.leaves 

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

1082 and self._block_is_part_of_overload_group(current_line) 

1083 ): 

1084 # else/elif continuing a conditional overload group: 

1085 # don't insert a blank line above. 

1086 before = 0 

1087 elif ( 

1088 Preview.pyi_blank_line_after_function_docstring in self.mode 

1089 and previous_def.is_def 

1090 and self.previous_line.is_docstring 

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

1092 and not self._def_is_followed_by_same_name_decorated_func( 

1093 previous_def, 

1094 include_conditional_blocks=overload_groups, 

1095 ) 

1096 ): 

1097 before = 1 

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

1099 if ( 

1100 overload_groups 

1101 and current_line.opens_block 

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

1103 and self._block_is_part_of_overload_group(current_line) 

1104 ): 

1105 before = 1 

1106 else: 

1107 # Empty lines between attributes and methods should 

1108 # be preserved. 

1109 before = 1 if user_had_newline else 0 

1110 elif ( 

1111 overload_groups 

1112 and current_line.is_comment 

1113 and self._pyi_previous_decorated_func is not None 

1114 and self._pyi_previous_decorated_func.depth == current_line.depth 

1115 ): 

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

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

1118 # For non-overload cases the comment_to_add_newlines 

1119 # mechanism will retroactively add needed blank lines. 

1120 before = 1 if user_had_newline else 0 

1121 elif depth: 

1122 before = 0 

1123 else: 

1124 before = 1 

1125 else: 

1126 if depth: 

1127 before = 1 

1128 elif ( 

1129 not depth 

1130 and previous_def.depth 

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

1132 and ( 

1133 current_line.leaves[0].value 

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

1135 ) 

1136 ): 

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

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

1139 # conditional function definitions that are technically top-level 

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

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

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

1143 # already. 

1144 before = 1 

1145 else: 

1146 before = 2 

1147 

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

1149 return self._maybe_empty_lines_for_class_or_def( 

1150 current_line, before, user_had_newline 

1151 ) 

1152 

1153 if ( 

1154 Preview.fmt_off_class_blank_lines in self.mode 

1155 and self.previous_line.is_import 

1156 and self.previous_line.depth == 0 

1157 and current_line.depth == 0 

1158 and current_line.is_fmt_pass_converted( 

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

1160 ) 

1161 ): 

1162 return 2, 0 

1163 

1164 if ( 

1165 self.previous_line.is_import 

1166 and self.previous_line.depth == 0 

1167 and current_line.depth == 0 

1168 and not current_line.is_import 

1169 and not current_line.is_fmt_pass_converted(first_leaf_matches=is_import) 

1170 ): 

1171 return 1, 0 

1172 

1173 if ( 

1174 self.previous_line.is_import 

1175 and not current_line.is_import 

1176 and not current_line.is_fmt_pass_converted(first_leaf_matches=is_import) 

1177 and depth == self.previous_line.depth 

1178 ): 

1179 return (before or 1), 0 

1180 

1181 return before, 0 

1182 

1183 def _maybe_empty_lines_for_class_or_def( 

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

1185 ) -> tuple[int, int]: 

1186 assert self.previous_line is not None 

1187 overload_groups = ( 

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

1189 ) 

1190 

1191 if self.previous_line.is_decorator: 

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

1193 # Insert an empty line after a decorated stub class 

1194 return 0, 1 

1195 return 0, 0 

1196 

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

1198 self.previous_line.is_class or self.previous_line.is_def 

1199 ): 

1200 if self.mode.is_pyi: 

1201 return 0, 0 

1202 return 1 if user_had_newline else 0, 0 

1203 

1204 comment_to_add_newlines: LinesBlock | None = None 

1205 if ( 

1206 self.previous_line.is_comment 

1207 and self.previous_line.depth == current_line.depth 

1208 and before == 0 

1209 ): 

1210 slc = self.semantic_leading_comment 

1211 if ( 

1212 slc is not None 

1213 and slc.previous_block is not None 

1214 and not slc.previous_block.original_line.is_class 

1215 and not slc.previous_block.original_line.opens_block 

1216 and slc.before <= 1 

1217 ): 

1218 comment_to_add_newlines = slc 

1219 else: 

1220 return 0, 0 

1221 

1222 if self.mode.is_pyi: 

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

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

1225 newlines = 0 

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

1227 newlines = 1 

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

1229 # No blank line between classes with an empty body 

1230 newlines = 0 

1231 else: 

1232 newlines = 1 

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

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

1235 # body. 

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

1237 if overload_groups and self._is_in_current_group(current_line): 

1238 newlines = 0 

1239 else: 

1240 newlines = 1 

1241 elif ( 

1242 overload_groups 

1243 and self._pyi_previous_decorated_func is not None 

1244 and self._pyi_previous_decorated_func.is_multi 

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

1246 ): 

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

1248 elif overload_groups and self._is_in_current_group(current_line): 

1249 # A comment between overloads may prevent is_multi from being 

1250 # set, but _is_in_current_group still detects name continuity. 

1251 newlines = 0 

1252 elif ( 

1253 overload_groups 

1254 and current_line.is_decorator 

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

1256 and self._is_start_of_decorated_group(current_line) 

1257 and ( 

1258 self._pyi_previous_decorated_func is None 

1259 or ( 

1260 self._pyi_previous_decorated_func.name 

1261 != self._get_decorator_target_name(current_line) 

1262 ) 

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

1264 ) 

1265 ): 

1266 newlines = 1 

1267 elif ( 

1268 current_line.is_def or current_line.is_decorator 

1269 ) and not self.previous_line.is_def: 

1270 if ( 

1271 overload_groups 

1272 and current_line.is_decorator 

1273 and self.previous_line.is_comment 

1274 and self._is_decorator_in_conditional_overload(current_line) 

1275 ): 

1276 # Comment before an overload inside a conditional block: 

1277 # remove blank lines between the comment and decorator. 

1278 newlines = 0 

1279 elif current_line.depth: 

1280 # In classes empty lines between attributes and methods should 

1281 # be preserved. 

1282 newlines = min(1, before) 

1283 else: 

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

1285 # decorators) and a block of non-functions 

1286 newlines = 1 

1287 elif ( 

1288 Preview.pyi_blank_line_before_decorated_class in self.mode 

1289 and current_line.is_decorator 

1290 and self._decorator_decorates_class(current_line) 

1291 ): 

1292 newlines = 1 

1293 else: 

1294 newlines = 0 

1295 else: 

1296 newlines = 1 if current_line.depth else 2 

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

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

1299 if self.previous_line.is_stub_def and not user_had_newline: 

1300 newlines = 0 

1301 if comment_to_add_newlines is not None: 

1302 previous_block = comment_to_add_newlines.previous_block 

1303 if previous_block is not None: 

1304 comment_to_add_newlines.before = ( 

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

1306 ) 

1307 newlines = 0 

1308 return newlines, 0 

1309 

1310 

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

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

1313 index = len(sequence) - 1 

1314 for element in reversed(sequence): 

1315 yield (index, element) 

1316 index -= 1 

1317 

1318 

1319def append_leaves( 

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

1321) -> None: 

1322 """ 

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

1324 underlying Node structure where appropriate. 

1325 

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

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

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

1329 the new leaves. 

1330 

1331 Pre-conditions: 

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

1333 """ 

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

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

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

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

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

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

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

1341 # formatting pass). 

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

1343 for old_leaf in leaves: 

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

1345 parent = old_leaf.parent 

1346 if parent is not None: 

1347 children = parent.children 

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

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

1350 index += 1 

1351 if index < len(children): 

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

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

1354 parent.set_child(index, new_leaf) 

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

1356 else: 

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

1358 # full scan so behaviour is unchanged. 

1359 replace_child(old_leaf, new_leaf) 

1360 new_line.append(new_leaf, preformatted=preformatted) 

1361 

1362 for comment_leaf in old_line.comments_after(old_leaf): 

1363 new_line.append(comment_leaf, preformatted=True) 

1364 

1365 

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

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

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

1369 if it should be inlined or split up. 

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

1371 """ 

1372 if not line_str: 

1373 line_str = line_to_string(line) 

1374 

1375 if line.contains_standalone_comments(): 

1376 return False 

1377 if "\n" not in line_str: 

1378 # No multiline strings (MLS) present 

1379 return str_width(line_str) <= mode.line_length 

1380 

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

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

1383 return False 

1384 

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

1386 # tracking aspects such as depth and comma existence, 

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

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

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

1390 # Includes special case for trailing commas. 

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

1392 multiline_string: Leaf | None = None 

1393 # store the leaves that contain parts of the MLS 

1394 multiline_string_contexts: list[LN] = [] 

1395 

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

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

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

1399 

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

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

1402 if max_level_to_update == math.inf: 

1403 had_comma: int | None = None 

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

1405 commas.append(0) 

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

1407 had_comma = commas.pop() 

1408 if ( 

1409 had_comma is not None 

1410 and multiline_string is not None 

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

1412 ): 

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

1414 max_level_to_update = leaf.bracket_depth 

1415 if had_comma > 0: 

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

1417 return False 

1418 

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

1420 # Inside brackets, ignore trailing comma 

1421 # directly after MLS/MLS-containing expression 

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

1423 ignore_ctxs += multiline_string_contexts 

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

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

1426 ): 

1427 commas[leaf.bracket_depth] += 1 

1428 if max_level_to_update != math.inf: 

1429 max_level_to_update = min(max_level_to_update, leaf.bracket_depth) 

1430 

1431 if is_multiline_string(leaf): 

1432 if leaf.parent and ( 

1433 leaf.parent.type == syms.test 

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

1435 ): 

1436 # Keep ternary and dictionary values parenthesized 

1437 return False 

1438 if len(multiline_string_contexts) > 0: 

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

1440 return False 

1441 multiline_string = leaf 

1442 ctx: LN = leaf 

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

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

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

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

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

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

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

1450 # many triple-quoted values). 

1451 while ( 

1452 id(first_leaf(ctx)) in line_leaf_ids 

1453 and id(last_leaf(ctx)) in line_leaf_ids 

1454 ): 

1455 multiline_string_contexts.append(ctx) 

1456 if ctx.parent is None: 

1457 break 

1458 ctx = ctx.parent 

1459 

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

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

1462 if len(multiline_string_contexts) == 0: 

1463 return True 

1464 

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

1466 

1467 

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

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

1470 

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

1472 avoid some unfortunate formattings (mostly around wrapping unsplittable code 

1473 in unnecessary parentheses). 

1474 """ 

1475 leaves = line.leaves 

1476 if len(leaves) < 2: 

1477 return False 

1478 

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

1480 call_count = 0 

1481 dot_count = 0 

1482 next = leaves[-1] 

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

1484 if leaf.type in OPENING_BRACKETS: 

1485 if next.type not in CLOSING_BRACKETS: 

1486 return False 

1487 

1488 call_count += 1 

1489 elif leaf.type == token.DOT: 

1490 dot_count += 1 

1491 elif leaf.type == token.NAME: 

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

1493 return False 

1494 

1495 elif leaf.type not in CLOSING_BRACKETS: 

1496 return False 

1497 

1498 if dot_count > 1 and call_count > 1: 

1499 return False 

1500 

1501 return True 

1502 

1503 

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

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

1506 

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

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

1509 """ 

1510 depth = 0 

1511 for leaf in head.leaves: 

1512 if not leaf.value: 

1513 continue 

1514 if leaf.type in OPENING_BRACKETS: 

1515 depth += 1 

1516 elif leaf.type in CLOSING_BRACKETS: 

1517 depth -= 1 

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

1519 return True 

1520 return False 

1521 

1522 

1523def can_omit_invisible_parens( 

1524 rhs: RHSResult, 

1525 line_length: int, 

1526 mode: Mode, 

1527) -> bool: 

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

1529 

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

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

1532 are too long. 

1533 """ 

1534 line = rhs.body 

1535 

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

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

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

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

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

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

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

1543 return False 

1544 

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

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

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

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

1549 if rhs.head.leaves: 

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

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

1552 

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

1554 # conflict with type: ignore comments in the body 

1555 if head_comments: 

1556 has_type_ignore_in_head = any( 

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

1558 for comment in head_comments 

1559 ) 

1560 has_other_comment_in_head = any( 

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

1562 for comment in head_comments 

1563 ) 

1564 

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

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

1567 has_type_ignore_in_body = False 

1568 has_other_comment_in_body = False 

1569 for leaf in rhs.body.leaves: 

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

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

1572 has_type_ignore_in_body = True 

1573 else: 

1574 has_other_comment_in_body = True 

1575 

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

1577 # could end up on the same line 

1578 if (has_type_ignore_in_head and has_other_comment_in_body) or ( 

1579 has_other_comment_in_head and has_type_ignore_in_body 

1580 ): 

1581 return False 

1582 

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

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

1585 closing_bracket: Leaf | None = None 

1586 for leaf in reversed(line.leaves): 

1587 if closing_bracket and leaf is closing_bracket.opening_bracket: 

1588 closing_bracket = None 

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

1590 return False 

1591 if ( 

1592 not closing_bracket 

1593 and leaf.type in CLOSING_BRACKETS 

1594 and leaf.opening_bracket in line.leaves 

1595 and leaf.value 

1596 ): 

1597 closing_bracket = leaf 

1598 

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

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

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

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

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

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

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

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

1607 if ( 

1608 Preview.fix_unnecessary_parens_in_indexed_assignment in mode 

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

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

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

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

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

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

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

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

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

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

1619 and not _is_annotated_assignment(rhs.head) 

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

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

1622 ): 

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

1624 tail_line_length = 4 * line.depth + 4 

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

1626 tail_line_length += leaf_length 

1627 if tail_line_length <= line_length: 

1628 return True 

1629 

1630 bt = line.bracket_tracker 

1631 if not bt.delimiters: 

1632 # Without delimiters the optional parentheses are useless. 

1633 return True 

1634 

1635 max_priority = bt.max_delimiter_priority() 

1636 delimiter_count = bt.delimiter_count_with_priority(max_priority) 

1637 if delimiter_count > 1: 

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

1639 return False 

1640 

1641 if delimiter_count == 1: 

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

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

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

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

1646 # line. 

1647 return False 

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

1649 # careful considerations for all possible cases. See 

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

1651 

1652 if max_priority == DOT_PRIORITY: 

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

1654 return True 

1655 

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

1657 

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

1659 # a bracket. 

1660 first = line.leaves[0] 

1661 second = line.leaves[1] 

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

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

1664 return True 

1665 

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

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

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

1669 

1670 penultimate = line.leaves[-2] 

1671 last = line.leaves[-1] 

1672 

1673 if ( 

1674 last.type == token.RPAR 

1675 or last.type == token.RBRACE 

1676 or ( 

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

1678 # it looks weird 

1679 last.type == token.RSQB 

1680 and last.parent 

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

1682 ) 

1683 ): 

1684 if penultimate.type in OPENING_BRACKETS: 

1685 # Empty brackets don't help. 

1686 return False 

1687 

1688 if is_multiline_string(first): 

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

1690 # unnecessary. 

1691 return True 

1692 

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

1694 return True 

1695 

1696 return False 

1697 

1698 

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

1700 """See `can_omit_invisible_parens`.""" 

1701 remainder = False 

1702 length = 4 * line.depth 

1703 _index = -1 

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

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

1706 remainder = True 

1707 if remainder: 

1708 length += leaf_length 

1709 if length > line_length: 

1710 break 

1711 

1712 if leaf.type in OPENING_BRACKETS: 

1713 # There are brackets we can further split on. 

1714 remainder = False 

1715 

1716 else: 

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

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

1719 return True 

1720 

1721 return False 

1722 

1723 

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

1725 """See `can_omit_invisible_parens`.""" 

1726 length = 4 * line.depth 

1727 seen_other_brackets = False 

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

1729 length += leaf_length 

1730 if leaf is last.opening_bracket: 

1731 if seen_other_brackets or length <= line_length: 

1732 return True 

1733 

1734 elif leaf.type in OPENING_BRACKETS: 

1735 # There are brackets we can further split on. 

1736 seen_other_brackets = True 

1737 

1738 return False 

1739 

1740 

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

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

1743 

1744 WARNING: This is known to be computationally expensive. 

1745 """ 

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