From ee9c2874a9a5cb886060635c9b1aeec16bbc9e55 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 12 Sep 2020 11:56:37 -0700 Subject: [PATCH] fix skipping of physical checks when file does not end in newline --- .pre-commit-config.yaml | 2 ++ src/flake8/checker.py | 17 +++++++++++++---- tests/integration/test_main.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b03ff3c..e1b34f3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,6 +2,8 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.3.0 hooks: + - id: debug-statements + exclude: ^tests/fixtures/example-code/invalid-syntax.py$ - id: end-of-file-fixer - id: trailing-whitespace exclude: ^tests/fixtures/diffs/ diff --git a/src/flake8/checker.py b/src/flake8/checker.py index ea8b5d3..be1c7f7 100644 --- a/src/flake8/checker.py +++ b/src/flake8/checker.py @@ -564,9 +564,10 @@ class FileChecker(object): parens = 0 statistics = self.statistics file_processor = self.processor + prev_physical = "" for token in file_processor.generate_tokens(): statistics["tokens"] += 1 - self.check_physical_eol(token) + self.check_physical_eol(token, prev_physical) token_type, text = token[0:2] processor.log_token(LOG, token) if token_type == tokenize.OP: @@ -574,6 +575,7 @@ class FileChecker(object): elif parens == 0: if processor.token_is_newline(token): self.handle_newline(token_type) + prev_physical = token[4] if file_processor.tokens: # If any tokens are left over, process them @@ -609,11 +611,18 @@ class FileChecker(object): else: self.run_logical_checks() - def check_physical_eol(self, token): + def check_physical_eol(self, token, prev_physical): + # type: (processor._Token, str) -> None """Run physical checks if and only if it is at the end of the line.""" + # a newline token ends a single physical line. if processor.is_eol_token(token): - # Obviously, a newline token ends a single physical line. - self.run_physical_checks(token[4]) + # if the file does not end with a newline, the NEWLINE + # token is inserted by the parser, but it does not contain + # the previous physical line in `token[4]` + if token[4] == "": + self.run_physical_checks(prev_physical) + else: + self.run_physical_checks(token[4]) elif processor.is_multiline_string(token): # Less obviously, a string that contains newlines is a # multiline string, either triple-quoted or with internal diff --git a/tests/integration/test_main.py b/tests/integration/test_main.py index ce8ad13..69cb12b 100644 --- a/tests/integration/test_main.py +++ b/tests/integration/test_main.py @@ -212,6 +212,37 @@ x = """ assert out == err == '' +def test_physical_line_file_not_ending_in_newline(tmpdir, capsys): + """See https://github.com/PyCQA/pycodestyle/issues/960.""" + t_py_src = 'def f():\n\tpass' + + with tmpdir.as_cwd(): + tmpdir.join('t.py').write(t_py_src) + _call_main(['t.py'], retv=1) + + out, err = capsys.readouterr() + assert out == '''\ +t.py:2:1: W191 indentation contains tabs +t.py:2:6: W292 no newline at end of file +''' + + +@pytest.mark.xfail(strict=True) # currently awaiting fix in pycodestyle +def test_physical_line_file_not_ending_in_newline_trailing_ws(tmpdir, capsys): + """See https://github.com/PyCQA/pycodestyle/issues/960.""" + t_py_src = 'x = 1 ' + + with tmpdir.as_cwd(): + tmpdir.join('t.py').write(t_py_src) + _call_main(['t.py'], retv=1) + + out, err = capsys.readouterr() + assert out == '''\ +t.py:1:6: W291 trailing whitespace +t.py:1:10: W292 no newline at end of file +''' + + def test_obtaining_args_from_sys_argv_when_not_explicity_provided(capsys): """Test that arguments are obtained from 'sys.argv'.""" with mock.patch('sys.argv', ['flake8', '--help']):