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

Fix Issue 400: Require two blank lines after toplevel def, class #536

Closed
wants to merge 1 commit into from
Closed

Fix Issue 400: Require two blank lines after toplevel def, class #536

wants to merge 1 commit into from

Conversation

ArcTanSusan
Copy link
Contributor

@ArcTanSusan ArcTanSusan commented Jun 3, 2016

The original author wrote #402 to fix #400, which has been in-active since August 2015. I've rebased the commits and squashed the work into 1 commit here.

@IanLee1521
Copy link
Member

Thanks! This looks good to me.

Could you add a note in the CHANGELOG before I merge it? Mentioning that its a change which adds a new check (E305) ?

Also, you'll need to update the error codes table in the docs to include this new error code.

@IanLee1521 IanLee1521 self-assigned this Jun 8, 2016
@IanLee1521 IanLee1521 added this to the 2.1 milestone Jun 8, 2016
@@ -270,6 +272,9 @@ def blank_lines(logical_line, blank_lines, indent_level, line_number,
yield 0, "E301 expected 1 blank line, found 0"
elif blank_before != 2:
yield 0, "E302 expected 2 blank lines, found %d" % blank_before
elif (logical_line and not indent_level and blank_before != 2 and
previous_logical_toplevel.startswith(('def', 'class'))):
yield 0, "E305 expected 2 blank lines before, found %d" % blank_before
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about changing the message to be:

E305 expected 2 blank lines after class or function definition, found %d

@jayvdb
Copy link
Member

jayvdb commented Jun 8, 2016

It would be more reusable if it was a list of all previous logical lines, and the check function finds the relevant previous lines.

@sigmavirus24
Copy link
Member

@jayvdb why would it be more useful? I see no practical value in storing every completely non-indented logical line in a file.

@jayvdb
Copy link
Member

jayvdb commented Jun 12, 2016

@sigmavirus24 , who suggested storing non-indented logical lines? That would be silly, bordering on bizarre, as that isnt what pycodestyle currently builds and provides to checkers.

I believe it will be useful to have all of the logical lines available in order to solve other edge cases in pycodestyle (like this one) and checkers not part of the pycodestyle package. The rationale is the Zero one infinity rule.

There is a small mostly theoretical memory footprint cost of keeping the previous logical lines already created, linear with file size, but that cost is only a real serious cost if the previous logical lines are garbage collected during the processing of one file, which I havent noticed even on fairly large source files being processed on a normal system. For a bare-bones implementation, the remaining footprint cost would be the list of logical lines each stored as a tuple with an int and a pointer to a str. Additional logical line attributes (that are already currently computed) could be added to the tuple, and each logical line could be a dict instead of a tuple for a nicer interface for backwards compatibility if another attribute is added. If others generally agree that a list of previous logical lines would be good, I'd be happy to write up a new issue where the nuts and bolts can be discussed.

If we want to get this patch in and refactor later, the attribute could be called _previous_logical_toplevel here so that we don't need to think about a good name for it now, or support it later. ;-)

@sigmavirus24
Copy link
Member

who suggested storing non-indented logical lines? That would be silly, bordering on bizarre, as that isnt what pycodestyle currently builds and provides to checkers.

This patch currently only stores the previous non-indented logical line. This is not storing the previous logical line because the Checker already does that. Currently pycodestyle is already following the Zero one infinity rule in that regard. What we're talking about in this patch is storing the previous non-indented logical line. So in the context of this patch, I understood your suggestion to be about storing all non-indented logical lines.

in order to solve other edge cases in pycodestyle

This isn't an edge case of not having all previous logical lines. This is an edge case where we need to know what the last non-indented logical line was. You're confusing the discussion on this patch.

Until we have found these other edge cases, I'm against adding the complete list of logical lines in a file, merely in an effort to keep pycodestyle as performant as it can be. (Appending to an ever growing list would work against that very effort.)

which I havent noticed even on fairly large source files being processed on a normal system.

So you've tested this and can share the tests separately?

If we want to get this patch in and refactor later

I really think this discussion is completely orthogonal to this pull request and here's why:

  1. Logical lines are built from tokens and ignore indentation
  2. In this pull request, we only care about logical lines that we know weren't indented (which we know through other state on the Checker object)
  3. Storing all logical lines may be useful, but I don't have any concrete examples of why or when and why that list would be sufficient to replace the attribute added here and the associated state of the checker (in fact, to be truly useful, the list probably needs a lot more than the logical line strings themselves)
  4. This pull request is solving the problem in the simplest way possible while still being correct (which is in the spirit of the project to date)

So please, let's curb this side discussion that is not helping @ArcTanSusan or the other reviewers.

@jayvdb
Copy link
Member

jayvdb commented Jun 13, 2016

Ok, coming at this from a different direction in the hope we can find consensus that Checker attributes for special cases are bad design.

"Top level" as a concept is ambiguous. It may change. I've left a comment on #400 about this, so discussion about the PEP can continue there.

We shouldnt have public Checker state attributes that implement specific check logic. That creates backwards compatibility problems, as the PEP does evolve over time.

If this is going to add a Checker state attribute for the most recent last logical line without any indent, we should name it accordingly. say no_indent_logical_line

@sigmavirus24
Copy link
Member

So with the definition of "top-level" as meaning "global scope" we should be enforcing this in the following cases:

def foo(): pass


def bar(): pass


class Foo: pass


class Bar: pass


if __name__ == '__main__':
    foo()

We should also be enforcing this here:

if c_ext:
    class Foo(c_ext.Foo): pass


    class Bar(c_ext.Bar): pass


else:
    class Foo(object): pass


    class Bar(object): pass


if some_other_cond:
    def biz(): pass

Presently, the way pycodestyle works we track indentation and not scope. I think it's also clear that this pull request is an improvement (but not a complete fix) for #400.

I also support @jayvdb's suggested name of previous_unindented_logical_line, but I wonder if we should scrap this approach altogether for something that tracks whether we're in the global scope or not and uses that to generate the warning we expect to be produced.

The logic for determining if we're in a global scope (based on what tools already exist for pycodestyle) will be tricky so we'll have to test it thoroughly and comment it well to prevent it from breaking in the future. We will probably also want to use private state to update the attribute for determining our current scope. (To be clear, I don't think we need anything other than "yes we're defining a function/class in global state" and "no we're not".)

@jayvdb
Copy link
Member

jayvdb commented Jun 13, 2016

fwiw, I think we should proceed with a new private attribute (prefixed with _) so that we have fixed the extremely common case of if __name__ == '__main__': after classes and functions. It is fairly neatly isolated addition to Checker.

Then we can come up with a more complex solution for the indented definitions and all that entails (#366 would seem like a good place to track that), and back out the private attribute at the same time.

@@ -23,6 +23,7 @@ Changes:
* Added check E275 for whitespace on `from ... import ...` lines; #489 / #491
* Added W503 to the list of codes ignored by default ignore list; #498
* Removed use of project level `.pep8` configuration file; #364
* Added check E305 for two blank lines after toplevel method and toplevel class; #400
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should actually be up in the unreleased next version changes section.

@benesch
Copy link
Contributor

benesch commented Jun 26, 2016

Thanks, @ArcTanSusan! 🍻

@ghost ghost mentioned this pull request Sep 17, 2016
cmaloney added a commit to dcos/dcos-cli that referenced this pull request Nov 15, 2016
In particular PyCQA/pycodestyle#536 which landed as part
of 2.1.0 today (https://github.com/PyCQA/pycodestyle/blob/2.2.0/CHANGES.txt#L15)
finds one place where there should have been another newline.
cmaloney added a commit to dcos/dcos-cli that referenced this pull request Nov 15, 2016
In particular PyCQA/pycodestyle#536 which landed as part
of 2.1.0 today (https://github.com/PyCQA/pycodestyle/blob/2.2.0/CHANGES.txt#L15)
finds one place where there should have been another newline.
cmaloney added a commit to dcos/dcos-cli that referenced this pull request Nov 15, 2016
In particular PyCQA/pycodestyle#536 which landed as part
of 2.1.0 today (https://github.com/PyCQA/pycodestyle/blob/2.2.0/CHANGES.txt#L15)
finds one place where there should have been another newline.
pointlessone added a commit to pointlessone/pep8 that referenced this pull request Dec 29, 2016
2.2.0 (2016-11-14)
------------------

Bugs:

* Fixed E305 regression caused by PyCQA#400;
  PyCQA#593

2.1.0 (2016-11-04)
------------------

Changes:

* Report E302 for blank lines before an "async def";
  PyCQA#556
* Update our list of tested and supported Python versions which are 2.6,
  2.7, 3.2, 3.3, 3.4 and 3.5 as well as the nightly Python build and
  PyPy.
* Report E742 and E743 for functions and classes badly named 'l', 'O',
  or 'I'.
* Report E741 on 'global' and 'nonlocal' statements, as well as
  prohibited single-letter variables.
* Deprecated use of `[pep8]` section name in favor of `[pycodestyle]`;
  PyCQA#591

Bugs:

* Fix opt_type AssertionError when using Flake8 2.6.2 and pycodestyle;
  PyCQA#561
* Require two blank lines after toplevel def, class;
  PyCQA#536
* Remove accidentally quadratic computation based on the number of
  colons. This will make pycodestyle faster in some cases;
  PyCQA#314

2.0.0 (2016-05-31)
------------------

Changes:

* Added tox test support for Python 3.5 and pypy3
* Added check E275 for whitespace on `from ... import ...` lines;
  PyCQA#489 / PyCQA#491
* Added W503 to the list of codes ignored by default ignore list;
  PyCQA#498
* Removed use of project level `.pep8` configuration file;
  PyCQA#364

Bugs:

* Fixed bug with treating `~` operator as binary; PyCQA#383
  / PyCQA#384
* Identify binary operators as unary; PyCQA#484 /
  PyCQA#485

1.7.0 (2016-01-12)
------------------

Changes:

* Reverted the fix in PyCQA#368, "options passed on command
  line are only ones accepted" feature. This has many unintended
  consequences in pep8 and flake8 and needs to be reworked when I have
  more time.
* Added support for Python 3.5. (Issue PyCQA#420 &
  PyCQA#459)
* Added support for multi-line config_file option parsing. (Issue
  PyCQA#429)
* Improved parameter parsing. (Issues PyCQA#420 &
  PyCQA#456)

Bugs:

* Fixed BytesWarning on Python 3. (Issue PyCQA#459)
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 this pull request may close these issues.

Require two blank lines *after* a def/class too
5 participants