eric7/Plugins/CheckerPlugins/CodeStyleChecker/pycodestyle.py

branch
eric7
changeset 8682
04e80d1aaebf
parent 8312
800c432b34c8
child 8881
54e42bc2437a
equal deleted inserted replaced
8681:6285e8374d99 8682:04e80d1aaebf
59 # - added code for eric integration 59 # - added code for eric integration
60 # 60 #
61 # Copyright (c) 2011 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 61 # Copyright (c) 2011 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
62 # 62 #
63 63
64 import bisect
64 import inspect 65 import inspect
65 import keyword 66 import keyword
66 import os 67 import os
67 import re 68 import re
68 import sys 69 import sys
69 import time 70 import time
70 import tokenize 71 import tokenize
71 import warnings 72 import warnings
72 import bisect
73 73
74 try: 74 try:
75 from functools import lru_cache 75 from functools import lru_cache
76 except ImportError: 76 except ImportError:
77 def lru_cache(maxsize=128): # noqa as it's a fake implementation. 77 def lru_cache(maxsize=128): # noqa as it's a fake implementation.
88 from configparser import RawConfigParser 88 from configparser import RawConfigParser
89 from io import TextIOWrapper 89 from io import TextIOWrapper
90 except ImportError: 90 except ImportError:
91 from ConfigParser import RawConfigParser # __IGNORE_WARNING__ 91 from ConfigParser import RawConfigParser # __IGNORE_WARNING__
92 92
93 __version__ = '2.7.0-eric' 93 # this is a performance hack. see https://bugs.python.org/issue43014
94 if (
95 sys.version_info < (3, 10) and
96 callable(getattr(tokenize, '_compile', None))
97 ): # pragma: no cover (<py310)
98 tokenize._compile = lru_cache()(tokenize._compile) # type: ignore
99
100 __version__ = '2.8.0-eric'
94 101
95 DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__,.tox' 102 DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__,.tox'
96 DEFAULT_IGNORE = 'E121,E123,E126,E226,E24,E704,W503,W504' 103 DEFAULT_IGNORE = 'E121,E123,E126,E226,E24,E704,W503,W504'
97 try: 104 try:
98 if sys.platform == 'win32': 105 if sys.platform == 'win32':
147 INDENT_REGEX = re.compile(r'([ \t]*)') 154 INDENT_REGEX = re.compile(r'([ \t]*)')
148 RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,') 155 RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,')
149 RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,.*,\s*\w+\s*$') 156 RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,.*,\s*\w+\s*$')
150 ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b') 157 ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b')
151 DOCSTRING_REGEX = re.compile(r'u?r?["\']') 158 DOCSTRING_REGEX = re.compile(r'u?r?["\']')
152 EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[\[({] | [\]}),;]| :(?!=)') 159 EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[\[({][ \t]|[ \t][\]}),;:](?!=)')
153 WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)') 160 WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)')
154 COMPARE_SINGLETON_REGEX = re.compile(r'(\bNone|\bFalse|\bTrue)?\s*([=!]=)' 161 COMPARE_SINGLETON_REGEX = re.compile(r'(\bNone|\bFalse|\bTrue)?\s*([=!]=)'
155 r'\s*(?(1)|(None|False|True))\b') 162 r'\s*(?(1)|(None|False|True))\b')
156 COMPARE_NEGATIVE_REGEX = re.compile(r'\b(?<!is\s)(not)\s+[^][)(}{ ]+\s+' 163 COMPARE_NEGATIVE_REGEX = re.compile(r'\b(?<!is\s)(not)\s+[^][)(}{ ]+\s+'
157 r'(in|is)\s') 164 r'(in|is)\s')
172 'with', 'async with', 179 'with', 'async with',
173 'class', 180 'class',
174 'while', 181 'while',
175 ))) 182 )))
176 ) 183 )
177 DUNDER_REGEX = re.compile(r'^__([^\s]+)__ = ') 184 DUNDER_REGEX = re.compile(r"^__([^\s]+)__(?::\s*[a-zA-Z.0-9_\[\]\"]+)? = ")
185 BLANK_EXCEPT_REGEX = re.compile(r"except\s*:")
178 186
179 _checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} 187 _checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}}
180 188
181 189
182 def _get_parameters(function): 190 def _get_parameters(function):
484 line = logical_line 492 line = logical_line
485 for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line): 493 for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line):
486 text = match.group() 494 text = match.group()
487 char = text.strip() 495 char = text.strip()
488 found = match.start() 496 found = match.start()
489 if text == char + ' ': 497 if text[-1].isspace():
490 # assert char in '([{' 498 # assert char in '([{'
491 yield found + 1, "E201 whitespace after '%s'", char 499 yield found + 1, "E201 whitespace after '%s'", char
492 elif line[found - 1] != ',': 500 elif line[found - 1] != ',':
493 code = ('E202' if char in '}])' else 'E203') # if char in ',;:' 501 code = ('E202' if char in '}])' else 'E203') # if char in ',;:'
494 yield found, "%s whitespace before '%s'" % (code, char), char 502 yield found, "%s whitespace before '%s'" % (code, char), char
567 575
568 576
569 @register_check 577 @register_check
570 def indentation(logical_line, previous_logical, indent_char, 578 def indentation(logical_line, previous_logical, indent_char,
571 indent_level, previous_indent_level, 579 indent_level, previous_indent_level,
572 indent_size, indent_size_str): 580 indent_size):
573 r"""Use indent_size (PEP8 says 4) spaces per indentation level. 581 r"""Use indent_size (PEP8 says 4) spaces per indentation level.
574 582
575 For really old code that you don't want to mess up, you can continue 583 For really old code that you don't want to mess up, you can continue
576 to use 8-space tabs. 584 to use 8-space tabs.
577 585
591 c = 0 if logical_line else 3 599 c = 0 if logical_line else 3
592 tmpl = "E11%d %s" if logical_line else "E11%d %s (comment)" 600 tmpl = "E11%d %s" if logical_line else "E11%d %s (comment)"
593 if indent_level % indent_size: 601 if indent_level % indent_size:
594 yield 0, tmpl % ( 602 yield 0, tmpl % (
595 1 + c, 603 1 + c,
596 "indentation is not a multiple of " + indent_size_str, 604 "indentation is not a multiple of " + str(indent_size),
597 ) 605 )
598 indent_expect = previous_logical.endswith(':') 606 indent_expect = previous_logical.endswith(':')
599 if indent_expect and indent_level <= previous_indent_level: 607 if indent_expect and indent_level <= previous_indent_level:
600 yield 0, tmpl % (2 + c, "expected an indented block") 608 yield 0, tmpl % (2 + c, "expected an indented block")
601 elif not indent_expect and indent_level > previous_indent_level: 609 elif not indent_expect and indent_level > previous_indent_level:
608 yield 0, tmpl % (7, 'over-indented') 616 yield 0, tmpl % (7, 'over-indented')
609 617
610 618
611 @register_check 619 @register_check
612 def continued_indentation(logical_line, tokens, indent_level, hang_closing, 620 def continued_indentation(logical_line, tokens, indent_level, hang_closing,
613 indent_char, indent_size, indent_size_str, noqa, 621 indent_char, indent_size, noqa, verbose):
614 verbose):
615 r"""Continuation lines indentation. 622 r"""Continuation lines indentation.
616 623
617 Continuation lines should align wrapped elements either vertically 624 Continuation lines should align wrapped elements either vertically
618 using Python's implicit line joining inside parentheses, brackets 625 using Python's implicit line joining inside parentheses, brackets
619 and braces, or using a hanging indent. 626 and braces, or using a hanging indent.
833 E211: dict['key'] = list [index] 840 E211: dict['key'] = list [index]
834 """ 841 """
835 prev_type, prev_text, __, prev_end, __ = tokens[0] 842 prev_type, prev_text, __, prev_end, __ = tokens[0]
836 for index in range(1, len(tokens)): 843 for index in range(1, len(tokens)):
837 token_type, text, start, end, __ = tokens[index] 844 token_type, text, start, end, __ = tokens[index]
838 if (token_type == tokenize.OP and 845 if (
846 token_type == tokenize.OP and
839 text in '([' and 847 text in '([' and
840 start != prev_end and 848 start != prev_end and
841 (prev_type == tokenize.NAME or prev_text in '}])') and 849 (prev_type == tokenize.NAME or prev_text in '}])') and
842 # Syntax "class A (B):" is allowed, but avoid it 850 # Syntax "class A (B):" is allowed, but avoid it
843 (index < 2 or tokens[index - 2][1] != 'class') and 851 (index < 2 or tokens[index - 2][1] != 'class') and
844 # Allow "return (a.foo for a in range(5))" 852 # Allow "return (a.foo for a in range(5))"
845 not keyword.iskeyword(prev_text)): 853 not keyword.iskeyword(prev_text) and
854 # 'match' and 'case' are only soft keywords
855 (
856 sys.version_info < (3, 9) or
857 not keyword.issoftkeyword(prev_text)
858 )
859 ):
846 yield prev_end, "E211 whitespace before '%s'", text 860 yield prev_end, "E211 whitespace before '%s'", text
847 prev_type = token_type 861 prev_type = token_type
848 prev_text = text 862 prev_text = text
849 prev_end = end 863 prev_end = end
850 864
929 elif ( 943 elif (
930 # def f(a, /, b): 944 # def f(a, /, b):
931 # ^ 945 # ^
932 # def f(a, b, /): 946 # def f(a, b, /):
933 # ^ 947 # ^
934 prev_text == '/' and text in {',', ')'} or 948 # f = lambda a, /:
949 # ^
950 prev_text == '/' and text in {',', ')', ':'} or
935 # def f(a, b, /): 951 # def f(a, b, /):
936 # ^ 952 # ^
937 prev_text == ')' and text == ':' 953 prev_text == ')' and text == ':'
938 ): 954 ):
939 # Tolerate the "/" operator in function definition 955 # Tolerate the "/" operator in function definition
960 need_space = True 976 need_space = True
961 elif text in UNARY_OPERATORS: 977 elif text in UNARY_OPERATORS:
962 # Check if the operator is used as a binary operator 978 # Check if the operator is used as a binary operator
963 # Allow unary operators: -123, -x, +1. 979 # Allow unary operators: -123, -x, +1.
964 # Allow argument unpacking: foo(*args, **kwargs). 980 # Allow argument unpacking: foo(*args, **kwargs).
965 if (prev_text in '}])' if prev_type == tokenize.OP 981 if prev_type == tokenize.OP and prev_text in '}])' or (
966 else prev_text not in KEYWORDS): 982 prev_type != tokenize.OP and
983 prev_text not in KEYWORDS and (
984 sys.version_info < (3, 9) or
985 not keyword.issoftkeyword(prev_text)
986 )
987 ):
967 need_space = None 988 need_space = None
968 elif text in WS_OPTIONAL_OPERATORS: 989 elif text in WS_OPTIONAL_OPERATORS:
969 need_space = None 990 need_space = None
970 991
971 if need_space is None: 992 if need_space is None:
1415 Also, beware of writing if x when you really mean if x is not None 1436 Also, beware of writing if x when you really mean if x is not None
1416 -- e.g. when testing whether a variable or argument that defaults to 1437 -- e.g. when testing whether a variable or argument that defaults to
1417 None was set to some other value. The other value might have a type 1438 None was set to some other value. The other value might have a type
1418 (such as a container) that could be false in a boolean context! 1439 (such as a container) that could be false in a boolean context!
1419 """ 1440 """
1420 match = not noqa and COMPARE_SINGLETON_REGEX.search(logical_line) 1441 if noqa:
1421 if match: 1442 return
1443
1444 for match in COMPARE_SINGLETON_REGEX.finditer(logical_line):
1422 singleton = match.group(1) or match.group(3) 1445 singleton = match.group(1) or match.group(3)
1423 same = (match.group(2) == '==') 1446 same = (match.group(2) == '==')
1424 1447
1425 msg = "'if cond is %s:'" % (('' if same else 'not ') + singleton) 1448 msg = "'if cond is %s:'" % (('' if same else 'not ') + singleton)
1426 if singleton in ('None',): 1449 if singleton in ('None',):
1490 E722: except: 1513 E722: except:
1491 """ 1514 """
1492 if noqa: 1515 if noqa:
1493 return 1516 return
1494 1517
1495 regex = re.compile(r"except\s*:") 1518 match = BLANK_EXCEPT_REGEX.match(logical_line)
1496 match = regex.match(logical_line)
1497 if match: 1519 if match:
1498 yield match.start(), "E722 do not use bare 'except'" 1520 yield match.start(), "E722 do not use bare 'except'"
1499 1521
1500 1522
1501 @register_check 1523 @register_check
1992 self.max_doc_length = options.max_doc_length 2014 self.max_doc_length = options.max_doc_length
1993 self.indent_size = options.indent_size 2015 self.indent_size = options.indent_size
1994 self.multiline = False # in a multiline string? 2016 self.multiline = False # in a multiline string?
1995 self.hang_closing = options.hang_closing 2017 self.hang_closing = options.hang_closing
1996 self.indent_size = options.indent_size 2018 self.indent_size = options.indent_size
1997 self.indent_size_str = ({2: 'two', 4: 'four', 8: 'eight'}
1998 .get(self.indent_size, str(self.indent_size)))
1999 self.verbose = options.verbose 2019 self.verbose = options.verbose
2000 self.filename = filename 2020 self.filename = filename
2001 # Dictionary where a checker can store its custom state. 2021 # Dictionary where a checker can store its custom state.
2002 self._checker_states = {} 2022 self._checker_states = {}
2003 if filename is None: 2023 if filename is None:

eric ide

mercurial