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

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

913 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 Union, cast 

12 

13from black.brackets import ( 

14 COMMA_PRIORITY, 

15 COMPARATOR_PRIORITY, 

16 DOT_PRIORITY, 

17 STRING_PRIORITY, 

18 get_leaves_inside_matching_brackets, 

19 max_delimiter_priority_in_atom, 

20) 

21from black.comments import ( 

22 FMT_OFF, 

23 FMT_ON, 

24 contains_fmt_directive, 

25 generate_comments, 

26 list_comments, 

27) 

28from black.lines import ( 

29 Line, 

30 RHSResult, 

31 append_leaves, 

32 can_be_split, 

33 can_omit_invisible_parens, 

34 is_line_short_enough, 

35 line_to_string, 

36) 

37from black.mode import Feature, Mode, Preview 

38from black.nodes import ( 

39 ASSIGNMENTS, 

40 BRACKETS, 

41 CLOSING_BRACKETS, 

42 OPENING_BRACKETS, 

43 STANDALONE_COMMENT, 

44 STATEMENT, 

45 WHITESPACE, 

46 Visitor, 

47 ensure_visible, 

48 fstring_tstring_to_string, 

49 get_annotation_type, 

50 has_sibling_with_type, 

51 is_arith_like, 

52 is_async_stmt_or_funcdef, 

53 is_atom_with_invisible_parens, 

54 is_docstring, 

55 is_empty_tuple, 

56 is_generator, 

57 is_lpar_token, 

58 is_multiline_string, 

59 is_name_token, 

60 is_one_sequence_between, 

61 is_one_tuple, 

62 is_parent_function_or_class, 

63 is_part_of_annotation, 

64 is_rpar_token, 

65 is_stub_body, 

66 is_stub_suite, 

67 is_tuple, 

68 is_tuple_containing_star, 

69 is_tuple_containing_walrus, 

70 is_type_ignore_comment, 

71 is_type_ignore_comment_string, 

72 is_vararg, 

73 is_walrus_assignment, 

74 is_yield, 

75 syms, 

76 wrap_in_parentheses, 

77) 

78from black.numerics import normalize_numeric_literal 

79from black.strings import ( 

80 fix_multiline_docstring, 

81 get_string_prefix, 

82 normalize_string_prefix, 

83 normalize_string_quotes, 

84 normalize_unicode_escape_sequences, 

85 str_width, 

86) 

87from black.trans import ( 

88 CannotTransform, 

89 StringMerger, 

90 StringParenStripper, 

91 StringParenWrapper, 

92 StringSplitter, 

93 Transformer, 

94 hug_power_op, 

95) 

96from blib2to3.pgen2 import token 

97from blib2to3.pytree import Leaf, Node 

98 

99# types 

100LeafID = int 

101LN = Union[Leaf, Node] 

102 

103 

104class CannotSplit(CannotTransform): 

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

106 

107 

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

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

110class LineGenerator(Visitor[Line]): 

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

112 

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

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

115 """ 

116 

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

118 self.mode = mode 

119 self.features = features 

120 self.current_line: Line 

121 self.__post_init__() 

122 

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

124 """Generate a line. 

125 

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

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

128 

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

130 """ 

131 if not self.current_line: 

132 self.current_line.depth += indent 

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

134 

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

136 self.current_line.leaves[0] 

137 ): 

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

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

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

141 # `ASYNC` leaf as a complete line. 

142 return 

143 

144 complete_line = self.current_line 

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

146 yield complete_line 

147 

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

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

150 if isinstance(node, Leaf): 

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

152 for comment in generate_comments(node, mode=self.mode): 

153 if any_open_brackets: 

154 # any comment within brackets is subject to splitting 

155 self.current_line.append(comment) 

156 elif comment.type == token.COMMENT: 

157 # regular trailing comment 

158 self.current_line.append(comment) 

159 yield from self.line() 

160 

161 else: 

162 # regular standalone comment 

163 yield from self.line() 

164 

165 self.current_line.append(comment) 

166 yield from self.line() 

167 

168 if any_open_brackets: 

169 node.prefix = "" 

170 if node.type not in WHITESPACE: 

171 self.current_line.append(node) 

172 yield from super().visit_default(node) 

173 

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

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

176 

177 already_parenthesized = ( 

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

179 ) 

180 

181 if not already_parenthesized: 

182 # Similar to logic in wrap_in_parentheses 

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

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

185 prefix = node.prefix 

186 node.prefix = "" 

187 lpar.prefix = prefix 

188 node.insert_child(0, lpar) 

189 node.append_child(rpar) 

190 

191 yield from self.visit_default(node) 

192 

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

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

195 # In blib2to3 INDENT never holds comments. 

196 yield from self.line(+1) 

197 yield from self.visit_default(node) 

198 

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

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

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

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

203 # Emit the line then. 

204 yield from self.line() 

205 

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

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

208 yield from self.visit_default(node) 

209 

210 # Finally, emit the dedent. 

211 yield from self.line(-1) 

212 

213 def visit_stmt( 

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

215 ) -> Iterator[Line]: 

216 """Visit a statement. 

217 

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

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

220 

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

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

223 separate line. 

224 

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

226 invisible parens should be put. 

227 """ 

228 normalize_invisible_parens( 

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

230 ) 

231 for child in node.children: 

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

233 yield from self.line() 

234 

235 yield from self.visit(child) 

236 

237 def visit_typeparams(self, node: Node) -> Iterator[Line]: 

238 yield from self.visit_default(node) 

239 node.children[0].prefix = "" 

240 

241 def visit_typevartuple(self, node: Node) -> Iterator[Line]: 

242 yield from self.visit_default(node) 

243 node.children[1].prefix = "" 

244 

245 def visit_paramspec(self, node: Node) -> Iterator[Line]: 

246 yield from self.visit_default(node) 

247 node.children[1].prefix = "" 

248 

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

250 if Preview.wrap_long_dict_values_in_parens in self.mode: 

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

252 if i == 0: 

253 continue 

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

255 if ( 

256 child.type == syms.atom 

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

258 and not is_walrus_assignment(child) 

259 ): 

260 maybe_make_parens_invisible_in_atom( 

261 child, 

262 parent=node, 

263 mode=self.mode, 

264 features=self.features, 

265 remove_brackets_around_comma=False, 

266 ) 

267 else: 

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

269 yield from self.visit_default(node) 

270 

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

272 """Visit function definition.""" 

273 yield from self.line() 

274 

275 # Remove redundant brackets around return type annotation. 

276 is_return_annotation = False 

277 for child in node.children: 

278 if child.type == token.RARROW: 

279 is_return_annotation = True 

280 elif is_return_annotation: 

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

282 if maybe_make_parens_invisible_in_atom( 

283 child, 

284 parent=node, 

285 mode=self.mode, 

286 features=self.features, 

287 remove_brackets_around_comma=False, 

288 ): 

289 wrap_in_parentheses(node, child, visible=False) 

290 else: 

291 wrap_in_parentheses(node, child, visible=False) 

292 is_return_annotation = False 

293 

294 for child in node.children: 

295 yield from self.visit(child) 

296 

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

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

299 normalize_invisible_parens( 

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

301 ) 

302 

303 yield from self.line() 

304 for child in node.children: 

305 yield from self.visit(child) 

306 

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

308 """Visit a suite.""" 

309 if is_stub_suite(node): 

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

311 else: 

312 yield from self.visit_default(node) 

313 

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

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

316 prev_type: int | None = None 

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

318 if (prev_type is None or prev_type == token.SEMI) and is_arith_like(child): 

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

320 prev_type = child.type 

321 

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

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

324 yield from self.visit_default(node) 

325 else: 

326 yield from self.line(+1) 

327 yield from self.visit_default(node) 

328 yield from self.line(-1) 

329 

330 else: 

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

332 node.prefix = "" 

333 yield from self.visit_default(node) 

334 return 

335 yield from self.line() 

336 yield from self.visit_default(node) 

337 

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

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

340 yield from self.line() 

341 

342 children = iter(node.children) 

343 for child in children: 

344 yield from self.visit(child) 

345 

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

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

348 # line. 

349 break 

350 

351 internal_stmt = next(children) 

352 yield from self.visit(internal_stmt) 

353 

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

355 """Visit decorators.""" 

356 for child in node.children: 

357 yield from self.line() 

358 yield from self.visit(child) 

359 

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

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

362 next_leaf = node.children[idx + 1] 

363 

364 if not isinstance(leaf, Leaf): 

365 continue 

366 

367 value = leaf.value.lower() 

368 if ( 

369 leaf.type == token.NUMBER 

370 and next_leaf.type == syms.trailer 

371 # Ensure that we are in an attribute trailer 

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

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

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

375 # It shouldn't wrap complex literals 

376 and "j" not in value 

377 ): 

378 wrap_in_parentheses(node, leaf) 

379 

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

381 

382 yield from self.visit_default(node) 

383 

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

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

386 yield from self.line() 

387 

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

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

390 yield from self.visit_default(leaf) 

391 yield from self.line() 

392 

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

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

395 if not any_open_brackets: 

396 yield from self.line() 

397 # STANDALONE_COMMENT nodes created by our special handling in 

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

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

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

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

402 # lose indentation. Normal STANDALONE_COMMENT nodes go through 

403 # visit_default. 

404 value = leaf.value 

405 lines = value.splitlines() 

406 is_fmt_off_block = ( 

407 len(lines) >= 2 

408 and contains_fmt_directive(lines[0], FMT_OFF) 

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

410 ) 

411 if is_fmt_off_block: 

412 is_after_invisible_lpar = ( 

413 leaf.fmt_pass_converted_first_leaf is None 

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

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

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

417 ) 

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

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

420 # the fmt block itself directly to preserve its formatting 

421 

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

423 if leaf.prefix and any( 

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

425 and not contains_fmt_directive(line.strip()) 

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

427 ): 

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

429 yield from self.line() 

430 self.current_line.append(comment) 

431 yield from self.line() 

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

433 leaf.prefix = "" 

434 

435 self.current_line.append(leaf) 

436 if not any_open_brackets or is_after_invisible_lpar: 

437 yield from self.line() 

438 else: 

439 # Normal standalone comment - process through visit_default 

440 yield from self.visit_default(leaf) 

441 

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

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

444 

445 -2 ** 8 -> -(2 ** 8) 

446 """ 

447 _operator, operand = node.children 

448 if ( 

449 operand.type == syms.power 

450 and len(operand.children) == 3 

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

452 ): 

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

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

455 index = operand.remove() or 0 

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

457 yield from self.visit_default(node) 

458 

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

460 """ 

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

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

463 Examples: 

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

465 

466 -> 

467 

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

469 """ 

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

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

472 ): 

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

474 

475 yield from self.visit_default(node) 

476 

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

478 normalize_unicode_escape_sequences(leaf) 

479 

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

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

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

483 if self.mode.string_normalization: 

484 docstring = normalize_string_prefix(leaf.value) 

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

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

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

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

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

490 docstring = normalize_string_quotes(docstring) 

491 else: 

492 docstring = leaf.value 

493 prefix = get_string_prefix(docstring) 

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

495 quote_char = docstring[0] 

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

497 # docstring = docstring.strip(quote_char) 

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

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

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

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

502 docstring = docstring[quote_len:-quote_len] 

503 docstring_started_empty = not docstring 

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

505 

506 if is_multiline_string(leaf): 

507 docstring = fix_multiline_docstring(docstring, indent) 

508 else: 

509 docstring = docstring.strip() 

510 

511 has_trailing_backslash = False 

512 if docstring: 

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

514 if docstring[0] == quote_char: 

515 docstring = " " + docstring 

516 if docstring[-1] == quote_char: 

517 docstring += " " 

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

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

520 if backslash_count % 2: 

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

522 # avoid escaping the closing string quote. 

523 docstring += " " 

524 has_trailing_backslash = True 

525 elif not docstring_started_empty: 

526 docstring = " " 

527 

528 # We could enforce triple quotes at this point. 

529 quote = quote_char * quote_len 

530 

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

532 if quote_len == 3: 

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

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

535 # exceeding the maximum line length. 

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

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

538 lines = docstring.splitlines() 

539 last_line_length = len(lines[-1]) if docstring else 0 

540 

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

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

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

544 # the closing quotes 

545 if ( 

546 len(lines) > 1 

547 and last_line_length + quote_len > self.mode.line_length 

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

549 and not has_trailing_backslash 

550 ): 

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

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

553 else: 

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

555 else: 

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

557 else: 

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

559 

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

561 leaf.value = normalize_string_prefix(leaf.value) 

562 leaf.value = normalize_string_quotes(leaf.value) 

563 yield from self.visit_default(leaf) 

564 

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

566 normalize_numeric_literal(leaf) 

567 yield from self.visit_default(leaf) 

568 

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

570 """Visit any atom""" 

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

572 first = node.children[0] 

573 last = node.children[-1] 

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

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

576 ): 

577 # Lists or sets of one item 

578 maybe_make_parens_invisible_in_atom( 

579 node.children[1], 

580 parent=node, 

581 mode=self.mode, 

582 features=self.features, 

583 ) 

584 

585 yield from self.visit_default(node) 

586 

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

588 # If the fstring was converted to a STANDALONE_COMMENT by 

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

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

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

592 yield from self.visit_default(node) 

593 return 

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

595 string_leaf = fstring_tstring_to_string(node) 

596 node.replace(string_leaf) 

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

598 "\\" in str(child) 

599 for child in node.children 

600 if child.type == syms.fstring_replacement_field 

601 ): 

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

603 # causing breakages. skip normalization when nested quotes exist 

604 yield from self.visit_default(string_leaf) 

605 return 

606 yield from self.visit_STRING(string_leaf) 

607 

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

609 # If the tstring was converted to a STANDALONE_COMMENT by 

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

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

612 yield from self.visit_default(node) 

613 return 

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

615 string_leaf = fstring_tstring_to_string(node) 

616 node.replace(string_leaf) 

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

618 "\\" in str(child) 

619 for child in node.children 

620 if child.type == syms.tstring_replacement_field 

621 ): 

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

623 # causing breakages. skip normalization when nested quotes exist 

624 yield from self.visit_default(string_leaf) 

625 return 

626 yield from self.visit_STRING(string_leaf) 

627 

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

629 # fstring_start = node.children[0] 

630 # fstring_end = node.children[-1] 

631 # assert isinstance(fstring_start, Leaf) 

632 # assert isinstance(fstring_end, Leaf) 

633 

634 # quote_char = fstring_end.value[0] 

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

636 # prefix, quote = ( 

637 # fstring_start.value[:quote_idx], 

638 # fstring_start.value[quote_idx:] 

639 # ) 

640 

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

642 # prefix = normalize_string_prefix(prefix) 

643 

644 # assert quote == fstring_end.value 

645 

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

647 # middles = [ 

648 # leaf 

649 # for leaf in node.leaves() 

650 # if leaf.type == token.FSTRING_MIDDLE 

651 # ] 

652 

653 # if self.mode.string_normalization: 

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

655 

656 # fstring_start.value = prefix + quote 

657 # fstring_end.value = quote 

658 

659 # yield from self.visit_default(node) 

660 

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

662 if Preview.wrap_comprehension_in in self.mode: 

663 normalize_invisible_parens( 

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

665 ) 

666 yield from self.visit_default(node) 

667 

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

669 yield from self.visit_comp_for(node) 

670 

671 def __post_init__(self) -> None: 

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

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

674 

675 v = self.visit_stmt 

676 Ø: set[str] = set() 

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

678 self.visit_if_stmt = partial( 

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

680 ) 

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

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

683 self.visit_try_stmt = partial( 

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

685 ) 

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

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

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

689 

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

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

692 self.visit_yield_expr = partial( 

693 v, 

694 keywords=Ø, 

695 parens=( 

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

697 ), 

698 ) 

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

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

701 self.visit_async_funcdef = self.visit_async_stmt 

702 self.visit_decorated = self.visit_decorators 

703 

704 # PEP 634 

705 self.visit_match_stmt = self.visit_match_case 

706 self.visit_case_block = self.visit_match_case 

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

708 

709 

710# Remove when `simplify_power_operator_hugging` becomes stable. 

711def _hugging_power_ops_line_to_string( 

712 line: Line, 

713 features: Collection[Feature], 

714 mode: Mode, 

715) -> str | None: 

716 try: 

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

718 except CannotTransform: 

719 return None 

720 

721 

722def transform_line( 

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

724) -> Iterator[Line]: 

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

726 

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

728 

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

730 """ 

731 if line.is_comment: 

732 yield line 

733 return 

734 

735 line_str = line_to_string(line) 

736 

737 if Preview.simplify_power_operator_hugging in mode: 

738 line_str_hugging_power_ops = line_str 

739 else: 

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

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

742 # on the line. 

743 line_str_hugging_power_ops = ( 

744 _hugging_power_ops_line_to_string(line, features, mode) or line_str 

745 ) 

746 

747 ll = mode.line_length 

748 sn = mode.string_normalization 

749 string_merge = StringMerger(ll, sn) 

750 string_paren_strip = StringParenStripper(ll, sn) 

751 string_split = StringSplitter(ll, sn) 

752 string_paren_wrap = StringParenWrapper(ll, sn) 

753 

754 transformers: list[Transformer] 

755 if ( 

756 not line.contains_uncollapsable_type_comments() 

757 and not line.should_split_rhs 

758 and not line.magic_trailing_comma 

759 and ( 

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

761 or line.contains_unsplittable_type_ignore() 

762 ) 

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

764 and not line.contains_implicit_multiline_string_with_comments() 

765 ): 

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

767 if Preview.string_processing in mode: 

768 transformers = [string_merge, string_paren_strip] 

769 else: 

770 transformers = [] 

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

772 transformers = [left_hand_split] 

773 else: 

774 

775 def _rhs( 

776 self: object, line: Line, features: Collection[Feature], mode: Mode 

777 ) -> Iterator[Line]: 

778 """Wraps calls to `right_hand_split`. 

779 

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

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

782 bracket pair instead. 

783 """ 

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

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

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

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

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

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

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

791 omit and _over_length_only_due_to_subscript_comment(lines[0], mode) 

792 ): 

793 yield from lines 

794 return 

795 

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

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

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

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

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

801 

802 # HACK: nested functions (like _rhs) compiled by mypyc don't retain their 

803 # __name__ attribute which is needed in `run_transformer` further down. 

804 # Unfortunately a nested class breaks mypyc too. So a class must be created 

805 # via type ... https://github.com/mypyc/mypyc/issues/884 

806 rhs = type("rhs", (), {"__call__": _rhs})() 

807 

808 if Preview.string_processing in mode: 

809 if line.inside_brackets: 

810 transformers = [ 

811 string_merge, 

812 string_paren_strip, 

813 string_split, 

814 delimiter_split, 

815 standalone_comment_split, 

816 string_paren_wrap, 

817 rhs, 

818 ] 

819 else: 

820 transformers = [ 

821 string_merge, 

822 string_paren_strip, 

823 string_split, 

824 string_paren_wrap, 

825 rhs, 

826 ] 

827 else: 

828 if line.inside_brackets: 

829 transformers = [delimiter_split, standalone_comment_split, rhs] 

830 else: 

831 transformers = [rhs] 

832 

833 if Preview.simplify_power_operator_hugging not in mode: 

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

835 # line could match. 

836 transformers.append(hug_power_op) 

837 

838 for transform in transformers: 

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

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

841 # split altogether. 

842 try: 

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

844 except CannotTransform: 

845 continue 

846 else: 

847 yield from result 

848 break 

849 

850 else: 

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

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

853 # output is at least valid Python. 

854 if line.contains_standalone_comments(): 

855 yield from _force_standalone_comment_split(line) 

856 else: 

857 yield line 

858 

859 

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

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

862 split the line with rhs to respect the comma. 

863 """ 

864 return_type_leaves: list[Leaf] = [] 

865 in_return_type = False 

866 

867 for leaf in line.leaves: 

868 if leaf.type == token.COLON: 

869 in_return_type = False 

870 if in_return_type: 

871 return_type_leaves.append(leaf) 

872 if leaf.type == token.RARROW: 

873 in_return_type = True 

874 

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

876 # couple lines from it. 

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

878 leaves_to_track = get_leaves_inside_matching_brackets(return_type_leaves) 

879 for leaf in return_type_leaves: 

880 result.append( 

881 leaf, 

882 preformatted=True, 

883 track_bracket=id(leaf) in leaves_to_track, 

884 ) 

885 

886 # we could also return true if the line is too long, and the return type is longer 

887 # than the param list. Or if `should_split_rhs` returns True. 

888 return result.magic_trailing_comma is not None 

889 

890 

891class _BracketSplitComponent(Enum): 

892 head = auto() 

893 body = auto() 

894 tail = auto() 

895 

896 

897def left_hand_split( 

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

899) -> Iterator[Line]: 

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

901 

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

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

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

905 """ 

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

907 tail_leaves: list[Leaf] = [] 

908 body_leaves: list[Leaf] = [] 

909 head_leaves: list[Leaf] = [] 

910 current_leaves = head_leaves 

911 matching_bracket: Leaf | None = None 

912 depth = 0 

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

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

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

916 # tracking the depth 

917 depth += 1 

918 elif depth > 0: 

919 if leaf.type == token.LSQB: 

920 depth += 1 

921 elif leaf.type == token.RSQB: 

922 depth -= 1 

923 if ( 

924 current_leaves is body_leaves 

925 and leaf.type in CLOSING_BRACKETS 

926 and leaf.opening_bracket is matching_bracket 

927 and isinstance(matching_bracket, Leaf) 

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

929 # param, ignore the match since this is searching 

930 # for the function arguments 

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

932 ): 

933 ensure_visible(leaf) 

934 ensure_visible(matching_bracket) 

935 current_leaves = tail_leaves if body_leaves else head_leaves 

936 current_leaves.append(leaf) 

937 if current_leaves is head_leaves: 

938 if leaf.type == leaf_type and ( 

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

940 ): 

941 matching_bracket = leaf 

942 current_leaves = body_leaves 

943 if matching_bracket and tail_leaves: 

944 break 

945 if not matching_bracket or not tail_leaves: 

946 raise CannotSplit("No brackets found") 

947 

948 head = bracket_split_build_line( 

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

950 ) 

951 body = bracket_split_build_line( 

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

953 ) 

954 tail = bracket_split_build_line( 

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

956 ) 

957 bracket_split_succeeded_or_raise(head, body, tail) 

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

959 if result: 

960 yield result 

961 

962 

963def right_hand_split( 

964 line: Line, 

965 mode: Mode, 

966 features: Collection[Feature] = (), 

967 omit: Collection[LeafID] = (), 

968) -> Iterator[Line]: 

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

970 

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

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

973 this split. 

974 

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

976 """ 

977 rhs_result = _first_right_hand_split(line, omit=omit) 

978 yield from _maybe_split_omitting_optional_parens( 

979 rhs_result, line, mode, features=features, omit=omit 

980 ) 

981 

982 

983def _first_right_hand_split( 

984 line: Line, 

985 omit: Collection[LeafID] = (), 

986) -> RHSResult: 

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

988 

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

990 _maybe_split_omitting_optional_parens to get an opinion whether to prefer 

991 splitting on the right side of an assignment statement. 

992 """ 

993 tail_leaves: list[Leaf] = [] 

994 body_leaves: list[Leaf] = [] 

995 head_leaves: list[Leaf] = [] 

996 current_leaves = tail_leaves 

997 opening_bracket: Leaf | None = None 

998 closing_bracket: Leaf | None = None 

999 for leaf in reversed(line.leaves): 

1000 if current_leaves is body_leaves: 

1001 if leaf is opening_bracket: 

1002 current_leaves = head_leaves if body_leaves else tail_leaves 

1003 current_leaves.append(leaf) 

1004 if current_leaves is tail_leaves: 

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

1006 opening_bracket = leaf.opening_bracket 

1007 closing_bracket = leaf 

1008 current_leaves = body_leaves 

1009 if not (opening_bracket and closing_bracket and head_leaves): 

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

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

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

1013 raise CannotSplit("No brackets found") 

1014 

1015 tail_leaves.reverse() 

1016 body_leaves.reverse() 

1017 head_leaves.reverse() 

1018 

1019 body: Line | None = None 

1020 if ( 

1021 Preview.hug_parens_with_braces_and_square_brackets in line.mode 

1022 and tail_leaves[0].value 

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

1024 ): 

1025 inner_body_leaves = list(body_leaves) 

1026 hugged_opening_leaves: list[Leaf] = [] 

1027 hugged_closing_leaves: list[Leaf] = [] 

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

1029 unpacking_offset: int = 1 if is_unpacking else 0 

1030 while ( 

1031 len(inner_body_leaves) >= 2 + unpacking_offset 

1032 and inner_body_leaves[-1].type in CLOSING_BRACKETS 

1033 and inner_body_leaves[-1].opening_bracket 

1034 is inner_body_leaves[unpacking_offset] 

1035 ): 

1036 if unpacking_offset: 

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

1038 unpacking_offset = 0 

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

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

1041 

1042 if hugged_opening_leaves and inner_body_leaves: 

1043 inner_body = bracket_split_build_line( 

1044 inner_body_leaves, 

1045 line, 

1046 hugged_opening_leaves[-1], 

1047 component=_BracketSplitComponent.body, 

1048 ) 

1049 if ( 

1050 line.mode.magic_trailing_comma 

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

1052 ): 

1053 should_hug = True 

1054 else: 

1055 line_length = line.mode.line_length - sum( 

1056 len(str(leaf)) 

1057 for leaf in hugged_opening_leaves + hugged_closing_leaves 

1058 ) 

1059 if is_line_short_enough( 

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

1061 ): 

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

1063 should_hug = False 

1064 else: 

1065 should_hug = True 

1066 if should_hug and ( 

1067 _hugging_merges_type_ignores(line, head_leaves, hugged_opening_leaves) 

1068 or _hugging_merges_type_ignores( 

1069 line, hugged_closing_leaves, tail_leaves 

1070 ) 

1071 ): 

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

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

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

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

1076 should_hug = False 

1077 if should_hug: 

1078 body_leaves = inner_body_leaves 

1079 head_leaves.extend(hugged_opening_leaves) 

1080 tail_leaves = hugged_closing_leaves + tail_leaves 

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

1082 

1083 head = bracket_split_build_line( 

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

1085 ) 

1086 if body is None: 

1087 body = bracket_split_build_line( 

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

1089 ) 

1090 tail = bracket_split_build_line( 

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

1092 ) 

1093 bracket_split_succeeded_or_raise(head, body, tail) 

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

1095 

1096 

1097def _maybe_split_omitting_optional_parens( 

1098 rhs: RHSResult, 

1099 line: Line, 

1100 mode: Mode, 

1101 features: Collection[Feature] = (), 

1102 omit: Collection[LeafID] = (), 

1103) -> Iterator[Line]: 

1104 if ( 

1105 Feature.FORCE_OPTIONAL_PARENTHESES not in features 

1106 # the opening bracket is an optional paren 

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

1108 and not rhs.opening_bracket.value 

1109 # the closing bracket is an optional paren 

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

1111 and not rhs.closing_bracket.value 

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

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

1114 and not line.is_import 

1115 # and we can actually remove the parens 

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

1117 ): 

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

1119 try: 

1120 # The RHSResult Omitting Optional Parens. 

1121 rhs_oop = _first_right_hand_split(line, omit=omit) 

1122 if _prefer_split_rhs_oop_over_rhs(rhs_oop, rhs, mode): 

1123 yield from _maybe_split_omitting_optional_parens( 

1124 rhs_oop, line, mode, features=features, omit=omit 

1125 ) 

1126 return 

1127 

1128 except CannotSplit as e: 

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

1130 if line.is_chained_assignment: 

1131 pass 

1132 

1133 elif ( 

1134 not can_be_split(rhs.body) 

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

1136 and not ( 

1137 Preview.wrap_long_dict_values_in_parens 

1138 and rhs.opening_bracket.parent 

1139 and rhs.opening_bracket.parent.parent 

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

1141 ) 

1142 and not ( 

1143 rhs.opening_bracket.parent 

1144 and rhs.opening_bracket.parent.parent 

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

1146 ) 

1147 ): 

1148 raise CannotSplit( 

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

1150 ) from e 

1151 

1152 elif ( 

1153 rhs.head.contains_multiline_strings() 

1154 or rhs.tail.contains_multiline_strings() 

1155 ): 

1156 raise CannotSplit( 

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

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

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

1160 " line." 

1161 ) from e 

1162 

1163 ensure_visible(rhs.opening_bracket) 

1164 ensure_visible(rhs.closing_bracket) 

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

1166 if result: 

1167 yield result 

1168 

1169 

1170def _prefer_split_rhs_oop_over_rhs( 

1171 rhs_oop: RHSResult, rhs: RHSResult, mode: Mode 

1172) -> bool: 

1173 """ 

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

1175 (rhs_oop) over the original (rhs). 

1176 """ 

1177 # contains unsplittable type ignore 

1178 if ( 

1179 rhs_oop.head.contains_unsplittable_type_ignore() 

1180 or rhs_oop.body.contains_unsplittable_type_ignore() 

1181 or rhs_oop.tail.contains_unsplittable_type_ignore() 

1182 ): 

1183 return True 

1184 

1185 # Retain optional parens around dictionary values 

1186 if ( 

1187 Preview.wrap_long_dict_values_in_parens 

1188 and rhs.opening_bracket.parent 

1189 and rhs.opening_bracket.parent.parent 

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

1191 and rhs.body.bracket_tracker.delimiters 

1192 ): 

1193 # Unless the split is inside the key 

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

1195 

1196 # the split is right after `=` 

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

1198 return True 

1199 

1200 # the left side of assignment contains brackets 

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

1202 return True 

1203 

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

1205 # paren) 

1206 if not is_line_short_enough( 

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

1208 ): 

1209 return True 

1210 

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

1212 if rhs.head.magic_trailing_comma is not None: 

1213 return True 

1214 

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

1216 # the body 

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

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

1219 token.EQUAL 

1220 ) 

1221 if rhs_head_equal_count > 1 and rhs_head_equal_count > rhs_oop_head_equal_count: 

1222 return False 

1223 

1224 has_closing_bracket_after_assign = False 

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

1226 if leaf.type == token.EQUAL: 

1227 break 

1228 if leaf.type in CLOSING_BRACKETS: 

1229 has_closing_bracket_after_assign = True 

1230 break 

1231 return ( 

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

1233 # closing bracket) 

1234 has_closing_bracket_after_assign 

1235 or ( 

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

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

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

1239 # the first line is short enough 

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

1241 ) 

1242 ) 

1243 

1244 

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

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

1247 

1248 Do nothing otherwise. 

1249 

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

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

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

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

1254 

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

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

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

1258 """ 

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

1260 if not body: 

1261 if tail_len == 0: 

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

1263 

1264 elif tail_len < 3: 

1265 raise CannotSplit( 

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

1267 " not worth it" 

1268 ) 

1269 

1270 

1271def _ensure_trailing_comma( 

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

1273) -> bool: 

1274 if not leaves: 

1275 return False 

1276 # Ensure a trailing comma for imports 

1277 if original.is_import: 

1278 return True 

1279 # ...and standalone function arguments 

1280 if not original.is_def: 

1281 return False 

1282 if opening_bracket.value != "(": 

1283 return False 

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

1285 if any( 

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

1287 ): 

1288 return False 

1289 

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

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

1292 if leaf_with_parent is None: 

1293 return True 

1294 # Don't add commas inside parenthesized return annotations 

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

1296 return False 

1297 # Don't add commas inside PEP 604 unions 

1298 if ( 

1299 leaf_with_parent.parent 

1300 and leaf_with_parent.parent.next_sibling 

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

1302 ): 

1303 return False 

1304 return True 

1305 

1306 

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

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

1309 seen = 0 

1310 for leaves in leaf_groups: 

1311 for leaf in leaves: 

1312 for comment in line.comments_after(leaf): 

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

1314 seen += 1 

1315 if seen > 1: 

1316 return True 

1317 return False 

1318 

1319 

1320def bracket_split_build_line( 

1321 leaves: list[Leaf], 

1322 original: Line, 

1323 opening_bracket: Leaf, 

1324 *, 

1325 component: _BracketSplitComponent, 

1326) -> Line: 

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

1328 

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

1330 respected. 

1331 

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

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

1334 expected. 

1335 """ 

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

1337 if component is _BracketSplitComponent.body: 

1338 result.inside_brackets = True 

1339 result.depth += 1 

1340 if _ensure_trailing_comma(leaves, original, opening_bracket): 

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

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

1343 continue 

1344 

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

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

1347 leaves.insert(i + 1, new_comma) 

1348 break 

1349 

1350 leaves_to_track: set[LeafID] = set() 

1351 if component is _BracketSplitComponent.head: 

1352 leaves_to_track = get_leaves_inside_matching_brackets(leaves) 

1353 # Populate the line 

1354 for leaf in leaves: 

1355 result.append( 

1356 leaf, 

1357 preformatted=True, 

1358 track_bracket=id(leaf) in leaves_to_track, 

1359 ) 

1360 for comment_after in original.comments_after(leaf): 

1361 result.append(comment_after, preformatted=True) 

1362 if component is _BracketSplitComponent.body and should_split_line( 

1363 result, opening_bracket 

1364 ): 

1365 result.should_split_rhs = True 

1366 return result 

1367 

1368 

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

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

1371 

1372 This is a decorator over relevant split functions. 

1373 """ 

1374 

1375 @wraps(split_func) 

1376 def split_wrapper( 

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

1378 ) -> Iterator[Line]: 

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

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

1381 yield split_line 

1382 

1383 return split_wrapper 

1384 

1385 

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

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

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

1389 return leaf_idx 

1390 return None 

1391 

1392 

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

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

1395 return Feature.TRAILING_COMMA_IN_DEF in features 

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

1397 return Feature.TRAILING_COMMA_IN_CALL in features 

1398 return True 

1399 

1400 

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

1402 if ( 

1403 safe 

1404 and delimiter_priority == COMMA_PRIORITY 

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

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

1407 ): 

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

1409 line.append(new_comma) 

1410 return line 

1411 

1412 

1413MIGRATE_COMMENT_DELIMITERS = {STRING_PRIORITY, COMMA_PRIORITY} 

1414 

1415 

1416def _can_defer_lone_comparator_to_rhs(line: Line, mode: Mode) -> bool: 

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

1418 

1419 Caller has already established exactly one delimiter at 

1420 `COMPARATOR_PRIORITY`. We defer only when: 

1421 

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

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

1424 split anyway, and 

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

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

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

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

1429 """ 

1430 past_comparator = False 

1431 for leaf in line.leaves: 

1432 if leaf.type in OPENING_BRACKETS and not past_comparator: 

1433 return False 

1434 if not past_comparator and ( 

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

1436 ): 

1437 past_comparator = True 

1438 try: 

1439 rhs = _first_right_hand_split(line) 

1440 except CannotSplit: 

1441 return False 

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

1443 

1444 

1445@dont_increase_indentation 

1446def delimiter_split( 

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

1448) -> Iterator[Line]: 

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

1450 

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

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

1453 """ 

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

1455 raise CannotSplit("Line empty") from None 

1456 last_leaf = line.leaves[-1] 

1457 

1458 bt = line.bracket_tracker 

1459 try: 

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

1461 except ValueError: 

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

1463 

1464 if ( 

1465 delimiter_priority == DOT_PRIORITY 

1466 and bt.delimiter_count_with_priority(delimiter_priority) == 1 

1467 ): 

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

1469 

1470 if ( 

1471 Preview.hug_comparator in mode 

1472 and delimiter_priority == COMPARATOR_PRIORITY 

1473 and bt.delimiter_count_with_priority(delimiter_priority) == 1 

1474 and _can_defer_lone_comparator_to_rhs(line, mode) 

1475 ): 

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

1477 

1478 current_line = Line( 

1479 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1480 ) 

1481 lowest_depth = sys.maxsize 

1482 trailing_comma_safe = True 

1483 

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

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

1486 nonlocal current_line 

1487 try: 

1488 current_line.append_safe(leaf, preformatted=True) 

1489 except ValueError: 

1490 yield current_line 

1491 

1492 current_line = Line( 

1493 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1494 ) 

1495 current_line.append(leaf) 

1496 

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

1498 for comment_after in line.comments_after(leaf): 

1499 yield from append_to_line(comment_after) 

1500 

1501 last_non_comment_leaf = _get_last_non_comment_leaf(line) 

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

1503 yield from append_to_line(leaf) 

1504 

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

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

1507 ) 

1508 if ( 

1509 previous_priority != delimiter_priority 

1510 or delimiter_priority in MIGRATE_COMMENT_DELIMITERS 

1511 ): 

1512 yield from append_comments(leaf) 

1513 

1514 lowest_depth = min(lowest_depth, leaf.bracket_depth) 

1515 if trailing_comma_safe and leaf.bracket_depth == lowest_depth: 

1516 trailing_comma_safe = _can_add_trailing_comma(leaf, features) 

1517 

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

1519 current_line = _safe_add_trailing_comma( 

1520 trailing_comma_safe, delimiter_priority, current_line 

1521 ) 

1522 

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

1524 if leaf_priority == delimiter_priority: 

1525 if ( 

1526 leaf_idx + 1 < len(line.leaves) 

1527 and delimiter_priority not in MIGRATE_COMMENT_DELIMITERS 

1528 ): 

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

1530 

1531 yield current_line 

1532 current_line = Line( 

1533 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1534 ) 

1535 

1536 if current_line: 

1537 current_line = _safe_add_trailing_comma( 

1538 trailing_comma_safe, delimiter_priority, current_line 

1539 ) 

1540 yield current_line 

1541 

1542 

1543@dont_increase_indentation 

1544def standalone_comment_split( 

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

1546) -> Iterator[Line]: 

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

1548 if not line.contains_standalone_comments(): 

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

1550 

1551 current_line = Line( 

1552 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1553 ) 

1554 

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

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

1557 nonlocal current_line 

1558 try: 

1559 current_line.append_safe(leaf, preformatted=True) 

1560 except ValueError: 

1561 yield current_line 

1562 

1563 current_line = Line( 

1564 line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1565 ) 

1566 current_line.append(leaf) 

1567 

1568 for leaf in line.leaves: 

1569 yield from append_to_line(leaf) 

1570 

1571 for comment_after in line.comments_after(leaf): 

1572 yield from append_to_line(comment_after) 

1573 

1574 if current_line: 

1575 yield current_line 

1576 

1577 

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

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

1580 current_line = Line( 

1581 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1582 ) 

1583 for leaf in line.leaves: 

1584 if current_line.leaves and ( 

1585 leaf.type == STANDALONE_COMMENT or current_line.is_comment 

1586 ): 

1587 yield current_line 

1588 current_line = Line( 

1589 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets 

1590 ) 

1591 current_line.append(leaf, preformatted=True) 

1592 if current_line: 

1593 yield current_line 

1594 

1595 

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

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

1598 looking through any redundant nested parentheses. 

1599 

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

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

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

1603 """ 

1604 while ( 

1605 node.type == syms.atom 

1606 and len(node.children) == 3 

1607 and is_lpar_token(node.children[0]) 

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

1609 ): 

1610 middle = node.children[1] 

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

1612 return True 

1613 node = middle 

1614 return False 

1615 

1616 

1617def normalize_invisible_parens( 

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

1619) -> None: 

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

1621 

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

1623 should be put. 

1624 

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

1626 existing visible parentheses for other tuples and generator expressions. 

1627 """ 

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

1629 if contains_fmt_directive(pc.value, FMT_OFF): 

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

1631 return 

1632 

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

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

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

1636 if node.type == syms.with_stmt: 

1637 _maybe_wrap_cms_in_parens(node, mode, features) 

1638 

1639 check_lpar = False 

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

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

1642 # assignment statements that contain type annotations. 

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

1644 normalize_invisible_parens( 

1645 child, parens_after=parens_after, mode=mode, features=features 

1646 ) 

1647 

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

1649 # case blocks. 

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

1651 normalize_invisible_parens( 

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

1653 ) 

1654 

1655 # Add parentheses around if guards in case blocks 

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

1657 normalize_invisible_parens( 

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

1659 ) 

1660 

1661 # Add parentheses around long tuple unpacking in assignments. 

1662 if ( 

1663 index == 0 

1664 and isinstance(child, Node) 

1665 and child.type == syms.testlist_star_expr 

1666 ): 

1667 check_lpar = True 

1668 

1669 if ( 

1670 index == 0 

1671 and isinstance(child, Node) 

1672 and child.type == syms.atom 

1673 and node.type == syms.expr_stmt 

1674 and not _atom_has_magic_trailing_comma(child, mode) 

1675 and not _is_atom_multiline(child) 

1676 ): 

1677 if maybe_make_parens_invisible_in_atom( 

1678 child, 

1679 parent=node, 

1680 mode=mode, 

1681 features=features, 

1682 remove_brackets_around_comma=True, 

1683 allow_star_expr=True, 

1684 ): 

1685 wrap_in_parentheses(node, child, visible=False) 

1686 

1687 if check_lpar: 

1688 if ( 

1689 child.type == syms.atom 

1690 and node.type == syms.for_stmt 

1691 and isinstance(child.prev_sibling, Leaf) 

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

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

1694 ): 

1695 if maybe_make_parens_invisible_in_atom( 

1696 child, 

1697 parent=node, 

1698 mode=mode, 

1699 features=features, 

1700 remove_brackets_around_comma=True, 

1701 ): 

1702 wrap_in_parentheses(node, child, visible=False) 

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

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

1705 elif ( 

1706 isinstance(child, Node) 

1707 and node.type == syms.yield_expr 

1708 and child.type == syms.yield_arg 

1709 and Preview.parenthesize_tuple_in_yield in mode 

1710 ): 

1711 if ( 

1712 len(child.children) == 1 

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

1714 and is_one_tuple(child.children[0]) 

1715 ): 

1716 wrap_in_parentheses(node, child, visible=True) 

1717 elif child.type == syms.atom: 

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

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

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

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

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

1723 # collapse them while keeping exactly one visible pair. 

1724 maybe_make_parens_invisible_in_atom( 

1725 child, parent=node, mode=mode, features=features 

1726 ) 

1727 opening = child.children[0] 

1728 closing = child.children[-1] 

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

1730 opening.value = "(" 

1731 closing.value = ")" 

1732 elif maybe_make_parens_invisible_in_atom( 

1733 child, parent=node, mode=mode, features=features 

1734 ): 

1735 wrap_in_parentheses(node, child, visible=False) 

1736 elif is_one_tuple(child): 

1737 wrap_in_parentheses(node, child, visible=True) 

1738 elif node.type == syms.import_from: 

1739 _normalize_import_from(node, child, index) 

1740 break 

1741 elif ( 

1742 index == 1 

1743 and child.type == token.STAR 

1744 and node.type == syms.except_clause 

1745 ): 

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

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

1748 # invisible parentheses to work more precisely. 

1749 continue 

1750 

1751 elif ( 

1752 isinstance(child, Leaf) 

1753 and child.next_sibling is not None 

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

1755 and child.value == "case" 

1756 ): 

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

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

1759 break 

1760 

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

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

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

1764 pass 

1765 

1766 elif not is_multiline_string(child): 

1767 if ( 

1768 Preview.fix_if_guard_explosion_in_case_statement in mode 

1769 and node.type == syms.guard 

1770 ): 

1771 mock_line = Line(mode=mode) 

1772 for leaf in child.leaves(): 

1773 mock_line.append(leaf) 

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

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

1776 wrap_in_parentheses(node, child, visible=False) 

1777 else: 

1778 wrap_in_parentheses(node, child, visible=False) 

1779 

1780 comma_check = child.type == token.COMMA 

1781 

1782 check_lpar = isinstance(child, Leaf) and ( 

1783 child.value in parens_after or comma_check 

1784 ) 

1785 

1786 

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

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

1789 # the statement 

1790 if is_lpar_token(child): 

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

1792 # make parentheses invisible 

1793 child.value = "" 

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

1795 elif child.type != token.STAR: 

1796 # insert invisible parentheses 

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

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

1799 

1800 

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

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

1803 if ( 

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

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

1806 ): 

1807 if maybe_make_parens_invisible_in_atom( 

1808 node.children[1], 

1809 parent=node, 

1810 mode=mode, 

1811 features=features, 

1812 remove_brackets_around_comma=True, 

1813 ): 

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

1815 

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

1817 # brackets in cases where this would change 

1818 # the AST due to operator precedence. 

1819 # Therefore we only aim to remove brackets around 

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

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

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

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

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

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

1826 if isinstance(bracket_contents, Node) and ( 

1827 bracket_contents.type != syms.power 

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

1829 or any( 

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

1831 for child in bracket_contents.children 

1832 ) 

1833 ): 

1834 ensure_visible(opening_bracket) 

1835 ensure_visible(closing_bracket) 

1836 

1837 

1838def _maybe_wrap_cms_in_parens( 

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

1840) -> None: 

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

1842 

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

1844 """ 

1845 if ( 

1846 Feature.PARENTHESIZED_CONTEXT_MANAGERS not in features 

1847 or len(node.children) <= 2 

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

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

1850 ): 

1851 return 

1852 colon_index: int | None = None 

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

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

1855 colon_index = i 

1856 break 

1857 if colon_index is not None: 

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

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

1860 context_managers = node.children[1:colon_index] 

1861 for child in context_managers: 

1862 child.remove() 

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

1864 # with_stmt 

1865 # NAME 'with' 

1866 # atom 

1867 # LPAR '' 

1868 # testlist_gexp 

1869 # ... <-- context_managers 

1870 # /testlist_gexp 

1871 # RPAR '' 

1872 # /atom 

1873 # COLON ':' 

1874 new_child = Node( 

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

1876 ) 

1877 node.insert_child(1, new_child) 

1878 

1879 

1880def remove_with_parens( 

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

1882) -> None: 

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

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

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

1886 # different parse trees: 

1887 # 

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

1889 # ... 

1890 # 

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

1892 # ... # asexpr_test 

1893 # 

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

1895 # ... # asexpr_test 

1896 # 

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

1898 # ... # testlist_gexp which then 

1899 # # contains multiple asexpr_test(s) 

1900 if node.type == syms.atom: 

1901 if maybe_make_parens_invisible_in_atom( 

1902 node, 

1903 parent=parent, 

1904 mode=mode, 

1905 features=features, 

1906 remove_brackets_around_comma=True, 

1907 ): 

1908 wrap_in_parentheses(parent, node, visible=False) 

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

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

1911 elif node.type == syms.testlist_gexp: 

1912 for child in node.children: 

1913 if isinstance(child, Node): 

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

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

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

1917 ): 

1918 if maybe_make_parens_invisible_in_atom( 

1919 node.children[0], 

1920 parent=node, 

1921 mode=mode, 

1922 features=features, 

1923 remove_brackets_around_comma=True, 

1924 ): 

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

1926 

1927 

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

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

1930 

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

1932 which should be preserved to maintain their tuple type. 

1933 """ 

1934 if not mode.magic_trailing_comma: 

1935 return False 

1936 

1937 return is_one_tuple(node) 

1938 

1939 

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

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

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

1943 return False 

1944 

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

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

1947 middle = node.children[1] 

1948 for child in middle.pre_order(): 

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

1950 return True 

1951 

1952 return False 

1953 

1954 

1955def maybe_make_parens_invisible_in_atom( 

1956 node: LN, 

1957 parent: LN, 

1958 mode: Mode, 

1959 features: Collection[Feature], 

1960 remove_brackets_around_comma: bool = False, 

1961 allow_star_expr: bool = False, 

1962) -> bool: 

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

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

1965 as they are redundant. 

1966 

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

1968 """ 

1969 if ( 

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

1971 or is_empty_tuple(node) 

1972 or is_one_tuple(node) 

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

1974 or ( 

1975 is_tuple(node) 

1976 and parent.type == syms.with_stmt 

1977 and has_sibling_with_type(node, token.COMMA) 

1978 ) 

1979 or (is_yield(node) and parent.type != syms.expr_stmt) 

1980 or ( 

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

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

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

1984 not remove_brackets_around_comma 

1985 and max_delimiter_priority_in_atom(node) >= COMMA_PRIORITY 

1986 # Remove parentheses around multiple exception types in except and 

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

1988 and not ( 

1989 Feature.UNPARENTHESIZED_EXCEPT_TYPES in features 

1990 # is a tuple 

1991 and is_tuple(node) 

1992 # has a parent node 

1993 and node.parent is not None 

1994 # parent is an except clause 

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

1996 # is not immediately followed by as clause 

1997 and not ( 

1998 node.next_sibling is not None 

1999 and is_name_token(node.next_sibling) 

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

2001 ) 

2002 ) 

2003 ) 

2004 or is_tuple_containing_walrus(node) 

2005 or (not allow_star_expr and is_tuple_containing_star(node)) 

2006 or is_generator(node) 

2007 ): 

2008 return False 

2009 

2010 if is_walrus_assignment(node): 

2011 if parent.type in [ 

2012 syms.annassign, 

2013 syms.expr_stmt, 

2014 syms.assert_stmt, 

2015 syms.return_stmt, 

2016 syms.yield_arg, 

2017 syms.yield_expr, 

2018 syms.except_clause, 

2019 syms.funcdef, 

2020 syms.with_stmt, 

2021 syms.testlist_gexp, 

2022 syms.tname, 

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

2024 syms.for_stmt, 

2025 syms.del_stmt, 

2026 ]: 

2027 return False 

2028 

2029 first = node.children[0] 

2030 last = node.children[-1] 

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

2032 middle = node.children[1] 

2033 # make parentheses invisible 

2034 if ( 

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

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

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

2038 ): 

2039 first.value = "" 

2040 last.value = "" 

2041 maybe_make_parens_invisible_in_atom( 

2042 middle, 

2043 parent=parent, 

2044 mode=mode, 

2045 features=features, 

2046 remove_brackets_around_comma=remove_brackets_around_comma, 

2047 ) 

2048 

2049 if is_atom_with_invisible_parens(middle): 

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

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

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

2053 

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

2055 # Preserve comments before first paren 

2056 middle.children[1].prefix = ( 

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

2058 ) 

2059 

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

2061 # Preserve comments before last paren 

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

2063 

2064 return False 

2065 

2066 return True 

2067 

2068 

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

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

2071 

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

2073 return False 

2074 

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

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

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

2078 exclude = set() 

2079 trailing_comma = False 

2080 try: 

2081 last_leaf = line.leaves[-1] 

2082 if last_leaf.type == token.COMMA: 

2083 trailing_comma = True 

2084 exclude.add(id(last_leaf)) 

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

2086 except (IndexError, ValueError): 

2087 return False 

2088 

2089 return max_priority == COMMA_PRIORITY and ( 

2090 (line.mode.magic_trailing_comma and trailing_comma) 

2091 # always explode imports 

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

2093 ) 

2094 

2095 

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

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

2098 

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

2100 a preceding closing bracket fits in one line. 

2101 

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

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

2104 the one that needs to explode are omitted. 

2105 """ 

2106 

2107 omit: set[LeafID] = set() 

2108 if not line.magic_trailing_comma: 

2109 yield omit 

2110 

2111 length = 4 * line.depth 

2112 opening_bracket: Leaf | None = None 

2113 closing_bracket: Leaf | None = None 

2114 inner_brackets: set[LeafID] = set() 

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

2116 length += leaf_length 

2117 if length > line_length: 

2118 break 

2119 

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

2121 if leaf.type == STANDALONE_COMMENT or has_inline_comment: 

2122 break 

2123 

2124 if opening_bracket: 

2125 if leaf is opening_bracket: 

2126 opening_bracket = None 

2127 elif leaf.type in CLOSING_BRACKETS: 

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

2129 if ( 

2130 prev 

2131 and prev.type == token.COMMA 

2132 and leaf.opening_bracket is not None 

2133 and not is_one_sequence_between( 

2134 leaf.opening_bracket, leaf, line.leaves 

2135 ) 

2136 ): 

2137 # Never omit bracket pairs with trailing commas. 

2138 # We need to explode on those. 

2139 break 

2140 

2141 inner_brackets.add(id(leaf)) 

2142 elif leaf.type in CLOSING_BRACKETS: 

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

2144 if prev and prev.type in OPENING_BRACKETS: 

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

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

2147 # pair of brackets was good enough. 

2148 inner_brackets.add(id(leaf)) 

2149 continue 

2150 

2151 if closing_bracket: 

2152 omit.add(id(closing_bracket)) 

2153 omit.update(inner_brackets) 

2154 inner_brackets.clear() 

2155 yield omit 

2156 

2157 if ( 

2158 prev 

2159 and prev.type == token.COMMA 

2160 and leaf.opening_bracket is not None 

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

2162 ): 

2163 # Never omit bracket pairs with trailing commas. 

2164 # We need to explode on those. 

2165 break 

2166 

2167 if leaf.value: 

2168 opening_bracket = leaf.opening_bracket 

2169 closing_bracket = leaf 

2170 

2171 

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

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

2174 comment attached to a subscript opening bracket (`[`). 

2175 

2176 This is the shape produced by the original of the issue #4733 reproducer: 

2177 a comment inside the annotation's subscript brackets renders at the end of 

2178 the head line after Black splits the statement, pushing it past the limit. 

2179 Taking the FORCE_OPTIONAL_PARENTHESES "second opinion" in that case wraps 

2180 the annotation in extra parens and migrates the comment outside the 

2181 subscript, which then oscillates on the next formatter pass. 

2182 """ 

2183 if not line.leaves: 

2184 return False 

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

2186 indent = " " * line.depth 

2187 leaves_iter = iter(line.leaves) 

2188 first = next(leaves_iter) 

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

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

2191 if str_width(text_without_comments) > mode.line_length: 

2192 return False 

2193 # And the comment must be attached to a subscript opening bracket. 

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

2195 if not comments: 

2196 continue 

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

2198 if leaf is None or leaf.type != token.LSQB: 

2199 return False 

2200 return True 

2201 

2202 

2203def run_transformer( 

2204 line: Line, 

2205 transform: Transformer, 

2206 mode: Mode, 

2207 features: Collection[Feature], 

2208 *, 

2209 line_str: str = "", 

2210) -> list[Line]: 

2211 if not line_str: 

2212 line_str = line_to_string(line) 

2213 result: list[Line] = [] 

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

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

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

2217 

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

2219 

2220 features_set = set(features) 

2221 if ( 

2222 Feature.FORCE_OPTIONAL_PARENTHESES in features_set 

2223 or transform.__class__.__name__ != "rhs" 

2224 or not line.bracket_tracker.invisible 

2225 or any(bracket.value for bracket in line.bracket_tracker.invisible) 

2226 or line.contains_multiline_strings() 

2227 or result[0].contains_uncollapsable_type_comments() 

2228 or result[0].contains_unsplittable_type_ignore() 

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

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

2231 # subscript opening bracket. Taking the FORCE_OPTIONAL_PARENTHESES 

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

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

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

2235 or _over_length_only_due_to_subscript_comment(result[0], mode) 

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

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

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

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

2240 or any(leaf.parent is None for leaf in line.leaves) 

2241 ): 

2242 return result 

2243 

2244 line_copy = line.clone() 

2245 append_leaves(line_copy, line, line.leaves) 

2246 features_fop = features_set | {Feature.FORCE_OPTIONAL_PARENTHESES} 

2247 second_opinion = run_transformer( 

2248 line_copy, transform, mode, features_fop, line_str=line_str 

2249 ) 

2250 if all(is_line_short_enough(ln, mode=mode) for ln in second_opinion): 

2251 result = second_opinion 

2252 return result