Skip to content

Only enforce blank line separation between functions and classes at the same level of nesting #450

@njsmith

Description

@njsmith

Black insists that function and class definitions should be separated by blank lines. Good rule! However, I think it's slightly overenthusiastic.

For functions at the same level of nesting, this is a good idea:

 def foo():
     pass
+
 def bar():
     pass

And it's not just between two functions, but between any start/end of a function and surrounding code:

 FOO = 1
+
 def foo():
     pass
+
 BAR = 2

But! This seems unnecessary – and dare I say, ugly and less readable – when the adjacent line of code is on a different nesting level:

    if clogged_stream_maker is not None:

        async def flipped_clogged_stream_maker():
            return reversed(await clogged_stream_maker())

    else:
        flipped_clogged_stream_maker = None

I claim that the above snippet (source would be better without any blank lines.

The same applies to classes, like, how does this blank line help anything? (source

 def test_wrap_non_iobase():
+
     class FakeFile:

         def close(self):  # pragma: no cover
             pass

The intuition here is that blank lines are separators, not headers/footers. And you only need separation between items at the same level of nesting. We don't insist on using blank lines to separate a class or def from the beginning or end of the file; similarly, we shouldn't insist on using them to separate a class or def from the beginning or end of a block.

This would also make black's handling of def and class more consistent with its handling of import. Currently black does use the rule I suggest for blank lines after import statements:

# Black insists on a blank line after the import here:
import foo

BAR = 1

# But not here:
try:
    import foo
except ImportError:
    pass

This annoys me because I have a codebase with lots of functions whose first order of business is to define a nested function:

async def foo():  # black wants to insert a blank line after this line
    def bar():
        ...

    return await run_sync_in_worker_thread(bar)

Note that making this change would not introduce churn into existing codebases, because black already preserves blank lines. It would just introduce fewer unnecessary blank lines in the future.

Metadata

Metadata

Assignees

No one assigned

    Labels

    F: empty linesWasting vertical space efficiently.T: styleWhat do we want Blackened code to look like?

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions