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

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

1169 statements  

1""" 

2Generating lines of code. 

3""" 

4 

5import re 

6import sys 

7from collections.abc import Collection, Iterable, Iterator 

8from dataclasses import replace 

9from enum import Enum, auto 

10from functools import partial, wraps 

11from typing import Final, Union, cast 

12 

13from black.brackets import ( 

14 COMMA_PRIORITY, 

15 COMPARATOR_PRIORITY, 

16 DOT_PRIORITY, 

17 LOGIC_PRIORITY, 

18 STRING_PRIORITY, 

19 get_leaves_inside_matching_brackets, 

20 max_delimiter_priority_in_atom, 

21) 

22from black.comments import ( 

23 FMT_OFF, 

24 FMT_ON, 

25 contains_fmt_directive, 

26 generate_comments, 

27 list_comments, 

28) 

29from black.lines import ( 

30 Line, 

31 RHSResult, 

32 append_leaves, 

33 can_be_split, 

34 can_omit_invisible_parens, 

35 is_line_short_enough, 

36 is_symmetric_collection_binop, 

37 line_to_string, 

38) 

39from black.mode import Feature, Mode, Preview 

40from black.nodes import ( 

41 ASSIGNMENTS, 

42 BRACKETS, 

43 CLOSING_BRACKETS, 

44 OPENING_BRACKETS, 

45 STANDALONE_COMMENT, 

46 STATEMENT, 

47 WHITESPACE, 

48 Visitor, 

49 ensure_visible, 

50 first_leaf_of, 

51 fstring_tstring_to_string, 

52 get_annotation_type, 

53 has_sibling_with_type, 

54 is_arith_like, 

55 is_async_stmt_or_funcdef, 

56 is_atom_with_invisible_parens, 

57 is_docstring, 

58 is_empty_tuple, 

59 is_generator, 

60 is_list, 

61 is_lpar_token, 

62 is_multiline_string, 

63 is_name_token, 

64 is_one_sequence_between, 

65 is_one_tuple, 

66 is_parent_function_or_class, 

67 is_part_of_annotation, 

68 is_rpar_token, 

69 is_stub_body, 

70 is_stub_suite, 

71 is_tuple, 

72 is_tuple_containing_star, 

73 is_tuple_containing_walrus, 

74 is_type_ignore_comment, 

75 is_type_ignore_comment_string, 

76 is_vararg, 

77 is_walrus_assignment, 

78 is_yield, 

79 syms, 

80 wrap_in_parentheses, 

81) 

82from black.numerics import normalize_numeric_literal 

83from black.strings import ( 

84 fix_multiline_docstring, 

85 get_string_prefix, 

86 normalize_fstring_unicode_escape_sequences, 

87 normalize_string_prefix, 

88 normalize_string_quotes, 

89 normalize_unicode_escape_sequences, 

90 str_width, 

91) 

92from black.trans import ( 

93 CannotTransform, 

94 StringMerger, 

95 StringParenStripper, 

96 StringParenWrapper, 

97 StringSplitter, 

98 Transformer, 

99 hug_power_op, 

100) 

101from blib2to3.pgen2 import token 

102from blib2to3.pytree import Leaf, Node 

103 

104# types 

105LeafID = int 

106LN = Union[Leaf, Node] 

107 

108 

109class CannotSplit(CannotTransform): 

110 """A readable split that fits the allotted line length is impossible.""" 

111 

112 

113# This isn't a dataclass because @dataclass + Generic breaks mypyc. 

114# See also https://github.com/mypyc/mypyc/issues/827. 

115class LineGenerator(Visitor[Line]): 

116 """Generates reformatted Line objects. Empty lines are not emitted. 

117 

118 Note: destroys the tree it's visiting by mutating prefixes of its leaves 

119 in ways that will no longer stringify to valid Python code on the tree. 

120 """ 

121 

122 def __init__(self, mode: Mode, features: Collection[Feature]) -> None: 

123 self.mode = mode 

124 self.features = features 

125 self.current_line: Line 

126 self.__post_init__() 

127 

128 def line(self, indent: int = 0) -> Iterator[Line]: 

129 """Generate a line. 

130 

131 If the line is empty, only emit if it makes sense. 

132 If the line is too long, split it first and then generate. 

133 

134 If any lines were generated, set up a new current_line. 

135 """ 

136 if not self.current_line: 

137 self.current_line.depth += indent 

138 return # Line is empty, don't emit. Creating a new one unnecessary. 

139 

140 if len(self.current_line.leaves) == 1 and is_async_stmt_or_funcdef( 

141 self.current_line.leaves[0] 

142 ): 

143 # Special case for async def/for/with statements. `visit_async_stmt` 

144 # adds an `ASYNC` leaf then visits the child def/for/with statement 

145 # nodes. Line yields from those nodes shouldn't treat the former 

146 # `ASYNC` leaf as a complete line. 

147 return 

148 

149 complete_line = self.current_line 

150 self.current_line = Line(mode=self.mode, depth=complete_line.depth + indent) 

151 yield complete_line 

152 

153 def visit_default(self, node: LN) -> Iterator[Line]: 

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

155 if isinstance(node, Leaf): 

156 any_open_brackets = self.current_line.bracket_tracker.any_open_brackets() 

157 for comment in generate_comments( 

158 node, mode=self.mode, block_depth=self.current_line.depth 

159 ): 

160 if any_open_brackets: 

161 # any comment within brackets is subject to splitting 

162 self.current_line.append(comment) 

163 elif comment.type == token.COMMENT: 

164 # regular trailing comment 

165 self.current_line.append(comment) 

166 yield from self.line() 

167 

168 else: 

169 # regular standalone comment 

170 yield from self.line() 

171 

172 self.current_line.append(comment) 

173 yield from self.line() 

174 

175 if any_open_brackets: 

176 node.prefix = "" 

177 if node.type not in WHITESPACE: 

178 self.current_line.append(node) 

179 yield from super().visit_default(node) 

180 

181 def visit_test(self, node: Node) -> Iterator[Line]: 

182 """Visit an `x if y else z` test""" 

183 

184 already_parenthesized = ( 

185 node.prev_sibling and node.prev_sibling.type == token.LPAR 

186 ) 

187 # `# fmt: skip` on an opening bracket can hide the start of the expression, 

188 # as in `x = ( # fmt: skip` ... `) if c else d`. Outside of brackets, the 

189 # rest has to stay on the line of the hidden closing bracket, so it can't be 

190 # wrapped in parentheses. 

191 # The `if` part can itself be hidden by another `# fmt: skip`. The hidden 

192 # code is then replaced by a STANDALONE_COMMENT at the index of its first 

193 # node in the same parent, so a comment in `node.children[0]` hid this 

194 # node's own `if` or `else`, and `fmt_pass_converted_first_leaf` is that 

195 # keyword (detached from the tree, only its value is read). 

196 first = node.children[0] 

197 if isinstance(first, Leaf) and first.fmt_pass_converted_first_leaf: 

198 first = first.fmt_pass_converted_first_leaf 

199 follows_fmt_skip = ( 

200 isinstance(first, Leaf) 

201 and first.value in {"if", "else"} 

202 and not self.current_line.bracket_tracker.any_open_brackets() 

203 ) 

204 

205 if not already_parenthesized and not follows_fmt_skip: 

206 # Similar to logic in wrap_in_parentheses 

207 lpar = Leaf(token.LPAR, "") 

208 rpar = Leaf(token.RPAR, "") 

209 prefix = node.prefix 

210 node.prefix = "" 

211 lpar.prefix = prefix 

212 node.insert_child(0, lpar) 

213 node.append_child(rpar) 

214 

215 yield from self.visit_default(node) 

216 

217 def visit_comparison(self, node: Node) -> Iterator[Line]: 

218 if Preview.remove_redundant_comparison_parentheses in self.mode: 

219 _normalize_comparison_operands(node, mode=self.mode, features=self.features) 

220 yield from self.visit_default(node) 

221 

222 def visit_INDENT(self, node: Leaf) -> Iterator[Line]: 

223 """Increase indentation level, maybe yield a line.""" 

224 # In blib2to3 INDENT never holds comments. 

225 yield from self.line(+1) 

226 yield from self.visit_default(node) 

227 

228 def visit_DEDENT(self, node: Leaf) -> Iterator[Line]: 

229 """Decrease indentation level, maybe yield a line.""" 

230 # The current line might still wait for trailing comments. At DEDENT time 

231 # there won't be any (they would be prefixes on the preceding NEWLINE). 

232 # Emit the line then. 

233 yield from self.line() 

234 

235 # While DEDENT has no value, its prefix may contain standalone comments 

236 # that belong to the current indentation level. Get 'em. 

237 yield from self.visit_default(node) 

238 

239 # Finally, emit the dedent. 

240 yield from self.line(-1) 

241 

242 def visit_stmt( 

243 self, node: Node, keywords: set[str], parens: set[str] 

244 ) -> Iterator[Line]: 

245 """Visit a statement. 

246 

247 This implementation is shared for `if`, `while`, `for`, `try`, `except`, 

248 `def`, `with`, `class`, `assert`, and assignments. 

249 

250 The relevant Python language `keywords` for a given statement 

251 appear as NAME leaves within it. This method puts those on a 

252 separate line. 

253 

254 `parens` holds a set of string leaf values immediately after which 

255 invisible parens should be put. 

256 """ 

257 normalize_invisible_parens( 

258 node, parens_after=parens, mode=self.mode, features=self.features 

259 ) 

260 for child in node.children: 

261 if is_name_token(child) and child.value in keywords: 

262 yield from self.line() 

263 

264 yield from self.visit(child) 

265 

266 def visit_dictsetmaker(self, node: Node) -> Iterator[Line]: 

267 if Preview.wrap_long_dict_values_in_parens in self.mode: 

268 for i, child in enumerate(node.children): 

269 if i == 0: 

270 continue 

271 if node.children[i - 1].type == token.COLON: 

272 if ( 

273 child.type == syms.atom 

274 and child.children[0].type in OPENING_BRACKETS 

275 and not is_walrus_assignment(child) 

276 ): 

277 maybe_make_parens_invisible_in_atom( 

278 child, 

279 parent=node, 

280 mode=self.mode, 

281 features=self.features, 

282 remove_brackets_around_comma=False, 

283 ) 

284 else: 

285 wrap_in_parentheses(node, child, visible=False, index=i) 

286 yield from self.visit_default(node) 

287 

288 def visit_funcdef(self, node: Node) -> Iterator[Line]: 

289 """Visit function definition.""" 

290 yield from self.line() 

291 

292 # Remove redundant brackets around return type annotation. 

293 is_return_annotation = False 

294 for child in node.children: 

295 if child.type == token.RARROW: 

296 is_return_annotation = True 

297 elif is_return_annotation: 

298 if child.type == syms.atom and child.children[0].type == token.LPAR: 

299 if maybe_make_parens_invisible_in_atom( 

300 child, 

301 parent=node, 

302 mode=self.mode, 

303 features=self.features, 

304 remove_brackets_around_comma=False, 

305 ): 

306 wrap_in_parentheses(node, child, visible=False) 

307 else: 

308 wrap_in_parentheses(node, child, visible=False) 

309 is_return_annotation = False 

310 

311 for child in node.children: 

312 yield from self.visit(child) 

313 

314 def visit_match_case(self, node: Node) -> Iterator[Line]: 

315 """Visit either a match or case statement.""" 

316 normalize_invisible_parens( 

317 node, parens_after=set(), mode=self.mode, features=self.features 

318 ) 

319 

320 yield from self.line() 

321 for child in node.children: 

322 yield from self.visit(child) 

323 

324 def visit_suite(self, node: Node) -> Iterator[Line]: 

325 """Visit a suite.""" 

326 if is_stub_suite(node): 

327 yield from self.visit(node.children[2]) 

328 else: 

329 yield from self.visit_default(node) 

330 

331 def visit_simple_stmt(self, node: Node) -> Iterator[Line]: 

332 """Visit a statement without nested statements.""" 

333 prev_type: int | None = None 

334 for i, child in enumerate(node.children): 

335 if prev_type is None or prev_type == token.SEMI: 

336 if is_arith_like(child): 

337 wrap_in_parentheses(node, child, visible=False, index=i) 

338 elif ( 

339 Preview.remove_redundant_top_level_parentheses in self.mode 

340 and child.type == syms.atom 

341 and not is_walrus_assignment(child) 

342 and not _is_implicitly_concatenated_string(child) 

343 ): 

344 maybe_make_parens_invisible_in_atom( 

345 child, 

346 parent=node, 

347 mode=self.mode, 

348 features=self.features, 

349 ) 

350 prev_type = child.type 

351 

352 if node.parent and node.parent.type in STATEMENT: 

353 if is_parent_function_or_class(node) and is_stub_body(node): 

354 yield from self.visit_default(node) 

355 else: 

356 yield from self.line(+1) 

357 yield from self.visit_default(node) 

358 yield from self.line(-1) 

359 

360 else: 

361 if node.parent and is_stub_suite(node.parent): 

362 node.prefix = "" 

363 yield from self.visit_default(node) 

364 return 

365 yield from self.line() 

366 yield from self.visit_default(node) 

367 

368 def visit_async_stmt(self, node: Node) -> Iterator[Line]: 

369 """Visit `async def`, `async for`, `async with`.""" 

370 yield from self.line() 

371 

372 children = iter(node.children) 

373 for child in children: 

374 yield from self.visit(child) 

375 

376 if child.type == token.ASYNC or child.type == STANDALONE_COMMENT: 

377 # STANDALONE_COMMENT happens when `# fmt: skip` is applied on the async 

378 # line. 

379 break 

380 

381 internal_stmt = next(children) 

382 yield from self.visit(internal_stmt) 

383 

384 def visit_decorators(self, node: Node) -> Iterator[Line]: 

385 """Visit decorators.""" 

386 for child in node.children: 

387 yield from self.line() 

388 yield from self.visit(child) 

389 

390 def visit_power(self, node: Node) -> Iterator[Line]: 

391 for idx, leaf in enumerate(node.children[:-1]): 

392 next_leaf = node.children[idx + 1] 

393 

394 if not isinstance(leaf, Leaf): 

395 continue 

396 

397 value = leaf.value.lower() 

398 if ( 

399 leaf.type == token.NUMBER 

400 and next_leaf.type == syms.trailer 

401 # Ensure that we are in an attribute trailer 

402 and next_leaf.children[0].type == token.DOT 

403 # It shouldn't wrap hexadecimal, binary and octal literals 

404 and not value.startswith(("0x", "0b", "0o")) 

405 # It shouldn't wrap complex literals 

406 and "j" not in value 

407 ): 

408 wrap_in_parentheses(node, leaf) 

409 

410 remove_generator_parens = ( 

411 Preview.remove_redundant_generator_parentheses in self.mode 

412 ) 

413 remove_subscript_parens = ( 

414 Preview.remove_redundant_subscript_parentheses in self.mode 

415 ) 

416 if remove_generator_parens or remove_subscript_parens: 

417 for child in node.children: 

418 if child.type == syms.trailer and len(child.children) == 3: 

419 if ( 

420 remove_generator_parens 

421 and is_lpar_token(child.children[0]) 

422 and ( 

423 is_generator(child.children[1]) 

424 or _has_redundant_generator_parentheses(child.children[1]) 

425 ) 

426 and is_rpar_token(child.children[2]) 

427 ): 

428 maybe_make_parens_invisible_in_atom( 

429 child.children[1], 

430 parent=child, 

431 mode=self.mode, 

432 features=self.features, 

433 remove_generator_parens=True, 

434 ) 

435 atom = child.children[1] 

436 if is_atom_with_invisible_parens(atom) and not any( 

437 "#" in leaf.prefix for leaf in atom.leaves() 

438 ): 

439 # Drop the now invisible parens so the generator splits 

440 # the same way as one passed without parentheses. 

441 atom.replace(atom.children[1]) 

442 elif ( 

443 remove_subscript_parens 

444 and child.children[0].type == token.LSQB 

445 and child.children[2].type == token.RSQB 

446 and child.children[1].type == syms.atom 

447 ): 

448 atom = child.children[1] 

449 if not is_empty_tuple(atom) and not is_generator(atom): 

450 maybe_make_parens_invisible_in_atom( 

451 atom, 

452 parent=child, 

453 mode=self.mode, 

454 features=self.features, 

455 remove_brackets_around_comma=True, 

456 is_subscript=True, 

457 ) 

458 if is_atom_with_invisible_parens(atom): 

459 inner = atom.children[1] 

460 if inner.type == syms.testlist_gexp: 

461 inner.type = syms.subscriptlist 

462 first_paren = atom.children[0] 

463 last_paren = atom.children[-1] 

464 atom.replace(inner) 

465 if first_paren.prefix.strip(): 

466 first_inner = first_leaf_of(inner) 

467 if first_inner: 

468 first_inner.prefix = ( 

469 first_paren.prefix + first_inner.prefix 

470 ) 

471 if last_paren.prefix.strip(): 

472 child.children[-1].prefix = ( 

473 last_paren.prefix + child.children[-1].prefix 

474 ) 

475 

476 remove_await_parens(node, mode=self.mode, features=self.features) 

477 

478 yield from self.visit_default(node) 

479 

480 def visit_SEMI(self, leaf: Leaf) -> Iterator[Line]: 

481 """Remove a semicolon and put the other statement on a separate line.""" 

482 yield from self.line() 

483 

484 def visit_ENDMARKER(self, leaf: Leaf) -> Iterator[Line]: 

485 """End of file. Process outstanding comments and end with a newline.""" 

486 yield from self.visit_default(leaf) 

487 yield from self.line() 

488 

489 def visit_STANDALONE_COMMENT(self, leaf: Leaf) -> Iterator[Line]: 

490 any_open_brackets = self.current_line.bracket_tracker.any_open_brackets() 

491 # Code hidden by a second `# fmt: skip` in the same statement stays on the 

492 # line where the previous hidden code ends, with its original spacing. 

493 continues_fmt_skip_line = False 

494 original_prefix = leaf.prefix 

495 if not any_open_brackets: 

496 continues_fmt_skip_line = _continues_fmt_skip_line(self.current_line, leaf) 

497 if not continues_fmt_skip_line: 

498 yield from self.line() 

499 # STANDALONE_COMMENT nodes created by our special handling in 

500 # normalize_fmt_off for comment-only blocks have fmt:off as the first 

501 # line and fmt:on as the last line (each directive on its own line, 

502 # not embedded in other text). These should be appended directly 

503 # without calling visit_default, which would process their prefix and 

504 # lose indentation. Normal STANDALONE_COMMENT nodes go through 

505 # visit_default. 

506 value = leaf.value 

507 lines = value.splitlines() 

508 is_fmt_off_block = ( 

509 len(lines) >= 2 

510 and contains_fmt_directive(lines[0], FMT_OFF) 

511 and contains_fmt_directive(lines[-1], FMT_ON) 

512 ) 

513 if is_fmt_off_block: 

514 is_after_invisible_lpar = ( 

515 leaf.fmt_pass_converted_first_leaf is None 

516 and len(self.current_line.leaves) == 1 

517 and self.current_line.leaves[0].type == token.LPAR 

518 and not self.current_line.leaves[0].value 

519 ) 

520 # This is a fmt:off/on block from normalize_fmt_off - we still need 

521 # to process any prefix comments (like markdown comments) but append 

522 # the fmt block itself directly to preserve its formatting 

523 

524 # Only process prefix comments if there actually is a prefix with comments 

525 if leaf.prefix and any( 

526 line.strip().startswith("#") 

527 and not contains_fmt_directive(line.strip()) 

528 for line in leaf.prefix.split("\n") 

529 ): 

530 for comment in generate_comments(leaf, mode=self.mode): 

531 yield from self.line() 

532 self.current_line.append(comment) 

533 yield from self.line() 

534 # Clear the prefix since we've processed it as comments above 

535 leaf.prefix = "" 

536 

537 self.current_line.append(leaf) 

538 if not any_open_brackets or is_after_invisible_lpar: 

539 yield from self.line() 

540 else: 

541 # Normal standalone comment - process through visit_default 

542 yield from self.visit_default(leaf) 

543 if continues_fmt_skip_line: 

544 leaf.prefix = original_prefix 

545 

546 def visit_factor(self, node: Node) -> Iterator[Line]: 

547 """Force parentheses between a unary op and a binary power: 

548 

549 -2 ** 8 -> -(2 ** 8) 

550 """ 

551 # The operator is gone when a `# fmt: skip` on the operand's opening bracket 

552 # hides it and the operand goes on after the closing bracket, as in 

553 # `x = -( # fmt: skip` ... `).y`. 

554 operand = node.children[-1] 

555 if ( 

556 len(node.children) == 2 

557 and operand.type == syms.power 

558 and len(operand.children) == 3 

559 and operand.children[1].type == token.DOUBLESTAR 

560 ): 

561 lpar = Leaf(token.LPAR, "(") 

562 rpar = Leaf(token.RPAR, ")") 

563 index = operand.remove() or 0 

564 node.insert_child(index, Node(syms.atom, [lpar, operand, rpar])) 

565 yield from self.visit_default(node) 

566 

567 def visit_tname(self, node: Node) -> Iterator[Line]: 

568 """ 

569 Add potential parentheses around types in function parameter lists to be made 

570 into real parentheses in case the type hint is too long to fit on a line 

571 Examples: 

572 def foo(a: int, b: float = 7): ... 

573 

574 -> 

575 

576 def foo(a: (int), b: (float) = 7): ... 

577 """ 

578 if len(node.children) == 3 and maybe_make_parens_invisible_in_atom( 

579 node.children[2], parent=node, mode=self.mode, features=self.features 

580 ): 

581 wrap_in_parentheses(node, node.children[2], visible=False) 

582 

583 yield from self.visit_default(node) 

584 

585 def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: 

586 normalize_unicode_escape_sequences(leaf) 

587 

588 if is_docstring(leaf) and not re.search(r"\\\s*\n", leaf.value): 

589 # We're ignoring docstrings with backslash newline escapes because changing 

590 # indentation of those changes the AST representation of the code. 

591 if self.mode.string_normalization: 

592 docstring = normalize_string_prefix(leaf.value, self.mode) 

593 # We handle string normalization at the end of this method, but since 

594 # what we do right now acts differently depending on quote style (ex. 

595 # see padding logic below), there's a possibility for unstable 

596 # formatting. To avoid a situation where this function formats a 

597 # docstring differently on the second pass, normalize it early. 

598 docstring = normalize_string_quotes(docstring) 

599 else: 

600 docstring = leaf.value 

601 prefix = get_string_prefix(docstring) 

602 docstring = docstring[len(prefix) :] # Remove the prefix 

603 quote_char = docstring[0] 

604 # A natural way to remove the outer quotes is to do: 

605 # docstring = docstring.strip(quote_char) 

606 # but that breaks on """""x""" (which is '""x'). 

607 # So we actually need to remove the first character and the next two 

608 # characters but only if they are the same as the first. 

609 quote_len = 1 if docstring[1] != quote_char else 3 

610 docstring = docstring[quote_len:-quote_len] 

611 docstring_started_empty = not docstring 

612 indent = " " * 4 * self.current_line.depth 

613 

614 if is_multiline_string(leaf): 

615 docstring = fix_multiline_docstring(docstring, indent) 

616 else: 

617 docstring = docstring.strip() 

618 

619 has_trailing_backslash = False 

620 if docstring: 

621 # Add some padding if the docstring starts / ends with a quote mark. 

622 if docstring[0] == quote_char: 

623 docstring = " " + docstring 

624 if docstring[-1] == quote_char: 

625 docstring += " " 

626 if docstring[-1] == "\\": 

627 backslash_count = len(docstring) - len(docstring.rstrip("\\")) 

628 if backslash_count % 2: 

629 # Odd number of tailing backslashes, add some padding to 

630 # avoid escaping the closing string quote. 

631 docstring += " " 

632 has_trailing_backslash = True 

633 elif not docstring_started_empty: 

634 docstring = " " 

635 

636 # We could enforce triple quotes at this point. 

637 quote = quote_char * quote_len 

638 

639 # It's invalid to put closing single-character quotes on a new line. 

640 if quote_len == 3: 

641 # We need to find the length of the last line of the docstring 

642 # to find if we can add the closing quotes to the line without 

643 # exceeding the maximum line length. 

644 # If docstring is one line, we don't put the closing quotes on a 

645 # separate line because it looks ugly (#3320). 

646 lines = docstring.splitlines() 

647 last_line_length = ( 

648 len(lines[-1]) if docstring and not docstring.endswith("\n") else 0 

649 ) 

650 

651 # If adding closing quotes would cause the last line to exceed 

652 # the maximum line length, and the closing quote is not 

653 # prefixed by a newline then put a line break before 

654 # the closing quotes 

655 if ( 

656 len(lines) > 1 

657 and last_line_length + quote_len > self.mode.line_length 

658 and len(indent) + quote_len <= self.mode.line_length 

659 and not has_trailing_backslash 

660 ): 

661 if leaf.value[-1 - quote_len] == "\n": 

662 leaf.value = prefix + quote + docstring + quote 

663 else: 

664 leaf.value = prefix + quote + docstring + "\n" + indent + quote 

665 else: 

666 leaf.value = prefix + quote + docstring + quote 

667 else: 

668 leaf.value = prefix + quote + docstring + quote 

669 

670 if self.mode.string_normalization and leaf.type == token.STRING: 

671 leaf.value = normalize_string_prefix(leaf.value, self.mode) 

672 leaf.value = normalize_string_quotes(leaf.value) 

673 yield from self.visit_default(leaf) 

674 

675 def visit_NUMBER(self, leaf: Leaf) -> Iterator[Line]: 

676 normalize_numeric_literal(leaf) 

677 yield from self.visit_default(leaf) 

678 

679 def visit_atom(self, node: Node) -> Iterator[Line]: 

680 """Visit any atom""" 

681 if ( 

682 Preview.remove_redundant_generator_parentheses in self.mode 

683 and _has_redundant_generator_parentheses(node) 

684 ): 

685 maybe_make_parens_invisible_in_atom( 

686 node, 

687 parent=node.parent or node, 

688 mode=self.mode, 

689 features=self.features, 

690 ) 

691 

692 if ( 

693 Preview.remove_redundant_list_parentheses in self.mode 

694 and _has_redundant_list_parentheses(node) 

695 ): 

696 maybe_make_parens_invisible_in_atom( 

697 node, 

698 parent=node.parent or node, 

699 mode=self.mode, 

700 features=self.features, 

701 ) 

702 

703 if len(node.children) == 3: 

704 first = node.children[0] 

705 last = node.children[-1] 

706 if (first.type == token.LSQB and last.type == token.RSQB) or ( 

707 first.type == token.LBRACE and last.type == token.RBRACE 

708 ): 

709 # Lists or sets of one item 

710 maybe_make_parens_invisible_in_atom( 

711 node.children[1], 

712 parent=node, 

713 mode=self.mode, 

714 features=self.features, 

715 ) 

716 

717 yield from self.visit_default(node) 

718 

719 def visit_fstring(self, node: Node) -> Iterator[Line]: 

720 # If the fstring was converted to a STANDALONE_COMMENT by 

721 # normalize_fmt_off (e.g. it was inside a # fmt: off block), 

722 # skip the fstring-to-string conversion and just visit normally. 

723 if any(child.type == STANDALONE_COMMENT for child in node.children): 

724 yield from self.visit_default(node) 

725 return 

726 normalize_fstring_unicode_escape_sequences(node) 

727 # currently we don't want to format and split f-strings at all. 

728 string_leaf = fstring_tstring_to_string(node) 

729 node.replace(string_leaf) 

730 if "\\" in string_leaf.value and any( 

731 "\\" in str(child) 

732 for child in node.children 

733 if child.type == syms.fstring_replacement_field 

734 ): 

735 # string normalization doesn't account for nested quotes, 

736 # causing breakages. skip normalization when nested quotes exist 

737 yield from self.visit_default(string_leaf) 

738 return 

739 yield from self.visit_STRING(string_leaf) 

740 

741 def visit_tstring(self, node: Node) -> Iterator[Line]: 

742 # If the tstring was converted to a STANDALONE_COMMENT by 

743 # normalize_fmt_off, skip the conversion and just visit normally. 

744 if any(child.type == STANDALONE_COMMENT for child in node.children): 

745 yield from self.visit_default(node) 

746 return 

747 normalize_fstring_unicode_escape_sequences(node) 

748 # currently we don't want to format and split t-strings at all. 

749 string_leaf = fstring_tstring_to_string(node) 

750 node.replace(string_leaf) 

751 if "\\" in string_leaf.value and any( 

752 "\\" in str(child) 

753 for child in node.children 

754 if child.type == syms.tstring_replacement_field 

755 ): 

756 # string normalization doesn't account for nested quotes, 

757 # causing breakages. skip normalization when nested quotes exist 

758 yield from self.visit_default(string_leaf) 

759 return 

760 yield from self.visit_STRING(string_leaf) 

761 

762 # TODO: Uncomment Implementation to format f-string children 

763 # fstring_start = node.children[0] 

764 # fstring_end = node.children[-1] 

765 # assert isinstance(fstring_start, Leaf) 

766 # assert isinstance(fstring_end, Leaf) 

767 

768 # quote_char = fstring_end.value[0] 

769 # quote_idx = fstring_start.value.index(quote_char) 

770 # prefix, quote = ( 

771 # fstring_start.value[:quote_idx], 

772 # fstring_start.value[quote_idx:] 

773 # ) 

774 

775 # if not is_docstring(node, self.mode): 

776 # prefix = normalize_string_prefix(prefix, self.mode) 

777 

778 # assert quote == fstring_end.value 

779 

780 # is_raw_fstring = "r" in prefix or "R" in prefix 

781 # middles = [ 

782 # leaf 

783 # for leaf in node.leaves() 

784 # if leaf.type == token.FSTRING_MIDDLE 

785 # ] 

786 

787 # if self.mode.string_normalization: 

788 # middles, quote = normalize_fstring_quotes(quote, middles, is_raw_fstring) 

789 

790 # fstring_start.value = prefix + quote 

791 # fstring_end.value = quote 

792 

793 # yield from self.visit_default(node) 

794 

795 def visit_comp_for(self, node: Node) -> Iterator[Line]: 

796 if ( 

797 Preview.remove_redundant_unpacking_parentheses in self.mode 

798 and len(node.children) > 1 

799 ): 

800 _normalize_unpacking_targets( 

801 node.children[1], mode=self.mode, features=self.features 

802 ) 

803 if Preview.wrap_comprehension_in in self.mode: 

804 normalize_invisible_parens( 

805 node, parens_after={"in"}, mode=self.mode, features=self.features 

806 ) 

807 yield from self.visit_default(node) 

808 

809 def visit_old_comp_for(self, node: Node) -> Iterator[Line]: 

810 yield from self.visit_comp_for(node) 

811 

812 def __post_init__(self) -> None: 

813 """You are in a twisty little maze of passages.""" 

814 self.current_line = Line(mode=self.mode) 

815 

816 v = self.visit_stmt 

817 Ø: set[str] = set() 

818 self.visit_assert_stmt = partial(v, keywords={"assert"}, parens={"assert", ","}) 

819 self.visit_if_stmt = partial( 

820 v, keywords={"if", "else", "elif"}, parens={"if", "elif"} 

821 ) 

822 self.visit_while_stmt = partial(v, keywords={"while", "else"}, parens={"while"}) 

823 self.visit_for_stmt = partial(v, keywords={"for", "else"}, parens={"for", "in"}) 

824 self.visit_try_stmt = partial( 

825 v, keywords={"try", "except", "else", "finally"}, parens=Ø 

826 ) 

827 self.visit_except_clause = partial(v, keywords={"except"}, parens={"except"}) 

828 self.visit_with_stmt = partial(v, keywords={"with"}, parens={"with"}) 

829 self.visit_classdef = partial(v, keywords={"class"}, parens=Ø) 

830 

831 self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS) 

832 self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"}) 

833 self.visit_yield_expr = partial( 

834 v, 

835 keywords=Ø, 

836 parens=( 

837 {"yield"} if Preview.parenthesize_tuple_in_yield in self.mode else Ø 

838 ), 

839 ) 

840 self.visit_import_from = partial(v, keywords=Ø, parens={"import"}) 

841 self.visit_del_stmt = partial(v, keywords=Ø, parens={"del"}) 

842 self.visit_async_funcdef = self.visit_async_stmt 

843 self.visit_decorated = self.visit_decorators 

844 

845 # PEP 634 

846 self.visit_match_stmt = self.visit_match_case 

847 self.visit_case_block = self.visit_match_case 

848 self.visit_guard = partial(v, keywords=Ø, parens={"if"}) 

849 

850 

851# Remove when `simplify_power_operator_hugging` becomes stable. 

852def _hugging_power_ops_line_to_string( 

853 line: Line, 

854 features: Collection[Feature], 

855 mode: Mode, 

856) -> str | None: 

857 try: 

858 return line_to_string(next(hug_power_op(line, features, mode))) 

859 except CannotTransform: 

860 return None 

861 

862 

863def transform_line( 

864 line: Line, mode: Mode, features: Collection[Feature] = () 

865) -> Iterator[Line]: 

866 """Transform a `line`, potentially splitting it into many lines. 

867 

868 They should fit in the allotted `line_length` but might not be able to. 

869 

870 `features` are syntactical features that may be used in the output. 

871 """ 

872 if line.is_comment: 

873 yield line 

874 return 

875 

876 line_str = line_to_string(line) 

877 

878 if Preview.simplify_power_operator_hugging in mode: 

879 line_str_hugging_power_ops = line_str 

880 else: 

881 # We need the line string when power operators are hugging to determine if we 

882 # should split the line. Default to line_str, if no power operator are present 

883 # on the line. 

884 line_str_hugging_power_ops = ( 

885 _hugging_power_ops_line_to_string(line, features, mode) or line_str 

886 ) 

887 

888 ll = mode.line_length 

889 sn = mode.string_normalization 

890 string_merge = StringMerger(ll, sn) 

891 string_paren_strip = StringParenStripper(ll, sn) 

892 string_split = StringSplitter(ll, sn) 

893 string_paren_wrap = StringParenWrapper(ll, sn) 

894 

895 transformers: list[Transformer] 

896 if ( 

897 not line.contains_uncollapsable_type_comments() 

898 and not line.should_split_rhs 

899 and not line.magic_trailing_comma 

900 and ( 

901 is_line_short_enough(line, mode=mode, line_str=line_str_hugging_power_ops) 

902 or line.contains_unsplittable_type_ignore() 

903 ) 

904 and not (line.inside_brackets and line.contains_standalone_comments()) 

905 and not line.contains_implicit_multiline_string_with_comments() 

906 ): 

907 # Only apply basic string preprocessing, since lines shouldn't be split here. 

908 if Preview.string_processing in mode: 

909 transformers = [string_merge, string_paren_strip] 

910 else: 

911 transformers = [] 

912 elif line.is_def and not should_split_funcdef_with_rhs(line, mode): 

913 transformers = [left_hand_split] 

914 else: 

915 

916 if Preview.string_processing in mode: 

917 if line.inside_brackets: 

918 transformers = [ 

919 string_merge, 

920 string_paren_strip, 

921 string_split, 

922 delimiter_split, 

923 type_ignore_comment_split, 

924 standalone_comment_split, 

925 string_paren_wrap, 

926 right_hand_split_with_omits, 

927 ] 

928 if _is_implicit_concatenation_value(line): 

929 # Wrap the whole concatenation in parens instead of 

930 # splitting it apart first (#3855). 

931 transformers.remove(string_paren_wrap) 

932 transformers.insert(3, string_paren_wrap) 

933 else: 

934 transformers = [ 

935 string_merge, 

936 string_paren_strip, 

937 string_split, 

938 string_paren_wrap, 

939 right_hand_split_with_omits, 

940 ] 

941 else: 

942 if line.inside_brackets: 

943 transformers = [ 

944 delimiter_split, 

945 type_ignore_comment_split, 

946 standalone_comment_split, 

947 right_hand_split_with_omits, 

948 ] 

949 else: 

950 transformers = [right_hand_split_with_omits] 

951 

952 if Preview.simplify_power_operator_hugging not in mode: 

953 # It's always safe to attempt hugging of power operations and pretty much every 

954 # line could match. 

955 transformers.append(hug_power_op) 

956 

957 for transform in transformers: 

958 # We are accumulating lines in `result` because we might want to abort 

959 # mission and return the original line in the end, or attempt a different 

960 # split altogether. 

961 try: 

962 result = run_transformer(line, transform, mode, features, line_str=line_str) 

963 except CannotTransform: 

964 continue 

965 else: 

966 yield from result 

967 break 

968 

969 else: 

970 # A leftover standalone comment would render inline and break the 

971 # second pass with a parse error (#4296). Force a split so the 

972 # output is at least valid Python. 

973 if line.contains_standalone_comments(): 

974 yield from _force_standalone_comment_split(line) 

975 else: 

976 yield line 

977 

978 

979def _is_implicit_concatenation_value(line: Line) -> bool: 

980 """Is `line` something like `a=STRING STRING`, where the only delimiters are 

981 between the implicitly concatenated strings?""" 

982 if line.leaves[0].type == token.STRING: 

983 return False 

984 

985 exclude = set() 

986 if line.leaves[-1].type == token.COMMA: 

987 exclude.add(id(line.leaves[-1])) 

988 try: 

989 max_priority = line.bracket_tracker.max_delimiter_priority(exclude=exclude) 

990 except ValueError: 

991 return False 

992 

993 return max_priority == STRING_PRIORITY 

994 

995 

996def should_split_funcdef_with_rhs(line: Line, mode: Mode) -> bool: 

997 """If a funcdef has a magic trailing comma in the return type, then we should first 

998 split the line with rhs to respect the comma. 

999 """ 

1000 return_type_leaves: list[Leaf] = [] 

1001 in_return_type = False 

1002 

1003 for leaf in line.leaves: 

1004 if leaf.type == token.COLON: 

1005 in_return_type = False 

1006 if in_return_type: 

1007 return_type_leaves.append(leaf) 

1008 if leaf.type == token.RARROW: 

1009 in_return_type = True 

1010 

1011 # using `bracket_split_build_line` will mess with whitespace, so we duplicate a 

1012 # couple lines from it. 

1013 result = Line(mode=line.mode, depth=line.depth) 

1014 leaves_to_track = get_leaves_inside_matching_brackets(return_type_leaves) 

1015 for leaf in return_type_leaves: 

1016 result.append( 

1017 leaf, 

1018 preformatted=True, 

1019 track_bracket=id(leaf) in leaves_to_track, 

1020 ) 

1021 

1022 first_visible_return_leaf = next( 

1023 (leaf for leaf in return_type_leaves if leaf.value), None 

1024 ) 

1025 return result.magic_trailing_comma is not None or ( 

1026 first_visible_return_leaf is not None 

1027 and first_visible_return_leaf.type == token.STRING 

1028 and not is_line_short_enough(result, mode=mode) 

1029 ) 

1030 

1031 

1032class _BracketSplitComponent(Enum): 

1033 head = auto() 

1034 body = auto() 

1035 tail = auto() 

1036 

1037 

1038def left_hand_split( 

1039 line: Line, _features: Collection[Feature], mode: Mode 

1040) -> Iterator[Line]: 

1041 """Split line into many lines, starting with the first matching bracket pair. 

1042 

1043 Note: this usually looks weird, only use this for function definitions. 

1044 Prefer RHS otherwise. This is why this function is not symmetrical with 

1045 :func:`right_hand_split` which also handles optional parentheses. 

1046 """ 

1047 for leaf_type in [token.LPAR, token.LSQB]: 

1048 tail_leaves: list[Leaf] = [] 

1049 body_leaves: list[Leaf] = [] 

1050 head_leaves: list[Leaf] = [] 

1051 current_leaves = head_leaves 

1052 matching_bracket: Leaf | None = None 

1053 depth = 0 

1054 for index, leaf in enumerate(line.leaves): 

1055 if index == 2 and leaf.type == token.LSQB: 

1056 # A [ at index 2 means this is a type param, so start 

1057 # tracking the depth 

1058 depth += 1 

1059 elif depth > 0: 

1060 if leaf.type == token.LSQB: 

1061 depth += 1 

1062 elif leaf.type == token.RSQB: 

1063 depth -= 1 

1064 if ( 

1065 current_leaves is body_leaves 

1066 and leaf.type in CLOSING_BRACKETS 

1067 and leaf.opening_bracket is matching_bracket 

1068 and isinstance(matching_bracket, Leaf) 

1069 # If the code is still on LPAR and we are inside a type 

1070 # param, ignore the match since this is searching 

1071 # for the function arguments 

1072 and not (leaf_type == token.LPAR and depth > 0) 

1073 ): 

1074 ensure_visible(leaf) 

1075 ensure_visible(matching_bracket) 

1076 current_leaves = tail_leaves if body_leaves else head_leaves 

1077 current_leaves.append(leaf) 

1078 if current_leaves is head_leaves: 

1079 if leaf.type == leaf_type and ( 

1080 not (leaf_type == token.LPAR and depth > 0) 

1081 ): 

1082 matching_bracket = leaf 

1083 current_leaves = body_leaves 

1084 if matching_bracket and tail_leaves: 

1085 break 

1086 if not matching_bracket or not tail_leaves: 

1087 raise CannotSplit("No brackets found") 

1088 

1089 head = bracket_split_build_line( 

1090 head_leaves, line, matching_bracket, component=_BracketSplitComponent.head 

1091 ) 

1092 body = bracket_split_build_line( 

1093 body_leaves, line, matching_bracket, component=_BracketSplitComponent.body 

1094 ) 

1095 tail = bracket_split_build_line( 

1096 tail_leaves, line, matching_bracket, component=_BracketSplitComponent.tail 

1097 ) 

1098 bracket_split_succeeded_or_raise(head, body, tail) 

1099 for result in (head, body, tail): 

1100 if result: 

1101 yield result 

1102 

1103 

1104def right_hand_split( 

1105 line: Line, 

1106 mode: Mode, 

1107 features: Collection[Feature] = (), 

1108 omit: Collection[LeafID] = (), 

1109) -> Iterator[Line]: 

1110 """Split line into many lines, starting with the last matching bracket pair. 

1111 

1112 If the split was by optional parentheses, attempt splitting without them, too. 

1113 `omit` is a collection of closing bracket IDs that shouldn't be considered for 

1114 this split. 

1115 

1116 Note: running this function modifies `bracket_depth` on the leaves of `line`. 

1117 """ 

1118 rhs_result = _first_right_hand_split(line, omit=omit) 

1119 yield from _maybe_split_omitting_optional_parens( 

1120 rhs_result, line, mode, features=features, omit=omit 

1121 ) 

1122 

1123 

1124def right_hand_split_with_omits( 

1125 line: Line, features: Collection[Feature], mode: Mode 

1126) -> Iterator[Line]: 

1127 """Wraps calls to `right_hand_split`. 

1128 

1129 The calls increasingly `omit` right-hand trailers (bracket pairs with 

1130 content), meaning the trailers get glued together to split on another 

1131 bracket pair instead. 

1132 """ 

1133 fallback_omit: set[LeafID] | None = None 

1134 first_lines: list[Line] | None = None 

1135 

1136 prefix_lengths: dict[LeafID, int] = {} 

1137 curr_len = 4 * line.depth 

1138 for leaf in line.leaves: 

1139 prefix_lengths[id(leaf)] = curr_len 

1140 curr_len += len(leaf.prefix) + len(leaf.value) 

1141 

1142 for omit in generate_trailers_to_omit(line, mode.line_length): 

1143 if omit: 

1144 target_opening: Leaf | None = None 

1145 for leaf in reversed(line.leaves): 

1146 if leaf.type in CLOSING_BRACKETS and id(leaf) not in omit: 

1147 target_opening = leaf.opening_bracket 

1148 break 

1149 # If the candidate split point is a concrete bracket whose prefix length 

1150 # from the start of the line already exceeds line_length, the head line 

1151 # is mathematically guaranteed to exceed line_length as well. 

1152 if ( 

1153 target_opening is not None 

1154 and target_opening.value 

1155 and prefix_lengths.get(id(target_opening), 0) > mode.line_length 

1156 ): 

1157 continue 

1158 

1159 lines = list(right_hand_split(line, mode, features, omit=omit)) 

1160 if first_lines is None and not omit: 

1161 first_lines = lines 

1162 # Note: this check is only able to figure out if the first line of the 

1163 # *current* transformation fits in the line length. This is true only 

1164 # for simple cases. All others require running more transforms via 

1165 # `transform_line()`. This check doesn't know if those would succeed. 

1166 if is_line_short_enough(lines[0], mode=mode) or ( 

1167 omit 

1168 and ( 

1169 _over_length_only_due_to_bracket_comment(lines[0], mode) 

1170 if Preview.keep_trailers_on_bracket_comment_overflow in mode 

1171 else _over_length_only_due_to_subscript_comment(lines[0], mode) 

1172 ) 

1173 ): 

1174 if ( 

1175 Preview.fix_magic_trailing_comma_trailer_split in mode 

1176 and line.magic_trailing_comma 

1177 and lines[0].magic_trailing_comma 

1178 ): 

1179 # The head still has to explode on its magic trailing comma, so 

1180 # splitting this trailer too is wasted. Prefer a later omit that 

1181 # splits on the magic trailing comma's own brackets. 

1182 if fallback_omit is None: 

1183 fallback_omit = set(omit) 

1184 continue 

1185 yield from lines 

1186 return 

1187 

1188 if fallback_omit is not None: 

1189 yield from right_hand_split(line, mode, features=features, omit=fallback_omit) 

1190 return 

1191 

1192 # All splits failed, best effort split with no omits. 

1193 # This mostly happens to multiline strings that are by definition 

1194 # reported as not fitting a single line, as well as lines that contain 

1195 # trailing commas (those have to be exploded). 

1196 if first_lines is not None: 

1197 yield from first_lines 

1198 else: 

1199 yield from right_hand_split(line, mode, features=features) 

1200 

1201 

1202def _first_right_hand_split( 

1203 line: Line, 

1204 omit: Collection[LeafID] = (), 

1205) -> RHSResult: 

1206 """Split the line into head, body, tail starting with the last bracket pair. 

1207 

1208 Note: this function should not have side effects. It's relied upon by 

1209 _maybe_split_omitting_optional_parens to get an opinion whether to prefer 

1210 splitting on the right side of an assignment statement. 

1211 """ 

1212 tail_leaves: list[Leaf] = [] 

1213 body_leaves: list[Leaf] = [] 

1214 head_leaves: list[Leaf] = [] 

1215 current_leaves = tail_leaves 

1216 opening_bracket: Leaf | None = None 

1217 closing_bracket: Leaf | None = None 

1218 for leaf in reversed(line.leaves): 

1219 if current_leaves is body_leaves: 

1220 if leaf is opening_bracket: 

1221 current_leaves = head_leaves if body_leaves else tail_leaves 

1222 current_leaves.append(leaf) 

1223 if current_leaves is tail_leaves: 

1224 if leaf.type in CLOSING_BRACKETS and id(leaf) not in omit: 

1225 opening_bracket = leaf.opening_bracket 

1226 closing_bracket = leaf 

1227 current_leaves = body_leaves 

1228 if not (opening_bracket and closing_bracket and head_leaves): 

1229 # If there is no opening or closing_bracket that means the split failed and 

1230 # all content is in the tail. Otherwise, if `head_leaves` are empty, it means 

1231 # the matching `opening_bracket` wasn't available on `line` anymore. 

1232 raise CannotSplit("No brackets found") 

1233 

1234 tail_leaves.reverse() 

1235 body_leaves.reverse() 

1236 head_leaves.reverse() 

1237 

1238 body: Line | None = None 

1239 if ( 

1240 Preview.hug_parens_with_braces_and_square_brackets in line.mode 

1241 and tail_leaves[0].value 

1242 and tail_leaves[0].opening_bracket is head_leaves[-1] 

1243 ): 

1244 inner_body_leaves = list(body_leaves) 

1245 hugged_opening_leaves: list[Leaf] = [] 

1246 hugged_closing_leaves: list[Leaf] = [] 

1247 is_unpacking = body_leaves[0].type in [token.STAR, token.DOUBLESTAR] 

1248 unpacking_offset: int = 1 if is_unpacking else 0 

1249 while ( 

1250 len(inner_body_leaves) >= 2 + unpacking_offset 

1251 and inner_body_leaves[-1].type in CLOSING_BRACKETS 

1252 and inner_body_leaves[-1].opening_bracket 

1253 is inner_body_leaves[unpacking_offset] 

1254 ): 

1255 if unpacking_offset: 

1256 hugged_opening_leaves.append(inner_body_leaves.pop(0)) 

1257 unpacking_offset = 0 

1258 hugged_opening_leaves.append(inner_body_leaves.pop(0)) 

1259 hugged_closing_leaves.insert(0, inner_body_leaves.pop()) 

1260 

1261 if hugged_opening_leaves and inner_body_leaves: 

1262 inner_body = bracket_split_build_line( 

1263 inner_body_leaves, 

1264 line, 

1265 hugged_opening_leaves[-1], 

1266 component=_BracketSplitComponent.body, 

1267 ) 

1268 if ( 

1269 line.mode.magic_trailing_comma 

1270 and inner_body_leaves[-1].type == token.COMMA 

1271 ): 

1272 should_hug = True 

1273 else: 

1274 line_length = line.mode.line_length - sum( 

1275 len(str(leaf)) 

1276 for leaf in hugged_opening_leaves + hugged_closing_leaves 

1277 ) 

1278 if is_line_short_enough( 

1279 inner_body, mode=replace(line.mode, line_length=line_length) 

1280 ): 

1281 # Do not hug if it fits on a single line. 

1282 should_hug = False 

1283 else: 

1284 should_hug = True 

1285 if should_hug and ( 

1286 _hugging_merges_type_ignores(line, head_leaves, hugged_opening_leaves) 

1287 or _hugging_merges_type_ignores( 

1288 line, hugged_closing_leaves, tail_leaves 

1289 ) 

1290 ): 

1291 # Hugging joins these leaves onto one physical line, and their 

1292 # trailing comments come along. `type: ignore` is recorded per 

1293 # line by the AST, so two of them landing on the same line would 

1294 # drop one and make the output non-equivalent. 

1295 should_hug = False 

1296 if should_hug: 

1297 body_leaves = inner_body_leaves 

1298 head_leaves.extend(hugged_opening_leaves) 

1299 tail_leaves = hugged_closing_leaves + tail_leaves 

1300 body = inner_body # No need to re-calculate the body again later. 

1301 

1302 head = bracket_split_build_line( 

1303 head_leaves, line, opening_bracket, component=_BracketSplitComponent.head 

1304 ) 

1305 if body is None: 

1306 body = bracket_split_build_line( 

1307 body_leaves, line, opening_bracket, component=_BracketSplitComponent.body 

1308 ) 

1309 tail = bracket_split_build_line( 

1310 tail_leaves, line, opening_bracket, component=_BracketSplitComponent.tail 

1311 ) 

1312 bracket_split_succeeded_or_raise(head, body, tail) 

1313 return RHSResult(head, body, tail, opening_bracket, closing_bracket) 

1314 

1315 

1316def _maybe_split_omitting_optional_parens( 

1317 rhs: RHSResult, 

1318 line: Line, 

1319 mode: Mode, 

1320 features: Collection[Feature] = (), 

1321 omit: Collection[LeafID] = (), 

1322) -> Iterator[Line]: 

1323 split_symmetric_collection_binops = ( 

1324 Preview.symmetric_collection_operations in mode 

1325 and rhs.opening_bracket.type == token.LPAR 

1326 and not rhs.opening_bracket.value 

1327 and rhs.closing_bracket.type == token.RPAR 

1328 and not rhs.closing_bracket.value 

1329 and is_symmetric_collection_binop(rhs.body, mode.line_length) 

1330 ) 

1331 if ( 

1332 Feature.FORCE_OPTIONAL_PARENTHESES not in features 

1333 # the opening bracket is an optional paren 

1334 and rhs.opening_bracket.type == token.LPAR 

1335 and not rhs.opening_bracket.value 

1336 # the closing bracket is an optional paren 

1337 and rhs.closing_bracket.type == token.RPAR 

1338 and not rhs.closing_bracket.value 

1339 # it's not an import (optional parens are the only thing we can split on 

1340 # in this case; attempting a split without them is a waste of time) 

1341 and not line.is_import 

1342 # and we can actually remove the parens 

1343 and can_omit_invisible_parens(rhs, mode.line_length, mode) 

1344 ): 

1345 omit = {id(rhs.closing_bracket), *omit} 

1346 try: 

1347 # The RHSResult Omitting Optional Parens. 

1348 rhs_oop = _first_right_hand_split(line, omit=omit) 

1349 if _prefer_split_rhs_oop_over_rhs(rhs_oop, rhs, mode): 

1350 yield from _maybe_split_omitting_optional_parens( 

1351 rhs_oop, line, mode, features=features, omit=omit 

1352 ) 

1353 return 

1354 elif Preview.avoid_parens_for_unbreakable_rhs_in_assignments in mode: 

1355 raise CannotSplit( 

1356 "Alternative split not preferred, falling back to original RHS" 

1357 ) 

1358 

1359 except CannotSplit as e: 

1360 # For chained assignments we want to use the previous successful split 

1361 if line.is_chained_assignment: 

1362 pass 

1363 

1364 elif ( 

1365 not can_be_split(rhs.body) 

1366 and not is_line_short_enough(rhs.body, mode=mode) 

1367 and not ( 

1368 Preview.wrap_long_dict_values_in_parens in mode 

1369 and rhs.opening_bracket.parent 

1370 and rhs.opening_bracket.parent.parent 

1371 and rhs.opening_bracket.parent.parent.type == syms.dictsetmaker 

1372 ) 

1373 and not ( 

1374 rhs.opening_bracket.parent 

1375 and rhs.opening_bracket.parent.parent 

1376 and rhs.opening_bracket.parent.parent.type == syms.case_block 

1377 ) 

1378 ): 

1379 raise CannotSplit( 

1380 "Splitting failed, body is still too long and can't be split." 

1381 ) from e 

1382 

1383 elif ( 

1384 rhs.head.contains_multiline_strings() 

1385 or rhs.tail.contains_multiline_strings() 

1386 ): 

1387 raise CannotSplit( 

1388 "The current optional pair of parentheses is bound to fail to" 

1389 " satisfy the splitting algorithm because the head or the tail" 

1390 " contains multiline strings which by definition never fit one" 

1391 " line." 

1392 ) from e 

1393 

1394 ensure_visible(rhs.opening_bracket) 

1395 ensure_visible(rhs.closing_bracket) 

1396 if split_symmetric_collection_binops: 

1397 rhs.body.should_split_rhs = True 

1398 for result in (rhs.head, rhs.body, rhs.tail): 

1399 if result: 

1400 yield result 

1401 

1402 

1403def _prefer_split_rhs_oop_over_rhs( 

1404 rhs_oop: RHSResult, rhs: RHSResult, mode: Mode 

1405) -> bool: 

1406 """ 

1407 Returns whether we should prefer the result from a split omitting optional parens 

1408 (rhs_oop) over the original (rhs). 

1409 """ 

1410 # contains unsplittable type ignore 

1411 if ( 

1412 rhs_oop.head.contains_unsplittable_type_ignore() 

1413 or rhs_oop.body.contains_unsplittable_type_ignore() 

1414 or rhs_oop.tail.contains_unsplittable_type_ignore() 

1415 ): 

1416 return True 

1417 

1418 # Retain optional parens around dictionary values 

1419 if ( 

1420 Preview.wrap_long_dict_values_in_parens in mode 

1421 and rhs.opening_bracket.parent 

1422 and rhs.opening_bracket.parent.parent 

1423 and rhs.opening_bracket.parent.parent.type == syms.dictsetmaker 

1424 and rhs.body.bracket_tracker.delimiters 

1425 ): 

1426 # Unless the split is inside the key 

1427 return any(leaf.type == token.COLON for leaf in rhs_oop.tail.leaves) 

1428 

1429 # the split is right after `=` 

1430 if not (len(rhs.head.leaves) >= 2 and rhs.head.leaves[-2].type == token.EQUAL): 

1431 return True 

1432 

1433 # the left side of assignment contains brackets 

1434 if not any(leaf.type in BRACKETS for leaf in rhs.head.leaves[:-1]): 

1435 return True 

1436 

1437 # the left side of assignment is short enough (the -1 is for the ending optional 

1438 # paren) 

1439 if not is_line_short_enough( 

1440 rhs.head, mode=replace(mode, line_length=mode.line_length - 1) 

1441 ): 

1442 return True 

1443 

1444 # the left side of assignment won't explode further because of magic trailing comma 

1445 if rhs.head.magic_trailing_comma is not None: 

1446 return True 

1447 

1448 # If we have multiple targets, we prefer more `=`s on the head vs pushing them to 

1449 # the body 

1450 rhs_head_equal_count = [leaf.type for leaf in rhs.head.leaves].count(token.EQUAL) 

1451 rhs_oop_head_equal_count = [leaf.type for leaf in rhs_oop.head.leaves].count( 

1452 token.EQUAL 

1453 ) 

1454 if rhs_head_equal_count > 1 and rhs_head_equal_count > rhs_oop_head_equal_count: 

1455 return False 

1456 

1457 has_closing_bracket_after_assign = False 

1458 for leaf in reversed(rhs_oop.head.leaves): 

1459 if leaf.type == token.EQUAL: 

1460 break 

1461 if leaf.type in CLOSING_BRACKETS: 

1462 has_closing_bracket_after_assign = True 

1463 break 

1464 return ( 

1465 # contains matching brackets after the `=` (done by checking there is a 

1466 # closing bracket) 

1467 has_closing_bracket_after_assign 

1468 or ( 

1469 # the split is actually from inside the optional parens (done by checking 

1470 # the first line still contains the `=`) 

1471 any(leaf.type == token.EQUAL for leaf in rhs_oop.head.leaves) 

1472 # the first line is short enough 

1473 and is_line_short_enough(rhs_oop.head, mode=mode) 

1474 ) 

1475 ) 

1476 

1477 

1478def bracket_split_succeeded_or_raise(head: Line, body: Line, tail: Line) -> None: 

1479 """Raise :exc:`CannotSplit` if the last left- or right-hand split failed. 

1480 

1481 Do nothing otherwise. 

1482 

1483 A left- or right-hand split is based on a pair of brackets. Content before 

1484 (and including) the opening bracket is left on one line, content inside the 

1485 brackets is put on a separate line, and finally content starting with and 

1486 following the closing bracket is put on a separate line. 

1487 

1488 Those are called `head`, `body`, and `tail`, respectively. If the split 

1489 produced the same line (all content in `head`) or ended up with an empty `body` 

1490 and the `tail` is just the closing bracket, then it's considered failed. 

1491 """ 

1492 tail_len = len(str(tail).strip()) 

1493 if not body: 

1494 if tail_len == 0: 

1495 raise CannotSplit("Splitting brackets produced the same line") 

1496 

1497 elif tail_len < 3: 

1498 raise CannotSplit( 

1499 f"Splitting brackets on an empty body to save {tail_len} characters is" 

1500 " not worth it" 

1501 ) 

1502 

1503 

1504def _ensure_trailing_comma( 

1505 leaves: list[Leaf], original: Line, opening_bracket: Leaf 

1506) -> bool: 

1507 if not leaves: 

1508 return False 

1509 # Ensure a trailing comma for imports 

1510 if original.is_import: 

1511 return True 

1512 # ...and standalone function arguments 

1513 if not original.is_def: 

1514 return False 

1515 if opening_bracket.value != "(": 

1516 return False 

1517 # Don't add commas if we already have any commas 

1518 if any( 

1519 leaf.type == token.COMMA and not is_part_of_annotation(leaf) for leaf in leaves 

1520 ): 

1521 return False 

1522 

1523 # Find a leaf with a parent (comments don't have parents) 

1524 leaf_with_parent = next((leaf for leaf in leaves if leaf.parent), None) 

1525 if leaf_with_parent is None: 

1526 return True 

1527 # Don't add commas inside parenthesized return annotations 

1528 if get_annotation_type(leaf_with_parent) == "return": 

1529 return False 

1530 # Don't add commas inside PEP 604 unions 

1531 if ( 

1532 leaf_with_parent.parent 

1533 and leaf_with_parent.parent.next_sibling 

1534 and leaf_with_parent.parent.next_sibling.type == token.VBAR 

1535 ): 

1536 return False 

1537 return True 

1538 

1539 

1540def _hugging_merges_type_ignores(line: Line, *leaf_groups: Iterable[Leaf]) -> bool: 

1541 """Return True if hugging these groups would put two `type: ignore`s on a line.""" 

1542 seen = 0 

1543 for leaves in leaf_groups: 

1544 for leaf in leaves: 

1545 for comment in line.comments_after(leaf): 

1546 if is_type_ignore_comment(comment, mode=line.mode): 

1547 seen += 1 

1548 if seen > 1: 

1549 return True 

1550 return False 

1551 

1552 

1553def bracket_split_build_line( 

1554 leaves: list[Leaf], 

1555 original: Line, 

1556 opening_bracket: Leaf, 

1557 *, 

1558 component: _BracketSplitComponent, 

1559) -> Line: 

1560 """Return a new line with given `leaves` and respective comments from `original`. 

1561 

1562 If it's the head component, brackets will be tracked so trailing commas are 

1563 respected. 

1564 

1565 If it's the body component, the result line is one-indented inside brackets and as 

1566 such has its first leaf's prefix normalized and a trailing comma added when 

1567 expected. 

1568 """ 

1569 result = Line(mode=original.mode, depth=original.depth) 

1570 if component is _BracketSplitComponent.body: 

1571 result.inside_brackets = True 

1572 result.depth += 1 

1573 if _ensure_trailing_comma(leaves, original, opening_bracket): 

1574 for i in range(len(leaves) - 1, -1, -1): 

1575 if leaves[i].type == STANDALONE_COMMENT: 

1576 continue 

1577 

1578 if leaves[i].type != token.COMMA: 

1579 new_comma = Leaf(token.COMMA, ",") 

1580 leaves.insert(i + 1, new_comma) 

1581 break 

1582 

1583 leaves_to_track: set[LeafID] = set() 

1584 if component is _BracketSplitComponent.head: 

1585 leaves_to_track = get_leaves_inside_matching_brackets(leaves) 

1586 # Populate the line 

1587 for leaf in leaves: 

1588 result.append( 

1589 leaf, 

1590 preformatted=True, 

1591 track_bracket=id(leaf) in leaves_to_track, 

1592 ) 

1593 for comment_after in original.comments_after(leaf): 

1594 result.append(comment_after, preformatted=True) 

1595 if component is _BracketSplitComponent.body and should_split_line( 

1596 result, opening_bracket 

1597 ): 

1598 result.should_split_rhs = True 

1599 return result 

1600 

1601 

1602def dont_increase_indentation(split_func: Transformer) -> Transformer: 

1603 """Normalize prefix of the first leaf in every line returned by `split_func`. 

1604 

1605 This is a decorator over relevant split functions. 

1606 """ 

1607 

1608 @wraps(split_func) 

1609 def split_wrapper( 

1610 line: Line, features: Collection[Feature], mode: Mode 

1611 ) -> Iterator[Line]: 

1612 for split_line in split_func(line, features, mode): 

1613 split_line.leaves[0].prefix = "" 

1614 yield split_line 

1615 

1616 return split_wrapper 

1617 

1618 

1619def _get_last_non_comment_leaf(line: Line) -> int | None: 

1620 for leaf_idx in range(len(line.leaves) - 1, 0, -1): 

1621 if line.leaves[leaf_idx].type != STANDALONE_COMMENT: 

1622 return leaf_idx 

1623 return None 

1624 

1625 

1626def _can_add_trailing_comma(leaf: Leaf, features: Collection[Feature]) -> bool: 

1627 if is_vararg(leaf, within={syms.typedargslist}): 

1628 return Feature.TRAILING_COMMA_IN_DEF in features 

1629 if is_vararg(leaf, within={syms.arglist, syms.argument}): 

1630 return Feature.TRAILING_COMMA_IN_CALL in features 

1631 return True 

1632 

1633 

1634def _has_unmatched_closing_bracket(line: Line) -> bool: 

1635 depth = 0 

1636 for leaf in line.leaves: 

1637 if leaf.type in OPENING_BRACKETS: 

1638 depth += 1 

1639 elif leaf.type in CLOSING_BRACKETS: 

1640 if depth == 0: 

1641 return True 

1642 depth -= 1 

1643 return False 

1644 

1645 

1646def _safe_add_trailing_comma(safe: bool, delimiter_priority: int, line: Line) -> Line: 

1647 if ( 

1648 safe 

1649 and delimiter_priority == COMMA_PRIORITY 

1650 and line.leaves[-1].type != token.COMMA 

1651 and line.leaves[-1].type != STANDALONE_COMMENT 

1652 ): 

1653 new_comma = Leaf(token.COMMA, ",") 

1654 line.append(new_comma) 

1655 return line 

1656 

1657 

1658MIGRATE_COMMENT_DELIMITERS = {STRING_PRIORITY, COMMA_PRIORITY} 

1659 

1660 

1661def _can_defer_lone_comparator_to_rhs(line: Line, rhs: RHSResult, mode: Mode) -> bool: 

1662 """Return True if the lone comparator on `line` can defer to right_hand_split. 

1663 

1664 Caller has already established exactly one delimiter at 

1665 `COMPARATOR_PRIORITY`. We defer only when: 

1666 

1667 - the LHS up to the comparator has no opening brackets, so the existing 

1668 "break before the comparator" wouldn't produce a balanced two-sided 

1669 split anyway, and 

1670 - `right_hand_split` would produce a head that fits in the line length, 

1671 so we don't strand `if t` on its own line just to push it back onto an 

1672 overflowing single line when the RHS bracket can't be exploded 

1673 usefully (e.g. an empty `decode()` paren). 

1674 """ 

1675 past_comparator = False 

1676 for leaf in line.leaves: 

1677 if leaf.type in OPENING_BRACKETS and not past_comparator: 

1678 return False 

1679 if not past_comparator and ( 

1680 line.bracket_tracker.delimiters.get(id(leaf)) == COMPARATOR_PRIORITY 

1681 ): 

1682 past_comparator = True 

1683 return is_line_short_enough(rhs.head, mode=mode) 

1684 

1685 

1686def _can_defer_dict_key_delimiter_to_rhs( 

1687 line: Line, rhs: RHSResult, mode: Mode 

1688) -> bool: 

1689 """Return True if delimiters on a dictionary key can defer to right_hand_split. 

1690 

1691 When a dictionary key contains operators (like +, -, %, etc.), delimiter_split 

1692 would split inside the key instead of allowing the dictionary value to be 

1693 wrapped onto a new line. We defer to right_hand_split when the key itself 

1694 (along with the opening paren around the value) fits within the line length. 

1695 """ 

1696 colon_idx: int | None = None 

1697 for idx, leaf in enumerate(line.leaves): 

1698 if ( 

1699 leaf.type == token.COLON 

1700 and leaf.bracket_depth == 0 

1701 and leaf.parent 

1702 and leaf.parent.type == syms.dictsetmaker 

1703 ): 

1704 colon_idx = idx 

1705 break 

1706 if colon_idx is None: 

1707 return False 

1708 

1709 if any(leaf.type == token.RBRACE for leaf in line.leaves[:colon_idx]): 

1710 return False 

1711 

1712 bt = line.bracket_tracker 

1713 last_leaf = line.leaves[-1] 

1714 try: 

1715 delimiter_priority = bt.max_delimiter_priority(exclude={id(last_leaf)}) 

1716 except ValueError: 

1717 return False 

1718 

1719 if delimiter_priority >= LOGIC_PRIORITY: 

1720 return False 

1721 

1722 for leaf_id, prio in bt.delimiters.items(): 

1723 if prio == delimiter_priority: 

1724 leaf_idx = next(i for i, l in enumerate(line.leaves) if id(l) == leaf_id) 

1725 if leaf_idx >= colon_idx: 

1726 return False 

1727 

1728 return is_line_short_enough(rhs.head, mode=mode) 

1729 

1730 

1731@dont_increase_indentation 

1732def delimiter_split( 

1733 line: Line, features: Collection[Feature], mode: Mode 

1734) -> Iterator[Line]: 

1735 """Split according to delimiters of the highest priority. 

1736 

1737 If the appropriate Features are given, the split will add trailing commas 

1738 also in function signatures and calls that contain `*` and `**`. 

1739 """ 

1740 if len(line.leaves) == 0: 

1741 raise CannotSplit("Line empty") from None 

1742 last_leaf = line.leaves[-1] 

1743 

1744 bt = line.bracket_tracker 

1745 try: 

1746 delimiter_priority = bt.max_delimiter_priority(exclude={id(last_leaf)}) 

1747 except ValueError: 

1748 raise CannotSplit("No delimiters found") from None 

1749 

1750 if ( 

1751 delimiter_priority == DOT_PRIORITY 

1752 and bt.delimiter_count_with_priority(delimiter_priority) == 1 

1753 ): 

1754 raise CannotSplit("Splitting a single attribute from its owner looks wrong") 

1755 

1756 rhs: RHSResult | None = None 

1757 if ( 

1758 Preview.keep_commented_expressions_together in mode 

1759 and line.contains_standalone_comments() 

1760 and delimiter_priority != COMMA_PRIORITY 

1761 ): 

1762 first_comment_idx = next( 

1763 i for i, leaf in enumerate(line.leaves) if leaf.type == STANDALONE_COMMENT 

1764 ) 

1765 first_delimiter_idx = next( 

1766 i 

1767 for i, leaf in enumerate(line.leaves) 

1768 if bt.delimiters.get(id(leaf)) == delimiter_priority 

1769 ) 

1770 if first_comment_idx < first_delimiter_idx: 

1771 raise CannotSplit("Standalone comments should be split first") 

1772 

1773 if ( 

1774 Preview.hug_comparator in mode 

1775 and delimiter_priority == COMPARATOR_PRIORITY 

1776 and bt.delimiter_count_with_priority(delimiter_priority) == 1 

1777 ) or Preview.keep_dict_keys_with_operators in mode: 

1778 try: 

1779 rhs = _first_right_hand_split(line) 

1780 except CannotSplit: 

1781 pass 

1782 

1783 if ( 

1784 rhs is not None 

1785 and Preview.hug_comparator in mode 

1786 and delimiter_priority == COMPARATOR_PRIORITY 

1787 and bt.delimiter_count_with_priority(delimiter_priority) == 1 

1788 and _can_defer_lone_comparator_to_rhs(line, rhs, mode) 

1789 ): 

1790 raise CannotSplit("Bracketed RHS will explode via right_hand_split") 

1791 

1792 if ( 

1793 rhs is not None 

1794 and Preview.keep_dict_keys_with_operators in mode 

1795 and _can_defer_dict_key_delimiter_to_rhs(line, rhs, mode) 

1796 ): 

1797 raise CannotSplit("Dict key delimiter will explode via right_hand_split") 

1798 

1799 current_line = Line( 

1800 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1801 ) 

1802 lowest_depth = sys.maxsize 

1803 # If the opening bracket was split onto an earlier line, this line isn't a whole 

1804 # collection and a trailing comma at its end would land outside of it. 

1805 trailing_comma_safe = not _has_unmatched_closing_bracket(line) 

1806 

1807 def append_to_line(leaf: Leaf) -> Iterator[Line]: 

1808 """Append `leaf` to current line or to new line if appending impossible.""" 

1809 nonlocal current_line 

1810 try: 

1811 current_line.append_safe(leaf, preformatted=True) 

1812 except ValueError: 

1813 yield current_line 

1814 

1815 current_line = Line( 

1816 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1817 ) 

1818 current_line.append(leaf) 

1819 

1820 def append_comments(leaf: Leaf) -> Iterator[Line]: 

1821 for comment_after in line.comments_after(leaf): 

1822 yield from append_to_line(comment_after) 

1823 

1824 last_non_comment_leaf = _get_last_non_comment_leaf(line) 

1825 for leaf_idx, leaf in enumerate(line.leaves): 

1826 yield from append_to_line(leaf) 

1827 

1828 previous_priority = leaf_idx > 0 and bt.delimiters.get( 

1829 id(line.leaves[leaf_idx - 1]) 

1830 ) 

1831 if ( 

1832 previous_priority != delimiter_priority 

1833 or delimiter_priority in MIGRATE_COMMENT_DELIMITERS 

1834 ): 

1835 yield from append_comments(leaf) 

1836 

1837 lowest_depth = min(lowest_depth, leaf.bracket_depth) 

1838 if trailing_comma_safe and leaf.bracket_depth == lowest_depth: 

1839 trailing_comma_safe = _can_add_trailing_comma(leaf, features) 

1840 

1841 if last_leaf.type == STANDALONE_COMMENT and leaf_idx == last_non_comment_leaf: 

1842 current_line = _safe_add_trailing_comma( 

1843 trailing_comma_safe, delimiter_priority, current_line 

1844 ) 

1845 

1846 leaf_priority = bt.delimiters.get(id(leaf)) 

1847 if leaf_priority == delimiter_priority: 

1848 if ( 

1849 leaf_idx + 1 < len(line.leaves) 

1850 and delimiter_priority not in MIGRATE_COMMENT_DELIMITERS 

1851 ): 

1852 yield from append_comments(line.leaves[leaf_idx + 1]) 

1853 

1854 yield current_line 

1855 current_line = Line( 

1856 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1857 ) 

1858 

1859 if current_line: 

1860 current_line = _safe_add_trailing_comma( 

1861 trailing_comma_safe, delimiter_priority, current_line 

1862 ) 

1863 yield current_line 

1864 

1865 

1866@dont_increase_indentation 

1867def standalone_comment_split( 

1868 line: Line, features: Collection[Feature], mode: Mode 

1869) -> Iterator[Line]: 

1870 """Split standalone comments from the rest of the line.""" 

1871 if not line.contains_standalone_comments(): 

1872 raise CannotSplit("Line does not have any standalone comments") 

1873 

1874 current_line = Line( 

1875 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1876 ) 

1877 

1878 def append_to_line(leaf: Leaf) -> Iterator[Line]: 

1879 """Append `leaf` to current line or to new line if appending impossible.""" 

1880 nonlocal current_line 

1881 try: 

1882 current_line.append_safe(leaf, preformatted=True) 

1883 except ValueError: 

1884 yield current_line 

1885 

1886 current_line = Line( 

1887 line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1888 ) 

1889 current_line.append(leaf) 

1890 

1891 for leaf in line.leaves: 

1892 yield from append_to_line(leaf) 

1893 

1894 for comment_after in line.comments_after(leaf): 

1895 yield from append_to_line(comment_after) 

1896 

1897 if current_line: 

1898 yield current_line 

1899 

1900 

1901@dont_increase_indentation 

1902def type_ignore_comment_split( 

1903 line: Line, features: Collection[Feature], mode: Mode 

1904) -> Iterator[Line]: 

1905 """Keep multiple type ignores on their original physical lines. 

1906 

1907 Any other comment in the chain also ends its line: a type ignore that 

1908 follows another comment on the same line is no longer a type ignore. 

1909 """ 

1910 if not line.contains_multiple_type_ignores_at_current_depth() or not any( 

1911 leaf.type == token.DOT for leaf in line.leaves 

1912 ): 

1913 raise CannotSplit("Line does not have multiple type ignore comments") 

1914 

1915 current_line = Line( 

1916 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1917 ) 

1918 for leaf in line.leaves: 

1919 current_line.append(leaf, preformatted=True) 

1920 comments = line.comments_after(leaf) 

1921 for comment in comments: 

1922 current_line.append(comment, preformatted=True) 

1923 if comments: 

1924 yield current_line 

1925 current_line = Line( 

1926 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1927 ) 

1928 

1929 if current_line: 

1930 yield current_line 

1931 

1932 

1933def _force_standalone_comment_split(line: Line) -> Iterator[Line]: 

1934 """Last-resort split at every standalone-comment boundary.""" 

1935 current_line = Line( 

1936 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1937 ) 

1938 for leaf in line.leaves: 

1939 if ( 

1940 current_line.leaves 

1941 and (leaf.type == STANDALONE_COMMENT or current_line.is_comment) 

1942 # Do not isolate the header colon onto its own line when a 

1943 # standalone comment (e.g. from `# fmt: skip`) ends with `)`. 

1944 and not (current_line.is_comment and leaf.type == token.COLON) 

1945 # Code hidden by `# fmt: skip` / `# fmt: off` that ends in code rather 

1946 # than a comment can be followed by the rest of the line. 

1947 and not ( 

1948 leaf.type != STANDALONE_COMMENT 

1949 and _is_fmt_pass_converted_ending_in_code(current_line) 

1950 ) 

1951 and not _continues_fmt_skip_line(current_line, leaf) 

1952 ): 

1953 yield current_line 

1954 current_line = Line( 

1955 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1956 ) 

1957 current_line.append(leaf, preformatted=True) 

1958 for comment_after in line.comments_after(leaf): 

1959 current_line.append(comment_after, preformatted=True) 

1960 if current_line: 

1961 yield current_line 

1962 

1963 

1964def _is_fmt_pass_converted_ending_in_code(line: Line) -> bool: 

1965 """Is `line` a single STANDALONE_COMMENT holding code hidden by `# fmt: skip` 

1966 or `# fmt: off` whose last line has no comment? 

1967 

1968 Any `#` on the last line counts as a comment, even inside a string, so in 

1969 doubt the caller keeps splitting. 

1970 """ 

1971 if not line.is_fmt_pass_converted(): 

1972 return False 

1973 return "#" not in line.leaves[0].value.rpartition("\n")[2] 

1974 

1975 

1976def _continues_fmt_skip_line(line: Line, leaf: Leaf) -> bool: 

1977 """Is `leaf` code hidden by `# fmt: skip` that starts on the line where the 

1978 hidden code at the end of `line` ends? 

1979 

1980 That happens with two `# fmt: skip` comments in one statement, as in 

1981 `x = a if ( # fmt: skip` ... `) else ( # fmt: skip` ... `)`. 

1982 """ 

1983 if not line.leaves: 

1984 return False 

1985 last = line.leaves[-1] 

1986 return ( 

1987 _is_hidden_by_fmt_skip(last) 

1988 and "#" not in last.value.rpartition("\n")[2] 

1989 and _is_hidden_by_fmt_skip(leaf) 

1990 and not leaf.value.startswith("#") 

1991 and "\n" not in leaf.prefix 

1992 ) 

1993 

1994 

1995def _is_hidden_by_fmt_skip(leaf: Leaf) -> bool: 

1996 """Is `leaf` a STANDALONE_COMMENT holding code hidden by `# fmt: skip` or 

1997 `# fmt: off`, rather than code left alone by `--line-ranges`?""" 

1998 return ( 

1999 leaf.type == STANDALONE_COMMENT 

2000 and leaf.fmt_pass_converted_first_leaf is not None 

2001 and leaf.line_ranges_selected is None 

2002 ) 

2003 

2004 

2005def _is_parenthesized_lambda_or_ternary(node: LN) -> bool: 

2006 """Whether `node` is an atom wrapping a lambda or conditional expression, 

2007 looking through any redundant nested parentheses. 

2008 

2009 As a comprehension's iterable, such an expression must keep at least one pair 

2010 of parentheses: without them the trailing `for`/`if` clauses would be parsed 

2011 as part of the lambda body (or break the ternary), producing invalid code. 

2012 """ 

2013 while ( 

2014 node.type == syms.atom 

2015 and len(node.children) == 3 

2016 and is_lpar_token(node.children[0]) 

2017 and is_rpar_token(node.children[-1]) 

2018 ): 

2019 middle = node.children[1] 

2020 if middle.type in {syms.test, syms.lambdef}: 

2021 return True 

2022 node = middle 

2023 return False 

2024 

2025 

2026def _has_redundant_generator_parentheses(node: LN) -> bool: 

2027 """Whether `node` adds parentheses around a parenthesized generator.""" 

2028 if ( 

2029 node.type != syms.atom 

2030 or len(node.children) != 3 

2031 or not is_lpar_token(node.children[0]) 

2032 or not is_rpar_token(node.children[-1]) 

2033 ): 

2034 return False 

2035 

2036 middle = node.children[1] 

2037 return is_generator(middle) or _has_redundant_generator_parentheses(middle) 

2038 

2039 

2040def _is_implicitly_concatenated_string(node: LN) -> bool: 

2041 """Return True if node wraps implicitly concatenated strings.""" 

2042 if not isinstance(node, Node) or node.type != syms.atom or len(node.children) < 3: 

2043 return False 

2044 if not (is_lpar_token(node.children[0]) and is_rpar_token(node.children[-1])): 

2045 return False 

2046 middle = node.children[1] 

2047 if not (isinstance(middle, Node) and middle.type == syms.atom and middle.children): 

2048 return False 

2049 first_child = middle.children[0] 

2050 return ( 

2051 first_child.type == token.STRING 

2052 or getattr(first_child, "type", None) == syms.fstring 

2053 or getattr(first_child, "type", None) == getattr(syms, "tstring", 999) 

2054 ) 

2055 

2056 

2057def _has_redundant_list_parentheses(node: LN) -> bool: 

2058 """Whether `node` adds parentheses around a list.""" 

2059 if ( 

2060 node.type != syms.atom 

2061 or len(node.children) != 3 

2062 or not is_lpar_token(node.children[0]) 

2063 or not is_rpar_token(node.children[-1]) 

2064 ): 

2065 return False 

2066 

2067 middle = node.children[1] 

2068 return is_list(middle) or _has_redundant_list_parentheses(middle) 

2069 

2070 

2071def _normalize_unpacking_targets( 

2072 node: LN, 

2073 mode: Mode, 

2074 features: Collection[Feature], 

2075) -> None: 

2076 """Remove redundant parentheses from individual elements in unpacking targets.""" 

2077 if not isinstance(node, Node): 

2078 return 

2079 

2080 if node.type in ( 

2081 syms.exprlist, 

2082 syms.testlist_star_expr, 

2083 syms.testlist_gexp, 

2084 syms.testlist, 

2085 syms.listmaker, 

2086 syms.star_expr, 

2087 ): 

2088 for child in node.children: 

2089 if child.type == syms.atom and not _atom_has_inner_comments(child): 

2090 maybe_make_parens_invisible_in_atom( 

2091 child, 

2092 parent=node, 

2093 mode=mode, 

2094 features=features, 

2095 ) 

2096 _normalize_unpacking_targets(child, mode=mode, features=features) 

2097 elif node.type == syms.atom: 

2098 for child in node.children: 

2099 if isinstance(child, Node): 

2100 _normalize_unpacking_targets(child, mode=mode, features=features) 

2101 

2102 

2103_DISALLOWED_COMPARISON_INNER_TYPES: Final = { 

2104 syms.comparison, 

2105 syms.not_test, 

2106 syms.and_test, 

2107 syms.or_test, 

2108 syms.test, 

2109 syms.lambdef, 

2110 syms.namedexpr_test, 

2111 syms.testlist_gexp, 

2112 syms.yield_expr, 

2113 syms.old_lambdef, 

2114 syms.old_test, 

2115} 

2116 

2117_BINARY_OP_TYPES: Final = { 

2118 syms.arith_expr, 

2119 syms.term, 

2120 syms.expr, 

2121 syms.xor_expr, 

2122 syms.and_expr, 

2123 syms.shift_expr, 

2124} 

2125 

2126 

2127def _is_binary_power(node: LN) -> bool: 

2128 """Whether node is an exponentiation expression (e.g. `a ** b`).""" 

2129 return ( 

2130 isinstance(node, Node) 

2131 and node.type == syms.power 

2132 and any(child.type == token.DOUBLESTAR for child in node.children) 

2133 ) 

2134 

2135 

2136def _is_parenthesized_atom(node: LN) -> bool: 

2137 """Whether node is an atom enclosed in parentheses.""" 

2138 return ( 

2139 node.type == syms.atom 

2140 and len(node.children) == 3 

2141 and is_lpar_token(node.children[0]) 

2142 and is_rpar_token(node.children[-1]) 

2143 ) 

2144 

2145 

2146def _is_disallowed_comparison_operand(node: LN) -> bool: 

2147 """Whether atom `node` encloses an expression that must retain parens in 

2148 comparison. 

2149 """ 

2150 if not _is_parenthesized_atom(node): 

2151 return False 

2152 

2153 middle = node.children[1] 

2154 return ( 

2155 middle.type in _DISALLOWED_COMPARISON_INNER_TYPES 

2156 or middle.type in _BINARY_OP_TYPES 

2157 or _is_binary_power(middle) 

2158 ) 

2159 

2160 

2161def _normalize_comparison_operands( 

2162 node: Node, mode: Mode, features: Collection[Feature] 

2163) -> None: 

2164 """Remove redundant parentheses around operands in a comparison expression.""" 

2165 for i in range(0, len(node.children), 2): 

2166 operand = node.children[i] 

2167 if operand.type == syms.atom and not _is_atom_multiline(operand): 

2168 maybe_make_parens_invisible_in_atom( 

2169 operand, parent=node, mode=mode, features=features 

2170 ) 

2171 if is_atom_with_invisible_parens(operand) and not any( 

2172 "#" in leaf.prefix for leaf in operand.leaves() 

2173 ): 

2174 # Drop the now invisible parens so the operand splits the same way 

2175 # as one written without parentheses. Otherwise the next pass, which 

2176 # no longer sees them, can split it differently: parens kept for 

2177 # being multiline are joined onto one line and only removed then. 

2178 middle = operand.children[1] 

2179 middle.prefix = operand.prefix 

2180 operand.replace(middle) 

2181 

2182 

2183def normalize_invisible_parens( 

2184 node: Node, parens_after: set[str], *, mode: Mode, features: Collection[Feature] 

2185) -> None: 

2186 """Make existing optional parentheses invisible or create new ones. 

2187 

2188 `parens_after` is a set of string leaf values immediately after which parens 

2189 should be put. 

2190 

2191 Standardizes on visible parentheses for single-element tuples, and keeps 

2192 existing visible parentheses for other tuples and generator expressions. 

2193 """ 

2194 for pc in list_comments(node.prefix, is_endmarker=False, mode=mode): 

2195 if contains_fmt_directive(pc.value, FMT_OFF): 

2196 # This `node` has a prefix with `# fmt: off`, don't mess with parens. 

2197 return 

2198 

2199 if Preview.remove_redundant_unpacking_parentheses in mode: 

2200 if node.type in (syms.for_stmt, syms.comp_for, syms.old_comp_for): 

2201 if len(node.children) > 1: 

2202 _normalize_unpacking_targets( 

2203 node.children[1], mode=mode, features=features 

2204 ) 

2205 elif node.type == syms.expr_stmt: 

2206 equal_indices = [ 

2207 i for i, child in enumerate(node.children) if child.type == token.EQUAL 

2208 ] 

2209 if equal_indices: 

2210 for child in node.children[: equal_indices[-1]]: 

2211 if child.type != token.EQUAL: 

2212 _normalize_unpacking_targets( 

2213 child, mode=mode, features=features 

2214 ) 

2215 elif node.type == syms.del_stmt: 

2216 if len(node.children) > 1: 

2217 _normalize_unpacking_targets( 

2218 node.children[1], mode=mode, features=features 

2219 ) 

2220 elif node.type == syms.with_stmt: 

2221 for child in node.children: 

2222 if child.type == syms.asexpr_test and len(child.children) > 2: 

2223 _normalize_unpacking_targets( 

2224 child.children[2], mode=mode, features=features 

2225 ) 

2226 

2227 # The multiple context managers grammar has a different pattern, thus this is 

2228 # separate from the for-loop below. This possibly wraps them in invisible parens, 

2229 # and later will be removed in remove_with_parens when needed. 

2230 if node.type == syms.with_stmt: 

2231 _maybe_wrap_cms_in_parens(node, mode, features) 

2232 

2233 check_lpar = False 

2234 for index, child in enumerate(list(node.children)): 

2235 # Fixes a bug where invisible parens are not properly stripped from 

2236 # assignment statements that contain type annotations. 

2237 if isinstance(child, Node) and child.type == syms.annassign: 

2238 normalize_invisible_parens( 

2239 child, parens_after=parens_after, mode=mode, features=features 

2240 ) 

2241 

2242 # Fixes a bug where invisible parens are not properly wrapped around 

2243 # case blocks. 

2244 if isinstance(child, Node) and child.type == syms.case_block: 

2245 normalize_invisible_parens( 

2246 child, parens_after={"case"}, mode=mode, features=features 

2247 ) 

2248 

2249 # Add parentheses around if guards in case blocks 

2250 if isinstance(child, Node) and child.type == syms.guard: 

2251 normalize_invisible_parens( 

2252 child, parens_after={"if"}, mode=mode, features=features 

2253 ) 

2254 

2255 # Add parentheses around long tuple unpacking in assignments. 

2256 if ( 

2257 index == 0 

2258 and isinstance(child, Node) 

2259 and child.type == syms.testlist_star_expr 

2260 ): 

2261 check_lpar = True 

2262 

2263 if ( 

2264 index == 0 

2265 and isinstance(child, Node) 

2266 and child.type == syms.atom 

2267 and node.type == syms.expr_stmt 

2268 and not _atom_has_magic_trailing_comma(child, mode) 

2269 and not _is_atom_multiline(child) 

2270 ): 

2271 if _is_parenthesized_annotation_target(node, child): 

2272 # One pair is what makes the target non-simple, so any nesting 

2273 # inside it is redundant and goes. 

2274 inner = child.children[1] 

2275 if isinstance(inner, Node) and inner.type == syms.atom: 

2276 maybe_make_parens_invisible_in_atom( 

2277 inner, 

2278 parent=child, 

2279 mode=mode, 

2280 features=features, 

2281 ) 

2282 elif maybe_make_parens_invisible_in_atom( 

2283 child, 

2284 parent=node, 

2285 mode=mode, 

2286 features=features, 

2287 remove_brackets_around_comma=True, 

2288 allow_star_expr=True, 

2289 ): 

2290 wrap_in_parentheses(node, child, visible=False, index=index) 

2291 elif ( 

2292 is_atom_with_invisible_parens(child) 

2293 and not is_tuple(child) 

2294 and not any("#" in leaf.prefix for leaf in child.leaves()) 

2295 ): 

2296 # Drop the now invisible parens so the target splits the same 

2297 # way as one written without parentheses. Otherwise the next 

2298 # pass, which no longer sees them, can split it differently. 

2299 # A tuple target keeps them, since one written without 

2300 # parentheses gets invisible parens too. 

2301 middle = child.children[1] 

2302 middle.prefix = child.prefix 

2303 child.replace(middle) 

2304 

2305 if check_lpar: 

2306 if ( 

2307 child.type == syms.atom 

2308 and node.type == syms.for_stmt 

2309 and isinstance(child.prev_sibling, Leaf) 

2310 and child.prev_sibling.type == token.NAME 

2311 and child.prev_sibling.value == "for" 

2312 ): 

2313 if maybe_make_parens_invisible_in_atom( 

2314 child, 

2315 parent=node, 

2316 mode=mode, 

2317 features=features, 

2318 remove_brackets_around_comma=True, 

2319 ): 

2320 wrap_in_parentheses(node, child, visible=False, index=index) 

2321 elif isinstance(child, Node) and node.type == syms.with_stmt: 

2322 remove_with_parens(child, node, mode=mode, features=features) 

2323 elif ( 

2324 isinstance(child, Node) 

2325 and node.type == syms.yield_expr 

2326 and child.type == syms.yield_arg 

2327 and Preview.parenthesize_tuple_in_yield in mode 

2328 ): 

2329 if ( 

2330 len(child.children) == 1 

2331 and child.children[0].type != syms.atom 

2332 and is_one_tuple(child.children[0]) 

2333 ): 

2334 wrap_in_parentheses(node, child, visible=True, index=index) 

2335 elif child.type == syms.atom: 

2336 if "in" in parens_after and _is_parenthesized_lambda_or_ternary(child): 

2337 # A lambda or conditional expression used as a comprehension's 

2338 # iterable must keep at least one pair of parentheses, otherwise 

2339 # the trailing `for`/`if` clauses get absorbed into it and the 

2340 # code becomes invalid. Any extra nested pairs are redundant, so 

2341 # collapse them while keeping exactly one visible pair. 

2342 maybe_make_parens_invisible_in_atom( 

2343 child, parent=node, mode=mode, features=features 

2344 ) 

2345 opening = child.children[0] 

2346 closing = child.children[-1] 

2347 if is_lpar_token(opening) and is_rpar_token(closing): 

2348 opening.value = "(" 

2349 closing.value = ")" 

2350 elif maybe_make_parens_invisible_in_atom( 

2351 child, parent=node, mode=mode, features=features 

2352 ): 

2353 wrap_in_parentheses(node, child, visible=False, index=index) 

2354 elif is_one_tuple(child): 

2355 wrap_in_parentheses(node, child, visible=True, index=index) 

2356 elif node.type == syms.import_from: 

2357 _normalize_import_from(node, child, index) 

2358 break 

2359 elif ( 

2360 index == 1 

2361 and child.type == token.STAR 

2362 and node.type == syms.except_clause 

2363 ): 

2364 # In except* (PEP 654), the star is actually part of 

2365 # of the keyword. So we need to skip the insertion of 

2366 # invisible parentheses to work more precisely. 

2367 continue 

2368 

2369 elif ( 

2370 isinstance(child, Leaf) 

2371 and child.next_sibling is not None 

2372 and child.next_sibling.type == token.COLON 

2373 and child.value == "case" 

2374 ): 

2375 # A special patch for "case case:" scenario, the second occurrence 

2376 # of case will be not parsed as a Python keyword. 

2377 break 

2378 

2379 elif isinstance(child, Node) and child.type == syms.guard: 

2380 # Guard nodes handle their own inner wrapping. Wrapping the guard 

2381 # itself can produce invalid output when the case pattern splits. 

2382 pass 

2383 

2384 elif not is_multiline_string(child): 

2385 if ( 

2386 Preview.fix_if_guard_explosion_in_case_statement in mode 

2387 and node.type == syms.guard 

2388 ): 

2389 mock_line = Line(mode=mode) 

2390 for leaf in child.leaves(): 

2391 mock_line.append(leaf) 

2392 # If it's a guard AND it's short, we DON'T wrap 

2393 if not is_line_short_enough(mock_line, mode=mode): 

2394 wrap_in_parentheses(node, child, visible=False, index=index) 

2395 else: 

2396 wrap_in_parentheses(node, child, visible=False, index=index) 

2397 

2398 comma_check = child.type == token.COMMA 

2399 

2400 check_lpar = isinstance(child, Leaf) and ( 

2401 child.value in parens_after or comma_check 

2402 ) 

2403 

2404 

2405def _normalize_import_from(parent: Node, child: LN, index: int) -> None: 

2406 # "import from" nodes store parentheses directly as part of 

2407 # the statement 

2408 if is_lpar_token(child): 

2409 assert is_rpar_token(parent.children[-1]) 

2410 # make parentheses invisible 

2411 child.value = "" 

2412 parent.children[-1].value = "" 

2413 elif child.type != token.STAR: 

2414 # insert invisible parentheses 

2415 parent.insert_child(index, Leaf(token.LPAR, "")) 

2416 parent.append_child(Leaf(token.RPAR, "")) 

2417 

2418 

2419def remove_await_parens(node: Node, mode: Mode, features: Collection[Feature]) -> None: 

2420 if node.children[0].type == token.AWAIT and len(node.children) > 1: 

2421 if ( 

2422 node.children[1].type == syms.atom 

2423 and node.children[1].children[0].type == token.LPAR 

2424 ): 

2425 if maybe_make_parens_invisible_in_atom( 

2426 node.children[1], 

2427 parent=node, 

2428 mode=mode, 

2429 features=features, 

2430 remove_brackets_around_comma=True, 

2431 ): 

2432 wrap_in_parentheses(node, node.children[1], visible=False) 

2433 

2434 # Since await is an expression we shouldn't remove 

2435 # brackets in cases where this would change 

2436 # the AST due to operator precedence. 

2437 # Therefore we only aim to remove brackets around 

2438 # power nodes that aren't also await expressions themselves. 

2439 # https://peps.python.org/pep-0492/#updated-operator-precedence-table 

2440 # N.B. We've still removed any redundant nested brackets though :) 

2441 opening_bracket = cast(Leaf, node.children[1].children[0]) 

2442 closing_bracket = cast(Leaf, node.children[1].children[-1]) 

2443 bracket_contents = node.children[1].children[1] 

2444 if isinstance(bracket_contents, Node) and ( 

2445 bracket_contents.type != syms.power 

2446 or bracket_contents.children[0].type == token.AWAIT 

2447 or any( 

2448 isinstance(child, Leaf) and child.type == token.DOUBLESTAR 

2449 for child in bracket_contents.children 

2450 ) 

2451 ): 

2452 ensure_visible(opening_bracket) 

2453 ensure_visible(closing_bracket) 

2454 

2455 

2456def _maybe_wrap_cms_in_parens( 

2457 node: Node, mode: Mode, features: Collection[Feature] 

2458) -> None: 

2459 """When enabled and safe, wrap the multiple context managers in invisible parens. 

2460 

2461 It is only safe when `features` contain Feature.PARENTHESIZED_CONTEXT_MANAGERS. 

2462 """ 

2463 if ( 

2464 Feature.PARENTHESIZED_CONTEXT_MANAGERS not in features 

2465 or len(node.children) <= 2 

2466 # If it's an atom, it's already wrapped in parens. 

2467 or node.children[1].type == syms.atom 

2468 ): 

2469 return 

2470 colon_index: int | None = None 

2471 for i in range(2, len(node.children)): 

2472 if node.children[i].type == token.COLON: 

2473 colon_index = i 

2474 break 

2475 if colon_index is not None: 

2476 lpar = Leaf(token.LPAR, "") 

2477 rpar = Leaf(token.RPAR, "") 

2478 context_managers = node.children[1:colon_index] 

2479 for child in context_managers: 

2480 child.remove() 

2481 # After wrapping, the with_stmt will look like this: 

2482 # with_stmt 

2483 # NAME 'with' 

2484 # atom 

2485 # LPAR '' 

2486 # testlist_gexp 

2487 # ... <-- context_managers 

2488 # /testlist_gexp 

2489 # RPAR '' 

2490 # /atom 

2491 # COLON ':' 

2492 new_child = Node( 

2493 syms.atom, [lpar, Node(syms.testlist_gexp, context_managers), rpar] 

2494 ) 

2495 node.insert_child(1, new_child) 

2496 

2497 

2498def remove_with_parens( 

2499 node: Node, parent: Node, mode: Mode, features: Collection[Feature] 

2500) -> None: 

2501 """Recursively hide optional parens in `with` statements.""" 

2502 # Removing all unnecessary parentheses in with statements in one pass is a tad 

2503 # complex as different variations of bracketed statements result in pretty 

2504 # different parse trees: 

2505 # 

2506 # with (open("file")) as f: # this is an asexpr_test 

2507 # ... 

2508 # 

2509 # with (open("file") as f): # this is an atom containing an 

2510 # ... # asexpr_test 

2511 # 

2512 # with (open("file")) as f, (open("file")) as f: # this is asexpr_test, COMMA, 

2513 # ... # asexpr_test 

2514 # 

2515 # with (open("file") as f, open("file") as f): # an atom containing a 

2516 # ... # testlist_gexp which then 

2517 # # contains multiple asexpr_test(s) 

2518 if node.type == syms.atom: 

2519 if maybe_make_parens_invisible_in_atom( 

2520 node, 

2521 parent=parent, 

2522 mode=mode, 

2523 features=features, 

2524 remove_brackets_around_comma=True, 

2525 ): 

2526 wrap_in_parentheses(parent, node, visible=False) 

2527 if isinstance(node.children[1], Node): 

2528 remove_with_parens(node.children[1], node, mode=mode, features=features) 

2529 elif node.type == syms.testlist_gexp: 

2530 for child in node.children: 

2531 if isinstance(child, Node): 

2532 remove_with_parens(child, node, mode=mode, features=features) 

2533 elif node.type == syms.asexpr_test and not any( 

2534 leaf.type == token.COLONEQUAL for leaf in node.leaves() 

2535 ): 

2536 if maybe_make_parens_invisible_in_atom( 

2537 node.children[0], 

2538 parent=node, 

2539 mode=mode, 

2540 features=features, 

2541 remove_brackets_around_comma=True, 

2542 ): 

2543 wrap_in_parentheses(node, node.children[0], visible=False) 

2544 

2545 

2546def _atom_has_magic_trailing_comma(node: LN, mode: Mode) -> bool: 

2547 """Check if an atom node has a magic trailing comma. 

2548 

2549 Returns True for single-element tuples with trailing commas like (a,), 

2550 which should be preserved to maintain their tuple type. 

2551 """ 

2552 if not mode.magic_trailing_comma: 

2553 return False 

2554 

2555 return is_one_tuple(node) 

2556 

2557 

2558def _is_parenthesized_annotation_target(node: Node, child: Node) -> bool: 

2559 """Is `child` a parenthesized plain name that `node` annotates? 

2560 

2561 `(x): int = 5` and `x: int = 5` do not do the same thing: only the second one 

2562 records `x` in `__annotations__`, because the parentheses make the target 

2563 non-simple (`AnnAssign.simple` is 0). Dropping them changes what the module 

2564 does at runtime, so they have to stay. Attribute and subscript targets are 

2565 non-simple either way, so those are left alone here. 

2566 """ 

2567 if len(node.children) < 2: 

2568 return False 

2569 

2570 annassign = node.children[1] 

2571 if not isinstance(annassign, Node) or annassign.type != syms.annassign: 

2572 return False 

2573 

2574 # `((x)): int = 5` is non-simple too, so look through any nesting. 

2575 target: LN = child 

2576 while ( 

2577 isinstance(target, Node) 

2578 and target.type == syms.atom 

2579 and len(target.children) == 3 

2580 ): 

2581 target = target.children[1] 

2582 

2583 return isinstance(target, Leaf) and target.type == token.NAME 

2584 

2585 

2586def _atom_has_inner_comments(node: LN) -> bool: 

2587 """Check if there are comments inside an atom's brackets.""" 

2588 if not isinstance(node, Node) or len(node.children) < 3: 

2589 return False 

2590 

2591 # The first child's prefix holds comments before the opening bracket. 

2592 return any( 

2593 "#" in leaf.prefix for child in node.children[1:] for leaf in child.leaves() 

2594 ) 

2595 

2596 

2597def _is_atom_multiline(node: LN) -> bool: 

2598 """Check if an atom node is multiline (indicating intentional formatting).""" 

2599 if not isinstance(node, Node) or len(node.children) < 3: 

2600 return False 

2601 

2602 # Check the middle child (between LPAR and RPAR) for newlines in its subtree 

2603 # The first child's prefix contains blank lines/comments before the opening paren 

2604 middle = node.children[1] 

2605 for child in middle.pre_order(): 

2606 if isinstance(child, Leaf) and "\n" in child.prefix: 

2607 return True 

2608 

2609 return False 

2610 

2611 

2612def maybe_make_parens_invisible_in_atom( 

2613 node: LN, 

2614 parent: LN, 

2615 mode: Mode, 

2616 features: Collection[Feature], 

2617 remove_brackets_around_comma: bool = False, 

2618 allow_star_expr: bool = False, 

2619 remove_generator_parens: bool = False, 

2620 is_subscript: bool = False, 

2621) -> bool: 

2622 """If it's safe, make the parens in the atom `node` invisible, recursively. 

2623 Additionally, remove repeated, adjacent invisible parens from the atom `node` 

2624 as they are redundant. 

2625 

2626 Returns whether the node should itself be wrapped in invisible parentheses. 

2627 """ 

2628 can_remove_generator_parens = remove_generator_parens and is_generator(node) 

2629 if ( 

2630 node.type not in (syms.atom, syms.expr) 

2631 or is_empty_tuple(node) 

2632 or (not is_subscript and is_one_tuple(node)) 

2633 or (is_tuple(node) and parent.type == syms.asexpr_test) 

2634 or ( 

2635 is_tuple(node) 

2636 and parent.type == syms.with_stmt 

2637 and has_sibling_with_type(node, token.COMMA) 

2638 ) 

2639 or ( 

2640 is_yield(node) 

2641 and parent.type != syms.expr_stmt 

2642 and ( 

2643 parent.type != syms.simple_stmt 

2644 or Preview.remove_redundant_top_level_parentheses not in mode 

2645 ) 

2646 ) 

2647 or ( 

2648 # This condition tries to prevent removing non-optional brackets 

2649 # around a tuple, however, can be a bit overzealous so we provide 

2650 # and option to skip this check for `for` and `with` statements. 

2651 not remove_brackets_around_comma 

2652 and max_delimiter_priority_in_atom(node) >= COMMA_PRIORITY 

2653 and not can_remove_generator_parens 

2654 # Remove parentheses around multiple exception types in except and 

2655 # except* without as. See PEP 758 for details. 

2656 and not ( 

2657 Feature.UNPARENTHESIZED_EXCEPT_TYPES in features 

2658 # is a tuple 

2659 and is_tuple(node) 

2660 # has a parent node 

2661 and node.parent is not None 

2662 # parent is an except clause 

2663 and node.parent.type == syms.except_clause 

2664 # is not immediately followed by as clause 

2665 and not ( 

2666 node.next_sibling is not None 

2667 and is_name_token(node.next_sibling) 

2668 and node.next_sibling.value == "as" 

2669 ) 

2670 ) 

2671 ) 

2672 or is_tuple_containing_walrus(node) 

2673 or (not allow_star_expr and is_tuple_containing_star(node)) 

2674 or (not can_remove_generator_parens and is_generator(node)) 

2675 or (parent.type == syms.comparison and _is_disallowed_comparison_operand(node)) 

2676 or ( 

2677 parent.type == syms.simple_stmt and _is_implicitly_concatenated_string(node) 

2678 ) 

2679 ): 

2680 return False 

2681 

2682 if is_walrus_assignment(node): 

2683 if parent.type in [ 

2684 syms.comparison, 

2685 syms.annassign, 

2686 syms.expr_stmt, 

2687 syms.assert_stmt, 

2688 syms.return_stmt, 

2689 syms.yield_arg, 

2690 syms.yield_expr, 

2691 syms.except_clause, 

2692 syms.funcdef, 

2693 syms.with_stmt, 

2694 syms.testlist_gexp, 

2695 syms.tname, 

2696 # these ones aren't useful to end users, but they do please fuzzers 

2697 syms.for_stmt, 

2698 syms.del_stmt, 

2699 syms.simple_stmt, 

2700 ]: 

2701 return False 

2702 if ( 

2703 parent.type == syms.atom 

2704 and parent.children[0].type == token.LBRACE 

2705 and Feature.UNPARENTHESIZED_WALRUS_IN_SETS not in features 

2706 ): 

2707 # A named expression in a set literal needs its parentheses before 

2708 # Python 3.10 (3.9.1 accepts it, but 3.9.0 doesn't). 

2709 return False 

2710 

2711 first = node.children[0] 

2712 last = node.children[-1] 

2713 if is_lpar_token(first) and is_rpar_token(last): 

2714 middle = node.children[1] 

2715 # make parentheses invisible 

2716 if ( 

2717 # If the prefix of `middle` includes a type comment with 

2718 # ignore annotation, then we do not remove the parentheses 

2719 not is_type_ignore_comment_string(middle.prefix.strip(), mode=mode) 

2720 ): 

2721 first.value = "" 

2722 last.value = "" 

2723 maybe_make_parens_invisible_in_atom( 

2724 middle, 

2725 parent=parent, 

2726 mode=mode, 

2727 features=features, 

2728 remove_brackets_around_comma=remove_brackets_around_comma, 

2729 remove_generator_parens=remove_generator_parens, 

2730 is_subscript=is_subscript, 

2731 ) 

2732 

2733 if is_atom_with_invisible_parens(middle): 

2734 # Strip the invisible parens from `middle` by replacing 

2735 # it with the child in-between the invisible parens 

2736 middle.replace(middle.children[1]) 

2737 

2738 if middle.children[0].prefix.strip(): 

2739 # Preserve comments before first paren 

2740 middle.children[1].prefix = ( 

2741 middle.children[0].prefix + middle.children[1].prefix 

2742 ) 

2743 

2744 if middle.children[-1].prefix.strip(): 

2745 # Preserve comments before last paren 

2746 last.prefix = middle.children[-1].prefix + last.prefix 

2747 

2748 return False 

2749 

2750 return True 

2751 

2752 

2753def should_split_line(line: Line, opening_bracket: Leaf) -> bool: 

2754 """Should `line` be immediately split with `delimiter_split()` after RHS?""" 

2755 

2756 if not (opening_bracket.parent and opening_bracket.value in "[{("): 

2757 return False 

2758 

2759 # We're essentially checking if the body is delimited by commas and there's more 

2760 # than one of them (we're excluding the trailing comma and if the delimiter priority 

2761 # is still commas, that means there's more). 

2762 exclude = set() 

2763 trailing_comma = False 

2764 try: 

2765 last_leaf = line.leaves[-1] 

2766 if last_leaf.type == token.COMMA: 

2767 trailing_comma = True 

2768 exclude.add(id(last_leaf)) 

2769 max_priority = line.bracket_tracker.max_delimiter_priority(exclude=exclude) 

2770 except (IndexError, ValueError): 

2771 return False 

2772 

2773 return max_priority == COMMA_PRIORITY and ( 

2774 (line.mode.magic_trailing_comma and trailing_comma) 

2775 # always explode imports 

2776 or opening_bracket.parent.type in {syms.atom, syms.import_from} 

2777 ) 

2778 

2779 

2780def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[set[LeafID]]: 

2781 """Generate sets of closing bracket IDs that should be omitted in a RHS. 

2782 

2783 Brackets can be omitted if the entire trailer up to and including 

2784 a preceding closing bracket fits in one line. 

2785 

2786 Yielded sets are cumulative (contain results of previous yields, too). First 

2787 set is empty, unless the line should explode, in which case bracket pairs until 

2788 the one that needs to explode are omitted. 

2789 """ 

2790 

2791 omit: set[LeafID] = set() 

2792 if not line.magic_trailing_comma: 

2793 yield omit 

2794 

2795 length = 4 * line.depth 

2796 opening_bracket: Leaf | None = None 

2797 closing_bracket: Leaf | None = None 

2798 inner_brackets: set[LeafID] = set() 

2799 for index, leaf, leaf_length in line.enumerate_with_length(is_reversed=True): 

2800 length += leaf_length 

2801 if length > line_length: 

2802 break 

2803 

2804 has_inline_comment = leaf_length > len(leaf.value) + len(leaf.prefix) 

2805 if leaf.type == STANDALONE_COMMENT or has_inline_comment: 

2806 break 

2807 

2808 if opening_bracket: 

2809 if leaf is opening_bracket: 

2810 opening_bracket = None 

2811 elif leaf.type in CLOSING_BRACKETS: 

2812 prev = line.leaves[index - 1] if index > 0 else None 

2813 if ( 

2814 prev 

2815 and prev.type == token.COMMA 

2816 and leaf.opening_bracket is not None 

2817 and not is_one_sequence_between( 

2818 leaf.opening_bracket, leaf, line.leaves 

2819 ) 

2820 ): 

2821 # Never omit bracket pairs with trailing commas. 

2822 # We need to explode on those. 

2823 break 

2824 

2825 inner_brackets.add(id(leaf)) 

2826 elif leaf.type in CLOSING_BRACKETS: 

2827 prev = line.leaves[index - 1] if index > 0 else None 

2828 if prev and prev.type in OPENING_BRACKETS: 

2829 # Empty brackets would fail a split so treat them as "inner" 

2830 # brackets (e.g. only add them to the `omit` set if another 

2831 # pair of brackets was good enough. 

2832 inner_brackets.add(id(leaf)) 

2833 continue 

2834 

2835 if closing_bracket: 

2836 omit.add(id(closing_bracket)) 

2837 omit.update(inner_brackets) 

2838 inner_brackets.clear() 

2839 yield omit 

2840 

2841 if ( 

2842 prev 

2843 and prev.type == token.COMMA 

2844 and leaf.opening_bracket is not None 

2845 and not is_one_sequence_between(leaf.opening_bracket, leaf, line.leaves) 

2846 ): 

2847 # Never omit bracket pairs with trailing commas. 

2848 # We need to explode on those. 

2849 break 

2850 

2851 if leaf.value: 

2852 opening_bracket = leaf.opening_bracket 

2853 closing_bracket = leaf 

2854 

2855 

2856def _over_length_only_due_to_bracket_comment( 

2857 line: Line, mode: Mode, allowed_brackets: Collection[int] = OPENING_BRACKETS 

2858) -> bool: 

2859 """Return True if `line` only exceeds `mode.line_length` because of an inline 

2860 comment attached to an opening bracket. 

2861 

2862 This is the shape produced by issues #4733 and #3681: 

2863 a comment inside brackets renders at the end of the head line after Black 

2864 splits the statement, pushing it past the limit. If the line without the 

2865 comment fits within `mode.line_length`, rejecting an omit-based split does 

2866 not prevent the line from exceeding the limit and causes unnecessary 

2867 splitting of other trailers (issue #3681). 

2868 """ 

2869 if not line.leaves: 

2870 return False 

2871 # The over-length must be caused entirely by a trailing comment. 

2872 indent = " " * line.depth 

2873 leaves_iter = iter(line.leaves) 

2874 first = next(leaves_iter) 

2875 text_without_comments = f"{first.prefix}{indent}{first.value}" 

2876 text_without_comments += "".join(str(leaf) for leaf in leaves_iter) 

2877 if str_width(text_without_comments) > mode.line_length: 

2878 return False 

2879 # And the comment must be attached to an opening bracket. 

2880 for leaf_id, comments in line.comments.items(): 

2881 if not comments: 

2882 continue 

2883 leaf = next((lf for lf in line.leaves if id(lf) == leaf_id), None) 

2884 if leaf is None or leaf.type not in allowed_brackets: 

2885 return False 

2886 return True 

2887 

2888 

2889def _over_length_only_due_to_subscript_comment(line: Line, mode: Mode) -> bool: 

2890 return _over_length_only_due_to_bracket_comment( 

2891 line, mode, allowed_brackets={token.LSQB} 

2892 ) 

2893 

2894 

2895def run_transformer( 

2896 line: Line, 

2897 transform: Transformer, 

2898 mode: Mode, 

2899 features: Collection[Feature], 

2900 *, 

2901 line_str: str = "", 

2902) -> list[Line]: 

2903 if not line_str: 

2904 line_str = line_to_string(line) 

2905 # A visible optional paren after the transform means the split already used 

2906 # it. Parens nested in the line (around dict values) can turn visible in a 

2907 # sub-line's split instead, so they don't count. 

2908 optional_parens = [ 

2909 bracket 

2910 for bracket in line.bracket_tracker.invisible 

2911 if bracket.bracket_depth == 0 

2912 ] 

2913 result: list[Line] = [] 

2914 for transformed_line in transform(line, features, mode): 

2915 if str(transformed_line).strip("\n") == line_str: 

2916 raise CannotTransform("Line transformer returned an unchanged result") 

2917 

2918 result.extend(transform_line(transformed_line, mode=mode, features=features)) 

2919 

2920 features_set = set(features) 

2921 if ( 

2922 Feature.FORCE_OPTIONAL_PARENTHESES in features_set 

2923 or transform is not right_hand_split_with_omits 

2924 or not line.bracket_tracker.invisible 

2925 or any(bracket.value for bracket in optional_parens) 

2926 or line.contains_multiline_strings() 

2927 or result[0].contains_uncollapsable_type_comments() 

2928 or result[0].contains_unsplittable_type_ignore() 

2929 or is_line_short_enough(result[0], mode=mode) 

2930 # result[0] only exceeds the length because of a comment attached to a 

2931 # subscript opening bracket. Taking the FORCE_OPTIONAL_PARENTHESES 

2932 # "second opinion" wraps the annotation in extra invisible parens and 

2933 # migrates the comment outside the subscript, which then oscillates with 

2934 # a deeper-bracket split on the next formatter pass (issue #4733). 

2935 or _over_length_only_due_to_subscript_comment(result[0], mode) 

2936 # If any leaves have no parents (which _can_ occur since 

2937 # `transform(line)` potentially destroys the line's underlying node 

2938 # structure), then we can't proceed. Doing so would cause the below 

2939 # call to `append_leaves()` to fail. 

2940 or any( 

2941 leaf.parent is None 

2942 for leaf in line.leaves 

2943 if ( 

2944 Preview.parenthesize_expressions_with_comments not in mode 

2945 or leaf.type != STANDALONE_COMMENT 

2946 ) 

2947 ) 

2948 ): 

2949 return result 

2950 

2951 line_copy = line.clone() 

2952 append_leaves(line_copy, line, line.leaves) 

2953 features_fop = features_set | {Feature.FORCE_OPTIONAL_PARENTHESES} 

2954 second_opinion = run_transformer( 

2955 line_copy, transform, mode, features_fop, line_str=line_str 

2956 ) 

2957 if all( 

2958 is_line_short_enough(ln, mode=mode) 

2959 for ln in second_opinion 

2960 if ( 

2961 Preview.parenthesize_expressions_with_comments not in mode 

2962 or not ln.is_comment 

2963 ) 

2964 ): 

2965 result = second_opinion 

2966 return result