Skip to content

Commit 9dd78b9

Browse files
authored
Merge pull request #970 from bukzor/multi-indent
Support for space indents with size other than 4
2 parents 07b113b + c060b1c commit 9dd78b9

File tree

3 files changed

+78
-12
lines changed

3 files changed

+78
-12
lines changed

docs/intro.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ Quick help is available on the command line::
159159
--max-line-length=n set maximum allowed line length (default: 79)
160160
--max-doc-length=n set maximum allowed doc line length and perform these
161161
checks (unchecked if not set)
162+
--indent-size=n set how many spaces make up an indent (default: 4)
162163
--hang-closing hang closing bracket instead of matching indentation of
163164
opening bracket's line
164165
--format=format set the error format [default|pylint|<custom>]

pycodestyle.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def lru_cache(maxsize=128): # noqa as it's a fake implementation.
104104
'method': 1,
105105
}
106106
MAX_DOC_LENGTH = 72
107+
INDENT_SIZE = 4
107108
REPORT_FORMAT = {
108109
'default': '%(path)s:%(row)d:%(col)d: %(code)s %(text)s',
109110
'pylint': '%(path)s:%(row)d: [%(code)s] %(text)s',
@@ -543,8 +544,9 @@ def missing_whitespace(logical_line):
543544

544545
@register_check
545546
def indentation(logical_line, previous_logical, indent_char,
546-
indent_level, previous_indent_level):
547-
r"""Use 4 spaces per indentation level.
547+
indent_level, previous_indent_level,
548+
indent_size, indent_size_str):
549+
r"""Use indent_size (PEP8 says 4) spaces per indentation level.
548550
549551
For really old code that you don't want to mess up, you can continue
550552
to use 8-space tabs.
@@ -564,8 +566,11 @@ def indentation(logical_line, previous_logical, indent_char,
564566
"""
565567
c = 0 if logical_line else 3
566568
tmpl = "E11%d %s" if logical_line else "E11%d %s (comment)"
567-
if indent_level % 4:
568-
yield 0, tmpl % (1 + c, "indentation is not a multiple of four")
569+
if indent_level % indent_size:
570+
yield 0, tmpl % (
571+
1 + c,
572+
"indentation is not a multiple of " + indent_size_str,
573+
)
569574
indent_expect = previous_logical.endswith(':')
570575
if indent_expect and indent_level <= previous_indent_level:
571576
yield 0, tmpl % (2 + c, "expected an indented block")
@@ -581,7 +586,8 @@ def indentation(logical_line, previous_logical, indent_char,
581586

582587
@register_check
583588
def continued_indentation(logical_line, tokens, indent_level, hang_closing,
584-
indent_char, noqa, verbose):
589+
indent_char, indent_size, indent_size_str, noqa,
590+
verbose):
585591
r"""Continuation lines indentation.
586592
587593
Continuation lines should align wrapped elements either vertically
@@ -620,7 +626,8 @@ def continued_indentation(logical_line, tokens, indent_level, hang_closing,
620626
indent_next = logical_line.endswith(':')
621627

622628
row = depth = 0
623-
valid_hangs = (4,) if indent_char != '\t' else (4, 8)
629+
valid_hangs = (indent_size,) if indent_char != '\t' \
630+
else (indent_size, indent_size * 2)
624631
# remember how many brackets were opened on each line
625632
parens = [0] * nrows
626633
# relative indents of physical lines
@@ -685,7 +692,8 @@ def continued_indentation(logical_line, tokens, indent_level, hang_closing,
685692
# visual indent is broken
686693
yield (start, "E128 continuation line "
687694
"under-indented for visual indent")
688-
elif hanging_indent or (indent_next and rel_indent[row] == 8):
695+
elif hanging_indent or (indent_next and
696+
rel_indent[row] == 2 * indent_size):
689697
# hanging indent is verified
690698
if close_bracket and not hang_closing:
691699
yield (start, "E123 closing bracket does not match "
@@ -708,7 +716,7 @@ def continued_indentation(logical_line, tokens, indent_level, hang_closing,
708716
error = "E131", "unaligned for hanging indent"
709717
else:
710718
hangs[depth] = hang
711-
if hang > 4:
719+
if hang > indent_size:
712720
error = "E126", "over-indented for hanging indent"
713721
else:
714722
error = "E121", "under-indented for hanging indent"
@@ -775,8 +783,8 @@ def continued_indentation(logical_line, tokens, indent_level, hang_closing,
775783
if last_token_multiline:
776784
rel_indent[end[0] - first_row] = rel_indent[row]
777785

778-
if indent_next and expand_indent(line) == indent_level + 4:
779-
pos = (start[0], indent[0] + 4)
786+
if indent_next and expand_indent(line) == indent_level + indent_size:
787+
pos = (start[0], indent[0] + indent_size)
780788
if visual_indent:
781789
code = "E129 visually indented line"
782790
else:
@@ -1960,8 +1968,12 @@ def __init__(self, filename=None, lines=None,
19601968
self._ast_checks = options.ast_checks
19611969
self.max_line_length = options.max_line_length
19621970
self.max_doc_length = options.max_doc_length
1971+
self.indent_size = options.indent_size
19631972
self.multiline = False # in a multiline string?
19641973
self.hang_closing = options.hang_closing
1974+
self.indent_size = options.indent_size
1975+
self.indent_size_str = ({2: 'two', 4: 'four', 8: 'eight'}
1976+
.get(self.indent_size, str(self.indent_size)))
19651977
self.verbose = options.verbose
19661978
self.filename = filename
19671979
# Dictionary where a checker can store its custom state.
@@ -2528,8 +2540,8 @@ def get_parser(prog='pycodestyle', version=__version__):
25282540
usage="%prog [options] input ...")
25292541
parser.config_options = [
25302542
'exclude', 'filename', 'select', 'ignore', 'max-line-length',
2531-
'max-doc-length', 'hang-closing', 'count', 'format', 'quiet',
2532-
'show-pep8', 'show-source', 'statistics', 'verbose']
2543+
'max-doc-length', 'indent-size', 'hang-closing', 'count', 'format',
2544+
'quiet', 'show-pep8', 'show-source', 'statistics', 'verbose']
25332545
parser.add_option('-v', '--verbose', default=0, action='count',
25342546
help="print status messages, or debug with -vv")
25352547
parser.add_option('-q', '--quiet', default=0, action='count',
@@ -2569,6 +2581,10 @@ def get_parser(prog='pycodestyle', version=__version__):
25692581
default=None,
25702582
help="set maximum allowed doc line length and perform "
25712583
"these checks (unchecked if not set)")
2584+
parser.add_option('--indent-size', type='int', metavar='n',
2585+
default=INDENT_SIZE,
2586+
help="set how many spaces make up an indent "
2587+
"(default: %default)")
25722588
parser.add_option('--hang-closing', action='store_true',
25732589
help="hang closing bracket instead of matching "
25742590
"indentation of opening bracket's line")

testsuite/test_api.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,3 +391,52 @@ def test_styleguide_continuation_line_outdented(self):
391391

392392
# TODO: runner
393393
# TODO: input_file
394+
395+
def test_styleguides_other_indent_size(self):
396+
pycodestyle.register_check(DummyChecker, ['Z701'])
397+
lines = [
398+
'def foo():\n',
399+
' pass\n',
400+
'\n',
401+
'\n',
402+
'def foo_correct():\n',
403+
' pass\n',
404+
'\n',
405+
'\n',
406+
'def bar():\n',
407+
' [1, 2, 3,\n',
408+
' 4, 5, 6,\n',
409+
' ]\n',
410+
'\n',
411+
'\n',
412+
'if (1 in [1, 2, 3]\n',
413+
' and bool(0) is False\n',
414+
' and bool(1) is True):\n',
415+
' pass\n'
416+
]
417+
418+
pep8style = pycodestyle.StyleGuide()
419+
pep8style.options.indent_size = 3
420+
count_errors = pep8style.input_file('stdin', lines=lines)
421+
stdout = sys.stdout.getvalue()
422+
self.assertEqual(count_errors, 4)
423+
expected = (
424+
'stdin:2:5: '
425+
'E111 indentation is not a multiple of 3'
426+
)
427+
self.assertTrue(expected in stdout)
428+
expected = (
429+
'stdin:11:6: '
430+
'E127 continuation line over-indented for visual indent'
431+
)
432+
self.assertTrue(expected in stdout)
433+
expected = (
434+
'stdin:12:6: '
435+
'E124 closing bracket does not match visual indentation'
436+
)
437+
self.assertTrue(expected in stdout)
438+
expected = (
439+
'stdin:17:6: '
440+
'E127 continuation line over-indented for visual indent'
441+
)
442+
self.assertTrue(expected in stdout)

0 commit comments

Comments
 (0)