Skip to content

Commit 5d31e7e

Browse files
adiroibansigmavirus24
authored andcommitted
Add variables so blank lines may be configures
This adds some module level configuration points for users to define how many blank lines they want in their code. It paves the way for someone to develop a flake8 plugin to configure this in pycodestyle. Fixes #732
1 parent 9f225ac commit 5d31e7e

File tree

5 files changed

+653
-15
lines changed

5 files changed

+653
-15
lines changed

CONTRIBUTING.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,44 @@ At this point you can create a pull request back to the official pycodestyles
6666
repository for review! For more information on how to make a pull request,
6767
GitHub has an excellent `guide`_.
6868

69+
The current tests are written in 2 styles:
70+
71+
* standard xUnit based only on stdlib unittest
72+
(can be executed with nose)
73+
* functional test using a custom framework and executed by the
74+
pycodestyle itself when installed in dev mode.
75+
76+
77+
Running unittest
78+
~~~~~~~~~~~~~~~~
79+
80+
While the tests are writted using stdlib `unittest` module, the existing
81+
test include unit, integration and functional tests.
82+
83+
There are a couple of ways to run the tests::
84+
85+
$ python setup.py test
86+
$ # Use nose to run specific test
87+
$ nosetests \
88+
> testsuite.test_blank_lines:TestBlankLinesDefault.test_initial_no_blank
89+
$ # Use nose to run a subset and check coverage, and check the resulting
90+
$ $ cover/pycodestyle_py.html in your browser
91+
$ nosetests --with-coverage --cover-html -s testsuite.test_blank_lines
92+
93+
94+
Running functional
95+
~~~~~~~~~~~~~~~~~~
96+
97+
When installed in dev mode, pycodestyle will have the `--testsuite`
98+
option which can be used to run the tests::
99+
100+
$ pip install -e .
101+
$ # Run all tests.
102+
$ pycodestyle --testsuite testsuite
103+
$ # Run a subset of the tests.
104+
$ pycodestyle --testsuite testsuite/E30.py
105+
106+
69107
.. _virtualenv: http://docs.python-guide.org/en/latest/dev/virtualenvs/
70108
.. _guide: https://guides.github.com/activities/forking/
71109
.. _tox: https://tox.readthedocs.io/en/latest/

pycodestyle.py

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@ def lru_cache(maxsize=128): # noqa as it's a fake implementation.
9595
PROJECT_CONFIG = ('setup.cfg', 'tox.ini')
9696
TESTSUITE_PATH = os.path.join(os.path.dirname(__file__), 'testsuite')
9797
MAX_LINE_LENGTH = 79
98+
# Number of blank lines between various code parts.
99+
BLANK_LINES_CONFIG = {
100+
# Top level class and function.
101+
'top_level': 2,
102+
# Methods and nested class and function.
103+
'method': 1,
104+
}
98105
REPORT_FORMAT = {
99106
'default': '%(path)s:%(row)d:%(col)d: %(code)s %(text)s',
100107
'pylint': '%(path)s:%(row)d: [%(code)s] %(text)s',
@@ -332,37 +339,50 @@ def blank_lines(logical_line, blank_lines, indent_level, line_number,
332339
E305: def a():\n pass\na()
333340
E306: def a():\n def b():\n pass\n def c():\n pass
334341
"""
335-
if line_number < 3 and not previous_logical:
342+
top_level_lines = BLANK_LINES_CONFIG['top_level']
343+
method_lines = BLANK_LINES_CONFIG['method']
344+
345+
if line_number < top_level_lines + 1 and not previous_logical:
336346
return # Don't expect blank lines before the first line
337347
if previous_logical.startswith('@'):
338348
if blank_lines:
339349
yield 0, "E304 blank lines found after function decorator"
340-
elif blank_lines > 2 or (indent_level and blank_lines == 2):
350+
elif (blank_lines > top_level_lines or
351+
(indent_level and blank_lines == method_lines + 1)
352+
):
341353
yield 0, "E303 too many blank lines (%d)" % blank_lines
342354
elif STARTSWITH_TOP_LEVEL_REGEX.match(logical_line):
343355
if indent_level:
344-
if not (blank_before or previous_indent_level < indent_level or
345-
DOCSTRING_REGEX.match(previous_logical)):
356+
if not (blank_before == method_lines or
357+
previous_indent_level < indent_level or
358+
DOCSTRING_REGEX.match(previous_logical)
359+
):
346360
ancestor_level = indent_level
347361
nested = False
348362
# Search backwards for a def ancestor or tree root (top level).
349-
for line in lines[line_number - 2::-1]:
363+
for line in lines[line_number - top_level_lines::-1]:
350364
if line.strip() and expand_indent(line) < ancestor_level:
351365
ancestor_level = expand_indent(line)
352366
nested = line.lstrip().startswith('def ')
353367
if nested or ancestor_level == 0:
354368
break
355369
if nested:
356-
yield 0, "E306 expected 1 blank line before a " \
357-
"nested definition, found 0"
370+
yield 0, "E306 expected %s blank line before a " \
371+
"nested definition, found 0" % (method_lines,)
358372
else:
359-
yield 0, "E301 expected 1 blank line, found 0"
360-
elif blank_before != 2:
361-
yield 0, "E302 expected 2 blank lines, found %d" % blank_before
362-
elif (logical_line and not indent_level and blank_before != 2 and
363-
previous_unindented_logical_line.startswith(('def ', 'class '))):
364-
yield 0, "E305 expected 2 blank lines after " \
365-
"class or function definition, found %d" % blank_before
373+
yield 0, "E301 expected %s blank line, found 0" % (
374+
method_lines,)
375+
elif blank_before != top_level_lines:
376+
yield 0, "E302 expected %s blank lines, found %d" % (
377+
top_level_lines, blank_before)
378+
elif (logical_line and
379+
not indent_level and
380+
blank_before != top_level_lines and
381+
previous_unindented_logical_line.startswith(('def ', 'class '))
382+
):
383+
yield 0, "E305 expected %s blank lines after " \
384+
"class or function definition, found %d" % (
385+
top_level_lines, blank_before)
366386

367387

368388
@register_check

testsuite/support.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,26 @@ def print_results(self):
8383
print("Test failed." if self.total_errors else "Test passed.")
8484

8585

86+
class InMemoryReport(BaseReport):
87+
"""
88+
Collect the results in memory, without printing anything.
89+
"""
90+
91+
def __init__(self, options):
92+
super(InMemoryReport, self).__init__(options)
93+
self.in_memory_errors = []
94+
95+
def error(self, line_number, offset, text, check):
96+
"""
97+
Report an error, according to options.
98+
"""
99+
code = text[:4]
100+
self.in_memory_errors.append('%s:%s:%s' % (
101+
code, line_number, offset + 1))
102+
return super(InMemoryReport, self).error(
103+
line_number, offset, text, check)
104+
105+
86106
def selftest(options):
87107
"""
88108
Test all check functions with test cases in docstrings.

testsuite/test_all.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,19 @@ def test_own_dog_food(self):
4848

4949

5050
def suite():
51-
from testsuite import test_api, test_parser, test_shell, test_util
51+
from testsuite import (
52+
test_api,
53+
test_blank_lines,
54+
test_parser,
55+
test_shell,
56+
test_util,
57+
)
5258

5359
suite = unittest.TestSuite()
5460
suite.addTest(unittest.makeSuite(PycodestyleTestCase))
5561
suite.addTest(unittest.makeSuite(test_api.APITestCase))
62+
suite.addTest(unittest.makeSuite(test_blank_lines.TestBlankLinesDefault))
63+
suite.addTest(unittest.makeSuite(test_blank_lines.TestBlankLinesTwisted))
5664
suite.addTest(unittest.makeSuite(test_parser.ParserTestCase))
5765
suite.addTest(unittest.makeSuite(test_shell.ShellTestCase))
5866
suite.addTest(unittest.makeSuite(test_util.UtilTestCase))

0 commit comments

Comments
 (0)