Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • pycqa/pycodestyle
1 result
Show changes
Commits on Source (11)
Changelog
=========
 
UNRELEASED
----------
2.4.0 (2018-04-10)
------------------
 
New checks:
 
* Add W504 warning for checking that a break doesn't happen after a binary
operator. This check is ignored by default
* Add W605 warning for invalid escape sequences in string literals
operator. This check is ignored by default. PR #502.
* Add W605 warning for invalid escape sequences in string literals. PR #676.
* Add W606 warning for 'async' and 'await' reserved keywords being introduced
in Python 3.7
in Python 3.7. PR #684.
* Add E252 error for missing whitespace around equal sign in type annotated
function arguments with defaults values. PR #717.
Changes:
* An internal bisect search has replaced a linear search in order to improve
efficiency. PR #648.
* pycodestyle now uses PyPI trove classifiers in order to document supported
python versions on PyPI. PR #654.
* 'setup.cfg' '[wheel]' section has been renamed to '[bdist_wheel]', as
the former is legacy. PR #653.
* pycodestyle now handles very long lines much more efficiently for python
3.2+. Fixes #643. PR #644.
* You can now write 'pycodestyle.StyleGuide(verbose=True)' instead of
'pycodestyle.StyleGuide(verbose=True, paths=['-v'])' in order to achieve
verbosity. PR #663.
* The distribution of pycodestyle now includes the license text in order to
comply with open source licenses which require this. PR #694.
* 'maximum_line_length' now ignores shebang ('#!') lines. PR #736.
* Add configuration option for the allowed number of blank lines. It is
implemented as a top level dictionary which can be easily overwritten. Fixes
#732. PR #733.
Bugs:
* Prevent a 'DeprecationWarning', and a 'SyntaxError' in future python, caused
by an invalid escape sequence. PR #625.
* Correctly report E501 when the first line of a docstring is too long.
Resolves #622. PR #630.
* Support variable annotation when variable start by a keyword, such as class
variable type annotations in python 3.6. PR #640.
* pycodestyle internals have been changed in order to allow 'python3 -m
cProfile' to report correct metrics. PR #647.
* Fix a spelling mistake in the description of E722. PR #697.
* 'pycodestyle --diff' now does not break if your 'gitconfig' enables
'mnemonicprefix'. PR #706.
 
2.3.1 (2017-01-31)
------------------
Loading
Loading
Loading
Loading
@@ -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/
Loading
Loading
Loading
Loading
@@ -77,7 +77,7 @@ try:
except ImportError:
from ConfigParser import RawConfigParser
 
__version__ = '2.3.1'
__version__ = '2.4.0'
 
DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__,.tox'
DEFAULT_IGNORE = 'E121,E123,E126,E226,E24,E704,W503,W504'
Loading
Loading
@@ -95,6 +95,13 @@ except ImportError:
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',
Loading
Loading
@@ -102,7 +109,7 @@ REPORT_FORMAT = {
 
PyCF_ONLY_AST = 1024
SINGLETONS = frozenset(['False', 'None', 'True'])
KEYWORDS = frozenset(keyword.kwlist + ['print']) - SINGLETONS
KEYWORDS = frozenset(keyword.kwlist + ['print', 'async']) - SINGLETONS
UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-'])
ARITHMETIC_OP = frozenset(['**', '*', '/', '//', '+', '-'])
WS_OPTIONAL_OPERATORS = ARITHMETIC_OP.union(['^', '&', '|', '<<', '>>', '%'])
Loading
Loading
@@ -121,7 +128,7 @@ RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,')
RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,.*,\s*\w+\s*$')
ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b')
DOCSTRING_REGEX = re.compile(r'u?r?["\']')
EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]')
EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[\[({] | [\]}),;:]')
WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)')
COMPARE_SINGLETON_REGEX = re.compile(r'(\bNone|\bFalse|\bTrue)?\s*([=!]=)'
r'\s*(?(1)|(None|False|True))\b')
Loading
Loading
@@ -260,7 +267,8 @@ def trailing_blank_lines(physical_line, lines, line_number, total_lines):
 
 
@register_check
def maximum_line_length(physical_line, max_line_length, multiline, noqa):
def maximum_line_length(physical_line, max_line_length, multiline,
line_number, noqa):
r"""Limit all lines to a maximum of 79 characters.
 
There are still many devices around that are limited to 80 character
Loading
Loading
@@ -275,6 +283,9 @@ def maximum_line_length(physical_line, max_line_length, multiline, noqa):
line = physical_line.rstrip()
length = len(line)
if length > max_line_length and not noqa:
# Special case: ignore long shebang lines.
if line_number == 1 and line.startswith('#!'):
return
# Special case for long URLs in multi-line docstrings or comments,
# but still report the error when the 72 first chars are whitespaces.
chunks = line.split()
Loading
Loading
@@ -328,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
Loading
Loading
@@ -1500,7 +1524,7 @@ def python_3000_invalid_escape_sequence(logical_line, tokens):
pos += 1
if string[pos] not in valid:
yield (
pos,
line.lstrip().find(text),
"W605 invalid escape sequence '\\%s'" %
string[pos],
)
Loading
Loading
@@ -1665,9 +1689,9 @@ def parse_udiff(diff, patterns=None, parent='.'):
if path[:2] in ('b/', 'w/', 'i/'):
path = path[2:]
rv[path] = set()
return dict([(os.path.join(parent, path), rows)
for (path, rows) in rv.items()
if rows and filename_match(path, patterns)])
return dict([(os.path.join(parent, filepath), rows)
for (filepath, rows) in rv.items()
if rows and filename_match(filepath, patterns)])
 
 
def normalize_paths(value, parent=os.curdir):
Loading
Loading
Loading
Loading
@@ -39,7 +39,7 @@ def munge(input: AnyStr, sep: AnyStr = None, limit=1000,
async def add(a: int = 0, b: int = 0) -> int:
return a + b
# Previously E251 four times
#: E272:1:6
#: E271:1:6
async def add(a: int = 0, b: int = 0) -> int:
return a + b
#: E252:1:15 E252:1:16 E252:1:27 E252:1:36
Loading
Loading
Loading
Loading
@@ -157,7 +157,7 @@ def main():
if __name__ == '__main__':
main()
# Previously just E272:1:6 E272:4:6
#: E302:4:1 E272:1:6 E272:4:6
#: E302:4:1 E271:1:6 E271:4:6
async def x():
pass
 
Loading
Loading
Loading
Loading
@@ -121,3 +121,7 @@ import this
#: E501
# This
# almost_empty_line
#
#: Okay
#!/reallylongpath/toexecutable --maybe --with --some ARGUMENTS TO DO WITH WHAT EXECUTABLE TO RUN
Loading
Loading
@@ -14,7 +14,7 @@ del a[:]; a.append(42);
def f(x): return 2
#: E704:1:1
async def f(x): return 2
#: E704:1:1 E272:1:6
#: E704:1:1 E271:1:6
async def f(x): return 2
#: E704:1:1 E226:1:19
def f(x): return 2*x
Loading
Loading
Loading
Loading
@@ -83,6 +83,26 @@ class TestReport(StandardReport):
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.
Loading
Loading
Loading
Loading
@@ -48,11 +48,19 @@ class PycodestyleTestCase(unittest.TestCase):
 
 
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))
Loading
Loading
"""
Tests for the blank_lines checker.
It uses dedicated assertions which work with TestReport.
"""
import unittest
import pycodestyle
from testsuite.support import InMemoryReport
class BlankLinesTestCase(unittest.TestCase):
"""
Common code for running blank_lines tests.
"""
def check(self, content):
"""
Run checks on `content` and return the the list of errors.
"""
sut = pycodestyle.StyleGuide()
reporter = sut.init_report(InMemoryReport)
sut.input_file(
filename='in-memory-test-file.py',
lines=content.splitlines(True),
)
return reporter.in_memory_errors
def assertNoErrors(self, actual):
"""
Check that the actual result from the checker has no errors.
"""
self.assertEqual([], actual)
class TestBlankLinesDefault(BlankLinesTestCase):
"""
Tests for default blank with 2 blank lines for top level and 1 blank line
for methods.
"""
def test_initial_no_blank(self):
"""
It will accept no blank lines at the start of the file.
"""
result = self.check("""def some_function():
pass
""")
self.assertNoErrors(result)
def test_initial_lines_one_blank(self):
"""
It will accept 1 blank lines before the first line of actual code,
even if in other places it asks for 2
"""
result = self.check("""
def some_function():
pass
""")
self.assertNoErrors(result)
def test_initial_lines_two_blanks(self):
"""
It will accept 2 blank lines before the first line of actual code,
as normal.
"""
result = self.check("""
def some_function():
pass
""")
self.assertNoErrors(result)
def test_method_less_blank_lines(self):
"""
It will trigger an error when less than 1 blank lin is found before
method definitions.
"""
result = self.check("""# First comment line.
class X:
def a():
pass
def b():
pass
""")
self.assertEqual([
'E301:6:5', # b() call
], result)
def test_method_less_blank_lines_comment(self):
"""
It will trigger an error when less than 1 blank lin is found before
method definition, ignoring comments.
"""
result = self.check("""# First comment line.
class X:
def a():
pass
# A comment will not make it better.
def b():
pass
""")
self.assertEqual([
'E301:7:5', # b() call
], result)
def test_top_level_fewer_blank_lines(self):
"""
It will trigger an error when less 2 blank lines are found before top
level definitions.
"""
result = self.check("""# First comment line.
# Second line of comment.
def some_function():
pass
async def another_function():
pass
def this_one_is_good():
pass
class SomeCloseClass(object):
pass
async def this_async_is_good():
pass
class AFarEnoughClass(object):
pass
""")
self.assertEqual([
'E302:4:1', # some_function
'E302:7:1', # another_function
'E302:14:1', # SomeCloseClass
], result)
def test_top_level_more_blank_lines(self):
"""
It will trigger an error when more 2 blank lines are found before top
level definitions.
"""
result = self.check("""# First comment line.
# Second line of comment.
def some_function():
pass
def this_one_is_good():
pass
class SomeFarClass(object):
pass
class AFarEnoughClass(object):
pass
""")
self.assertEqual([
'E303:6:1', # some_function
'E303:15:1', # SomeFarClass
], result)
def test_method_more_blank_lines(self):
"""
It will trigger an error when more than 1 blank line is found before
method definition
"""
result = self.check("""# First comment line.
class SomeCloseClass(object):
def oneMethod(self):
pass
def anotherMethod(self):
pass
def methodOK(self):
pass
def veryFar(self):
pass
""")
self.assertEqual([
'E303:7:5', # oneMethod
'E303:11:5', # anotherMethod
'E303:19:5', # veryFar
], result)
def test_initial_lines_more_blank(self):
"""
It will trigger an error for more than 2 blank lines before the first
line of actual code.
"""
result = self.check("""
def some_function():
pass
""")
self.assertEqual(['E303:4:1'], result)
def test_blank_line_between_decorator(self):
"""
It will trigger an error when the decorator is followed by a blank
line.
"""
result = self.check("""# First line.
@some_decorator
def some_function():
pass
class SomeClass(object):
@method_decorator
def some_method(self):
pass
""")
self.assertEqual(['E304:6:1', 'E304:14:5'], result)
def test_blank_line_decorator(self):
"""
It will accept the decorators which are adjacent to the function and
method definition.
"""
result = self.check("""# First line.
@another_decorator
@some_decorator
def some_function():
pass
class SomeClass(object):
@method_decorator
def some_method(self):
pass
""")
self.assertNoErrors(result)
def test_top_level_fewer_follow_lines(self):
"""
It will trigger an error when less than 2 blank lines are
found between a top level definitions and other top level code.
"""
result = self.check("""
def a():
print('Something')
a()
""")
self.assertEqual([
'E305:5:1', # a call
], result)
def test_top_level_fewer_follow_lines_comments(self):
"""
It will trigger an error when less than 2 blank lines are
found between a top level definitions and other top level code,
even if we have comments before
"""
result = self.check("""
def a():
print('Something')
# comment
# another comment
# With comment still needs 2 spaces before,
# as comments are ignored.
a()
""")
self.assertEqual([
'E305:11:1', # a call
], result)
def test_top_level_good_follow_lines(self):
"""
It not trigger an error when 2 blank lines are
found between a top level definitions and other top level code.
"""
result = self.check("""
def a():
print('Something')
# Some comments in other parts.
# More comments.
# With the right spaces,
# It will work, even when we have comments.
a()
""")
self.assertNoErrors(result)
def test_method_fewer_follow_lines(self):
"""
It will trigger an error when less than 1 blank line is
found between a method and previous definitions.
"""
result = self.check("""
def a():
x = 1
def b():
pass
""")
self.assertEqual([
'E306:4:5', # b() call
], result)
def test_method_nested_fewer_follow_lines(self):
"""
It will trigger an error when less than 1 blank line is
found between a method and previous definitions, even when nested.
"""
result = self.check("""
def a():
x = 2
def b():
x = 1
def c():
pass
""")
self.assertEqual([
'E306:7:9', # c() call
], result)
def test_method_nested_less_class(self):
"""
It will trigger an error when less than 1 blank line is found
between a method and previous definitions, even when used to
define a class.
"""
result = self.check("""
def a():
x = 1
class C:
pass
""")
self.assertEqual([
'E306:4:5', # class C definition.
], result)
def test_method_nested_ok(self):
"""
Will not trigger an error when 1 blank line is found
found between a method and previous definitions, even when nested.
"""
result = self.check("""
def a():
x = 2
def b():
x = 1
def c():
pass
class C:
pass
""")
self.assertNoErrors(result)
class TestBlankLinesTwisted(BlankLinesTestCase):
"""
Tests for blank_lines with 3 blank lines for top level and 2 blank line
for methods as used by the Twisted coding style.
"""
def setUp(self):
self._original_lines_config = pycodestyle.BLANK_LINES_CONFIG.copy()
pycodestyle.BLANK_LINES_CONFIG['top_level'] = 3
pycodestyle.BLANK_LINES_CONFIG['method'] = 2
def tearDown(self):
pycodestyle.BLANK_LINES_CONFIG = self._original_lines_config
def test_initial_lines_one_blanks(self):
"""
It will accept less than 3 blank lines before the first line of actual
code.
"""
result = self.check("""
def some_function():
pass
""")
self.assertNoErrors(result)
def test_initial_lines_tree_blanks(self):
"""
It will accept 3 blank lines before the first line of actual code,
as normal.
"""
result = self.check("""
def some_function():
pass
""")
self.assertNoErrors(result)
def test_top_level_fewer_blank_lines(self):
"""
It will trigger an error when less 2 blank lines are found before top
level definitions.
"""
result = self.check("""# First comment line.
# Second line of comment.
def some_function():
pass
async def another_function():
pass
def this_one_is_good():
pass
class SomeCloseClass(object):
pass
async def this_async_is_good():
pass
class AFarEnoughClass(object):
pass
""")
self.assertEqual([
'E302:5:1', # some_function
'E302:9:1', # another_function
'E302:17:1', # SomeCloseClass
], result)
def test_top_level_more_blank_lines(self):
"""
It will trigger an error when more 2 blank lines are found before top
level definitions.
"""
result = self.check("""# First comment line.
# Second line of comment.
def some_function():
pass
def this_one_is_good():
pass
class SomeVeryFarClass(object):
pass
class AFarEnoughClass(object):
pass
""")
self.assertEqual([
'E303:7:1', # some_function
'E303:18:1', # SomeVeryFarClass
], result)
def test_the_right_blanks(self):
"""
It will accept 3 blank for top level and 2 for nested.
"""
result = self.check("""
def some_function():
pass
# With comments.
some_other = code_here
class SomeClass:
'''
Docstring here.
'''
def some_method():
pass
def another_method():
pass
# More methods.
def another_method_with_comment():
pass
@decorator
def another_method_with_comment():
pass
""")
self.assertNoErrors(result)
Loading
Loading
@@ -5,7 +5,8 @@
 
[tox]
envlist = py26, py27, py32, py33, py34, py35, py36, pypy, pypy3, jython
skip_missing_interpreters=True
skipsdist = True
skip_missing_interpreters = True
 
[testenv]
commands =
Loading
Loading