Tue, 12 Oct 2021 21:55:56 +0200
Updated pyflakes to 2.4.0 and pycodestyle to 2.8.0.
--- a/docs/changelog Tue Oct 12 19:54:03 2021 +0200 +++ b/docs/changelog Tue Oct 12 21:55:56 2021 +0200 @@ -3,6 +3,8 @@ Version 22.1: - bug fixes - first release of eric7 (i.e. the PyQt6 port of eric6) +- Code Style Checker + -- updated pycodestyle to version 2.8.0 - Debugger -- added code to remember the list of recently used breakpoint conditions in the editor and the breakpoint viewer @@ -28,6 +30,8 @@ -- added a 'Start' context sub menu to the project sources browser - Shell -- added capability to save the contents of the shell window into a file +- Syntax Checker + -- updated pyflakes to version 2.4.0 - Unit Test -- added capability to remember the most recently used test data - Viewmanager
--- 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.
--- a/eric7/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/__init__.py Tue Oct 12 19:54:03 2021 +0200 +++ b/eric7/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/__init__.py Tue Oct 12 21:55:56 2021 +0200 @@ -31,13 +31,19 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -__version__ = '2.3.1' +__version__ = '2.4.0' """ -pyflakes repository date: 2021-03-24. +pyflakes repository date: 2021-10-06. """ """ Changes +2.4.0 (2021-10-06) + +- Remove unused tracing code (``traceTree``) +- Add support for ``match`` statement +- Detect ``typing`` module attributes when imported with ``import ... as ...`` + 2.3.1 (2021-03-24) - Fix regression in 2.3.0: type annotations no longer redefine imports
--- a/eric7/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/checker.py Tue Oct 12 19:54:03 2021 +0200 +++ b/eric7/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/checker.py Tue Oct 12 21:55:56 2021 +0200 @@ -282,7 +282,8 @@ yield field elif isinstance(field, list): for item in field: - yield item + if isinstance(item, ast.AST): + yield item def convert_to_value(item): @@ -698,6 +699,8 @@ return node.id if hasattr(node, 'name'): # an ExceptHandler node return node.name + if hasattr(node, 'rest'): # a MatchMapping node + return node.rest TYPING_MODULES = frozenset(('typing', 'typing_extensions')) @@ -724,6 +727,16 @@ return False + def _module_scope_is_typing(name): + for scope in reversed(scope_stack): + if name in scope: + return ( + isinstance(scope[name], Importation) and + scope[name].fullName in TYPING_MODULES + ) + + return False + return ( ( isinstance(node, ast.Name) and @@ -731,7 +744,7 @@ ) or ( isinstance(node, ast.Attribute) and isinstance(node.value, ast.Name) and - node.value.id in TYPING_MODULES and + _module_scope_is_typing(node.value.id) and is_name_match_fn(node.attr) ) ) @@ -875,7 +888,6 @@ nodeDepth = 0 offset = None - traceTree = False _in_annotation = AnnotationState.NONE _in_deferred = False @@ -1400,8 +1412,6 @@ if self.offset and getattr(node, 'lineno', None) is not None: node.lineno += self.offset[0] node.col_offset += self.offset[1] - if self.traceTree: - print(' ' * self.nodeDepth + node.__class__.__name__) if self.futuresAllowed and not (isinstance(node, ast.ImportFrom) or self.isDocstring(node)): self.futuresAllowed = False @@ -1413,8 +1423,6 @@ handler(node) finally: self.nodeDepth -= 1 - if self.traceTree: - print(' ' * self.nodeDepth + 'end ' + node.__class__.__name__) _getDoctestExamples = doctest.DocTestParser().get_examples @@ -2390,3 +2398,12 @@ left = right self.handleChildren(node) + + MATCH = MATCH_CASE = MATCHCLASS = MATCHOR = MATCHSEQUENCE = handleChildren + MATCHSINGLETON = MATCHVALUE = handleChildren + + def _match_target(self, node): + self.handleNodeStore(node) + self.handleChildren(node) + + MATCHAS = MATCHMAPPING = MATCHSTAR = _match_target