Skip to content

Fix crash on # fmt: skip on a nested bracket after with or yield - #5620

Merged
cobaltt7 merged 3 commits into
psf:mainfrom
LuisFigueroaG:fix/fmt-skip-nested-bracket
Oct 26, 2026
Merged

cobaltt7 merged 3 commits into
psf:mainfrom
LuisFigueroaG:fix/fmt-skip-nested-bracket

Conversation

@LuisFigueroaG

Copy link
Copy Markdown
Contributor

Description

# fmt: skip on an opening bracket crashes Black when the bracket starts an operand that's nested inside a longer expression after with or yield:

with (  # fmt: skip
    a
) == 1 and b:
    pass

fails with "INTERNAL ERROR: Black produced invalid code: cannot parse: 1:4". The same happens with ) + 1 == 2:, with not ( # fmt: skip ... ) or b:, async with, and with ... as c. After yield the output still parses but means something else (yield ends up on its own line), so Black reports "produced code that is not equivalent to the source". 26.10.1 crashes on these too.

To find the code before the bracket on the same line, the walk in _generate_ignored_nodes_from_fmt_skip climbs to the parent when a node has no previous sibling, but only by one level. Here the bracket is the first leaf of a comparison inside an and_test, so the walk stops at the comparison and with is never added to the skipped line. The skipped code then becomes a standalone comment after with, which gets split onto its own line.

Both climbs (the one before the walk and the one inside it) now keep going up while the node is the first child of its parent. The inner loop that collapsed fully ignored parents is folded into the outer one, since it did the same climb with an extra condition.

With the whole line skipped, a unary operator before the bracket can be hidden while its operand goes on after the closing bracket (with -( # fmt: skip ... ).y and b:). That leaves a factor node with only the operand, so visit_factor now checks the number of children instead of unpacking two.

In other statements this didn't crash, but the line wasn't kept as is either. For example x = ( # fmt: skip ... ) == 1 and b was turned into x = ( followed by the bracket on its own line. Those lines are now left unchanged. Code that was already formatted by Black stays the same: I ran ~19k generated variants (x =, return, yield, with, if, assert, lambdas, unary operators, trailers, and/or/comparison/arithmetic operands) and the output of main for each one is unchanged with this branch, in stable, preview and unstable. None of them crash now.

Checklist - did you ...

  • Implement any code style changes under the --preview style, following the stability policy?
  • Add an entry in CHANGES.md if necessary?
  • Add / update tests if necessary?
  • Add new / update outdated documentation?

@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

diff-shades results comparing this PR (137b284) to main (04afd89):

--preview style: no changes

--stable style: no changes


What is this? | Workflow run | diff-shades documentation

@cobaltt7
cobaltt7 merged commit 062d76a into psf:main Oct 26, 2026
58 checks passed
@cobaltt7

Copy link
Copy Markdown
Collaborator

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants