Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaks multiline function type annotation comments #282

Closed
ned opened this issue May 30, 2018 · 8 comments
Closed

Breaks multiline function type annotation comments #282

ned opened this issue May 30, 2018 · 8 comments
Labels
F: comments The syntactic kind. Not in the language grammar, always on our minds. Best bugs. T: bug Something isn't working T: style What do we want Blackened code to look like?

Comments

@ned
Copy link

ned commented May 30, 2018

Operating system: Fedora 27 4.13.9-300.fc27.x86_64)
Python version: 3.6.2
Black version: 18.5b1
Does also happen on master: yes

PEP484 was amended (python/typing#186) to extend Python 2 compatible type annotation comments to allow placing each function parameter on a separate line with its own type annotation and the return type at the end. The motivation for this was to be able to write type annotations which would otherwise extend beyond the maximum line limit. But when it will fit in the maximum line length, Black combines all the arguments onto one line, including the type annotations, which become malformed.

Given this input

def f(a,  # type: int
      b,  # type: str
      c,  # type: bool
      ):  # type: (...) -> None
    ...

Black produces this

def f(a, b, c):  # type: int  # type: str  # type: bool  # type: (...) -> None
    ...

which has a malformed type annotation.

I think the correct behaviour would be either leave the function parameters on separate lines (do nothing) or restructure the type annotation like this:

def f(a, b, c):  # (int, str, bool) -> None
    ...

or if the resultant line is too long, this

def f(a, b, c): 
    # (int, str, bool) -> None
    ...

This might deserve a separate issue, but it'd be nice if Black did the reverse as well: given a very long function type annotation on one line, annotate each parameter separately.

@ambv
Copy link
Collaborator

ambv commented Jun 4, 2018

Yes, this is unfortunate and I should probably have special handling for it. Not quite clear how I would special-case it yet.

@ambv ambv added T: bug Something isn't working T: style What do we want Blackened code to look like? F: comments The syntactic kind. Not in the language grammar, always on our minds. Best bugs. labels Jun 5, 2018
@perrygeo
Copy link

perrygeo commented Jun 6, 2018

Noting that this is a more general issue with inline comments. For example, black changes the meaning of this comment intended to be associated with a particular kwarg:

$ cat example.py
myfunc(
    a,
    b=1,  # this is critical due to #123
    c=None)

$ black example.py
reformatted example.py

$ cat example.py
myfunc(a, b=1, c=None)  # this is critical due to #123

@ambv
Copy link
Collaborator

ambv commented Jun 6, 2018

@perrygeo, that's intentional.

@tony-cox
Copy link

There are many cases where neither of the suggestions (apart from 'do nothing') from @ned will work without making a line that is too long. Usually when I use a multiline type annotation it's because it won't fit on a single line after the function. That is,

def f(a, b, c): 
    # (int, str, bool) -> None

in some cases would cause the type annotation # (int, str, bool) -> None to extend beyond the character limit. In these cases, Black should probably do nothing at all.

@whatisaphone
Copy link

I just ran into this as well. We're still on Python 2 (just a little bit longer now…) and I'd like to adopt Black, but this is a showstopper.

@whatisaphone
Copy link

Thinking about it more, I wonder if maybe Black shouldn't be moving comments inside lists at all. Aside from that pattern being used for human-readable comments (perrygeo's example above), it is also universal among tools that work with Python code:

mypy:

def func(
    arg1,  # type: int
    arg2,  # type: int
):

flake8:

foo(
    f(),
    g(),  # noqa: E123
):

pylint:

obj.things = [
    a.b,
    c.d,  # pylint: disable=no-member
]

isort:

from os import (
    path,  # isort:skip
	setenv,
	getenv,
)

coverage.py:

foo(
    x() if cover_me else y(),
    a() if ignore_me else b(),  # pragma: no cover
)

In each of these cases, moving the comment away from its line would either weaken the intended semantics or break things entirely. So maybe this issue is a little further-reaching than just Python 2 + mypy.

@gvanrossum
Copy link

A small plug for #642 -- until this issue is fixed we won't be able to use black at Dropbox. We use this syntax extensively.

@JelleZijlstra
Copy link
Collaborator

Fixed by #642.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
F: comments The syntactic kind. Not in the language grammar, always on our minds. Best bugs. T: bug Something isn't working T: style What do we want Blackened code to look like?
Projects
None yet
Development

No branches or pull requests

7 participants