Skip to content

Fix --line-ranges crash on # fmt: skip before a closing bracket - #5477

Merged
cobaltt7 merged 5 commits into
psf:mainfrom
SulimanAbdulrazzaq:fix/line-ranges-nested-standalone-comment
Oct 5, 2026
Merged

cobaltt7 merged 5 commits into
psf:mainfrom
SulimanAbdulrazzaq:fix/line-ranges-nested-standalone-comment

Conversation

@SulimanAbdulrazzaq

@SulimanAbdulrazzaq SulimanAbdulrazzaq commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Description

With --line-ranges, Black turns statements outside the selected ranges into a single STANDALONE_COMMENT using str(node). If such a statement has a # fmt: skip on the last line before a closing bracket, that comment has already become a nested STANDALONE_COMMENT whose value has no trailing newline (the line generator normally adds it). str(node) then glues the closing bracket onto the comment's line, so the bracket becomes part of the comment and Black fails on its own output:

$ printf 'x = 1\nv = (\n    a  # fmt: skip\n)\n' > t.py
$ black --line-ranges=1-1 t.py
error: cannot format t.py: INTERNAL ERROR: Black ... produced invalid code:
cannot parse: t.py:3:19
    a  # fmt: skip)
                  ^

Without --line-ranges the same file formats fine, and the lines outside the range should be left alone, so this is an internal error on code the user did not ask to touch.

The fix: when converting an unselected node (_convert_node_to_standalone_comment and _convert_nodes_to_standalone_comment in ranges.py) stringify the node with a helper that ends a nested STANDALONE_COMMENT with a newline when it ends in a # fmt: skip comment and the next leaf does not already start with a newline. Nothing else changes: a standalone comment that ends in code, such as a header followed by : (#5405), is joined as before.

Tests:

  • tests/data/cases/line_ranges_fmt_skip_in_brackets.py: statements with # fmt: skip before a closing bracket (call, tuple, dict, if header, for iterable) outside the range stay untouched while the selected line is formatted.
  • tests/data/line_ranges_formatted/fmt_skip_in_brackets.py: the existing line-by-line test runs --line-ranges over every line of a formatted file.

Both fail on main with INTERNAL ERROR ... produced invalid code and pass with the change.

Sibling code paths checked: the other str(node) consumer in this feature, _convert_nodes_to_standalone_comment (match/suite headers), is changed the same way; comments.py already adds newlines for nested standalone comments in its own stringification; visit_suite already skips suites that contain a standalone comment. Not in scope: some other --line-ranges + # fmt: off/# fmt: skip combinations still fail (for example an async for ... in ( # fmt: skip header outside the range, and tests/data/cases/fmtonoff5.py); they have different causes.

Performance: the helper walks the leaves of each converted node once, as str(node) did; I saw no consistent difference in a noisy local benchmark (--line-ranges=1-1 on a 6000-line file).

Checklist - did you ...

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

Statements outside the selected ranges are turned into a single
STANDALONE_COMMENT using `str(node)`. A `# fmt: skip` (or a comment-only
`# fmt: off` block) inside such a statement has already become a nested
STANDALONE_COMMENT whose value has no trailing newline, so the following
leaf, for example a closing bracket, was joined onto the comment's line.
That made Black emit unparseable or non-equivalent code and fail with an
internal error. End such a comment with a newline when stringifying.
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

diff-shades results comparing this PR (918a806) to main (6e94088):

--preview style: no changes

--stable style: no changes


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

Comment thread src/black/ranges.py Resolved
Comment thread src/black/ranges.py Resolved
@cobaltt7

cobaltt7 commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

Thanks!

@cobaltt7
cobaltt7 merged commit a3cb9e1 into psf:main Oct 5, 2026
57 checks passed
luketainton pushed a commit to luketainton/luke_instant-msg-api that referenced this pull request Oct 10, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [black](https://github.com/psf/black) ([changelog](https://github.com/psf/black/blob/main/CHANGES.md)) | `<26.10.1,>=26.10.0` β†’ `<26.10.2,>=26.10.1` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/black/26.10.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/black/26.10.0/26.10.1?slim=true) |

---

### Release Notes

<details>
<summary>psf/black (black)</summary>

### [`v26.10.1`](https://github.com/psf/black/blob/HEAD/CHANGES.md#Version-26101)

[Compare Source](psf/black@26.10.0...26.10.1)

##### Highlights

This release fixes a security issue in Black's bundled GitHub Action. Repositories that
use the action are encouraged to update to the latest version of Black immediately. This
update is received automatically when using `psf/black@stable`, and is independent of
the version of Black installed by the action.

When the GitHub Action reads version specifiers from `tool.black.required-version`, it
now only accepts released versions of Black, as it already did for `black` requirements
in dependency lists. Other values, such as URL references, are now rejected. This issue
has been registered as `GHSA-cg8m-r9f2-5wm2`; a CVE identifier is pending.

##### Stable style

- Fix a long Jupyter notebook assignment magic (for example `x = !ls -la`) being wrapped
  in parentheses, which IPython can no longer run; such cells are now left unchanged
  ([#&#8203;5481](psf/black#5481))
- Keep repeated lines outside the selected `--line-ranges` unchanged ([#&#8203;5436](psf/black#5436))
- Fix `--line-ranges` formatting lines that follow a statement when formatting joins or
  re-indents that statement ([#&#8203;5484](psf/black#5484))
- Fix `--line-ranges` crashing with an internal error when an unselected statement has a
  `# fmt: skip` comment on the last line before a closing bracket ([#&#8203;5477](psf/black#5477))
- Prevent moving an encoding declaration (for example `# -*- coding: latin-1 -*-`) onto
  the first two lines of a file ([#&#8203;5487](psf/black#5487))
- Fix adding an extra trailing blank line inside a multi-line module docstring when its
  last line is near the line length limit ([#&#8203;5494](psf/black#5494))

##### Preview style

- Do not split short expressions on delimiters (such as binary operators, comparisons,
  or comprehensions) inside brackets when preceded by a standalone comment ([#&#8203;5455](psf/black#5455))
- Fix the unstable `string_processing` feature removing backslash-newline sequences from
  raw strings, which changed their value ([#&#8203;5482](psf/black#5482))
- Avoid adding unnecessary parentheses around unbreakable right-hand side expressions in
  assignments (such as annotated assignments or subscript targets) ([#&#8203;5473](psf/black#5473))
- Parenthesize expressions with parameter comments when they exceed the line length
  under `--preview` ([#&#8203;5442](psf/black#5442))
- Fix `wrap_long_dict_values_in_parens` dropping the parentheses around a right-hand
  side that contains a dictionary, leaving a line over the length limit ([#&#8203;5492](psf/black#5492))
- Fix the unstable `string_processing` feature adding a space after `*` or `**` in a PEP
  695 type parameter (`* Ts = ...`) when it splits a string in the parameter's default
  ([#&#8203;5504](psf/black#5504))
- Avoid unnecessary line splits of trailers (such as indexing/subscripts) when an
  earlier bracket split exceeds the line length only due to a trailing comment ([#&#8203;5498](psf/black#5498))

##### Configuration

- Add `--cache-dir` to configure the cache directory from the command line ([#&#8203;5433](psf/black#5433))
- Don't cache a file as formatted after formatting or checking it with `--line-ranges`,
  which made a later full `black --check` pass and `black` skip the unformatted lines
  ([#&#8203;5476](psf/black#5476))
- Respect a `.gitignore` that starts with a UTF-8 byte order mark. The mark became part
  of the first pattern, so that pattern never matched and Black formatted files that git
  ignores ([#&#8203;5497](psf/black#5497))

##### Performance

- Avoid quadratic runtime when splitting very long lines with many trailing bracket
  pairs (such as consecutive subscripts) ([#&#8203;5475](psf/black#5475))

##### Integrations

- Fix the GitHub Action crashing on dependency-group includes when `use_pyproject` is
  enabled ([#&#8203;5479](psf/black#5479))
- When the GitHub Action reads version specifiers from `tool.black.required-version`, it
  now only accepts released versions of Black, as it already did for `black`
  requirements in dependency lists. Other values, such as URL references, are now
  rejected. ([#&#8203;5510](psf/black#5510))

</details>

---

### Configuration

πŸ“… **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

β™» **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

πŸ”• **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xNDkuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjE0OS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJ0eXBlL2RlcGVuZGVuY2llcyJdfQ==-->

Reviewed-on: https://git.tainton.uk/luke/instant-msg-api/pulls/292
Co-authored-by: renovate[bot] <renovate-bot@git.tainton.uk>
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