Skip to content

Commit 68335d1

Browse files
committed
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
1 parent db80d07 commit 68335d1

File tree

9 files changed

+40
-30
lines changed

9 files changed

+40
-30
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ UNRELEASED / 3.0.0
77
Changes:
88

99
* Remove support for EOL Python 2.6 and 3.3. PR #720.
10+
* Add E117 error for over-indented code blocks.
1011

1112
2.4.0 (2018-04-10)
1213
------------------

docs/intro.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ This is the current list of error and warning codes:
232232
+------------+----------------------------------------------------------------------+
233233
| E116 | unexpected indentation (comment) |
234234
+------------+----------------------------------------------------------------------+
235+
| E117 | over-indented |
235236
+------------+----------------------------------------------------------------------+
236237
| E121 (\*^) | continuation line under-indented for hanging indent |
237238
+------------+----------------------------------------------------------------------+

pycodestyle.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def tabs_or_spaces(physical_line, indent_char):
207207
tabs and spaces. When using -tt these warnings become errors.
208208
These options are highly recommended!
209209
210-
Okay: if a == 0:\n a = 1\n b = 1
210+
Okay: if a == 0:\n a = 1\n b = 1
211211
E101: if a == 0:\n a = 1\n\tb = 1
212212
"""
213213
indent = INDENT_REGEX.match(physical_line).group(1)
@@ -534,6 +534,10 @@ def indentation(logical_line, previous_logical, indent_char,
534534
elif not indent_expect and indent_level > previous_indent_level:
535535
yield 0, tmpl % (3 + c, "unexpected indentation")
536536

537+
expected_indent_level = previous_indent_level + 4
538+
if indent_expect and indent_level > expected_indent_level:
539+
yield 0, tmpl % (7, 'over-indented')
540+
537541

538542
@register_check
539543
def continued_indentation(logical_line, tokens, indent_level, hang_closing,

testsuite/E10.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
for b in 'xyz':
44
print a # indented with 8 spaces
55
print b # indented with 1 tab
6-
#: E101 E122 W191 W191
6+
#: E101 E117 E122 W191 W191
77
if True:
88
pass
99

@@ -27,7 +27,7 @@ def tearDown(self):
2727
pass
2828

2929
#
30-
#: E101 W191 W191
30+
#: E101 E117 W191 W191
3131
if True:
3232
foo(1,
3333
2)

testsuite/E11.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#: E111
22
if x > 2:
33
print x
4-
#: E111
4+
#: E111 E117
55
if True:
66
print
77
#: E112
@@ -34,3 +34,6 @@ def start(self):
3434
# finally:
3535
# sys.exit()
3636
self.master.start()
37+
#: E117
38+
def start():
39+
print

testsuite/E90.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
}
33
#: E901
44
= [x
5-
#: E901 E101 W191
5+
#: E901 E101 E117 W191
66
while True:
77
try:
88
pass

testsuite/W19.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#: W191
1+
#: E117 W191
22
if False:
33
print # indented with 1 tab
44
#:
@@ -7,56 +7,56 @@
77
#: W191
88
y = x == 2 \
99
or x == 3
10-
#: E101 W191 W504
10+
#: E101 E117 W191 W504
1111
if (
1212
x == (
1313
3
1414
) or
1515
y == 4):
1616
pass
17-
#: E101 W191
17+
#: E101 E117 W191
1818
if x == 2 \
1919
or y > 1 \
2020
or x == 3:
2121
pass
22-
#: E101 W191
22+
#: E101 E117 W191
2323
if x == 2 \
2424
or y > 1 \
2525
or x == 3:
2626
pass
2727
#:
2828

29-
#: E101 W191 W504
29+
#: E101 E117 W191 W504
3030
if (foo == bar and
3131
baz == frop):
3232
pass
33-
#: E101 W191 W504
33+
#: E101 E117 W191 W504
3434
if (
3535
foo == bar and
3636
baz == frop
3737
):
3838
pass
3939
#:
4040

41-
#: E101 E101 W191 W191
41+
#: E101 E101 E117 W191 W191
4242
if start[1] > end_col and not (
4343
over_indent == 4 and indent_next):
4444
return(0, "E121 continuation line over-"
4545
"indented for visual indent")
4646
#:
4747

48-
#: E101 W191
48+
#: E101 E117 W191
4949

5050

5151
def long_function_name(
5252
var_one, var_two, var_three,
5353
var_four):
5454
print(var_one)
55-
#: E101 W191 W504
55+
#: E101 E117 W191 W504
5656
if ((row < 0 or self.moduleCount <= row or
5757
col < 0 or self.moduleCount <= col)):
5858
raise Exception("%s,%s - %s" % (row, col, self.moduleCount))
59-
#: E101 E101 E101 E101 W191 W191 W191 W191 W191 W191
59+
#: E101 E101 E101 E101 E117 W191 W191 W191 W191 W191 W191
6060
if bar:
6161
return(
6262
start, 'E121 lines starting with a '
@@ -65,35 +65,35 @@ def long_function_name(
6565
"bracket's line"
6666
)
6767
#
68-
#: E101 W191 W504
68+
#: E101 E117 W191 W504
6969
# you want vertical alignment, so use a parens
7070
if ((foo.bar("baz") and
7171
foo.bar("frop")
7272
)):
7373
print "yes"
74-
#: E101 W191 W504
74+
#: E101 E117 W191 W504
7575
# also ok, but starting to look like LISP
7676
if ((foo.bar("baz") and
7777
foo.bar("frop"))):
7878
print "yes"
79-
#: E101 W191 W504
79+
#: E101 E117 W191 W504
8080
if (a == 2 or
8181
b == "abc def ghi"
8282
"jkl mno"):
8383
return True
84-
#: E101 W191 W504
84+
#: E101 E117 W191 W504
8585
if (a == 2 or
8686
b == """abc def ghi
8787
jkl mno"""):
8888
return True
89-
#: W191:2:1 W191:3:1 E101:3:2
89+
#: W191:2:1 W191:3:1 E101:3:2 E117
9090
if length > options.max_line_length:
9191
return options.max_line_length, \
9292
"E501 line too long (%d characters)" % length
9393

9494

9595
#
96-
#: E101 W191 W191 W504
96+
#: E101 E117 W191 W191 W504
9797
if os.path.exists(os.path.join(path, PEP8_BIN)):
9898
cmd = ([os.path.join(path, PEP8_BIN)] +
9999
self._pep8_options(targetfile))
@@ -119,19 +119,19 @@ def long_function_name(
119119
even though the noqa comment is not immediately after the string
120120
''' + foo # noqa
121121
#
122-
#: E101 W191
122+
#: E101 E117 W191
123123
if foo is None and bar is "frop" and \
124124
blah == 'yeah':
125125
blah = 'yeahnah'
126126

127127

128128
#
129-
#: W191 W191 W191
129+
#: E117 W191 W191 W191
130130
if True:
131131
foo(
132132
1,
133133
2)
134-
#: W191 W191 W191 W191 W191
134+
#: E117 W191 W191 W191 W191 W191
135135
def test_keys(self):
136136
"""areas.json - All regions are accounted for."""
137137
expected = set([

testsuite/test_api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,15 @@ def test_styleguide(self):
125125
report = pycodestyle.StyleGuide().check_files([E11])
126126
stdout = sys.stdout.getvalue().splitlines()
127127
self.assertEqual(len(stdout), report.total_errors)
128-
self.assertEqual(report.total_errors, 17)
128+
self.assertEqual(report.total_errors, 20)
129129
self.assertFalse(sys.stderr)
130130
self.reset()
131131

132132
# Passing the paths in the constructor gives same result
133133
report = pycodestyle.StyleGuide(paths=[E11]).check_files()
134134
stdout = sys.stdout.getvalue().splitlines()
135135
self.assertEqual(len(stdout), report.total_errors)
136-
self.assertEqual(report.total_errors, 17)
136+
self.assertEqual(report.total_errors, 20)
137137
self.assertFalse(sys.stderr)
138138
self.reset()
139139

testsuite/test_shell.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ def test_check_simple(self):
7777
stdout = stdout.splitlines()
7878
self.assertEqual(errcode, 1)
7979
self.assertFalse(stderr)
80-
self.assertEqual(len(stdout), 17)
81-
for line, num, col in zip(stdout, (3, 6, 9, 12), (3, 6, 1, 5)):
80+
self.assertEqual(len(stdout), 20)
81+
for line, num, col in zip(stdout, (3, 6, 6, 9, 12), (3, 6, 6, 1, 5)):
8282
path, x, y, msg = line.split(':')
8383
self.assertTrue(path.endswith(E11))
8484
self.assertEqual(x, str(num))
@@ -170,10 +170,11 @@ def test_check_diff(self):
170170
"+ print"]
171171
self.stdin = '\n'.join(diff_lines)
172172
stdout, stderr, errcode = self.pycodestyle('--diff')
173-
(stdout,) = stdout.splitlines()
173+
stdout = stdout.splitlines()
174174
self.assertEqual(errcode, 1)
175175
self.assertFalse(stderr)
176-
self.assertTrue('testsuite/E11.py:6:6: E111 ' in stdout)
176+
self.assertTrue('testsuite/E11.py:6:6: E111 ' in stdout[0])
177+
self.assertTrue('testsuite/E11.py:6:6: E117 ' in stdout[1])
177178

178179
# missing '--diff'
179180
self.stdin = '\n'.join(diff_lines)

0 commit comments

Comments
 (0)