As of 26.1, Black forces 1 line after imports which—no matter how you feel about that—conflicts with flake8’s E302 that enforces 2 empty lines btw classes/functions.

I've been told that it was in --preview for a yr & nobody complained… I can hear Douglas Adams giggling.

As one of the OG Black cheerleaders this makes me sad since this change seems entirely unnecessary. Making THE Python-based formatter incompatible w/ THE Python-based linter… This was the fallback in case of a ruff rugpull. 🙁

@hynek I get two empty lines?

import math
def thing():
print(math.pi)

->

import math

def thing():
print(math.pi)

@hugovk I hate it to break it to you, but that's one line :D are you an LLM?! how many Rs are in Strawberry?!?

@hynek Urgh, Mastodon collapsing whitespace! Let's try again...

And seven Rs in strawberry.

@hugovk ah, this actually makes it even worse. try ```
import math
foo = math.pi
```
@hynek I get same for Black 25.12.0 and 26.1.0, for this and the other one.
@hugovk yep, that's the one

@hynek I don't follow, Black's done this since its first 18.3a0, and Ruff since at least 0.0.292.

And neither Flake8 nor Ruff complain about E302.

@hugovk Black has kept out of imports before 26.1, unless you ran --preview. It's part of “2026 stable style”. Dunno if there's some subtlety around flake8 but Towncrier uses Black + isort and after begrudgingly setting `tool.isort.lines_after_imports = 1`, I got a screen full of E302s and I'm officially giving up.

See: https://github.com/psf/black/pull/4489

Two blank lines after an import should be reduced to one by kastkeepitjumpinlikekangaroos · Pull Request #4489 · psf/black

Description Hey! Trying to contribute for the first time to the project so let me know if I'm missing anything :) I've implemented changes to address this issue #2020 where there's some...

GitHub

@hynek Ah right, 2->1 is the thing, not 0->1.

That's annoying Black does one thing and then isort reverts it!

Looks like part of the issue is using isort's "attrs" profile rather than "black", which sets lines_after_imports=2.

I see changing to the "black" profile resolves the Black/isort fight, but you may understandably have a preference for the "attrs" profile!

Or replacing profile="attrs" with all the config but lines_after_imports=2 from https://pycqa.github.io/isort/docs/configuration/profiles.html is another option.

Profiles - isort

@hugovk you can do profile=attrs and overwrite lines_after_imports=1 that's not the problem and that was what i did initially and very begrudgingly (because one empty line after imports but two between functions!? where is the logic in there??) but then flake8 entered the chat. i'm just so tired of this completely unnecessary and escalating problem. i'm not shaving another yak, i'm switching to ruff.
@hynek Yeah, but I guess you can't *unset* lines_after_imports to let it vary. Anyway, I hear Ruff is quite good.
@hugovk Ruff is great, but having only a VC-backed Rust project as the only option not so much :|
@hynek @hugovk Is there a reasonable argument that it’s “only” a linter/formatter? As in, they only make superficial changes, so does it matter if you use them for a couple of years and then switch to something else? Or is it more of a principled take?
@hynek @hugovk shameless plug but give ufmt a try as an alternative, it will wrap black+usort in a way where there’s never a conflict and you don’t need configuration to make the sorter work with the formatter