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

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

862 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 COMPARATORS, 

14 MATH_OPERATORS, 

15 OPENING_BRACKETS, 

16 STANDALONE_COMMENT, 

17 TEST_DESCENDANTS, 

18 child_towards, 

19 first_leaf, 

20 is_docstring, 

21 is_generator, 

22 is_import, 

23 is_multiline_string, 

24 is_one_element_subscript, 

25 is_one_sequence_between, 

26 is_one_tuple, 

27 is_tuple, 

28 is_type_comment, 

29 is_type_ignore_comment, 

30 is_with_or_async_with_stmt, 

31 last_leaf, 

32 make_simple_prefix, 

33 replace_child, 

34 syms, 

35 whitespace, 

36) 

37from black.strings import str_width 

38from blib2to3.pgen2 import token 

39from blib2to3.pytree import Leaf, Node 

40 

41# types 

42T = TypeVar("T") 

43Index = int 

44LeafID = int 

45LN = Union[Leaf, Node] 

46 

47 

48@dataclass 

49class Line: 

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

51 

52 mode: Mode = field(repr=False) 

53 depth: int = 0 

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

55 # keys ordered like `leaves` 

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

57 bracket_tracker: BracketTracker = field(default_factory=BracketTracker) 

58 inside_brackets: bool = False 

59 should_split_rhs: bool = False 

60 magic_trailing_comma: Leaf | None = None 

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

62 default_factory=dict, repr=False 

63 ) 

64 

65 def append( 

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

67 ) -> None: 

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

69 

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

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

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

73 demoted from being delimiters. 

74 

75 Inline comments are put aside. 

76 """ 

77 has_value = ( 

78 leaf.type in BRACKETS 

79 # empty fstring and tstring middles must not be truncated 

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

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

82 ) 

83 if not has_value: 

84 return 

85 

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

87 del self.leaves[-2:] 

88 if self.leaves and not preformatted: 

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

90 # imports, for which we only preserve newlines. 

91 leaf.prefix += whitespace( 

92 leaf, 

93 complex_subscript=self.is_complex_subscript(leaf), 

94 mode=self.mode, 

95 ) 

96 if self.inside_brackets or not preformatted or track_bracket: 

97 self.bracket_tracker.mark(leaf) 

98 if self.mode.magic_trailing_comma: 

99 if self.has_magic_trailing_comma(leaf): 

100 self.magic_trailing_comma = leaf 

101 elif self.has_magic_trailing_comma(leaf) and not ( 

102 # The trailing comma of a one-element tuple or subscript is 

103 # syntactically required, not magic, so it must never be removed. 

104 # This is normally caught by has_magic_trailing_comma, but that 

105 # check misses it when the opening bracket was split onto an 

106 # earlier line (for example by a standalone comment inside the 

107 # brackets), so verify against the tree here before dropping it. 

108 leaf.parent is not None 

109 and (is_one_tuple(leaf.parent) or is_one_element_subscript(leaf.parent)) 

110 ): 

111 self.remove_trailing_comma() 

112 if not self.append_comment(leaf): 

113 self.leaves.append(leaf) 

114 

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

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

117 

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

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

120 """ 

121 if ( 

122 self.bracket_tracker.depth == 0 

123 or self.bracket_tracker.any_open_for_or_lambda() 

124 ): 

125 if self.is_comment: 

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

127 

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

129 raise ValueError( 

130 "cannot append standalone comments to a populated line" 

131 ) 

132 

133 self.append(leaf, preformatted=preformatted) 

134 

135 @property 

136 def is_comment(self) -> bool: 

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

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

139 

140 @property 

141 def is_decorator(self) -> bool: 

142 """Is this line a decorator?""" 

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

144 

145 @property 

146 def is_import(self) -> bool: 

147 """Is this an import line?""" 

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

149 

150 @property 

151 def is_with_or_async_with_stmt(self) -> bool: 

152 """Is this a with_stmt line?""" 

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

154 

155 @property 

156 def is_class(self) -> bool: 

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

158 return ( 

159 bool(self) 

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

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

162 ) 

163 

164 @property 

165 def is_stub_class(self) -> bool: 

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

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

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

169 ] 

170 

171 @property 

172 def is_def(self) -> bool: 

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

174 try: 

175 first_leaf = self.leaves[0] 

176 except IndexError: 

177 return False 

178 

179 try: 

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

181 except IndexError: 

182 second_leaf = None 

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

184 first_leaf.type == token.ASYNC 

185 and second_leaf is not None 

186 and second_leaf.type == token.NAME 

187 and second_leaf.value == "def" 

188 ) 

189 

190 @property 

191 def is_stub_def(self) -> bool: 

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

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

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

195 ] 

196 

197 @property 

198 def is_class_paren_empty(self) -> bool: 

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

200 

201 Those are unnecessary and should be removed. 

202 """ 

203 return ( 

204 bool(self) 

205 and len(self.leaves) == 4 

206 and self.is_class 

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

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

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

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

211 ) 

212 

213 @property 

214 def is_docstring(self) -> bool: 

215 """Is the line a docstring?""" 

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

217 

218 @property 

219 def is_chained_assignment(self) -> bool: 

220 """Is the line a chained assignment""" 

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

222 

223 @property 

224 def opens_block(self) -> bool: 

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

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

227 return False 

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

229 

230 def is_fmt_pass_converted( 

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

232 ) -> bool: 

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

234 

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

236 leaf of converted code matches. 

237 """ 

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

239 return False 

240 leaf = self.leaves[0] 

241 if ( 

242 leaf.type != STANDALONE_COMMENT 

243 or leaf.fmt_pass_converted_first_leaf is None 

244 ): 

245 return False 

246 return first_leaf_matches is None or first_leaf_matches( 

247 leaf.fmt_pass_converted_first_leaf 

248 ) 

249 

250 def contains_standalone_comments(self) -> bool: 

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

252 for leaf in self.leaves: 

253 if leaf.type == STANDALONE_COMMENT: 

254 return True 

255 

256 return False 

257 

258 def contains_implicit_multiline_string_with_comments(self) -> bool: 

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

260 for leaf_type, leaf_group_iterator in itertools.groupby( 

261 self.leaves, lambda leaf: leaf.type 

262 ): 

263 if leaf_type != token.STRING: 

264 continue 

265 leaf_list = list(leaf_group_iterator) 

266 if len(leaf_list) == 1: 

267 continue 

268 if leaf_list[-1] is self.leaves[-1] and any(self.comments.values()): 

269 # Joining the line would print its comments after these strings. 

270 return True 

271 for leaf in leaf_list: 

272 if self.comments_after(leaf): 

273 return True 

274 return False 

275 

276 def contains_uncollapsable_type_comments(self) -> bool: 

277 ignored_ids = set() 

278 try: 

279 last_leaf = self.leaves[-1] 

280 ignored_ids.add(id(last_leaf)) 

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

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

283 ): 

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

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

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

287 # trailing commas and invisible. 

288 last_leaf = self.leaves[-2] 

289 ignored_ids.add(id(last_leaf)) 

290 except IndexError: 

291 return False 

292 

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

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

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

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

297 # behind a comment. 

298 comment_seen = False 

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

300 for comment in comments: 

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

302 if comment_seen or ( 

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

304 and leaf_id not in ignored_ids 

305 ): 

306 return True 

307 

308 comment_seen = True 

309 

310 return False 

311 

312 def contains_multiple_type_ignores_at_current_depth(self) -> bool: 

313 count = 0 

314 for leaf in self.leaves: 

315 if leaf.bracket_depth != 0: 

316 continue 

317 for comment in self.comments_after(leaf): 

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

319 count += 1 

320 if count > 1: 

321 return True 

322 return False 

323 

324 def contains_unsplittable_type_ignore(self) -> bool: 

325 if not self.leaves: 

326 return False 

327 

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

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

330 # subexpressions the ignore was meant to apply to. 

331 # 

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

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

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

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

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

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

338 # one line in the original code. 

339 

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

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

342 last_line = next( 

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

344 ) 

345 

346 if first_line == last_line and first_line != 0: 

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

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

349 # line. 

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

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

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

353 return True 

354 

355 return False 

356 

357 def contains_multiline_strings(self) -> bool: 

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

359 

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

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

362 - there's a trailing comma here 

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

364 - it's not a one-tuple 

365 """ 

366 if not ( 

367 closing.type in CLOSING_BRACKETS 

368 and self.leaves 

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

370 ): 

371 return False 

372 

373 if closing.type == token.RBRACE: 

374 return True 

375 

376 if closing.type == token.RSQB: 

377 if ( 

378 closing.parent is not None 

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

380 and closing.opening_bracket is not None 

381 and is_one_sequence_between( 

382 closing.opening_bracket, 

383 closing, 

384 self.leaves, 

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

386 ) 

387 ): 

388 assert closing.prev_sibling is not None 

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

390 return False 

391 

392 return True 

393 

394 if self.is_import: 

395 return True 

396 

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

398 closing.opening_bracket, closing, self.leaves 

399 ): 

400 return True 

401 

402 return False 

403 

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

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

406 if ( 

407 comment.type == STANDALONE_COMMENT 

408 and self.bracket_tracker.any_open_brackets() 

409 ): 

410 comment.prefix = "" 

411 return False 

412 

413 if comment.type != token.COMMENT: 

414 return False 

415 

416 if not self.leaves: 

417 comment.type = STANDALONE_COMMENT 

418 comment.prefix = "" 

419 return False 

420 

421 last_leaf = self.leaves[-1] 

422 if ( 

423 last_leaf.type == token.RPAR 

424 and not last_leaf.value 

425 and last_leaf.parent 

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

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

428 and not self.contains_standalone_comments() 

429 ): 

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

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

432 # this avoids unstable formatting caused by comment migration. If the 

433 # parens contain standalone comments they are going to stay visible, so 

434 # the comment belongs to the closing paren, as it was written. 

435 if len(self.leaves) >= 2: 

436 last_leaf = self.leaves[-2] 

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

438 return True 

439 

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

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

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

443 

444 def remove_trailing_comma(self) -> None: 

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

446 trailing_comma = self.leaves.pop() 

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

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

449 trailing_comma_comments 

450 ) 

451 

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

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

454 open_lsqb = self.bracket_tracker.get_open_lsqb() 

455 if open_lsqb is None: 

456 return False 

457 

458 subscript_start = open_lsqb.next_sibling 

459 

460 if isinstance(subscript_start, Node): 

461 if subscript_start.type == syms.listmaker: 

462 return False 

463 

464 if subscript_start.type == syms.subscriptlist: 

465 subscript_start = child_towards(subscript_start, leaf) 

466 

467 if subscript_start is None: 

468 return False 

469 

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

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

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

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

474 # whole subtree each time, which is quadratic. 

475 key = id(subscript_start) 

476 cached = self._complex_subscript_cache.get(key) 

477 if cached is None: 

478 cached = any( 

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

480 ) 

481 self._complex_subscript_cache[key] = cached 

482 return cached 

483 

484 def enumerate_with_length( 

485 self, is_reversed: bool = False 

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

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

488 

489 Stops prematurely on multiline strings and standalone comments. 

490 """ 

491 op = cast( 

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

493 enumerate_reversed if is_reversed else enumerate, 

494 ) 

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

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

497 if "\n" in leaf.value: 

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

499 

500 for comment in self.comments_after(leaf): 

501 length += len(comment.value) 

502 

503 yield index, leaf, length 

504 

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

506 return Line( 

507 mode=self.mode, 

508 depth=self.depth, 

509 inside_brackets=self.inside_brackets, 

510 should_split_rhs=self.should_split_rhs, 

511 magic_trailing_comma=self.magic_trailing_comma, 

512 ) 

513 

514 def __str__(self) -> str: 

515 """Render the line.""" 

516 if not self: 

517 return "\n" 

518 

519 indent = " " * self.depth 

520 leaves = iter(self.leaves) 

521 first = next(leaves) 

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

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

524 comments = list(itertools.chain.from_iterable(self.comments.values())) 

525 if len(comments) > 1: 

526 # Python only recognizes a `# type: ignore` at the start of a comment, so 

527 # it has to come first when comments from several lines end up on one. 

528 comments.sort( 

529 key=lambda comment: not is_type_ignore_comment(comment, mode=self.mode) 

530 ) 

531 res += "".join(str(comment) for comment in comments) 

532 

533 return res + "\n" 

534 

535 def __bool__(self) -> bool: 

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

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

538 

539 

540@dataclass 

541class RHSResult: 

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

543 

544 head: Line 

545 body: Line 

546 tail: Line 

547 opening_bracket: Leaf 

548 closing_bracket: Leaf 

549 

550 

551@dataclass 

552class LinesBlock: 

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

554 

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

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

557 """ 

558 

559 mode: Mode 

560 previous_block: Optional["LinesBlock"] 

561 original_line: Line 

562 before: int = 0 

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

564 after: int = 0 

565 form_feed: bool = False 

566 

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

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

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

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

571 

572 

573_WHITESPACE_TOKENS = ( 

574 token.NEWLINE, 

575 token.NL, 

576 token.INDENT, 

577 token.DEDENT, 

578 token.COMMENT, 

579 token.ENDMARKER, 

580) 

581 

582 

583class _DecoratedFuncInfo(NamedTuple): 

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

585 

586 name: str 

587 depth: int 

588 is_multi: bool 

589 

590 

591@dataclass 

592class EmptyLineTracker: 

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

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

595 

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

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

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

599 """ 

600 

601 mode: Mode 

602 previous_line: Line | None = None 

603 previous_block: LinesBlock | None = None 

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

605 semantic_leading_comment: LinesBlock | None = None 

606 _pyi_previous_decorated_func: _DecoratedFuncInfo | None = None 

607 

608 @staticmethod 

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

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

611 funcdef: Node | Leaf = node 

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

613 for sub in node.children: 

614 if sub.type == syms.funcdef: 

615 funcdef = sub 

616 break 

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

618 return None 

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

620 # `# fmt: skip` turns the leaves of a one-line definition into a 

621 # STANDALONE_COMMENT, so there may be no name left to read. 

622 if len(funcdef.children) < 2 or funcdef.children[1].type != token.NAME: 

623 return None 

624 name_node = funcdef.children[1] 

625 assert isinstance(name_node, Leaf) 

626 return name_node.value 

627 

628 @staticmethod 

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

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

631 return any( 

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

633 and EmptyLineTracker._get_funcdef_name(sub) == name 

634 for sub in node.children 

635 ) 

636 

637 @staticmethod 

638 def _find_adjacent_decorated( 

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

640 ) -> Node | None: 

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

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

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

644 while sibling is not None: 

645 if sibling.type == syms.decorated: 

646 assert isinstance(sibling, Node) 

647 return sibling 

648 elif sibling.type in _WHITESPACE_TOKENS: 

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

650 else: 

651 return None 

652 return None 

653 

654 @staticmethod 

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

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

657 for child in suite.children: 

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

659 for sub in child.children: 

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

661 return EmptyLineTracker._get_funcdef_name(sub) 

662 break 

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

664 break 

665 return None 

666 

667 @staticmethod 

668 def _if_stmt_branch_has_func_named( 

669 if_stmt: Node, exclude_suite: Node, name: str 

670 ) -> bool: 

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

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

673 for child in if_stmt.children: 

674 if not isinstance(child, Node): 

675 continue 

676 if child.type != syms.suite: 

677 continue 

678 if child is exclude_suite: 

679 continue 

680 for stmt in child.children: 

681 if not isinstance(stmt, Node): 

682 continue 

683 if stmt.type != syms.decorated: 

684 continue 

685 if EmptyLineTracker._decorated_node_has_func_named(stmt, name): 

686 return True 

687 break 

688 return False 

689 

690 @staticmethod 

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

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

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

694 return None 

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

696 return line.leaves[1].value 

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

698 return line.leaves[2].value 

699 

700 @staticmethod 

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

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

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

704 return False 

705 return EmptyLineTracker._find_decorated_node(line) is not None 

706 

707 @staticmethod 

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

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

710 if not line.leaves: 

711 return None 

712 node = line.leaves[0].parent 

713 while node is not None: 

714 if node.type == syms.decorated: 

715 assert isinstance(node, Node) 

716 return node 

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

718 return None 

719 node = node.parent 

720 return None 

721 

722 @staticmethod 

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

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

725 

726 Only handles decorated functions, not decorated classes. 

727 """ 

728 if not line.is_decorator: 

729 return None 

730 decorated = EmptyLineTracker._find_decorated_node(line) 

731 if decorated is None: 

732 return None 

733 for child in decorated.children: 

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

735 return EmptyLineTracker._get_funcdef_name(child) 

736 return None 

737 

738 @staticmethod 

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

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

741 if not line.is_decorator: 

742 return False 

743 decorated = EmptyLineTracker._find_decorated_node(line) 

744 if decorated is None: 

745 return False 

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

747 

748 @staticmethod 

749 def _def_is_followed_by_same_name_decorated_func( 

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

751 ) -> bool: 

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

753 

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

755 be inside an adjacent conditional block. 

756 """ 

757 name = EmptyLineTracker._get_def_name(line) 

758 decorated = EmptyLineTracker._find_decorated_node(line) 

759 if name is None or decorated is None: 

760 return False 

761 

762 sibling = decorated.next_sibling 

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

764 sibling = sibling.next_sibling 

765 

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

767 return False 

768 if sibling.type == syms.decorated: 

769 return EmptyLineTracker._decorated_node_has_func_named(sibling, name) 

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

771 return False 

772 for child in sibling.children: 

773 if not isinstance(child, Node): 

774 continue 

775 if child.type != syms.suite: 

776 continue 

777 first_decorated_funcname = ( 

778 EmptyLineTracker._get_suite_first_decorated_funcname(child) 

779 ) 

780 return first_decorated_funcname == name 

781 return False 

782 

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

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

785 prev = self._pyi_previous_decorated_func 

786 if prev is None: 

787 return False 

788 cur_name = ( 

789 self._get_decorator_target_name(current_line) 

790 if current_line.is_decorator 

791 else self._get_def_name(current_line) 

792 ) 

793 return ( 

794 cur_name is not None 

795 and cur_name == prev.name 

796 and prev.depth == current_line.depth 

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

798 ) 

799 

800 @staticmethod 

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

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

803 

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

805 function with the same name. 

806 """ 

807 name = None 

808 for child in decorated_node.children: 

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

810 name = EmptyLineTracker._get_funcdef_name(child) 

811 break 

812 if name is None: 

813 return False 

814 adjacent = EmptyLineTracker._find_adjacent_decorated(decorated_node) 

815 return adjacent is not None and EmptyLineTracker._decorated_node_has_func_named( 

816 adjacent, name 

817 ) 

818 

819 @staticmethod 

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

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

822 

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

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

825 """ 

826 if not line.is_decorator: 

827 return False 

828 decorated_node = EmptyLineTracker._find_decorated_node(line) 

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

830 return False 

831 return EmptyLineTracker._decorated_node_starts_group(decorated_node) 

832 

833 @staticmethod 

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

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

836 

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

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

839 decorated function. 

840 """ 

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

842 return None 

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

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

845 return None 

846 return EmptyLineTracker._get_suite_first_decorated_funcname(suite) 

847 

848 @staticmethod 

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

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

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

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

853 the same ``if_stmt`` has one. 

854 """ 

855 func_name = EmptyLineTracker._get_block_first_decorated_funcname(line) 

856 if func_name is None: 

857 return False 

858 

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

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

861 return False 

862 if_stmt = suite.parent 

863 if if_stmt is None: 

864 return False 

865 

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

867 adjacent = EmptyLineTracker._find_adjacent_decorated(if_stmt) 

868 if adjacent is not None and EmptyLineTracker._decorated_node_has_func_named( 

869 adjacent, func_name 

870 ): 

871 return True 

872 

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

874 return EmptyLineTracker._if_stmt_branch_has_func_named( 

875 if_stmt, suite, func_name 

876 ) 

877 

878 @staticmethod 

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

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

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

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

883 name = EmptyLineTracker._get_decorator_target_name(line) 

884 if name is None: 

885 return False 

886 

887 decorated = EmptyLineTracker._find_decorated_node(line) 

888 if decorated is None: 

889 return False 

890 

891 suite = decorated.parent 

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

893 return False 

894 

895 if_stmt = suite.parent 

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

897 return False 

898 

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

900 for reverse in (True, False): 

901 adjacent = EmptyLineTracker._find_adjacent_decorated( 

902 if_stmt, reverse=reverse 

903 ) 

904 if ( 

905 adjacent is not None 

906 and EmptyLineTracker._decorated_node_has_func_named(adjacent, name) 

907 ): 

908 return True 

909 

910 # Check other branches of the same if_stmt. 

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

912 

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

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

915 

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

917 lines (two on module-level). 

918 """ 

919 form_feed = ( 

920 current_line.depth == 0 

921 and bool(current_line.leaves) 

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

923 ) 

924 before, after = self._maybe_empty_lines(current_line) 

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

926 before = max(0, before - previous_after) 

927 

928 # Always have one empty line after a module docstring 

929 if self._line_is_module_docstring(current_line): 

930 before = 1 

931 

932 block = LinesBlock( 

933 mode=self.mode, 

934 previous_block=self.previous_block, 

935 original_line=current_line, 

936 before=before, 

937 after=after, 

938 form_feed=form_feed, 

939 ) 

940 

941 # Maintain the semantic_leading_comment state. 

942 if current_line.is_comment: 

943 if self.previous_line is None or ( 

944 not self.previous_line.is_decorator 

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

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

947 and (self.semantic_leading_comment is None or before) 

948 ): 

949 self.semantic_leading_comment = block 

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

951 elif not current_line.is_decorator or before: 

952 self.semantic_leading_comment = None 

953 

954 # Maintain _pyi_previous_decorated_func state for overload groups. 

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

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

957 overload_groups = ( 

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

959 ) 

960 if overload_groups: 

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

962 name = self._get_def_name(current_line) 

963 if name is not None: 

964 prev = self._pyi_previous_decorated_func 

965 is_multi = ( 

966 prev is not None 

967 and prev.name == name 

968 and prev.depth == current_line.depth 

969 ) 

970 self._pyi_previous_decorated_func = _DecoratedFuncInfo( 

971 name=name, 

972 depth=current_line.depth, 

973 is_multi=is_multi 

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

975 ) 

976 elif ( 

977 not current_line.is_decorator 

978 and not current_line.is_comment 

979 and ( 

980 self._pyi_previous_decorated_func is None 

981 or ( 

982 current_line.depth <= self._pyi_previous_decorated_func.depth 

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

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

985 and not ( 

986 current_line.leaves 

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

988 ) 

989 ) 

990 ) 

991 ): 

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

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

994 # should not clear the state. 

995 self._pyi_previous_decorated_func = None 

996 

997 self.previous_line = current_line 

998 self.previous_block = block 

999 return block 

1000 

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

1002 previous_block = self.previous_block 

1003 if not previous_block: 

1004 return False 

1005 if ( 

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

1007 or not previous_block.original_line.is_docstring 

1008 or previous_block.original_line.depth != 0 

1009 or current_line.is_class 

1010 or current_line.is_def 

1011 ): 

1012 return False 

1013 while previous_block := previous_block.previous_block: 

1014 if not previous_block.original_line.is_comment: 

1015 return False 

1016 return True 

1017 

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

1019 max_allowed = 1 

1020 if current_line.depth == 0: 

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

1022 overload_groups = ( 

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

1024 ) 

1025 

1026 if current_line.leaves: 

1027 # Consume the first leaf's extra newlines. 

1028 first_leaf = current_line.leaves[0] 

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

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

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

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

1033 # Likewise for a comment kept from the prefix of a line outside 

1034 # --line-ranges. 

1035 if ( 

1036 not current_line.is_fmt_pass_converted() 

1037 and not ( 

1038 first_leaf.type == STANDALONE_COMMENT 

1039 and contains_fmt_directive(first_leaf.value, FMT_ON) 

1040 and self.previous_line is not None 

1041 and self.previous_line.is_fmt_pass_converted() 

1042 ) 

1043 and not ( 

1044 current_line.is_comment 

1045 and first_leaf.line_ranges_selected is not None 

1046 ) 

1047 ): 

1048 before = min(before, max_allowed) 

1049 first_leaf.prefix = "" 

1050 else: 

1051 before = 0 

1052 

1053 user_had_newline = bool(before) 

1054 depth = current_line.depth 

1055 

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

1057 previous_def = None 

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

1059 previous_def = self.previous_defs.pop() 

1060 if current_line.is_def or current_line.is_class: 

1061 self.previous_defs.append(current_line) 

1062 

1063 if self.previous_line is None: 

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

1065 return 0, 0 

1066 

1067 if current_line.is_docstring: 

1068 if self.previous_line.is_class: 

1069 return 0, 1 

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

1071 return 0, 0 

1072 

1073 if previous_def is not None: 

1074 assert self.previous_line is not None 

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

1076 # passed to _maybe_empty_lines_for_class_or_def which may override 

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

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

1079 if self.mode.is_pyi: 

1080 if previous_def.is_class and not previous_def.is_stub_class: 

1081 before = 1 

1082 elif ( 

1083 overload_groups 

1084 and self._pyi_previous_decorated_func is not None 

1085 and self._pyi_previous_decorated_func.is_multi 

1086 and not current_line.is_comment 

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

1088 and not ( 

1089 current_line.leaves 

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

1091 ) 

1092 ): 

1093 if self._is_in_current_group(current_line): 

1094 before = 0 

1095 elif current_line.opens_block and ( 

1096 self._get_block_first_decorated_funcname(current_line) 

1097 == self._pyi_previous_decorated_func.name 

1098 ): 

1099 before = 0 

1100 else: 

1101 before = 1 

1102 elif ( 

1103 overload_groups 

1104 and current_line.opens_block 

1105 and current_line.leaves 

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

1107 and self._block_is_part_of_overload_group(current_line) 

1108 ): 

1109 # else/elif continuing a conditional overload group: 

1110 # don't insert a blank line above. 

1111 before = 0 

1112 elif ( 

1113 Preview.pyi_blank_line_after_function_docstring in self.mode 

1114 and previous_def.is_def 

1115 and self.previous_line.is_docstring 

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

1117 and not self._def_is_followed_by_same_name_decorated_func( 

1118 previous_def, 

1119 include_conditional_blocks=overload_groups, 

1120 ) 

1121 ): 

1122 before = 1 

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

1124 if ( 

1125 overload_groups 

1126 and current_line.opens_block 

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

1128 and self._block_is_part_of_overload_group(current_line) 

1129 ): 

1130 before = 1 

1131 else: 

1132 # Empty lines between attributes and methods should 

1133 # be preserved. 

1134 before = 1 if user_had_newline else 0 

1135 elif ( 

1136 overload_groups 

1137 and current_line.is_comment 

1138 and self._pyi_previous_decorated_func is not None 

1139 and self._pyi_previous_decorated_func.depth == current_line.depth 

1140 ): 

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

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

1143 # For non-overload cases the comment_to_add_newlines 

1144 # mechanism will retroactively add needed blank lines. 

1145 before = 1 if user_had_newline else 0 

1146 elif depth: 

1147 before = 0 

1148 else: 

1149 before = 1 

1150 else: 

1151 if depth: 

1152 before = 1 

1153 elif ( 

1154 not depth 

1155 and previous_def.depth 

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

1157 and ( 

1158 current_line.leaves[0].value 

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

1160 ) 

1161 ): 

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

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

1164 # conditional function definitions that are technically top-level 

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

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

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

1168 # already. 

1169 before = 1 

1170 else: 

1171 before = 2 

1172 

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

1174 return self._maybe_empty_lines_for_class_or_def( 

1175 current_line, before, user_had_newline 

1176 ) 

1177 

1178 if ( 

1179 Preview.fmt_off_class_blank_lines in self.mode 

1180 and self.previous_line.is_import 

1181 and self.previous_line.depth == 0 

1182 and current_line.depth == 0 

1183 and current_line.is_fmt_pass_converted( 

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

1185 ) 

1186 ): 

1187 return 2, 0 

1188 

1189 if ( 

1190 self.previous_line.is_import 

1191 and self.previous_line.depth == 0 

1192 and current_line.depth == 0 

1193 and not current_line.is_import 

1194 and not current_line.is_fmt_pass_converted(first_leaf_matches=is_import) 

1195 ): 

1196 return 1, 0 

1197 

1198 if ( 

1199 self.previous_line.is_import 

1200 and not current_line.is_import 

1201 and not current_line.is_fmt_pass_converted(first_leaf_matches=is_import) 

1202 and depth == self.previous_line.depth 

1203 ): 

1204 return (before or 1), 0 

1205 

1206 return before, 0 

1207 

1208 def _maybe_empty_lines_for_class_or_def( 

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

1210 ) -> tuple[int, int]: 

1211 assert self.previous_line is not None 

1212 overload_groups = ( 

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

1214 ) 

1215 

1216 if self.previous_line.is_decorator: 

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

1218 # Insert an empty line after a decorated stub class 

1219 return 0, 1 

1220 return 0, 0 

1221 

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

1223 self.previous_line.is_class or self.previous_line.is_def 

1224 ): 

1225 if self.mode.is_pyi: 

1226 return 0, 0 

1227 return 1 if user_had_newline else 0, 0 

1228 

1229 comment_to_add_newlines: LinesBlock | None = None 

1230 if ( 

1231 self.previous_line.is_comment 

1232 and self.previous_line.depth == current_line.depth 

1233 and before == 0 

1234 ): 

1235 slc = self.semantic_leading_comment 

1236 if ( 

1237 slc is not None 

1238 and slc.previous_block is not None 

1239 and not slc.previous_block.original_line.is_class 

1240 and not slc.previous_block.original_line.opens_block 

1241 and slc.before <= 1 

1242 ): 

1243 comment_to_add_newlines = slc 

1244 else: 

1245 return 0, 0 

1246 

1247 if self.mode.is_pyi: 

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

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

1250 newlines = 0 

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

1252 newlines = 1 

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

1254 # No blank line between classes with an empty body 

1255 newlines = 0 

1256 else: 

1257 newlines = 1 

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

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

1260 # body. 

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

1262 if overload_groups and self._is_in_current_group(current_line): 

1263 newlines = 0 

1264 else: 

1265 newlines = 1 

1266 elif ( 

1267 overload_groups 

1268 and self._pyi_previous_decorated_func is not None 

1269 and self._pyi_previous_decorated_func.is_multi 

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

1271 ): 

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

1273 elif overload_groups and self._is_in_current_group(current_line): 

1274 # A comment between overloads may prevent is_multi from being 

1275 # set, but _is_in_current_group still detects name continuity. 

1276 newlines = 0 

1277 elif ( 

1278 overload_groups 

1279 and current_line.is_decorator 

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

1281 and self._is_start_of_decorated_group(current_line) 

1282 and ( 

1283 self._pyi_previous_decorated_func is None 

1284 or ( 

1285 self._pyi_previous_decorated_func.name 

1286 != self._get_decorator_target_name(current_line) 

1287 ) 

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

1289 ) 

1290 ): 

1291 newlines = 1 

1292 elif ( 

1293 current_line.is_def or current_line.is_decorator 

1294 ) and not self.previous_line.is_def: 

1295 if ( 

1296 overload_groups 

1297 and current_line.is_decorator 

1298 and self.previous_line.is_comment 

1299 and self._is_decorator_in_conditional_overload(current_line) 

1300 ): 

1301 # Comment before an overload inside a conditional block: 

1302 # remove blank lines between the comment and decorator. 

1303 newlines = 0 

1304 elif current_line.depth: 

1305 # In classes empty lines between attributes and methods should 

1306 # be preserved. 

1307 newlines = min(1, before) 

1308 else: 

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

1310 # decorators) and a block of non-functions 

1311 newlines = 1 

1312 elif ( 

1313 Preview.pyi_blank_line_before_decorated_class in self.mode 

1314 and current_line.is_decorator 

1315 and self._decorator_decorates_class(current_line) 

1316 ): 

1317 newlines = 1 

1318 else: 

1319 newlines = 0 

1320 else: 

1321 newlines = 1 if current_line.depth else 2 

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

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

1324 if ( 

1325 self.previous_line.is_stub_def 

1326 and not user_had_newline 

1327 and ( 

1328 Preview.blank_line_after_stub_method not in self.mode 

1329 or self.previous_line.depth == current_line.depth 

1330 ) 

1331 ): 

1332 newlines = 0 

1333 if comment_to_add_newlines is not None: 

1334 previous_block = comment_to_add_newlines.previous_block 

1335 if previous_block is not None: 

1336 comment_to_add_newlines.before = ( 

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

1338 ) 

1339 newlines = 0 

1340 return newlines, 0 

1341 

1342 

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

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

1345 index = len(sequence) - 1 

1346 for element in reversed(sequence): 

1347 yield (index, element) 

1348 index -= 1 

1349 

1350 

1351def append_leaves( 

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

1353) -> None: 

1354 """ 

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

1356 underlying Node structure where appropriate. 

1357 

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

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

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

1361 the new leaves. 

1362 

1363 Pre-conditions: 

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

1365 """ 

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

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

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

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

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

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

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

1373 # formatting pass). 

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

1375 for old_leaf in leaves: 

1376 new_leaf = old_leaf.clone() 

1377 new_leaf.prefix = "" 

1378 parent = old_leaf.parent 

1379 if parent is not None: 

1380 children = parent.children 

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

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

1383 index += 1 

1384 if index < len(children): 

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

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

1387 parent.set_child(index, new_leaf) 

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

1389 else: 

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

1391 # full scan so behaviour is unchanged. 

1392 replace_child(old_leaf, new_leaf) 

1393 new_line.append(new_leaf, preformatted=preformatted) 

1394 

1395 for comment_leaf in old_line.comments_after(old_leaf): 

1396 new_line.append(comment_leaf, preformatted=True) 

1397 

1398 

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

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

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

1402 if it should be inlined or split up. 

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

1404 """ 

1405 if not line_str: 

1406 line_str = line_to_string(line) 

1407 

1408 if line.contains_standalone_comments(): 

1409 return False 

1410 if "\n" not in line_str: 

1411 # No multiline strings (MLS) present 

1412 return str_width(line_str) <= mode.line_length 

1413 

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

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

1416 return False 

1417 

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

1419 # tracking aspects such as depth and comma existence, 

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

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

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

1423 # Includes special case for trailing commas. 

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

1425 multiline_string: Leaf | None = None 

1426 # store the leaves that contain parts of the MLS 

1427 multiline_string_contexts: list[LN] = [] 

1428 

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

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

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

1432 

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

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

1435 if max_level_to_update == math.inf: 

1436 had_comma: int | None = None 

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

1438 commas.append(0) 

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

1440 had_comma = commas.pop() 

1441 if ( 

1442 had_comma is not None 

1443 and multiline_string is not None 

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

1445 ): 

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

1447 max_level_to_update = leaf.bracket_depth 

1448 if had_comma > 0: 

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

1450 return False 

1451 

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

1453 # Inside brackets, ignore trailing comma 

1454 # directly after MLS/MLS-containing expression 

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

1456 ignore_ctxs += multiline_string_contexts 

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

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

1459 ): 

1460 commas[leaf.bracket_depth] += 1 

1461 if max_level_to_update != math.inf: 

1462 max_level_to_update = min(max_level_to_update, leaf.bracket_depth) 

1463 

1464 if is_multiline_string(leaf): 

1465 if leaf.parent and ( 

1466 leaf.parent.type == syms.test 

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

1468 ): 

1469 # Keep ternary and dictionary values parenthesized 

1470 return False 

1471 if len(multiline_string_contexts) > 0: 

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

1473 return False 

1474 multiline_string = leaf 

1475 ctx: LN = leaf 

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

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

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

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

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

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

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

1483 # many triple-quoted values). 

1484 while ( 

1485 id(first_leaf(ctx)) in line_leaf_ids 

1486 and id(last_leaf(ctx)) in line_leaf_ids 

1487 ): 

1488 multiline_string_contexts.append(ctx) 

1489 if ctx.parent is None: 

1490 break 

1491 ctx = ctx.parent 

1492 

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

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

1495 if len(multiline_string_contexts) == 0: 

1496 return True 

1497 

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

1499 

1500 

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

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

1503 

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

1505 avoid some unfortunate formattings (mostly around wrapping unsplittable code 

1506 in unnecessary parentheses). 

1507 """ 

1508 leaves = line.leaves 

1509 if len(leaves) < 2: 

1510 return False 

1511 

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

1513 call_count = 0 

1514 dot_count = 0 

1515 next = leaves[-1] 

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

1517 if leaf.type in OPENING_BRACKETS: 

1518 if next.type not in CLOSING_BRACKETS: 

1519 return False 

1520 

1521 call_count += 1 

1522 elif leaf.type == token.DOT: 

1523 dot_count += 1 

1524 elif leaf.type == token.NAME: 

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

1526 return False 

1527 

1528 elif leaf.type not in CLOSING_BRACKETS: 

1529 return False 

1530 

1531 if dot_count > 1 and call_count > 1: 

1532 return False 

1533 

1534 return True 

1535 

1536 

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

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

1539 

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

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

1542 """ 

1543 depth = 0 

1544 for leaf in head.leaves: 

1545 if not leaf.value: 

1546 continue 

1547 if leaf.type in OPENING_BRACKETS: 

1548 depth += 1 

1549 elif leaf.type in CLOSING_BRACKETS: 

1550 depth -= 1 

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

1552 return True 

1553 return False 

1554 

1555 

1556def is_symmetric_collection_binop(line: Line, line_length: int) -> bool: 

1557 """Is `line` a binary operation between single-line collection displays?""" 

1558 if len(line.bracket_tracker.delimiters) != 1: 

1559 return False 

1560 

1561 # Delimiters is keyed by leaf ID, not line position. 

1562 delimiter_id = next(iter(line.bracket_tracker.delimiters)) 

1563 try: 

1564 left_closing_index = next( 

1565 index for index, leaf in enumerate(line.leaves) if id(leaf) == delimiter_id 

1566 ) 

1567 except StopIteration: 

1568 return False 

1569 

1570 # Math operators are split *before* the delimiter, so BracketTracker keys 

1571 # them by the preceding leaf. 

1572 delimiter_index = left_closing_index + 1 

1573 if delimiter_index >= len(line.leaves) - 1: 

1574 return False 

1575 

1576 first = line.leaves[0] 

1577 left_closing = line.leaves[left_closing_index] 

1578 delimiter = line.leaves[delimiter_index] 

1579 right_opening = line.leaves[delimiter_index + 1] 

1580 last = line.leaves[-1] 

1581 

1582 collection_display = { 

1583 (token.LSQB, token.RSQB), 

1584 (token.LPAR, token.RPAR), 

1585 (token.LBRACE, token.RBRACE), 

1586 } 

1587 

1588 def is_collection_display(opening: Leaf, closing: Leaf) -> bool: 

1589 if (opening.type, closing.type) not in collection_display: 

1590 return False 

1591 return opening.type != token.LPAR or ( 

1592 opening.parent is not None 

1593 and is_tuple(opening.parent) 

1594 and not is_generator(opening.parent) 

1595 ) 

1596 

1597 if ( 

1598 not is_collection_display(first, left_closing) 

1599 or not is_collection_display(right_opening, last) 

1600 or delimiter.type not in MATH_OPERATORS | COMPARATORS 

1601 or left_closing.opening_bracket is not first 

1602 or last.opening_bracket is not right_opening 

1603 ): 

1604 return False 

1605 

1606 # Keep the existing asymmetric split when either display is already forced 

1607 # to split by a magic trailing comma. 

1608 if line.mode.magic_trailing_comma and ( 

1609 line.leaves[left_closing_index - 1].type == token.COMMA 

1610 or line.leaves[-2].type == token.COMMA 

1611 ): 

1612 return False 

1613 

1614 def rendered_width(start: int, end: int) -> int | None: 

1615 leaves = line.leaves[start:end] 

1616 rendered = " " * line.depth 

1617 # End-of-line comments do not determine whether the operand itself fits. 

1618 for index, leaf in enumerate(leaves): 

1619 rendered += leaf.value if index == 0 else str(leaf) 

1620 if "\n" in rendered: 

1621 return None 

1622 return str_width(rendered) 

1623 

1624 left_width = rendered_width(0, delimiter_index) 

1625 right_width = rendered_width(delimiter_index, len(line.leaves)) 

1626 return ( 

1627 left_width is not None 

1628 and right_width is not None 

1629 and left_width <= line_length 

1630 and right_width <= line_length 

1631 ) 

1632 

1633 

1634def can_omit_invisible_parens( 

1635 rhs: RHSResult, 

1636 line_length: int, 

1637 mode: Mode, 

1638) -> bool: 

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

1640 

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

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

1643 are too long. 

1644 """ 

1645 line = rhs.body 

1646 

1647 # Multiple type ignores must stay on separate physical lines. Keeping the 

1648 # optional parens gives the line transformer a safe place to split them. 

1649 if ( 

1650 line.contains_multiple_type_ignores_at_current_depth() 

1651 and not line.bracket_tracker.delimiters 

1652 and any(leaf.type == token.DOT for leaf in line.leaves) 

1653 ): 

1654 return False 

1655 

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

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

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

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

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

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

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

1663 return False 

1664 

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

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

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

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

1669 if rhs.head.leaves: 

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

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

1672 

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

1674 # conflict with type: ignore comments in the body 

1675 if head_comments: 

1676 has_type_ignore_in_head = any( 

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

1678 for comment in head_comments 

1679 ) 

1680 has_other_comment_in_head = any( 

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

1682 for comment in head_comments 

1683 ) 

1684 

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

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

1687 has_type_ignore_in_body = False 

1688 has_other_comment_in_body = False 

1689 for leaf in rhs.body.leaves: 

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

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

1692 has_type_ignore_in_body = True 

1693 else: 

1694 has_other_comment_in_body = True 

1695 

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

1697 # could end up on the same line 

1698 if (has_type_ignore_in_head and has_other_comment_in_body) or ( 

1699 has_other_comment_in_head and has_type_ignore_in_body 

1700 ): 

1701 return False 

1702 

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

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

1705 closing_bracket: Leaf | None = None 

1706 for leaf in reversed(line.leaves): 

1707 if closing_bracket and leaf is closing_bracket.opening_bracket: 

1708 closing_bracket = None 

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

1710 return False 

1711 if ( 

1712 not closing_bracket 

1713 and leaf.type in CLOSING_BRACKETS 

1714 and leaf.opening_bracket in line.leaves 

1715 and leaf.value 

1716 ): 

1717 closing_bracket = leaf 

1718 

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

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

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

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

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

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

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

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

1727 if ( 

1728 Preview.fix_unnecessary_parens_in_indexed_assignment in mode 

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

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

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

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

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

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

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

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

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

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

1739 and not _is_annotated_assignment(rhs.head) 

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

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

1742 ): 

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

1744 tail_line_length = 4 * line.depth + 4 

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

1746 tail_line_length += leaf_length 

1747 if tail_line_length <= line_length: 

1748 return True 

1749 

1750 bt = line.bracket_tracker 

1751 if not bt.delimiters: 

1752 # Without delimiters the optional parentheses are useless. 

1753 return True 

1754 

1755 max_priority = bt.max_delimiter_priority() 

1756 delimiter_count = bt.delimiter_count_with_priority(max_priority) 

1757 if delimiter_count > 1: 

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

1759 return False 

1760 

1761 if delimiter_count == 1: 

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

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

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

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

1766 # line. 

1767 return False 

1768 if ( 

1769 Preview.symmetric_collection_operations in mode 

1770 and is_symmetric_collection_binop(line, line_length) 

1771 ): 

1772 # Retaining the optional parentheses lets the delimiter splitter put 

1773 # each display operand on its own line instead of exploding just one. 

1774 return False 

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

1776 # careful considerations for all possible cases. See 

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

1778 

1779 if max_priority == DOT_PRIORITY: 

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

1781 return True 

1782 

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

1784 

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

1786 # a bracket. 

1787 first = line.leaves[0] 

1788 second = line.leaves[1] 

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

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

1791 return True 

1792 

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

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

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

1796 

1797 penultimate = line.leaves[-2] 

1798 last = line.leaves[-1] 

1799 

1800 if ( 

1801 last.type == token.RPAR 

1802 or last.type == token.RBRACE 

1803 or ( 

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

1805 # it looks weird 

1806 last.type == token.RSQB and last.parent and last.parent.type != syms.trailer 

1807 ) 

1808 ): 

1809 if penultimate.type in OPENING_BRACKETS: 

1810 # Empty brackets don't help. 

1811 return False 

1812 

1813 if is_multiline_string(first): 

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

1815 # unnecessary. 

1816 return True 

1817 

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

1819 return True 

1820 

1821 return False 

1822 

1823 

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

1825 """See `can_omit_invisible_parens`.""" 

1826 remainder = False 

1827 length = 4 * line.depth 

1828 _index = -1 

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

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

1831 remainder = True 

1832 if remainder: 

1833 length += leaf_length 

1834 if length > line_length: 

1835 break 

1836 

1837 if leaf.type in OPENING_BRACKETS: 

1838 # There are brackets we can further split on. 

1839 remainder = False 

1840 

1841 else: 

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

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

1844 return True 

1845 

1846 return False 

1847 

1848 

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

1850 """See `can_omit_invisible_parens`.""" 

1851 length = 4 * line.depth 

1852 seen_other_brackets = False 

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

1854 length += leaf_length 

1855 if leaf is last.opening_bracket: 

1856 if seen_other_brackets or length <= line_length: 

1857 return True 

1858 

1859 elif leaf.type in OPENING_BRACKETS: 

1860 # There are brackets we can further split on. 

1861 seen_other_brackets = True 

1862 

1863 return False 

1864 

1865 

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

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

1868 

1869 WARNING: This is known to be computationally expensive. 

1870 """ 

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