Skip to content

Consecutive @overload definitions have two distinct stable formatting states for the same AST #5001

@louisabraham

Description

@louisabraham

Description

Black produces two different idempotent outputs for consecutive @overload function definitions depending on the initial whitespace between them. Both outputs are stable (running Black again produces no changes), but they correspond to the same AST.

This means Black does not converge to a single canonical form for this pattern — the output depends on the input formatting, not only on the AST.

Steps to reproduce

Input A — no blank lines between overloads:

from typing import overload


@overload
def f(x: int) -> int: ...
@overload
def f(x: str) -> str: ...
def f(x): ...

Input B — two blank lines between overloads:

from typing import overload


@overload
def f(x: int) -> int: ...


@overload
def f(x: str) -> str: ...


def f(x): ...

Running black --check on both files exits with code 0 (no changes).

Both parse to the same AST (ast.dump comparison returns True).

Expected behavior

I would expect Black to normalize both inputs to a single canonical layout, since they represent the same AST and neither contains comments or other distinguishing CST information.

Environment

  • Reproduced on Black 24.4.2 and 26.1.0
  • Python 3.12

Question

Is this intentional behavior (e.g. preserving the author's grouping intent), or should Black canonicalize blank lines between consecutive @overload definitions?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions