eric7/Plugins/CheckerPlugins/CodeStyleChecker/pycodestyle.py

branch
eric7
changeset 8682
04e80d1aaebf
parent 8312
800c432b34c8
child 8881
54e42bc2437a
diff -r 6285e8374d99 -r 04e80d1aaebf eric7/Plugins/CheckerPlugins/CodeStyleChecker/pycodestyle.py
--- 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.

eric ide

mercurial