Visitar URL original
Ruff 2026 Style Guide · Issue #20482 · astral-sh/ruff · GitHub
Skip to content

Ruff 2026 Style Guide #20482

Closed
Closed

Description

@dylwil3

From Black's changelog and Preview enum. Last checked 19.09.2025, Last release: v25.9.0.

Goals:

  • Close parity with Black when migrating from Black to Ruff
  • Fix Ruff specific bugs
  • Innovate on local formatting (to keep close parity with black)

Preview Styles

Stabilized in Black and not available in Ruff (even in preview)

Black preview styles

Black unstable styles

Ruff preview styles

Ruff specific preview styles that we may want to stabilize

Ruff improvements

Open Bugs

Existing bugs in the ruff formatter for which no preview style exists.

Require a new style guide

Bug fixes that change how existing code is formatted and require a breaking change.

Bug fixes that don't change existing formatting

Bug fixes that don't require a new style guide because they don't change existing code

Activity

  1. added
    formatterRelated to the Ruff formatter
    help wantedContributions especially welcome
    styleHow should Ruff-formatted code look
    on Sep 19, 2025
  2. added theissue type on Sep 19, 2025
  3. s-banach commented on Sep 20, 2025

    @s-banach

    Ruff specific preview styles that we may want to stabilize

    I have been using ruff format with preview = true since 2023, just for this one feature.
    The only reason I'm reading this issue is to see whether it will be stabilized.

  4. MeGaGiGaGon commented on Sep 20, 2025

    @MeGaGiGaGon
    Contributor

    ruff already has the behavior from normalize_cr_newlines. I guess it was technically a formatting deviation before? But like usual no one noticed because dealing with newlines is so annoying.

    Demonstrating difference

    Input code powershell escaped: "a`rb`nc`r`n#1"
    Input code backslash escaped: a\rb\nc\r\n#1\r\n

    Formatter Escaped Output
    Black 25.9.0 no preview a\nb\nc\n# 1\n
    ruff format a\rb\rc\r# 1\r
    Black 25.9.0 preview a\rb\rc\r# 1\r

    Input code without visible newlines:

    a
    b
    c
    #1

    Input code with written out newlines:

    a<cr>
    b<nl>
    c<cr><nl>
    #1<cr><nl>

    Where the #1 at the end is to cause the reformatting.

    PS ~\Desktop\New_folder>Set-Content issue.py "a`rb`nc`r`n#1"
    PS ~\Desktop\New_folder>Get-Escaped-Content issue.py
    a\rb\nc\r\n#1\r\n
    PS ~\Desktop\New_folder>uvx black issue.py
    reformatted issue.py
    
    All done! ✨ 🍰 ✨
    1 file reformatted.
    PS ~\Desktop\New_folder>Get-Escaped-Content issue.py
    a\nb\nc\n# 1\n
    PS ~\Desktop\New_folder>Set-Content issue.py "a`rb`nc`r`n#1"
    PS ~\Desktop\New_folder>uvx ruff format issue.py
    1 file reformatted
    PS ~\Desktop\New_folder>Get-Escaped-Content issue.py
    a\rb\rc\r# 1\r
    PS ~\Desktop\New_folder>Set-Content issue.py "a`rb`nc`r`n#1"
    PS ~\Desktop\New_folder>uvx black issue.py --preview
    reformatted issue.py
    
    All done! ✨ 🍰 ✨
    1 file reformatted.
    PS ~\Desktop\New_folder>Get-Escaped-Content issue.py
    a\rb\rc\r# 1\r

    Where Get-Escaped-Content is a custom powershell function specifically to help deal with these annoying newlines:

    function Get-Escaped-Content {
    	param (
    		$Path
    	)
    	return Get-Content $Path -Raw | ForEach-Object { $_ -replace "\n", "\n"} | ForEach-Object { $_ -replace "\r", "\r"}
    }
  5. MichaReiser commented on Sep 20, 2025

    @MichaReiser
    Member

    For reference the tracking issue for the 2025 style guide.

    • It was an intentional decision not to implement https://github.com/psf/black/pull/3440 because of Nested expression formatting #12856.
    • The unstable styles are styles that black doesn't consider "ready" yet because of bugs. We should focus on the preview sytles first (but it's good to work on unstable styles in case black fixes all remaining issues and promotes them to preview and then stable)
    • multiline_string_handling: Ruff now automatically joins implicitly concatenated strings. That's as far as we want to go for now.
    • I'm not sure what string_processing is
    • We should consider implementing and shipping Allow newlines after block open (or not) #9745 (requires some design, maybe there's a middle ground that we dont' need to go as far as black)
    • There are also some black 2025 style changes that we didn't implement, see [formatter]: Black 2025 style guide differences #15927

    @vlkorsakov is there a specific change that you dislike or do you dislike style changes in general?

  6. MeGaGiGaGon commented on Sep 20, 2025

    @MeGaGiGaGon
    Contributor

    Ruff already has part of string_processing in that short string literals will be merged . Also part of it is splitting long string literals, see the docs for more info, but string_processing is almost certainly going to remain unstable for 2026. See the 2024 Black Stable Style issue psf/black#4042 for more details, but there are still many outstanding serious issues https://github.com/psf/black/issues?q=is%3Aissue%20state%3Aopen%20string_processing . (But it also won't be removed since there are a ton of people that like it, so it's stuck in a difficult place)

  7. dylwil3 commented on Sep 26, 2025

    @dylwil3
    CollaboratorAuthor

    It looks like the preview style wrap_comprehension_in also results in quite a few added parentheses: https://github.com/psf/black/blob/903bef54aa72e111c034ccec9997aac82bafbacc/tests/data/cases/preview_wrap_comprehension_in.py

    So we may get into a similar situation as with #12856

  8. MichaReiser commented on Nov 10, 2025

    @MichaReiser
    Member

    Black published a new release today and they moved multiline_string from unstable into preview. We should double check how their multiline string handling differs from our (I might have confused it with the string_processing unstable style)

  9. ntBre commented on Nov 10, 2025

    @ntBre
    Contributor

    I tried updating our Black tests again, and the only changes to the multiline string styles were from this PR: psf/black#4760, deleting some of the cases that I think do relate more to string_processing. So our preview_multiline_strings snapshot should still accurately reflect how closely we match Black.

    Mostly it looks like we insert more breaks around multiline strings:

    call(
    3,
    {"dog", "say"},
    - textwrap.dedent("""pig
    - oink""" % "cowabunga"),
    + textwrap.dedent(
    + """pig
    + oink"""
    + % "cowabunga"
    + ),

  10. hauntsaninja commented on Nov 10, 2025

    @hauntsaninja
    Contributor

    Does ruff have anything planned for #12856 ? Especially curious about parenthesize_conditional_expressions , I get asked about that at work sometimes

  11. MichaReiser commented on Nov 11, 2025

    @MichaReiser
    Member

    I tried updating our Black tests again, and the only changes to the multiline string styles were from this PR

    I see. I suggest we wait then. Ruff uses some heuristics to preserve the indent for multiline strings to reduce the diff compared to the old style guide and I remember that it was a main concern for Black when they considered stabilizing in previous years. We can always follow along next year if the change works well for Black.

    Does ruff have anything planned for #12856 ? Especially curious about parenthesize_conditional_expressions , I get asked about that at work sometimes

    Unfortunately not. I very much hope that we'll have more dedicated time to improve Ruff's style guide next year. Shipping #12856 probably also requires formatter editions (that are independent of the ruff version), because I expect the diff compared to the current style guide to be rather large. You can upvote (or comment on) #12856 or #9722 specifically. This would help us prioritize the work next year.

  12. laymonage commented on Nov 11, 2025

    @laymonage

    I'd like to suggest #21380 to be included in the "Stabilized in Black and not available in Ruff (even in preview)" section, next to #21110 that has been released. Thanks!

  13. dylwil3 commented on Dec 6, 2025

    @dylwil3
    CollaboratorAuthor

    Black added a new preview style on main (not yet released): psf/black#4865

    Removes LHS parentheses in examples such as

    (b) = a()[0]
    (c, *_) = a()

    At the moment we preserve the user's parentheses or lack thereof.

    I'm sort of okay making this a preview style, but I would hesitate stabilizing for 2026 - it seems like not a huge improvement but would cause big diffs everywhere.

  14. MichaReiser commented on Dec 6, 2025

    @MichaReiser
    Member

    I'm sort of okay making this a preview style, but I would hesitate stabilizing for 2026 - it seems like not a huge improvement but would cause big diffs everywhere.

    I'm not sure I follow the reasoning here. The diff size doesn't depend on when we stabilize it; it's only defined by the change itself.

    Removing the parentheses around tuples requires matching on the target and call the tuple formatting with TupleParentheses::NeverPreserve.

    Removing unnecessary parentheses around (b) might be slightly trickier. I do agree that this isn't a change that we need to rush and I'd keep our existing prioritization:

    • method chain formatting
    • fmt:skip bug
    • subscript issue
  15. MichaReiser commented on Dec 9, 2025

    @MichaReiser
    Member

    Regarding: (c, *_) = a(). We intentionally preserve tuple parentheses in more places than black because it can be very easy to miss that something's a tuple if the parentheses are missing. I don't remember the exact details but we should ensure we preserve parentheses in assignment targets the same as we do in assignment values.

  16. hugovk commented on Jan 18, 2026

    @hugovk
    Contributor

    Black 26.1.0 is now out with their new 2026 style:

    https://github.com/psf/black/releases/tag/26.1.0

  17. RazerM commented on Jan 20, 2026

    @RazerM
    Contributor

    Now that black has stabilized multiline_string_handling I'd like to see it in ruff, it's the one thing keeping me from adopting ruff format in some projects.

    To summarise:

    ruff allows this:

    op.execute("""
        CREATE TRIGGER ...
        ...
    """)

    but changes:

    op.execute(dedent("""
        CREATE TRIGGER ...
        ...
    """).strip())

    to

    op.execute(
        dedent("""
        CREATE TRIGGER ...
        ...
        """).strip()
    )

    which I can't get behind

  18. MichaReiser commented on Jan 20, 2026

    @MichaReiser
    Member

    @RazerM please create a separate issue to discuss this style change.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    formatterRelated to the Ruff formatterhelp wantedContributions especially welcomestyleHow should Ruff-formatted code look

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions