Black also behaves decently wrt empty lines. It does have some opinions on that, like two lines between functions and in some other contexts, and at most one line elsewhere. But inside function bodies, it will collapse multiple consequent blank lines into a single one, but it will never remove them altogether, so it's still possible to use them to logically structure code.