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

E303: Does not report, that a blank line between classes is needed, if they're defined inside an if statement #366

Open
CreamyCookie opened this issue Jan 3, 2015 · 21 comments · Fixed by #367
Assignees

Comments

@CreamyCookie
Copy link

As stated in PEP8:

Separate top-level function and class definitions with two blank lines.

if __name__ == '__main__':
    class Pos:

        def __init__(self, x, y):
            self.x = x
            self.y = y

    class Lifetime:

        def __init__(self, lifetime):
            if lifetime < 0:
                raise ValueError('lifetime must be above 0')
            self.lifetime = lifetime

    class Name:

        def __init__(self, name):
            self.name = '"' + name + '"'
@sigmavirus24
Copy link
Member

That will define the bindings Pos, Lifetime, and Name as globals. I'm not sure if I agree that they should be separated by two blank lines. Maybe @ncoghlan can help us out here. I can't decide if top-level is referring to indentation or scope.

@CreamyCookie
Copy link
Author

@sigmavirus24 Hmm, I wonder about that as well.

Whatever the PEP8 actually means, the class statement hanging there between two methods definitely looks a bit off to me:

            self.y = y

    class Lifetime:

        def __init__(self, lifetime):

Even without the blank line after the class statement, I would prefer two blank lines between classes, because there's one line between methods of the class:

    class Lifetime:
        def __init__(self, lifetime):
            if lifetime < 0:
                raise ValueError('lifetime must be above 0')
            self.lifetime = lifetime

        def do_stuff(self):
            pass


    class Name:
        def __init__(self, name):
            self.name = '"' + name + '"'

@sigmavirus24
Copy link
Member

So that's fine if that's your preference. Does the latter raise a warning or error from pep8 though? If not, just go ahead and do it until we can clear up what the document is actually suggesting. I personally don't feel too strongly about this matter so either outcome is fine by me. I just want to get it right and not add additional checks that will upset developers.

@CreamyCookie
Copy link
Author

For this

if True:
    class Lifetime:
        def __init__(self, lifetime):
            if lifetime < 0:
                raise ValueError('lifetime must be above 0')
            self.lifetime = lifetime

        def do_stuff(self):
            pass


    class Name:
        def __init__(self, name):
            self.name = '"' + name + '"'

pep8 returns:

bla.py:12:5: E303 too many blank lines (2)

@IanLee1521
Copy link
Member

I would tend to think that the document is referring to "top-level" as an indentation, and therefore this previous example is "global" but not "top-level". I agree this is something that could be clarified in the document.

@IanLee1521 IanLee1521 self-assigned this Jan 3, 2015
@CreamyCookie
Copy link
Author

@IanLee1521 I'm no authority on this nor am I criticising you, but I don't think this makes sense (and it looks weird with one blank line).

How do we know if it is referring to indentation and not scope? And what could the logic behind "only one blank line between classes within an if statement" be? (honest question)

@ncoghlan
Copy link

ncoghlan commented Jan 4, 2015

For cases like the one described here, "do whatever seems most readable in the specific situation" is about as good as the PEP can do - there are too many variables affecting readability once you move the opening header line for class and function definitions away from the left hand margin.

In particular, the PEP guidelines on vertical whitespace are designed to create coherency within class and function definitions by creating additional space between them. That's desirable at the module top level, but when a suite is involved, excessive use of vertical whitespace means you end up reducing the coherency of the suite.

So once you get beyond unindented code, the overarching guidelines on when to ignore other PEP guidelines are far more likely to come into play. In particular, this one: "[Don't apply PEP 8 guidelines when] ... applying the guideline would make the code less readable, even for someone who is used to reading code that follows this PEP".

@ncoghlan
Copy link

ncoghlan commented Jan 4, 2015

From a pep8 perspective, I think "no opinion" is the right approach here - folks that want a tool that also covers cases where PEP 8 defers to the developer's judgement would likely be better served by starting with pylint's expansive default settings, and tuning those down to a subset they like.

@CreamyCookie
Copy link
Author

@ncoghlan Hmm, do I get it right that by "no opinion" you mean, that pep8 shouldn't return bla.py:12:5: E303 too many blank lines (2)? That would be great, because it would solve my problem more or less.

@ncoghlan
Copy link

ncoghlan commented Jan 4, 2015

Yes, I think if a function or class definition is nested inside another suite, pep8 would be better off ignoring the vertical whitespace guidelines. I don't know how practical that is to implement, though.

@sigmavirus24
Copy link
Member

I'll let @IanLee1521 comment on the feasibility

IanLee1521 added a commit that referenced this issue Jan 7, 2015
pep8 should ignore whitespace errors E30 when evaluating non-top level code.
IanLee1521 added a commit that referenced this issue Jan 7, 2015
Move checking of def / class to higher spot in if statement.
This changes some E303 errors into E302 errors, such as for:

    def a():\n    pass\n\n\n\ndef b():\n    pass
IanLee1521 added a commit that referenced this issue Jan 7, 2015
@IanLee1521 IanLee1521 mentioned this issue Jan 7, 2015
@IanLee1521
Copy link
Member

Ok, I just made the pull request for this fix. Only had to re-order some of the if statement cases to have the def / class checked first before the blank lines. Note this changes some E303 errors into E302 errors, e.g.:

def a():
    pass



def b():
    pass

foo.py:7:1: E302 expected 2 blank lines, found 4

@sigmavirus24
Copy link
Member

The fact that E303 and E302 is swapped in aome cases now seems unacceptable to me. If people are ignoring E303 in their configs, they're going to be extraordinarily angry that they now have to ignore E302

@IanLee1521
Copy link
Member

To me, it seems like this was actually a bug before, and that pep8 always should have been reporting foo.py:7:1: E302 expected 2 blank lines, found 4, which is specifically for function / class definitions instead of the more general foo.py:7:1: E303 too many blank lines (4).

@sigmavirus24
Copy link
Member

It may have been a bug. That aside, people are still going to be rather annoyed. I don't have evidence if people are ignoring E303 and not E302 though, so take this with a grain of salt.

@myint
Copy link
Member

myint commented Jan 13, 2015

@IanLee1521, I want to make sure I understand the changes correctly. Is this expected behavior for pep8 now?

foo.py:

class Foo(object):



    def a(self):
        pass




    def b(self):
        pass
$ ./pep8.py foo.py

bar.py:

if True:



    pass
    pass




    pass
    pass
$ ./pep8.py bar.py
bar.py:5:5: E303 too many blank lines (3)
bar.py:11:5: E303 too many blank lines (4)

Thanks

@IanLee1521
Copy link
Member

Good catch, looks like there wasn't a test case for the case you listed in foo.py. I've created a new branch that has that in E30.py (d8441d1). I'll work on a patch for it that continues to report it as an error.

@IanLee1521
Copy link
Member

I've rolled back this branch from e57e293 to 5100035 while I take some time to figure out best how to handle this. @myint -- This issue comes up primarily due to some of the user freedoms provided by the PEP:

Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations).

I don't think that is really whats happening here, but it is one of the tricky points to handle in code. I'll re-open the issue and work on a better way to handle this.

@IanLee1521 IanLee1521 reopened this Jan 26, 2015
@jayvdb
Copy link
Member

jayvdb commented Oct 20, 2015

Is this a dup of #168

@jayvdb
Copy link
Member

jayvdb commented Jun 27, 2016

Note that on #400 clarification on the PEP was given that "top-level function and class definitions" refers to scope, not indent level. There was a little discussion about this on #536 , which might be of interest to someone intending to implement this.

#536 was only a partial solution, introducing previous_unindented_logical_line, solving only cases that are not indented. It seems that identification of "top level" scoped objects is central to this issue, so it will likely solve the remainder of #400.

@zyv
Copy link

zyv commented Feb 26, 2018

Hi folks,

So what is the current status of this issue?

At the moment, PyCharm formatter with default settings inserts double blank line also if classes are defined in an if, which is apparently the desired behavior after all the clarifications over here, but then this triggers a flake8 warning. This means that I'd either have to silence the warning for this file / line, or constantly fight with PyCharm formatter.

I've reported this problem against PyCharm here: https://youtrack.jetbrains.com/issue/PY-28615 and was indirectly referred to this issue.

Are you planning to fix this anytime soon, or else I should push for PyCharm folks to adjust the formatter?

Many thanks!

@asottile asottile changed the title Does not report, that a blank line between classes is needed, if they're defined inside an if statement E303: Does not report, that a blank line between classes is needed, if they're defined inside an if statement Jun 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants