Black: Nested Function Definitions Introduce Whitespace

Created on 8 May 2018  路  5Comments  路  Source: psf/black

Operating system: macOS
Python version: 3.6
Black version: black, version 18.4a4
Does also happen on master: Yes

Black is persistent about function declarations having vertical whitespace even though pep8 only prescribes them for top-level and class method declarations.

Examples:
https://github.com/python/cpython/blob/master/Lib/contextlib.py#L600-L618

Example

from functools import wraps


def my_decorator(func=None, a=1, b=2):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            kwargs.update(dict(a=a, b=b))
            return func(*args, **kwargs)
        return wrapper

    if func:
        return decorator(func)
    return decorator

Result

kstone@mbp ~> black --diff .
--- test.py  (original)
+++ test.py  (formatted)
@@ -1,14 +1,18 @@
 from functools import wraps


 def my_decorator(func=None, a=1, b=2):
+
     def decorator(func):
+
         @wraps(func)
         def wrapper(*args, **kwargs):
             kwargs.update(dict(a=a, b=b))
             return func(*args, **kwargs)
+
         return wrapper

     if func:
         return decorator(func)
     return decorator

reformatted test.py

All 5 comments

Noted. I'm not sure how easy this is going to be to change since Black doesn't keep track whether a function definition is internal to a function definition or to a class definition. Spacing it out always doesn't hurt, especially when differentiating where the inner function ends. I guess your example wouldn't look as weird if there were docstrings involved.

I will look into improving this.

I think black would be better to track module/class/function context. The current 1 if depth else 2 feels brittle.

I can submit a PR to see how much complication this introduces.

Resolution: nested function definitions will introduce whitespace between docstrings and other instructions but will not introduce leading empty lines if they are the start of the wrapping functions body.

The diff from your example will look like this now:

--- /tmp/asd.py  (original)
+++ /tmp/asd.py  (formatted)
@@ -5,10 +5,11 @@
     def decorator(func):
         @wraps(func)
         def wrapper(*args, **kwargs):
             kwargs.update(dict(a=a, b=b))
             return func(*args, **kwargs)
+
         return wrapper

     if func:
         return decorator(func)
     return decorator

I think this is reasonable and does what you wanted.

So that would format the example in https://github.com/ambv/black/issues/144#issuecomment-392650993 as:

def decorator(f):
    """A decorator."""

    @wraps(f)
    def inner():
        return f()

    return inner

?

@rouge8, yes. While it's not compatible with PEP 257 to the letter, I think it's close enough in spirit. I want inner functions to be delimited and there's no reason to hug them to a docstring. Other statements after a function docstring don't generate an empty line.

Was this page helpful?
0 / 5 - 0 ratings