Skip to content

Fix crash on # fmt: skip after an opening bracket with a standalone comment#5161

Open
sarathfrancis90 wants to merge 1 commit into
psf:mainfrom
sarathfrancis90:fix-fmtskip-bracket-comment-crash
Open

Fix crash on # fmt: skip after an opening bracket with a standalone comment#5161
sarathfrancis90 wants to merge 1 commit into
psf:mainfrom
sarathfrancis90:fix-fmtskip-bracket-comment-crash

Conversation

@sarathfrancis90
Copy link
Copy Markdown

Description

black crashes with AttributeError: 'Leaf' object has no attribute 'bracket_depth' on valid Python when a # fmt: skip line is placed directly after an opening bracket and a standalone comment also appears among the bracket's contents. For example, all of the following crash:

from m import (
# fmt: skip
    # comment
    a
)

f(
# fmt: skip
    # comment
    a
)

x[
# fmt: skip
    # comment
    a
]

from m import (  # fmt: skip
    # comment
    a
)
error: cannot format ...: 'Leaf' object has no attribute 'bracket_depth'

Root cause. The crash happens in is_line_short_enough (src/black/lines.py). When the rendered line spans multiple physical lines, the function walks every leaf and reads leaf.bracket_depth. That attribute is set by BracketTracker.mark, but mark deliberately early-returns for a closing bracket that is continued from a previous line break (a leftover ) / ] / } left on its own line) — see BracketTracker.mark in src/black/brackets.py. Such a leaf therefore never receives a bracket_depth, and accessing it raises AttributeError.

The combination of # fmt: skip right after an opening bracket plus a standalone comment is exactly what produces a line containing such an unmarked, continued closing bracket, so is_line_short_enough blows up.

Fix. A leaf that the bracket tracker skipped is, by construction, not inside any bracket that opened on the current line, so its effective depth is 0. Read the attribute defensively with that default (getattr(leaf, "bracket_depth", 0)). This is a no-op for every already-marked leaf (behaviour is unchanged) and removes the crash, producing valid, AST-equivalent and idempotent output. Since this only fixes a crash (it does not change formatting of any code that previously formatted successfully), it is a stable-style fix per the contributing docs.

Testing. Added tests/data/cases/fmtskip_after_bracket_with_comment.py covering the import, call, subscript and inline # fmt: skip variants. The test fails on main (crash inside is_line_short_enough) and passes with this change. The produced output is AST-preserving and idempotent (formatting it a second time is a no-op).

Checklist - did you ...

  • Implement any code style changes under the --preview style, following the stability policy? (N/A — this is a crash fix only, no formatting changes to previously-working code, so it belongs in stable style)
  • Add an entry in CHANGES.md if necessary?
  • Add / update tests if necessary?
  • Add new / update outdated documentation? (N/A — no user-facing behaviour or documented option changes)

A `# fmt: skip` line placed directly after an opening bracket, combined
with a standalone comment among the bracket's contents, crashed with
`AttributeError: 'Leaf' object has no attribute 'bracket_depth'`.

The crash happened in `is_line_short_enough`. When the rendered line
spans multiple physical lines, the function walks the leaves and reads
`leaf.bracket_depth` on each of them. However, a closing bracket that is
continued from a previous line break (a leftover `)` / `]` left on its
own line) is intentionally skipped by `BracketTracker.mark` and never
receives a `bracket_depth` attribute. Accessing it therefore raised.

Such a leaf is not inside any bracket that opened on the current line, so
its effective depth is 0. Read the attribute defensively with that
default, which leaves behaviour unchanged for all already-marked leaves
and stops the crash, producing valid and stable output instead.

Added a regression test covering the import, call, subscript and inline
`# fmt: skip` variants.
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.

1 participant