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

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

540 statements  

1import itertools 

2import math 

3from collections.abc import Callable, Iterator, Sequence 

4from dataclasses import dataclass, field 

5from typing import Optional, TypeVar, Union, cast 

6 

7from black.brackets import COMMA_PRIORITY, DOT_PRIORITY, BracketTracker 

8from black.mode import Mode 

9from black.nodes import ( 

10 BRACKETS, 

11 CLOSING_BRACKETS, 

12 OPENING_BRACKETS, 

13 STANDALONE_COMMENT, 

14 TEST_DESCENDANTS, 

15 child_towards, 

16 is_docstring, 

17 is_import, 

18 is_multiline_string, 

19 is_one_sequence_between, 

20 is_type_comment, 

21 is_type_ignore_comment, 

22 is_with_or_async_with_stmt, 

23 make_simple_prefix, 

24 replace_child, 

25 syms, 

26 whitespace, 

27) 

28from black.strings import str_width 

29from blib2to3.pgen2 import token 

30from blib2to3.pytree import Leaf, Node 

31 

32# types 

33T = TypeVar("T") 

34Index = int 

35LeafID = int 

36LN = Union[Leaf, Node] 

37 

38 

39@dataclass 

40class Line: 

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

42 

43 mode: Mode = field(repr=False) 

44 depth: int = 0 

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

46 # keys ordered like `leaves` 

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

48 bracket_tracker: BracketTracker = field(default_factory=BracketTracker) 

49 inside_brackets: bool = False 

50 should_split_rhs: bool = False 

51 magic_trailing_comma: Leaf | None = None 

52 

53 def append( 

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

55 ) -> None: 

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

57 

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

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

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

61 demoted from being delimiters. 

62 

63 Inline comments are put aside. 

64 """ 

65 has_value = ( 

66 leaf.type in BRACKETS 

67 # empty fstring and tstring middles must not be truncated 

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

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

70 ) 

71 if not has_value: 

72 return 

73 

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

75 del self.leaves[-2:] 

76 if self.leaves and not preformatted: 

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

78 # imports, for which we only preserve newlines. 

79 leaf.prefix += whitespace( 

80 leaf, 

81 complex_subscript=self.is_complex_subscript(leaf), 

82 mode=self.mode, 

83 ) 

84 if self.inside_brackets or not preformatted or track_bracket: 

85 self.bracket_tracker.mark(leaf) 

86 if self.mode.magic_trailing_comma: 

87 if self.has_magic_trailing_comma(leaf): 

88 self.magic_trailing_comma = leaf 

89 elif self.has_magic_trailing_comma(leaf): 

90 self.remove_trailing_comma() 

91 if not self.append_comment(leaf): 

92 self.leaves.append(leaf) 

93 

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

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

96 

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

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

99 """ 

100 if ( 

101 self.bracket_tracker.depth == 0 

102 or self.bracket_tracker.any_open_for_or_lambda() 

103 ): 

104 if self.is_comment: 

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

106 

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

108 raise ValueError( 

109 "cannot append standalone comments to a populated line" 

110 ) 

111 

112 self.append(leaf, preformatted=preformatted) 

113 

114 @property 

115 def is_comment(self) -> bool: 

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

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

118 

119 @property 

120 def is_decorator(self) -> bool: 

121 """Is this line a decorator?""" 

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

123 

124 @property 

125 def is_import(self) -> bool: 

126 """Is this an import line?""" 

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

128 

129 @property 

130 def is_with_or_async_with_stmt(self) -> bool: 

131 """Is this a with_stmt line?""" 

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

133 

134 @property 

135 def is_class(self) -> bool: 

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

137 return ( 

138 bool(self) 

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

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

141 ) 

142 

143 @property 

144 def is_stub_class(self) -> bool: 

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

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

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

148 ] 

149 

150 @property 

151 def is_def(self) -> bool: 

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

153 try: 

154 first_leaf = self.leaves[0] 

155 except IndexError: 

156 return False 

157 

158 try: 

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

160 except IndexError: 

161 second_leaf = None 

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

163 first_leaf.type == token.ASYNC 

164 and second_leaf is not None 

165 and second_leaf.type == token.NAME 

166 and second_leaf.value == "def" 

167 ) 

168 

169 @property 

170 def is_stub_def(self) -> bool: 

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

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

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

174 ] 

175 

176 @property 

177 def is_class_paren_empty(self) -> bool: 

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

179 

180 Those are unnecessary and should be removed. 

181 """ 

182 return ( 

183 bool(self) 

184 and len(self.leaves) == 4 

185 and self.is_class 

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

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

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

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

190 ) 

191 

192 @property 

193 def _is_triple_quoted_string(self) -> bool: 

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

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

196 return False 

197 value = self.leaves[0].value 

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

199 return True 

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

201 return True 

202 return False 

203 

204 @property 

205 def is_docstring(self) -> bool: 

206 """Is the line a docstring?""" 

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

208 

209 @property 

210 def is_chained_assignment(self) -> bool: 

211 """Is the line a chained assignment""" 

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

213 

214 @property 

215 def opens_block(self) -> bool: 

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

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

218 return False 

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

220 

221 def is_fmt_pass_converted( 

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

223 ) -> bool: 

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

225 

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

227 leaf of converted code matches. 

228 """ 

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

230 return False 

231 leaf = self.leaves[0] 

232 if ( 

233 leaf.type != STANDALONE_COMMENT 

234 or leaf.fmt_pass_converted_first_leaf is None 

235 ): 

236 return False 

237 return first_leaf_matches is None or first_leaf_matches( 

238 leaf.fmt_pass_converted_first_leaf 

239 ) 

240 

241 def contains_standalone_comments(self) -> bool: 

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

243 for leaf in self.leaves: 

244 if leaf.type == STANDALONE_COMMENT: 

245 return True 

246 

247 return False 

248 

249 def contains_implicit_multiline_string_with_comments(self) -> bool: 

250 """Chck if we have an implicit multiline string with comments on the line""" 

251 for leaf_type, leaf_group_iterator in itertools.groupby( 

252 self.leaves, lambda leaf: leaf.type 

253 ): 

254 if leaf_type != token.STRING: 

255 continue 

256 leaf_list = list(leaf_group_iterator) 

257 if len(leaf_list) == 1: 

258 continue 

259 for leaf in leaf_list: 

260 if self.comments_after(leaf): 

261 return True 

262 return False 

263 

264 def contains_uncollapsable_type_comments(self) -> bool: 

265 ignored_ids = set() 

266 try: 

267 last_leaf = self.leaves[-1] 

268 ignored_ids.add(id(last_leaf)) 

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

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

271 ): 

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

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

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

275 # trailing commas and invisible. 

276 last_leaf = self.leaves[-2] 

277 ignored_ids.add(id(last_leaf)) 

278 except IndexError: 

279 return False 

280 

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

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

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

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

285 # behind a comment. 

286 comment_seen = False 

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

288 for comment in comments: 

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

290 if comment_seen or ( 

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

292 and leaf_id not in ignored_ids 

293 ): 

294 return True 

295 

296 comment_seen = True 

297 

298 return False 

299 

300 def contains_unsplittable_type_ignore(self) -> bool: 

301 if not self.leaves: 

302 return False 

303 

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

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

306 # subexpressions the ignore was meant to apply to. 

307 # 

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

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

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

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

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

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

314 # one line in the original code. 

315 

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

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

318 last_line = next( 

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

320 ) 

321 

322 if first_line == last_line: 

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

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

325 # line. 

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

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

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

329 return True 

330 

331 return False 

332 

333 def contains_multiline_strings(self) -> bool: 

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

335 

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

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

338 - there's a trailing comma here 

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

340 - it's not a one-tuple 

341 """ 

342 if not ( 

343 closing.type in CLOSING_BRACKETS 

344 and self.leaves 

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

346 ): 

347 return False 

348 

349 if closing.type == token.RBRACE: 

350 return True 

351 

352 if closing.type == token.RSQB: 

353 if ( 

354 closing.parent is not None 

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

356 and closing.opening_bracket is not None 

357 and is_one_sequence_between( 

358 closing.opening_bracket, 

359 closing, 

360 self.leaves, 

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

362 ) 

363 ): 

364 assert closing.prev_sibling is not None 

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

366 return False 

367 

368 return True 

369 

370 if self.is_import: 

371 return True 

372 

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

374 closing.opening_bracket, closing, self.leaves 

375 ): 

376 return True 

377 

378 return False 

379 

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

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

382 if ( 

383 comment.type == STANDALONE_COMMENT 

384 and self.bracket_tracker.any_open_brackets() 

385 ): 

386 comment.prefix = "" 

387 return False 

388 

389 if comment.type != token.COMMENT: 

390 return False 

391 

392 if not self.leaves: 

393 comment.type = STANDALONE_COMMENT 

394 comment.prefix = "" 

395 return False 

396 

397 last_leaf = self.leaves[-1] 

398 if ( 

399 last_leaf.type == token.RPAR 

400 and not last_leaf.value 

401 and last_leaf.parent 

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

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

404 ): 

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

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

407 # this avoids unstable formatting caused by comment migration. 

408 if len(self.leaves) < 2: 

409 comment.type = STANDALONE_COMMENT 

410 comment.prefix = "" 

411 return False 

412 

413 last_leaf = self.leaves[-2] 

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

415 return True 

416 

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

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

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

420 

421 def remove_trailing_comma(self) -> None: 

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

423 trailing_comma = self.leaves.pop() 

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

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

426 trailing_comma_comments 

427 ) 

428 

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

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

431 open_lsqb = self.bracket_tracker.get_open_lsqb() 

432 if open_lsqb is None: 

433 return False 

434 

435 subscript_start = open_lsqb.next_sibling 

436 

437 if isinstance(subscript_start, Node): 

438 if subscript_start.type == syms.listmaker: 

439 return False 

440 

441 if subscript_start.type == syms.subscriptlist: 

442 subscript_start = child_towards(subscript_start, leaf) 

443 

444 return subscript_start is not None and any( 

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

446 ) 

447 

448 def enumerate_with_length( 

449 self, is_reversed: bool = False 

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

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

452 

453 Stops prematurely on multiline strings and standalone comments. 

454 """ 

455 op = cast( 

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

457 enumerate_reversed if is_reversed else enumerate, 

458 ) 

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

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

461 if "\n" in leaf.value: 

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

463 

464 for comment in self.comments_after(leaf): 

465 length += len(comment.value) 

466 

467 yield index, leaf, length 

468 

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

470 return Line( 

471 mode=self.mode, 

472 depth=self.depth, 

473 inside_brackets=self.inside_brackets, 

474 should_split_rhs=self.should_split_rhs, 

475 magic_trailing_comma=self.magic_trailing_comma, 

476 ) 

477 

478 def __str__(self) -> str: 

479 """Render the line.""" 

480 if not self: 

481 return "\n" 

482 

483 indent = " " * self.depth 

484 leaves = iter(self.leaves) 

485 first = next(leaves) 

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

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

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

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

490 res += "".join(comments) 

491 

492 return res + "\n" 

493 

494 def __bool__(self) -> bool: 

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

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

497 

498 

499@dataclass 

500class RHSResult: 

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

502 

503 head: Line 

504 body: Line 

505 tail: Line 

506 opening_bracket: Leaf 

507 closing_bracket: Leaf 

508 

509 

510@dataclass 

511class LinesBlock: 

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

513 

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

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

516 """ 

517 

518 mode: Mode 

519 previous_block: Optional["LinesBlock"] 

520 original_line: Line 

521 before: int = 0 

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

523 after: int = 0 

524 form_feed: bool = False 

525 

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

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

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

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

530 

531 

532@dataclass 

533class EmptyLineTracker: 

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

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

536 

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

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

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

540 """ 

541 

542 mode: Mode 

543 previous_line: Line | None = None 

544 previous_block: LinesBlock | None = None 

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

546 semantic_leading_comment: LinesBlock | None = None 

547 

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

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

550 

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

552 lines (two on module-level). 

553 """ 

554 form_feed = ( 

555 current_line.depth == 0 

556 and bool(current_line.leaves) 

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

558 ) 

559 before, after = self._maybe_empty_lines(current_line) 

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

561 before = max(0, before - previous_after) 

562 

563 # Always have one empty line after a module docstring 

564 if self._line_is_module_docstring(current_line): 

565 before = 1 

566 

567 block = LinesBlock( 

568 mode=self.mode, 

569 previous_block=self.previous_block, 

570 original_line=current_line, 

571 before=before, 

572 after=after, 

573 form_feed=form_feed, 

574 ) 

575 

576 # Maintain the semantic_leading_comment state. 

577 if current_line.is_comment: 

578 if self.previous_line is None or ( 

579 not self.previous_line.is_decorator 

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

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

582 and (self.semantic_leading_comment is None or before) 

583 ): 

584 self.semantic_leading_comment = block 

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

586 elif not current_line.is_decorator or before: 

587 self.semantic_leading_comment = None 

588 

589 self.previous_line = current_line 

590 self.previous_block = block 

591 return block 

592 

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

594 previous_block = self.previous_block 

595 if not previous_block: 

596 return False 

597 if ( 

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

599 or not previous_block.original_line.is_docstring 

600 or current_line.is_class 

601 or current_line.is_def 

602 ): 

603 return False 

604 while previous_block := previous_block.previous_block: 

605 if not previous_block.original_line.is_comment: 

606 return False 

607 return True 

608 

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

610 max_allowed = 1 

611 if current_line.depth == 0: 

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

613 

614 if current_line.leaves: 

615 # Consume the first leaf's extra newlines. 

616 first_leaf = current_line.leaves[0] 

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

618 before = min(before, max_allowed) 

619 first_leaf.prefix = "" 

620 else: 

621 before = 0 

622 

623 user_had_newline = bool(before) 

624 depth = current_line.depth 

625 

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

627 previous_def = None 

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

629 previous_def = self.previous_defs.pop() 

630 if current_line.is_def or current_line.is_class: 

631 self.previous_defs.append(current_line) 

632 

633 if self.previous_line is None: 

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

635 return 0, 0 

636 

637 if current_line.is_docstring: 

638 if self.previous_line.is_class: 

639 return 0, 1 

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

641 return 0, 0 

642 

643 if previous_def is not None: 

644 assert self.previous_line is not None 

645 if self.mode.is_pyi: 

646 if previous_def.is_class and not previous_def.is_stub_class: 

647 before = 1 

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

649 # Empty lines between attributes and methods should be preserved. 

650 before = 1 if user_had_newline else 0 

651 elif depth: 

652 before = 0 

653 else: 

654 before = 1 

655 else: 

656 if depth: 

657 before = 1 

658 elif ( 

659 not depth 

660 and previous_def.depth 

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

662 and ( 

663 current_line.leaves[0].value 

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

665 ) 

666 ): 

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

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

669 # conditional function definitions that are technically top-level 

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

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

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

673 # already. 

674 before = 1 

675 else: 

676 before = 2 

677 

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

679 return self._maybe_empty_lines_for_class_or_def( 

680 current_line, before, user_had_newline 

681 ) 

682 

683 if ( 

684 self.previous_line.is_import 

685 and self.previous_line.depth == 0 

686 and current_line.depth == 0 

687 and not current_line.is_import 

688 and not current_line.is_fmt_pass_converted(first_leaf_matches=is_import) 

689 ): 

690 return 1, 0 

691 

692 if ( 

693 self.previous_line.is_import 

694 and not current_line.is_import 

695 and not current_line.is_fmt_pass_converted(first_leaf_matches=is_import) 

696 and depth == self.previous_line.depth 

697 ): 

698 return (before or 1), 0 

699 

700 return before, 0 

701 

702 def _maybe_empty_lines_for_class_or_def( 

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

704 ) -> tuple[int, int]: 

705 assert self.previous_line is not None 

706 

707 if self.previous_line.is_decorator: 

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

709 # Insert an empty line after a decorated stub class 

710 return 0, 1 

711 return 0, 0 

712 

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

714 self.previous_line.is_class or self.previous_line.is_def 

715 ): 

716 if self.mode.is_pyi: 

717 return 0, 0 

718 return 1 if user_had_newline else 0, 0 

719 

720 comment_to_add_newlines: LinesBlock | None = None 

721 if ( 

722 self.previous_line.is_comment 

723 and self.previous_line.depth == current_line.depth 

724 and before == 0 

725 ): 

726 slc = self.semantic_leading_comment 

727 if ( 

728 slc is not None 

729 and slc.previous_block is not None 

730 and not slc.previous_block.original_line.is_class 

731 and not slc.previous_block.original_line.opens_block 

732 and slc.before <= 1 

733 ): 

734 comment_to_add_newlines = slc 

735 else: 

736 return 0, 0 

737 

738 if self.mode.is_pyi: 

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

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

741 newlines = 0 

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

743 newlines = 1 

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

745 # No blank line between classes with an empty body 

746 newlines = 0 

747 else: 

748 newlines = 1 

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

750 # statement in the same level, we always want a blank line if there's 

751 # something with a body preceding. 

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

753 newlines = 1 

754 elif ( 

755 current_line.is_def or current_line.is_decorator 

756 ) and not self.previous_line.is_def: 

757 if current_line.depth: 

758 # In classes empty lines between attributes and methods should 

759 # be preserved. 

760 newlines = min(1, before) 

761 else: 

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

763 # decorators) and a block of non-functions 

764 newlines = 1 

765 else: 

766 newlines = 0 

767 else: 

768 newlines = 1 if current_line.depth else 2 

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

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

771 if self.previous_line.is_stub_def and not user_had_newline: 

772 newlines = 0 

773 if comment_to_add_newlines is not None: 

774 previous_block = comment_to_add_newlines.previous_block 

775 if previous_block is not None: 

776 comment_to_add_newlines.before = ( 

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

778 ) 

779 newlines = 0 

780 return newlines, 0 

781 

782 

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

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

785 index = len(sequence) - 1 

786 for element in reversed(sequence): 

787 yield (index, element) 

788 index -= 1 

789 

790 

791def append_leaves( 

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

793) -> None: 

794 """ 

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

796 underlying Node structure where appropriate. 

797 

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

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

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

801 the new leaves. 

802 

803 Pre-conditions: 

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

805 """ 

806 for old_leaf in leaves: 

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

808 replace_child(old_leaf, new_leaf) 

809 new_line.append(new_leaf, preformatted=preformatted) 

810 

811 for comment_leaf in old_line.comments_after(old_leaf): 

812 new_line.append(comment_leaf, preformatted=True) 

813 

814 

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

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

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

818 if it should be inlined or split up. 

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

820 """ 

821 if not line_str: 

822 line_str = line_to_string(line) 

823 

824 if line.contains_standalone_comments(): 

825 return False 

826 if "\n" not in line_str: 

827 # No multiline strings (MLS) present 

828 return str_width(line_str) <= mode.line_length 

829 

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

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

832 return False 

833 

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

835 # tracking aspects such as depth and comma existence, 

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

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

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

839 # Includes special case for trailing commas. 

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

841 multiline_string: Leaf | None = None 

842 # store the leaves that contain parts of the MLS 

843 multiline_string_contexts: list[LN] = [] 

844 

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

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

847 if max_level_to_update == math.inf: 

848 had_comma: int | None = None 

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

850 commas.append(0) 

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

852 had_comma = commas.pop() 

853 if ( 

854 had_comma is not None 

855 and multiline_string is not None 

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

857 ): 

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

859 max_level_to_update = leaf.bracket_depth 

860 if had_comma > 0: 

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

862 return False 

863 

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

865 # Inside brackets, ignore trailing comma 

866 # directly after MLS/MLS-containing expression 

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

868 ignore_ctxs += multiline_string_contexts 

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

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

871 ): 

872 commas[leaf.bracket_depth] += 1 

873 if max_level_to_update != math.inf: 

874 max_level_to_update = min(max_level_to_update, leaf.bracket_depth) 

875 

876 if is_multiline_string(leaf): 

877 if leaf.parent and ( 

878 leaf.parent.type == syms.test 

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

880 ): 

881 # Keep ternary and dictionary values parenthesized 

882 return False 

883 if len(multiline_string_contexts) > 0: 

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

885 return False 

886 multiline_string = leaf 

887 ctx: LN = leaf 

888 # fetch the leaf components of the MLS in the AST 

889 while str(ctx) in line_str: 

890 multiline_string_contexts.append(ctx) 

891 if ctx.parent is None: 

892 break 

893 ctx = ctx.parent 

894 

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

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

897 if len(multiline_string_contexts) == 0: 

898 return True 

899 

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

901 

902 

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

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

905 

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

907 avoid some unfortunate formattings (mostly around wrapping unsplittable code 

908 in unnecessary parentheses). 

909 """ 

910 leaves = line.leaves 

911 if len(leaves) < 2: 

912 return False 

913 

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

915 call_count = 0 

916 dot_count = 0 

917 next = leaves[-1] 

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

919 if leaf.type in OPENING_BRACKETS: 

920 if next.type not in CLOSING_BRACKETS: 

921 return False 

922 

923 call_count += 1 

924 elif leaf.type == token.DOT: 

925 dot_count += 1 

926 elif leaf.type == token.NAME: 

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

928 return False 

929 

930 elif leaf.type not in CLOSING_BRACKETS: 

931 return False 

932 

933 if dot_count > 1 and call_count > 1: 

934 return False 

935 

936 return True 

937 

938 

939def can_omit_invisible_parens( 

940 rhs: RHSResult, 

941 line_length: int, 

942) -> bool: 

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

944 

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

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

947 are too long. 

948 """ 

949 line = rhs.body 

950 

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

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

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

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

955 if rhs.head.leaves: 

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

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

958 

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

960 # conflict with type: ignore comments in the body 

961 if head_comments: 

962 has_type_ignore_in_head = any( 

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

964 for comment in head_comments 

965 ) 

966 has_other_comment_in_head = any( 

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

968 for comment in head_comments 

969 ) 

970 

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

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

973 has_type_ignore_in_body = False 

974 has_other_comment_in_body = False 

975 for leaf in rhs.body.leaves: 

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

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

978 has_type_ignore_in_body = True 

979 else: 

980 has_other_comment_in_body = True 

981 

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

983 # could end up on the same line 

984 if (has_type_ignore_in_head and has_other_comment_in_body) or ( 

985 has_other_comment_in_head and has_type_ignore_in_body 

986 ): 

987 return False 

988 

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

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

991 closing_bracket: Leaf | None = None 

992 for leaf in reversed(line.leaves): 

993 if closing_bracket and leaf is closing_bracket.opening_bracket: 

994 closing_bracket = None 

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

996 return False 

997 if ( 

998 not closing_bracket 

999 and leaf.type in CLOSING_BRACKETS 

1000 and leaf.opening_bracket in line.leaves 

1001 and leaf.value 

1002 ): 

1003 closing_bracket = leaf 

1004 

1005 bt = line.bracket_tracker 

1006 if not bt.delimiters: 

1007 # Without delimiters the optional parentheses are useless. 

1008 return True 

1009 

1010 max_priority = bt.max_delimiter_priority() 

1011 delimiter_count = bt.delimiter_count_with_priority(max_priority) 

1012 if delimiter_count > 1: 

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

1014 return False 

1015 

1016 if delimiter_count == 1: 

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

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

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

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

1021 # line. 

1022 return False 

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

1024 # careful considerations for all possible cases. See 

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

1026 

1027 if max_priority == DOT_PRIORITY: 

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

1029 return True 

1030 

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

1032 

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

1034 # a bracket. 

1035 first = line.leaves[0] 

1036 second = line.leaves[1] 

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

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

1039 return True 

1040 

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

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

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

1044 

1045 penultimate = line.leaves[-2] 

1046 last = line.leaves[-1] 

1047 

1048 if ( 

1049 last.type == token.RPAR 

1050 or last.type == token.RBRACE 

1051 or ( 

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

1053 # it looks weird 

1054 last.type == token.RSQB 

1055 and last.parent 

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

1057 ) 

1058 ): 

1059 if penultimate.type in OPENING_BRACKETS: 

1060 # Empty brackets don't help. 

1061 return False 

1062 

1063 if is_multiline_string(first): 

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

1065 # unnecessary. 

1066 return True 

1067 

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

1069 return True 

1070 

1071 return False 

1072 

1073 

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

1075 """See `can_omit_invisible_parens`.""" 

1076 remainder = False 

1077 length = 4 * line.depth 

1078 _index = -1 

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

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

1081 remainder = True 

1082 if remainder: 

1083 length += leaf_length 

1084 if length > line_length: 

1085 break 

1086 

1087 if leaf.type in OPENING_BRACKETS: 

1088 # There are brackets we can further split on. 

1089 remainder = False 

1090 

1091 else: 

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

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

1094 return True 

1095 

1096 return False 

1097 

1098 

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

1100 """See `can_omit_invisible_parens`.""" 

1101 length = 4 * line.depth 

1102 seen_other_brackets = False 

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

1104 length += leaf_length 

1105 if leaf is last.opening_bracket: 

1106 if seen_other_brackets or length <= line_length: 

1107 return True 

1108 

1109 elif leaf.type in OPENING_BRACKETS: 

1110 # There are brackets we can further split on. 

1111 seen_other_brackets = True 

1112 

1113 return False 

1114 

1115 

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

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

1118 

1119 WARNING: This is known to be computationally expensive. 

1120 """ 

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