From 68335d1a6d65de7ed09af580bdb909c5f6c139c2 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Sun, 5 Nov 2017 15:46:52 -0800 Subject: Add check for over-indented blocks In a project with all lines indented 4 spaces, I noticed pycodestyle was not catching code that was accidentally indented two levels (8 spaces). The over indentation was unintended and can be caught during static analysis. Fixes #430 --- CHANGES.txt | 1 + docs/intro.rst | 1 + pycodestyle.py | 6 +++++- testsuite/E10.py | 4 ++-- testsuite/E11.py | 5 ++++- testsuite/E90.py | 2 +- testsuite/W19.py | 38 +++++++++++++++++++------------------- testsuite/test_api.py | 4 ++-- testsuite/test_shell.py | 9 +++++---- 9 files changed, 40 insertions(+), 30 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 19e2c34..48c985c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,6 +7,7 @@ UNRELEASED / 3.0.0 Changes: * Remove support for EOL Python 2.6 and 3.3. PR #720. +* Add E117 error for over-indented code blocks. 2.4.0 (2018-04-10) ------------------ diff --git a/docs/intro.rst b/docs/intro.rst index a374a87..991db47 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -232,6 +232,7 @@ This is the current list of error and warning codes: +------------+----------------------------------------------------------------------+ | E116 | unexpected indentation (comment) | +------------+----------------------------------------------------------------------+ +| E117 | over-indented | +------------+----------------------------------------------------------------------+ | E121 (\*^) | continuation line under-indented for hanging indent | +------------+----------------------------------------------------------------------+ diff --git a/pycodestyle.py b/pycodestyle.py index 094e24f..766e6bd 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -207,7 +207,7 @@ def tabs_or_spaces(physical_line, indent_char): tabs and spaces. When using -tt these warnings become errors. These options are highly recommended! - Okay: if a == 0:\n a = 1\n b = 1 + Okay: if a == 0:\n a = 1\n b = 1 E101: if a == 0:\n a = 1\n\tb = 1 """ indent = INDENT_REGEX.match(physical_line).group(1) @@ -534,6 +534,10 @@ def indentation(logical_line, previous_logical, indent_char, elif not indent_expect and indent_level > previous_indent_level: yield 0, tmpl % (3 + c, "unexpected indentation") + expected_indent_level = previous_indent_level + 4 + if indent_expect and indent_level > expected_indent_level: + yield 0, tmpl % (7, 'over-indented') + @register_check def continued_indentation(logical_line, tokens, indent_level, hang_closing, diff --git a/testsuite/E10.py b/testsuite/E10.py index 7b42594..08b03a0 100644 --- a/testsuite/E10.py +++ b/testsuite/E10.py @@ -3,7 +3,7 @@ for a in 'abc': for b in 'xyz': print a # indented with 8 spaces print b # indented with 1 tab -#: E101 E122 W191 W191 +#: E101 E117 E122 W191 W191 if True: pass @@ -27,7 +27,7 @@ class TestP4Poller(unittest.TestCase): pass # -#: E101 W191 W191 +#: E101 E117 W191 W191 if True: foo(1, 2) diff --git a/testsuite/E11.py b/testsuite/E11.py index 4ed1096..c7b11d1 100644 --- a/testsuite/E11.py +++ b/testsuite/E11.py @@ -1,7 +1,7 @@ #: E111 if x > 2: print x -#: E111 +#: E111 E117 if True: print #: E112 @@ -34,3 +34,6 @@ def start(self): # finally: # sys.exit() self.master.start() +#: E117 +def start(): + print diff --git a/testsuite/E90.py b/testsuite/E90.py index 2c18e9a..3fd2b32 100644 --- a/testsuite/E90.py +++ b/testsuite/E90.py @@ -2,7 +2,7 @@ } #: E901 = [x -#: E901 E101 W191 +#: E901 E101 E117 W191 while True: try: pass diff --git a/testsuite/W19.py b/testsuite/W19.py index ed69e2b..d77bc9e 100644 --- a/testsuite/W19.py +++ b/testsuite/W19.py @@ -1,4 +1,4 @@ -#: W191 +#: E117 W191 if False: print # indented with 1 tab #: @@ -7,30 +7,30 @@ if False: #: W191 y = x == 2 \ or x == 3 -#: E101 W191 W504 +#: E101 E117 W191 W504 if ( x == ( 3 ) or y == 4): pass -#: E101 W191 +#: E101 E117 W191 if x == 2 \ or y > 1 \ or x == 3: pass -#: E101 W191 +#: E101 E117 W191 if x == 2 \ or y > 1 \ or x == 3: pass #: -#: E101 W191 W504 +#: E101 E117 W191 W504 if (foo == bar and baz == frop): pass -#: E101 W191 W504 +#: E101 E117 W191 W504 if ( foo == bar and baz == frop @@ -38,25 +38,25 @@ if ( pass #: -#: E101 E101 W191 W191 +#: E101 E101 E117 W191 W191 if start[1] > end_col and not ( over_indent == 4 and indent_next): return(0, "E121 continuation line over-" "indented for visual indent") #: -#: E101 W191 +#: E101 E117 W191 def long_function_name( var_one, var_two, var_three, var_four): print(var_one) -#: E101 W191 W504 +#: E101 E117 W191 W504 if ((row < 0 or self.moduleCount <= row or col < 0 or self.moduleCount <= col)): raise Exception("%s,%s - %s" % (row, col, self.moduleCount)) -#: E101 E101 E101 E101 W191 W191 W191 W191 W191 W191 +#: E101 E101 E101 E101 E117 W191 W191 W191 W191 W191 W191 if bar: return( start, 'E121 lines starting with a ' @@ -65,35 +65,35 @@ if bar: "bracket's line" ) # -#: E101 W191 W504 +#: E101 E117 W191 W504 # you want vertical alignment, so use a parens if ((foo.bar("baz") and foo.bar("frop") )): print "yes" -#: E101 W191 W504 +#: E101 E117 W191 W504 # also ok, but starting to look like LISP if ((foo.bar("baz") and foo.bar("frop"))): print "yes" -#: E101 W191 W504 +#: E101 E117 W191 W504 if (a == 2 or b == "abc def ghi" "jkl mno"): return True -#: E101 W191 W504 +#: E101 E117 W191 W504 if (a == 2 or b == """abc def ghi jkl mno"""): return True -#: W191:2:1 W191:3:1 E101:3:2 +#: W191:2:1 W191:3:1 E101:3:2 E117 if length > options.max_line_length: return options.max_line_length, \ "E501 line too long (%d characters)" % length # -#: E101 W191 W191 W504 +#: E101 E117 W191 W191 W504 if os.path.exists(os.path.join(path, PEP8_BIN)): cmd = ([os.path.join(path, PEP8_BIN)] + self._pep8_options(targetfile)) @@ -119,19 +119,19 @@ or long long long long long long long long long long long long long long long lo even though the noqa comment is not immediately after the string ''' + foo # noqa # -#: E101 W191 +#: E101 E117 W191 if foo is None and bar is "frop" and \ blah == 'yeah': blah = 'yeahnah' # -#: W191 W191 W191 +#: E117 W191 W191 W191 if True: foo( 1, 2) -#: W191 W191 W191 W191 W191 +#: E117 W191 W191 W191 W191 W191 def test_keys(self): """areas.json - All regions are accounted for.""" expected = set([ diff --git a/testsuite/test_api.py b/testsuite/test_api.py index 6342764..ddc28b8 100644 --- a/testsuite/test_api.py +++ b/testsuite/test_api.py @@ -125,7 +125,7 @@ class APITestCase(unittest.TestCase): report = pycodestyle.StyleGuide().check_files([E11]) stdout = sys.stdout.getvalue().splitlines() self.assertEqual(len(stdout), report.total_errors) - self.assertEqual(report.total_errors, 17) + self.assertEqual(report.total_errors, 20) self.assertFalse(sys.stderr) self.reset() @@ -133,7 +133,7 @@ class APITestCase(unittest.TestCase): report = pycodestyle.StyleGuide(paths=[E11]).check_files() stdout = sys.stdout.getvalue().splitlines() self.assertEqual(len(stdout), report.total_errors) - self.assertEqual(report.total_errors, 17) + self.assertEqual(report.total_errors, 20) self.assertFalse(sys.stderr) self.reset() diff --git a/testsuite/test_shell.py b/testsuite/test_shell.py index 7ada1a4..e4d0871 100644 --- a/testsuite/test_shell.py +++ b/testsuite/test_shell.py @@ -77,8 +77,8 @@ class ShellTestCase(unittest.TestCase): stdout = stdout.splitlines() self.assertEqual(errcode, 1) self.assertFalse(stderr) - self.assertEqual(len(stdout), 17) - for line, num, col in zip(stdout, (3, 6, 9, 12), (3, 6, 1, 5)): + self.assertEqual(len(stdout), 20) + for line, num, col in zip(stdout, (3, 6, 6, 9, 12), (3, 6, 6, 1, 5)): path, x, y, msg = line.split(':') self.assertTrue(path.endswith(E11)) self.assertEqual(x, str(num)) @@ -170,10 +170,11 @@ class ShellTestCase(unittest.TestCase): "+ print"] self.stdin = '\n'.join(diff_lines) stdout, stderr, errcode = self.pycodestyle('--diff') - (stdout,) = stdout.splitlines() + stdout = stdout.splitlines() self.assertEqual(errcode, 1) self.assertFalse(stderr) - self.assertTrue('testsuite/E11.py:6:6: E111 ' in stdout) + self.assertTrue('testsuite/E11.py:6:6: E111 ' in stdout[0]) + self.assertTrue('testsuite/E11.py:6:6: E117 ' in stdout[1]) # missing '--diff' self.stdin = '\n'.join(diff_lines) -- cgit v1.2.1