black: flake8 #noqa comments get moved a place where they're ignored

Operating system: Linux Python version: 3.6 Black version: 18.4a5 Does also happen on master: ?

Using a long list of typing imports with #noqa because my code is py2 compatible so I’m using the comment syntax for type hints

original code

from typing import Dict, List, Set, ...  # noqa

becomes

from typing import (
    Dict,
    List,
    Set,
    ...,
)  # noqa

but flake8 wants

from typing import (  # noqa
    Dict,
    List,
    Set,
    ...,
)

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 10
  • Comments: 23 (4 by maintainers)

Commits related to this issue

Most upvoted comments

Maybe consider handling # pylint: comments as well, as described in #386

I think we still want Black to expand an overlong single-line statement with a normal comment at the end, even though that transformation is unsound for # type: ignore, hence the need for some kind of matching. Giving that up would be simpler but perhaps too simple.

Of course, if the horizontal-only behavior is configurable with prefixes, then you can configure the empty prefix to get it on all comments.

(I should mention where I’m coming from: I’m a big fan of Black, but this issue is a blocker for considering its use in Zulip, which was one of the early adopters of mypy and has hundreds of # type: ignore comments.)

I wonder if “when reformatting a one-line statement with a comment into a multi-line statement with a comment, put the comment at the end of the first line instead of at the end of the last line” would be a good approach? Running through some examples in my head I think that that should be equally consistent as the current behaviour, but it would Just Work more in more cases (and it feels more intuitive IMO - if I’m commenting a block of code, I put the comment at the start of the block)

I ran into a similar issue with # type: ignore comments; black moved around my # type: ignores on some long lines, and now mypy no longer likes my code.

Could Black guarantee to apply only horizontal reformatting to lines with a comment matching a configurable set of prefixes (noqa, pragma:, pylint: type:)?

Why only apply this for certain prefixes? If a multi-line statement has a comment in a middle line, surely the author meant for it to apply to that line for a reason?

I think what I can do for # noqa and # type: ignore is to move the comment after the opening parenthesis instead of after the closing parenthesis (because the latter is guaranteed to have no effect).

We now have some code (in comments.py) that attempts to keep magical comments like # noqa in the right place, and #2272 added discussion of this topic to the docs.

Perhaps the easier rule is to just not have black expand or collapse any lines with trailing comments?

I think a policy of leaving comments pinned to the start of the line they started on, rather than the end of the line they started on, would fix many/most of these problems.

That wouldn’t be enough for mypy # type: ignore, which applies to just the line it’s on, not the whole statement if it’s part of a longer statement.

value = (
    checked()
    + ignored() + ignored()  # type: ignore
    + checked()
)

Here’s a proposal that doesn’t require understanding whether each magic comment applies to the entire statement or some specific part. Could Black guarantee to apply only horizontal reformatting to lines with a comment matching a configurable set of prefixes (noqa, pragma:, pylint: type:)?

In other words, Black would disable the length limit for that line to avoid breaking it into multiple lines, and if the line is part of a longer statement, Black would preserve the line breaks preceding and succeeding the line. So it would not reformat the above to

value = (
    checked()
    + ignored()
    + ignored()  # type: ignore
    + checked()
)

nor to

value = checked() + ignored() + ignored() + checked()  # type: ignore

either of which would change the comment’s semantics.

I mentioned this over in #379, but I’ll add it again here: I think a policy of leaving comments pinned to the start of the line they started on, rather than the end of the line they started on, would fix many/most of these problems.

You can work around this by doing

from typing import (
    Dict,  # noqa
    List,  # noqa
    ...,
)