Skip to content

Commit

Permalink
Add variables so blank lines may be configures
Browse files Browse the repository at this point in the history
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
  • Loading branch information
adiroiban authored and sigmavirus24 committed Apr 8, 2018
1 parent 9f225ac commit 5d31e7e
Show file tree
Hide file tree
Showing 5 changed files with 653 additions and 15 deletions.
38 changes: 38 additions & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,44 @@ At this point you can create a pull request back to the official pycodestyles
repository for review! For more information on how to make a pull request,
GitHub has an excellent `guide`_.

The current tests are written in 2 styles:

* standard xUnit based only on stdlib unittest
(can be executed with nose)
* functional test using a custom framework and executed by the
pycodestyle itself when installed in dev mode.


Running unittest
~~~~~~~~~~~~~~~~

While the tests are writted using stdlib `unittest` module, the existing
test include unit, integration and functional tests.

There are a couple of ways to run the tests::

$ python setup.py test
$ # Use nose to run specific test
$ nosetests \
> testsuite.test_blank_lines:TestBlankLinesDefault.test_initial_no_blank
$ # Use nose to run a subset and check coverage, and check the resulting
$ $ cover/pycodestyle_py.html in your browser
$ nosetests --with-coverage --cover-html -s testsuite.test_blank_lines


Running functional
~~~~~~~~~~~~~~~~~~

When installed in dev mode, pycodestyle will have the `--testsuite`
option which can be used to run the tests::

$ pip install -e .
$ # Run all tests.
$ pycodestyle --testsuite testsuite
$ # Run a subset of the tests.
$ pycodestyle --testsuite testsuite/E30.py


.. _virtualenv: http://docs.python-guide.org/en/latest/dev/virtualenvs/
.. _guide: https://guides.github.com/activities/forking/
.. _tox: https://tox.readthedocs.io/en/latest/
Expand Down
48 changes: 34 additions & 14 deletions pycodestyle.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ def lru_cache(maxsize=128): # noqa as it's a fake implementation.
PROJECT_CONFIG = ('setup.cfg', 'tox.ini')
TESTSUITE_PATH = os.path.join(os.path.dirname(__file__), 'testsuite')
MAX_LINE_LENGTH = 79
# Number of blank lines between various code parts.
BLANK_LINES_CONFIG = {
# Top level class and function.
'top_level': 2,
# Methods and nested class and function.
'method': 1,
}
REPORT_FORMAT = {
'default': '%(path)s:%(row)d:%(col)d: %(code)s %(text)s',
'pylint': '%(path)s:%(row)d: [%(code)s] %(text)s',
Expand Down Expand Up @@ -332,37 +339,50 @@ def blank_lines(logical_line, blank_lines, indent_level, line_number,
E305: def a():\n pass\na()
E306: def a():\n def b():\n pass\n def c():\n pass
"""
if line_number < 3 and not previous_logical:
top_level_lines = BLANK_LINES_CONFIG['top_level']
method_lines = BLANK_LINES_CONFIG['method']

if line_number < top_level_lines + 1 and not previous_logical:
return # Don't expect blank lines before the first line
if previous_logical.startswith('@'):
if blank_lines:
yield 0, "E304 blank lines found after function decorator"
elif blank_lines > 2 or (indent_level and blank_lines == 2):
elif (blank_lines > top_level_lines or
(indent_level and blank_lines == method_lines + 1)
):
yield 0, "E303 too many blank lines (%d)" % blank_lines
elif STARTSWITH_TOP_LEVEL_REGEX.match(logical_line):
if indent_level:
if not (blank_before or previous_indent_level < indent_level or
DOCSTRING_REGEX.match(previous_logical)):
if not (blank_before == method_lines or
previous_indent_level < indent_level or
DOCSTRING_REGEX.match(previous_logical)
):
ancestor_level = indent_level
nested = False
# Search backwards for a def ancestor or tree root (top level).
for line in lines[line_number - 2::-1]:
for line in lines[line_number - top_level_lines::-1]:
if line.strip() and expand_indent(line) < ancestor_level:
ancestor_level = expand_indent(line)
nested = line.lstrip().startswith('def ')
if nested or ancestor_level == 0:
break
if nested:
yield 0, "E306 expected 1 blank line before a " \
"nested definition, found 0"
yield 0, "E306 expected %s blank line before a " \
"nested definition, found 0" % (method_lines,)
else:
yield 0, "E301 expected 1 blank line, found 0"
elif blank_before != 2:
yield 0, "E302 expected 2 blank lines, found %d" % blank_before
elif (logical_line and not indent_level and blank_before != 2 and
previous_unindented_logical_line.startswith(('def ', 'class '))):
yield 0, "E305 expected 2 blank lines after " \
"class or function definition, found %d" % blank_before
yield 0, "E301 expected %s blank line, found 0" % (
method_lines,)
elif blank_before != top_level_lines:
yield 0, "E302 expected %s blank lines, found %d" % (
top_level_lines, blank_before)
elif (logical_line and
not indent_level and
blank_before != top_level_lines and
previous_unindented_logical_line.startswith(('def ', 'class '))
):
yield 0, "E305 expected %s blank lines after " \
"class or function definition, found %d" % (
top_level_lines, blank_before)


@register_check
Expand Down
20 changes: 20 additions & 0 deletions testsuite/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,26 @@ def print_results(self):
print("Test failed." if self.total_errors else "Test passed.")


class InMemoryReport(BaseReport):
"""
Collect the results in memory, without printing anything.
"""

def __init__(self, options):
super(InMemoryReport, self).__init__(options)
self.in_memory_errors = []

def error(self, line_number, offset, text, check):
"""
Report an error, according to options.
"""
code = text[:4]
self.in_memory_errors.append('%s:%s:%s' % (
code, line_number, offset + 1))
return super(InMemoryReport, self).error(
line_number, offset, text, check)


def selftest(options):
"""
Test all check functions with test cases in docstrings.
Expand Down
10 changes: 9 additions & 1 deletion testsuite/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,19 @@ def test_own_dog_food(self):


def suite():
from testsuite import test_api, test_parser, test_shell, test_util
from testsuite import (
test_api,
test_blank_lines,
test_parser,
test_shell,
test_util,
)

suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(PycodestyleTestCase))
suite.addTest(unittest.makeSuite(test_api.APITestCase))
suite.addTest(unittest.makeSuite(test_blank_lines.TestBlankLinesDefault))
suite.addTest(unittest.makeSuite(test_blank_lines.TestBlankLinesTwisted))
suite.addTest(unittest.makeSuite(test_parser.ParserTestCase))
suite.addTest(unittest.makeSuite(test_shell.ShellTestCase))
suite.addTest(unittest.makeSuite(test_util.UtilTestCase))
Expand Down
Loading

0 comments on commit 5d31e7e

Please sign in to comment.