--- a/eric7/Plugins/CheckerPlugins/CodeStyleChecker/pycodestyle.py Tue Oct 12 19:54:03 2021 +0200 +++ b/eric7/Plugins/CheckerPlugins/CodeStyleChecker/pycodestyle.py Tue Oct 12 21:55:56 2021 +0200 @@ -61,6 +61,7 @@ # Copyright (c) 2011 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> # +import bisect import inspect import keyword import os @@ -69,7 +70,6 @@ import time import tokenize import warnings -import bisect try: from functools import lru_cache @@ -90,7 +90,14 @@ except ImportError: from ConfigParser import RawConfigParser # __IGNORE_WARNING__ -__version__ = '2.7.0-eric' +# this is a performance hack. see https://bugs.python.org/issue43014 +if ( + sys.version_info < (3, 10) and + callable(getattr(tokenize, '_compile', None)) +): # pragma: no cover (<py310) + tokenize._compile = lru_cache()(tokenize._compile) # type: ignore + +__version__ = '2.8.0-eric' DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__,.tox' DEFAULT_IGNORE = 'E121,E123,E126,E226,E24,E704,W503,W504' @@ -149,7 +156,7 @@ 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'[\[({][ \t]|[ \t][\]}),;:](?!=)') 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') @@ -174,7 +181,8 @@ 'while', ))) ) -DUNDER_REGEX = re.compile(r'^__([^\s]+)__ = ') +DUNDER_REGEX = re.compile(r"^__([^\s]+)__(?::\s*[a-zA-Z.0-9_\[\]\"]+)? = ") +BLANK_EXCEPT_REGEX = re.compile(r"except\s*:") _checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} @@ -486,7 +494,7 @@ text = match.group() char = text.strip() found = match.start() - if text == char + ' ': + if text[-1].isspace(): # assert char in '([{' yield found + 1, "E201 whitespace after '%s'", char elif line[found - 1] != ',': @@ -569,7 +577,7 @@ @register_check def indentation(logical_line, previous_logical, indent_char, indent_level, previous_indent_level, - indent_size, indent_size_str): + indent_size): r"""Use indent_size (PEP8 says 4) spaces per indentation level. For really old code that you don't want to mess up, you can continue @@ -593,7 +601,7 @@ if indent_level % indent_size: yield 0, tmpl % ( 1 + c, - "indentation is not a multiple of " + indent_size_str, + "indentation is not a multiple of " + str(indent_size), ) indent_expect = previous_logical.endswith(':') if indent_expect and indent_level <= previous_indent_level: @@ -610,8 +618,7 @@ @register_check def continued_indentation(logical_line, tokens, indent_level, hang_closing, - indent_char, indent_size, indent_size_str, noqa, - verbose): + indent_char, indent_size, noqa, verbose): r"""Continuation lines indentation. Continuation lines should align wrapped elements either vertically @@ -835,14 +842,21 @@ prev_type, prev_text, __, prev_end, __ = tokens[0] for index in range(1, len(tokens)): token_type, text, start, end, __ = tokens[index] - if (token_type == tokenize.OP and + if ( + token_type == tokenize.OP and text in '([' and start != prev_end and (prev_type == tokenize.NAME or prev_text in '}])') and # Syntax "class A (B):" is allowed, but avoid it (index < 2 or tokens[index - 2][1] != 'class') and - # Allow "return (a.foo for a in range(5))" - not keyword.iskeyword(prev_text)): + # Allow "return (a.foo for a in range(5))" + not keyword.iskeyword(prev_text) and + # 'match' and 'case' are only soft keywords + ( + sys.version_info < (3, 9) or + not keyword.issoftkeyword(prev_text) + ) + ): yield prev_end, "E211 whitespace before '%s'", text prev_type = token_type prev_text = text @@ -931,7 +945,9 @@ # ^ # def f(a, b, /): # ^ - prev_text == '/' and text in {',', ')'} or + # f = lambda a, /: + # ^ + prev_text == '/' and text in {',', ')', ':'} or # def f(a, b, /): # ^ prev_text == ')' and text == ':' @@ -962,8 +978,13 @@ # Check if the operator is used as a binary operator # Allow unary operators: -123, -x, +1. # Allow argument unpacking: foo(*args, **kwargs). - if (prev_text in '}])' if prev_type == tokenize.OP - else prev_text not in KEYWORDS): + if prev_type == tokenize.OP and prev_text in '}])' or ( + prev_type != tokenize.OP and + prev_text not in KEYWORDS and ( + sys.version_info < (3, 9) or + not keyword.issoftkeyword(prev_text) + ) + ): need_space = None elif text in WS_OPTIONAL_OPERATORS: need_space = None @@ -1417,8 +1438,10 @@ None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context! """ - match = not noqa and COMPARE_SINGLETON_REGEX.search(logical_line) - if match: + if noqa: + return + + for match in COMPARE_SINGLETON_REGEX.finditer(logical_line): singleton = match.group(1) or match.group(3) same = (match.group(2) == '==') @@ -1492,8 +1515,7 @@ if noqa: return - regex = re.compile(r"except\s*:") - match = regex.match(logical_line) + match = BLANK_EXCEPT_REGEX.match(logical_line) if match: yield match.start(), "E722 do not use bare 'except'" @@ -1994,8 +2016,6 @@ self.multiline = False # in a multiline string? self.hang_closing = options.hang_closing self.indent_size = options.indent_size - self.indent_size_str = ({2: 'two', 4: 'four', 8: 'eight'} - .get(self.indent_size, str(self.indent_size))) self.verbose = options.verbose self.filename = filename # Dictionary where a checker can store its custom state.