From 110a5c3bcd4ac407a471410f23f82cb6e706e6bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Fri, 24 Feb 2017 11:27:57 +0200 Subject: Python 3.6 invalid escape sequence deprecation fix https://docs.python.org/3/whatsnew/3.6.html#deprecated-python-behavior "backslash-character pair that is not a valid escape sequence now generates a DeprecationWarning. Although this will eventually become a SyntaxError, that will not be for several Python releases." --- pycodestyle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycodestyle.py b/pycodestyle.py index 5d8c2ac..76fce9b 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -124,7 +124,7 @@ HUNK_REGEX = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$') STARTSWITH_DEF_REGEX = re.compile(r'^(async\s+def|def)') STARTSWITH_TOP_LEVEL_REGEX = re.compile(r'^(async\s+def\s+|def\s+|class\s+|@)') STARTSWITH_INDENT_STATEMENT_REGEX = re.compile( - r'^\s*({0})'.format('|'.join(s.replace(' ', '\s+') for s in ( + r'^\s*({0})'.format('|'.join(s.replace(' ', r'\s+') for s in ( 'def', 'async def', 'for', 'async for', 'if', 'elif', 'else', -- cgit v1.2.1 From cb17d2739696df66c5b1eed2de559b4d778836e2 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Wed, 15 Mar 2017 08:37:40 -0700 Subject: Correctly report E501 when the first line of a docstring is too long Resolves #622 --- pycodestyle.py | 6 ++++-- testsuite/E50.py | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pycodestyle.py b/pycodestyle.py index 5d8c2ac..556467b 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -1199,7 +1199,7 @@ def comparison_type(logical_line, noqa): def bare_except(logical_line, noqa): - r"""When catching exceptions, mention specific exceptions whenever possible. + r"""When catching exceptions, mention specific exceptions when possible. Okay: except Exception: Okay: except BaseException: @@ -1727,7 +1727,9 @@ class Checker(object): return self.multiline = True self.line_number = token[2][0] - for line in token[1].split('\n')[:-1]: + _, src, (_, offset), _, _ = token + src = self.lines[self.line_number - 1][:offset] + src + for line in src.split('\n')[:-1]: self.check_physical(line + '\n') self.line_number += 1 self.multiline = False diff --git a/testsuite/E50.py b/testsuite/E50.py index f60f389..189f416 100644 --- a/testsuite/E50.py +++ b/testsuite/E50.py @@ -82,6 +82,11 @@ that I'm calling: #: E501 """ longnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaces""" +#: E501 +# Regression test for #622 +def foo(): + """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pulvinar vitae + """ #: Okay """ This -- cgit v1.2.1 From 7b98ff3a9471c6b111f477ea06e8fc207568172a Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Sat, 6 May 2017 09:57:02 +0200 Subject: Avoid relying on inspect to list functions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allow to run python3 -m cProfile ./pycodestyle.py …. Otherwise, as cProfile looks to wrap every functions (in a way I did not studied in depth), the inspection of test function found no function, and no test were run while running under cProfile, meaning that: $ time python3 ./pycodestyle.py coefficients-148K.py [loads and loads of errors properly reported] real 0m4.712s user 0m4.684s sys 0m0.024s $ time python3 -m cProfile ./pycodestyle.py /home/mdk/Downloads/pystyle/stats/git-clones/PmagPy/PmagPy/coefficients-148K.py [no error reported as no check were found by inspection] real 0m0.447s user 0m0.436s sys 0m0.008s With this patch: $ time python3 -m cProfile ./pycodestyle.py /home/mdk/Downloads/pystyle/stats/git-clones/PmagPy/PmagPy/coefficients-148K.py [loads and loads of errors properly reported] real 0m4.889s user 0m4.852s sys 0m0.032s --- pycodestyle.py | 109 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 45 deletions(-) diff --git a/pycodestyle.py b/pycodestyle.py index 556467b..2e03301 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -141,11 +141,43 @@ DUNDER_REGEX = re.compile(r'^__([^\s]+)__ = ') COMMENT_WITH_NL = tokenize.generate_tokens(['#\n'].pop).send(None)[1] == '#\n' +_checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} + + +def _get_parameters(function): + if sys.version_info >= (3, 3): + return [parameter.name + for parameter + in inspect.signature(function).parameters.values() + if parameter.kind == parameter.POSITIONAL_OR_KEYWORD] + else: + return inspect.getargspec(function)[0] + + +def register_check(check, codes=None): + """Register a new check object.""" + def _add_check(check, kind, codes, args): + if check in _checks[kind]: + _checks[kind][check][0].extend(codes or []) + else: + _checks[kind][check] = (codes or [''], args) + if inspect.isfunction(check): + args = _get_parameters(check) + if args and args[0] in ('physical_line', 'logical_line'): + if codes is None: + codes = ERRORCODE_REGEX.findall(check.__doc__ or '') + _add_check(check, args[0], codes, args) + elif inspect.isclass(check): + if _get_parameters(check.__init__)[:2] == ['self', 'tree']: + _add_check(check, 'tree', codes, None) + return check + + ############################################################################## # Plugins (check functions) for physical lines ############################################################################## - +@register_check def tabs_or_spaces(physical_line, indent_char): r"""Never mix tabs and spaces. @@ -165,6 +197,7 @@ def tabs_or_spaces(physical_line, indent_char): return offset, "E101 indentation contains mixed spaces and tabs" +@register_check def tabs_obsolete(physical_line): r"""For new projects, spaces-only are strongly recommended over tabs. @@ -176,6 +209,7 @@ def tabs_obsolete(physical_line): return indent.index('\t'), "W191 indentation contains tabs" +@register_check def trailing_whitespace(physical_line): r"""Trailing whitespace is superfluous. @@ -197,6 +231,7 @@ def trailing_whitespace(physical_line): return 0, "W293 blank line contains whitespace" +@register_check def trailing_blank_lines(physical_line, lines, line_number, total_lines): r"""Trailing blank lines are superfluous. @@ -213,6 +248,7 @@ def trailing_blank_lines(physical_line, lines, line_number, total_lines): return len(physical_line), "W292 no newline at end of file" +@register_check def maximum_line_length(physical_line, max_line_length, multiline, noqa): r"""Limit all lines to a maximum of 79 characters. @@ -251,6 +287,7 @@ def maximum_line_length(physical_line, max_line_length, multiline, noqa): ############################################################################## +@register_check def blank_lines(logical_line, blank_lines, indent_level, line_number, blank_before, previous_logical, previous_unindented_logical_line, previous_indent_level, @@ -313,6 +350,7 @@ def blank_lines(logical_line, blank_lines, indent_level, line_number, "class or function definition, found %d" % blank_before +@register_check def extraneous_whitespace(logical_line): r"""Avoid extraneous whitespace. @@ -345,6 +383,7 @@ def extraneous_whitespace(logical_line): yield found, "%s whitespace before '%s'" % (code, char) +@register_check def whitespace_around_keywords(logical_line): r"""Avoid extraneous whitespace around keywords. @@ -368,6 +407,7 @@ def whitespace_around_keywords(logical_line): yield match.start(2), "E271 multiple spaces after keyword" +@register_check def missing_whitespace_after_import_keyword(logical_line): r"""Multiple imports in form from x import (a, b, c) should have space between import statement and parenthesised name list. @@ -385,6 +425,7 @@ def missing_whitespace_after_import_keyword(logical_line): yield pos, "E275 missing whitespace after keyword" +@register_check def missing_whitespace(logical_line): r"""Each comma, semicolon or colon should be followed by whitespace. @@ -411,6 +452,7 @@ def missing_whitespace(logical_line): yield index, "E231 missing whitespace after '%s'" % char +@register_check def indentation(logical_line, previous_logical, indent_char, indent_level, previous_indent_level): r"""Use 4 spaces per indentation level. @@ -442,6 +484,7 @@ def indentation(logical_line, previous_logical, indent_char, yield 0, tmpl % (3 + c, "unexpected indentation") +@register_check def continued_indentation(logical_line, tokens, indent_level, hang_closing, indent_char, noqa, verbose): r"""Continuation lines indentation. @@ -641,6 +684,7 @@ def continued_indentation(logical_line, tokens, indent_level, hang_closing, yield pos, "%s with same indent as next logical line" % code +@register_check def whitespace_before_parameters(logical_line, tokens): r"""Avoid extraneous whitespace. @@ -673,6 +717,7 @@ def whitespace_before_parameters(logical_line, tokens): prev_end = end +@register_check def whitespace_around_operator(logical_line): r"""Avoid extraneous whitespace around an operator. @@ -696,6 +741,7 @@ def whitespace_around_operator(logical_line): yield match.start(2), "E222 multiple spaces after operator" +@register_check def missing_whitespace_around_operator(logical_line, tokens): r"""Surround operators with a single space on either side. @@ -788,6 +834,7 @@ def missing_whitespace_around_operator(logical_line, tokens): prev_end = end +@register_check def whitespace_around_comma(logical_line): r"""Avoid extraneous whitespace after a comma or a colon. @@ -806,6 +853,7 @@ def whitespace_around_comma(logical_line): yield found, "E241 multiple spaces after '%s'" % m.group()[0] +@register_check def whitespace_around_named_parameter_equals(logical_line, tokens): r"""Don't use spaces around the '=' sign in function arguments. @@ -856,6 +904,7 @@ def whitespace_around_named_parameter_equals(logical_line, tokens): prev_end = end +@register_check def whitespace_before_comment(logical_line, tokens): r"""Separate inline comments by at least two spaces. @@ -897,6 +946,7 @@ def whitespace_before_comment(logical_line, tokens): prev_end = end +@register_check def imports_on_separate_lines(logical_line): r"""Place imports on separate lines. @@ -916,6 +966,7 @@ def imports_on_separate_lines(logical_line): yield found, "E401 multiple imports on one line" +@register_check def module_imports_on_top_of_file( logical_line, indent_level, checker_state, noqa): r"""Place imports at the top of the file. @@ -972,6 +1023,7 @@ def module_imports_on_top_of_file( checker_state['seen_non_imports'] = True +@register_check def compound_statements(logical_line): r"""Compound statements (on the same line) are generally discouraged. @@ -1032,6 +1084,7 @@ def compound_statements(logical_line): found = line.find(';', found + 1) +@register_check def explicit_line_join(logical_line, tokens): r"""Avoid explicit line join between brackets. @@ -1071,6 +1124,7 @@ def explicit_line_join(logical_line, tokens): parens -= 1 +@register_check def break_around_binary_operator(logical_line, tokens): r""" Avoid breaks before binary operators. @@ -1120,6 +1174,7 @@ def break_around_binary_operator(logical_line, tokens): previous_text = text +@register_check def comparison_to_singleton(logical_line, noqa): r"""Comparison to singletons should use "is" or "is not". @@ -1154,6 +1209,7 @@ def comparison_to_singleton(logical_line, noqa): (code, singleton, msg)) +@register_check def comparison_negative(logical_line): r"""Negative comparison should be done using "not in" and "is not". @@ -1175,6 +1231,7 @@ def comparison_negative(logical_line): yield pos, "E714 test for object identity should be 'is not'" +@register_check def comparison_type(logical_line, noqa): r"""Object type comparisons should always use isinstance(). @@ -1198,6 +1255,7 @@ def comparison_type(logical_line, noqa): yield match.start(), "E721 do not compare types, use 'isinstance()'" +@register_check def bare_except(logical_line, noqa): r"""When catching exceptions, mention specific exceptions when possible. @@ -1214,6 +1272,7 @@ def bare_except(logical_line, noqa): yield match.start(), "E722 do not use bare except'" +@register_check def ambiguous_identifier(logical_line, tokens): r"""Never use the characters 'l', 'O', or 'I' as variable names. @@ -1266,6 +1325,7 @@ def ambiguous_identifier(logical_line, tokens): prev_start = start +@register_check def python_3000_has_key(logical_line, noqa): r"""The {}.has_key() method is removed in Python 3: use the 'in' operator. @@ -1277,6 +1337,7 @@ def python_3000_has_key(logical_line, noqa): yield pos, "W601 .has_key() is deprecated, use 'in'" +@register_check def python_3000_raise_comma(logical_line): r"""When raising an exception, use "raise ValueError('message')". @@ -1290,6 +1351,7 @@ def python_3000_raise_comma(logical_line): yield match.end() - 1, "W602 deprecated form of raising exception" +@register_check def python_3000_not_equal(logical_line): r"""New code should always use != instead of <>. @@ -1303,6 +1365,7 @@ def python_3000_not_equal(logical_line): yield pos, "W603 '<>' is deprecated, use '!='" +@register_check def python_3000_backticks(logical_line): r"""Use repr() instead of backticks in Python 3. @@ -1471,50 +1534,6 @@ if COMMENT_WITH_NL: ############################################################################## -_checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} - - -def _get_parameters(function): - if sys.version_info >= (3, 3): - return [parameter.name - for parameter - in inspect.signature(function).parameters.values() - if parameter.kind == parameter.POSITIONAL_OR_KEYWORD] - else: - return inspect.getargspec(function)[0] - - -def register_check(check, codes=None): - """Register a new check object.""" - def _add_check(check, kind, codes, args): - if check in _checks[kind]: - _checks[kind][check][0].extend(codes or []) - else: - _checks[kind][check] = (codes or [''], args) - if inspect.isfunction(check): - args = _get_parameters(check) - if args and args[0] in ('physical_line', 'logical_line'): - if codes is None: - codes = ERRORCODE_REGEX.findall(check.__doc__ or '') - _add_check(check, args[0], codes, args) - elif inspect.isclass(check): - if _get_parameters(check.__init__)[:2] == ['self', 'tree']: - _add_check(check, 'tree', codes, None) - - -def init_checks_registry(): - """Register all globally visible functions. - - The first argument name is either 'physical_line' or 'logical_line'. - """ - mod = inspect.getmodule(register_check) - for (name, function) in inspect.getmembers(mod, inspect.isfunction): - register_check(function) - - -init_checks_registry() - - class Checker(object): """Load a Python source file, tokenize it, check coding style.""" -- cgit v1.2.1 From 83782f6cb617ef10186af270c05bfc87a4b4d0bb Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Sat, 6 May 2017 16:39:34 +0200 Subject: Use bisect instead of iterating over every offsets. As the offsets looks already sorted (which looks logical so I assumed they always are), using a bisection if faster than iterating over all of them. On a specific test I encontered, I got nice enhancement with this patch: $ time python3 ./pycodestyle.py ~/Downloads/PmagPy/PmagPy/coefficients-552K.py > /dev/null real 1m16.405s $ time python3 ./pycodestyle.py ~/Downloads/PmagPy/PmagPy/coefficients-552K.py > /dev/null real 0m3.318s --- pycodestyle.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pycodestyle.py b/pycodestyle.py index 2e03301..763747e 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -56,6 +56,7 @@ import sys import time import tokenize import warnings +import bisect from fnmatch import fnmatch from optparse import OptionParser @@ -1664,10 +1665,10 @@ class Checker(object): """Build a line from tokens and run all logical checks on it.""" self.report.increment_logical_line() mapping = self.build_tokens_line() - if not mapping: return + mapping_offsets = [offset for offset, _ in mapping] (start_row, start_col) = mapping[0][1] start_line = self.lines[start_row - 1] self.indent_level = expand_indent(start_line[:start_col]) @@ -1681,9 +1682,10 @@ class Checker(object): self.init_checker_state(name, argument_names) for offset, text in self.run_check(check, argument_names) or (): if not isinstance(offset, tuple): - for token_offset, pos in mapping: - if offset <= token_offset: - break + # As mappings are ordered, bisecting is a fast way + # to find a given offset in them. + token_offset, pos = mapping[bisect.bisect_left( + mapping_offsets, offset)] offset = (pos[0], pos[1] + offset - token_offset) self.report_error(offset[0], offset[1], text, check) if self.logical_line: -- cgit v1.2.1 From 4538f43989a49aef163355cae44ebd686d1c780c Mon Sep 17 00:00:00 2001 From: Kevin Cole Date: Tue, 30 May 2017 15:33:14 -0400 Subject: Added .gitattributes to prevent auto-munging intentional line endings Users' global .gitattributes files may attempt to auto-fix certain files. --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0aadd30 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +testsuite/E90.py -text -- cgit v1.2.1 From f913dfc041bab577cdea34e261153545b98d3e4c Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Sat, 3 Jun 2017 17:14:19 -0700 Subject: Rename [wheel] section to [bdist_wheel] as the former is legacy (#653) See: https://bitbucket.org/pypa/wheel/src/54ddbcc9cec25e1f4d111a142b8bfaa163130a61/wheel/bdist_wheel.py?fileviewer=file-view-default#bdist_wheel.py-119:125 http://pythonwheels.com/ --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 803bc10..fcc079a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,4 @@ -[wheel] +[bdist_wheel] universal = 1 [pycodestyle] -- cgit v1.2.1 From 454e035fd969ee5d6da76fcc169ee028933f391e Mon Sep 17 00:00:00 2001 From: Julien Date: Sun, 4 Jun 2017 02:20:30 +0200 Subject: Fix issue #643: Optimize noqa() with an lru_cache for Python 3.2+. (#644) Had to catch a "No signature found for builtin " in 3.4: In python3.4 the search was not detected as a function, now that it's wrapped in an lru_cache it is, yet it still has no signature (it has in 3.5+). --- pycodestyle.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pycodestyle.py b/pycodestyle.py index bb96195..8afa286 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -58,6 +58,16 @@ import tokenize import warnings import bisect +try: + from functools import lru_cache +except ImportError: + def lru_cache(maxsize=128): # noqa as it's a fake implementation. + """Does not really need a real a lru_cache, it's just optimization, so + let's just do nothing here. Python 3.2+ will just get better + performances, time to upgrade? + """ + return lambda function: function + from fnmatch import fnmatch from optparse import OptionParser @@ -1410,7 +1420,7 @@ else: """Read the value from stdin.""" return TextIOWrapper(sys.stdin.buffer, errors='ignore').read() -noqa = re.compile(r'# no(?:qa|pep8)\b', re.I).search +noqa = lru_cache(512)(re.compile(r'# no(?:qa|pep8)\b', re.I).search) def expand_indent(line): -- cgit v1.2.1